381 lines
11 KiB
C++
381 lines
11 KiB
C++
//---------------------------------------------------------------------------------------
|
|
//
|
|
// Simple Windows DirectShow Example Code
|
|
//
|
|
// Jim Shaw 12/3/2004
|
|
//
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Always include windows.h for windows programs
|
|
//---------------------------------------------------------------------------------------
|
|
#define _WIN32_DCOM
|
|
#include <windows.h>
|
|
|
|
#ifdef __WATCOMC__
|
|
#define _WINGDI_
|
|
#define _IAMFilterGraphCallback_
|
|
#endif
|
|
|
|
#include <dshow.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
|
|
#include <assert.h>
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Screen dimensions
|
|
//---------------------------------------------------------------------------------------
|
|
#define SCREEN_X 640
|
|
#define SCREEN_Y 480
|
|
#define IN_OWN_WINDOW 1
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Variable to hold the address of the Windows window
|
|
//---------------------------------------------------------------------------------------
|
|
HWND game_window;
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Name of our window class. every class has a name. every window is one of a class.
|
|
// this can be pretty much anything
|
|
//---------------------------------------------------------------------------------------
|
|
char game_class[]="DSCPPWindow";
|
|
static int quit = 0;
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Function prototypes
|
|
//---------------------------------------------------------------------------------------
|
|
void draw(void);
|
|
void flip(void);
|
|
int init_dshow(wchar_t *, int);
|
|
void shutdown_dshow(void);
|
|
int handle_dshow_event(void);
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// DirectShow Variables
|
|
//---------------------------------------------------------------------------------------
|
|
IGraphBuilder *pGB=NULL;
|
|
IMediaControl *pMC=NULL;
|
|
IMediaEventEx *pME=NULL;
|
|
IMediaSeeking *pMS=NULL;
|
|
IVMRWindowlessControl *pWC=NULL;
|
|
|
|
#define WM_GRAPHNOTIFY0 (WM_USER+13)
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Every window has a WindowProc which handles messages Windows sends to them. These
|
|
// are things like key presses, mouse clicks, and thousands of other things you might
|
|
// be interested in.
|
|
//---------------------------------------------------------------------------------------
|
|
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
//this message gets sent to the window when you click the close box on it
|
|
case WM_DESTROY:
|
|
//tell Windows to close the app
|
|
PostQuitMessage(0);
|
|
quit = 1;
|
|
break;
|
|
|
|
//this message gets sent when you press a key
|
|
case WM_KEYDOWN:
|
|
switch (wParam)
|
|
{
|
|
//pressed Escape key?
|
|
case VK_ESCAPE:
|
|
//tell Windows to close the app
|
|
PostQuitMessage(0);
|
|
quit = 1;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_GRAPHNOTIFY0:
|
|
if (handle_dshow_event())
|
|
{
|
|
PostQuitMessage(0);
|
|
quit = 1;
|
|
}
|
|
break;
|
|
|
|
//any message you don't process gets passed to this function inside Windows.
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
//any message you do process, return 0 (unless the message documentation says otherwise)
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Convert char string to wchar_t string
|
|
//---------------------------------------------------------------------------------------
|
|
static wchar_t c[_MAX_PATH];
|
|
static wchar_t *ctowc(char *p)
|
|
{
|
|
#ifdef __WATCOMC__
|
|
swprintf(c, _MAX_PATH, L"%hs", p);
|
|
#else
|
|
swprintf(c, L"%S", p);
|
|
#endif
|
|
return c;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// WinMain
|
|
//---------------------------------------------------------------------------------------
|
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
|
{
|
|
WNDCLASSEX clas;
|
|
MSG msg;
|
|
int style;
|
|
RECT rect;
|
|
|
|
//need COM for DirectShow
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
|
|
//Here we create the Class we named above
|
|
clas.cbSize = sizeof(WNDCLASSEX);
|
|
clas.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
clas.lpfnWndProc = WindowProc;//<- tell it where the WindowProc is
|
|
clas.cbClsExtra = 0;
|
|
clas.cbWndExtra = 0;
|
|
clas.hInstance = hInstance;
|
|
clas.hIcon = NULL;
|
|
clas.hCursor = NULL;
|
|
clas.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);//<- background colour of window
|
|
clas.lpszMenuName = NULL;
|
|
clas.lpszClassName = game_class;//<- the class name
|
|
clas.hIconSm = 0;
|
|
//do it!
|
|
RegisterClassEx(&clas);
|
|
|
|
//style of the window - what boxes do we need (close minimised etc)
|
|
style = WS_CAPTION|WS_SYSMENU|WS_MAXIMIZEBOX|WS_MINIMIZEBOX;
|
|
//create the window
|
|
game_window = CreateWindowEx(0, game_class, "DirectShow", style, CW_USEDEFAULT, CW_USEDEFAULT, 1,1, NULL, NULL, hInstance, 0);
|
|
|
|
//adjust the window size so that a SCREEN_X x SCREEN_Y window will fit inside its frame
|
|
rect.left = rect.top = 0;
|
|
rect.right = SCREEN_X;
|
|
rect.bottom = SCREEN_Y;
|
|
AdjustWindowRectEx(&rect, style , FALSE, 0);
|
|
SetWindowPos(game_window, NULL, 0,0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE|SWP_NOZORDER);
|
|
|
|
//show the window on the desktop
|
|
ShowWindow(game_window, nCmdShow);
|
|
|
|
char filename[_MAX_PATH];
|
|
OPENFILENAME ofn;
|
|
BOOL res;
|
|
|
|
filename[0] = '\0';
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = NULL;
|
|
ofn.hInstance = NULL;
|
|
ofn.lpstrFilter = "AVI Files\0*.avi\0";
|
|
ofn.lpstrCustomFilter = NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 0;
|
|
ofn.lpstrFile = filename;
|
|
ofn.nMaxFile = _MAX_PATH;
|
|
ofn.lpstrFileTitle = NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = NULL;
|
|
ofn.lpstrTitle = "Select Video File";
|
|
ofn.Flags = OFN_FILEMUSTEXIST|OFN_EXPLORER;
|
|
ofn.nFileOffset=0;
|
|
ofn.nFileExtension=0;
|
|
ofn.lpstrDefExt="bin";
|
|
ofn.lCustData=0;
|
|
ofn.lpfnHook=NULL;
|
|
ofn.lpTemplateName="";
|
|
|
|
res = GetOpenFileName(&ofn);
|
|
if (res == 0)
|
|
return 0;
|
|
|
|
if (init_dshow(ctowc(filename), IN_OWN_WINDOW)==0)
|
|
{
|
|
MessageBox(NULL, "A problem occurred creating the DirectShow FilterGraph.\nCheck your video CODECs and "
|
|
"DirectX version.\nThis demo requires DirectX9.0 or better.", "DirectX Initialisation Error", MB_ICONWARNING|MB_OK);
|
|
shutdown_dshow();
|
|
return 0;
|
|
}
|
|
|
|
//message processing loop
|
|
//all Windows programs have one of these. It receives the messages Windows sends to the program and
|
|
//passes them to the WindowProc in the Class we registered for the window.
|
|
quit = 0;
|
|
do
|
|
{
|
|
//Are there any messages waiting?
|
|
while (PeekMessage(&msg, game_window, 0, 0, PM_NOREMOVE))
|
|
{
|
|
//yes! read it.
|
|
if (GetMessage(&msg, game_window, 0,0) < 0)
|
|
break;
|
|
|
|
//pass the message to WindowProc
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
} while (!quit);
|
|
|
|
shutdown_dshow();
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Initialise all the DirectShow structures we need
|
|
//---------------------------------------------------------------------------------------
|
|
int init_dshow(wchar_t *FileName, int render_in_own_window)
|
|
{
|
|
HRESULT err;
|
|
FILTER_STATE state = State_Stopped;
|
|
|
|
// Create a new graph
|
|
err = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGB);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
// Get access to the video controls
|
|
err = pGB->QueryInterface(IID_IMediaControl, (void **)&pMC);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
err = pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
err = pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
if (render_in_own_window)
|
|
{
|
|
IBaseFilter *pVMR;
|
|
IVMRFilterConfig *pFC;
|
|
long lWidth, lHeight;
|
|
RECT rcSrc, rcDest;
|
|
|
|
err = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVMR);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
// Add the VMR to the filter graph.
|
|
err = pGB->AddFilter(pVMR, L"VMR");
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
// Set the rendering mode.
|
|
err = pVMR->QueryInterface(IID_IVMRFilterConfig, (void**)&pFC);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
err = pFC->SetRenderingMode(VMRMode_Windowless);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
pFC->Release();
|
|
|
|
// Set the window.
|
|
err = pVMR->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWC);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
err = pWC->SetVideoClippingWindow(game_window);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
pVMR->Release();
|
|
|
|
// Find the native video size.
|
|
err = pWC->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
|
|
// Set the source rectangle.
|
|
SetRect(&rcSrc, 0, 0, lWidth/2, lHeight/2);
|
|
|
|
// Get the window client area.
|
|
GetClientRect(game_window, &rcDest);
|
|
// Set the destination rectangle.
|
|
SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom);
|
|
|
|
// Set the video position.
|
|
err = pWC->SetVideoPosition(&rcSrc, &rcDest);
|
|
}
|
|
|
|
// Add the source file
|
|
err = pGB->RenderFile(FileName, NULL);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
// Have the graph signal event via window callbacks for performance
|
|
err = pME->SetNotifyWindow((OAHWND)game_window, WM_GRAPHNOTIFY0, 0);
|
|
if (err != S_OK)
|
|
return 0;
|
|
|
|
err = pMS->SetTimeFormat(&TIME_FORMAT_FRAME);
|
|
if(err != S_OK)
|
|
{
|
|
err = pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
|
|
}
|
|
|
|
err = pMC->Run();
|
|
do
|
|
{
|
|
err = pMC->GetState(0, (long *)&state);
|
|
} while (state != State_Running);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Shutdown DirectShow
|
|
//---------------------------------------------------------------------------------------
|
|
void shutdown_dshow(void)
|
|
{
|
|
if (pWC) pWC->Release();
|
|
if (pMS) pMS->Release();
|
|
if (pME) pME->Release();
|
|
if (pMC) pMC->Release();
|
|
if (pGB) pGB->Release();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Handle DirectShow Event
|
|
//---------------------------------------------------------------------------------------
|
|
int handle_dshow_event(void)
|
|
{
|
|
LONG evCode, evParam1, evParam2;
|
|
int stopping = 0;
|
|
|
|
// Make sure that we don't access the MediaEventEx interface after it has already been released.
|
|
if (!pME)
|
|
return S_OK;
|
|
|
|
// Process all queued events
|
|
while (pME->GetEvent(&evCode, &evParam1, &evParam2, 0) == S_OK)
|
|
{
|
|
switch (evCode)
|
|
{
|
|
case EC_COMPLETE:
|
|
stopping = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Free memory associated with callback, since we're not using it
|
|
pME->FreeEventParams(evCode, evParam1, evParam2);
|
|
}
|
|
|
|
return stopping;
|
|
}
|