i
i
i
i
i
i
i
i
15.3. A Movie Player 395
In any DirectShow application, there are a couple of essential function
calls required so that it can use the COM:
CoInitialize(NULL); // tell Windows we use COM
...
...
CoUninitialize(); // tell Windows we have finished with COM
The DirectShow FilterGraph is created when a specific movie is opened
for presentation. We need pointers to the graph builder (IGraphBuilder
*
pGB;
) interface and the media control interface (IMediaControl
*
pMC;),
which allows us to play the movie, stop it or pause it. All the actions in the
program are tied to menu commands calling the functions in Listing 15.2.
The four functions in this listing do the following:
OpenClip(): Obtain the video filename using a FileOpen dialog.
CloseClip(): Stop the running graph and delete it.
CloseInterfaces(): Destroy the graph by releasing all the interfaces
it uses.
PlayMovieInWindow():Thisisthekey step in playing the movie. It
simply builds the FilterGraph object gets pointers to the graphs graph
builder interface and its media control interface. The graph builder
interface’s
RenderFile() method does all the work of building the
necessary graph using intelligent connect. Finally, the run() method
from the media control interface plays the movie.
Given that this short program plays video files (with and without a sound
track) and can decode the video stream using any codec installed on the com-
puter, the 100 or so lines of code it takes to write the whole program serve
to illustrate the power of DirectShow. However , the behavior of the program
is still a little untidy. For one thing, the movie plays in a separate pop-up
window. This is because the intelligent connect mechanism has inserted one
of DirectShows internal video renderer filters, and it will create a window of
its own in which to present the video. This may be acceptable, but we can do
better, because DirectShow offers a way for an application program to tell the
renderer filter
3
to draw its output in one of its own windows.
3
One can of course write one’s own renderer filter . We shall do this later and also in
Chapter 16 when we write some programs for stereoscopic work.
i
i
i
i
i
i
i
i
396 15. Using Multimedia in VR
void OpenClip(void){
HRESULT hr;
if (! GetClipFileName(gFileName))return; // get the movie filename
hr = PlayMovieInWindow(g_szFileName); // build the graph and play it
if (FAILED(hr)) CloseClip(); // FAILED is DS macro
}
void CloseClip(void){
HRESULT hr;
if(pMC) hr = pMC->Stop(); // Stop media playback
CloseInterfaces(); // Free DirectShow interfaces
}
HRESULT PlayMovieInWindow(LPTSTR szFile){
USES_CONVERSION; // 8 bit char to wide char macros are used
WCHAR wFile[MAX_PATH];
HRESULT hr;
if (!szFile) return E_POINTER;
// Convert filename to wide character string
wcsncpy(wFile, A2W(szFile), NUMELMS(wFile)-1);
// Get the interface for DirectShow’s GraphBuilder
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void
**
)&pGB);
// Have the graph builder construct the appropriate graph automatically
hr=pGB->RenderFile(wFile, NULL);
// QueryInterface for DirectShow interfaces
hr=pGB->QueryInterface(IID_IMediaControl, (void
**
)&pMC);
// Complete window initialization
// Run the graph to play the media file
hr=pMC->Run();
return hr;
}
void CloseInterfaces(void){ // release the interfaces
SAFE_RELEASE(pMC); // DS macro which checks
SAFE_RELEASE(pGB); // pointer before freeing.
}
Listing 15.2. These functions build and execute the movie player graph.
This is done by using two interfaces. The IVideoWindow interface tells
DirectShows renderer filter where and how to draw its output. We will
also need to use the
IMediaEventEx interface, since it tells DirectShow to
route notification of events (such as the graph stopping when the movie is
over) through the applications message handler functions. When events pass
through a message handler routine, we can act on them to do such things as
i
i
i
i
i
i
i
i
15.3. A Movie Player 397
.. // add these lines of code to PlayMovieInWindow(..)
RECT rect;
GetClientRect(ghApp, &rect);
// get an interface for passing commands to the video output window
pGB->QueryInterface(IID_IVideoWindow, (void
**
)&pVW);
// tell DirectShow to render into main window’s client area
pVW->put_Owner((OAHWND)ghApp);
pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom);
// Have the graph signal events
pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));
.. //
..//add these lines to the code for CloseInterfaces(..)
hr = pVW->put_Visible(OAFALSE); // Relinquish ownership (IMPORTANT!)
hr = pVW->put_Owner(NULL); // no wnser
// Disable event callbacks
hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
SAFE_RELEASE(pME); // Release DirectShow interfaces
SAFE_RELEASE(pVW); // SAFE_RELEASE is a DS macro
..//
..// add these functions to the code
void MoveVideoWindow(void){ // new function to handle size changes
// Track the movement of the window being drawn into and resize if needed
if(pVW){
RECT client;
GetClientRect(ghApp, &client);
hr = pVW->SetWindowPosition(client.left,client.top,
client.right,client.bottom);
}
}
HRESULT HandleGraphEvent(void){ // new functions to handle graph events
LONG evCode, evParam1, evParam2;
HRESULT hr=S_OK;
if (!pME) return S_OK; // Otherwise process all queued events
while(SUCCEEDED(pME->GetEvent(&evCode,&evParam1,&evParam2,0))){
//
// handle any event codes here !!!!!!!!!!!!!!!
//
// Free memory associated with callback - required behaviour
hr = pME->FreeEventParams(evCode, evParam1, evParam2);
}
return hr;
}
Listing 15.3. Changes required to make the renderer filter play the movie within the
applications client area.
i
i
i
i
i
i
i
i
398 15. Using Multimedia in VR
rewind the movie back to the start or load another one. To put this function-
ality into the application, the code in Listings 15.1 and 15.4 is modified by
adding some instructions, global variables and extra functions as follows.
Two extra interface pointers are required:
IVideoWindow
*
pVW = NULL;
IMediaEventEx
*
pME = NULL;
In the WndMainProc() message handler, we intercept graph event mes-
sages and act upon a change of client a rea size so that the video window always
fits inside the client area of the container window. (We are really asking an-
other thread, the one running the renderer filter, to make sure it draws inside
the applications window.) This is done with the following code fragment:
case WM_GRAPHNOTIFY:
HandleGraphEvent(); break;
case WM_SIZE:
if ((hWnd == ghApp))MoveVideoWindow(); break;
We tell DirectShow to route all messages through our applications win-
dow with the pVW->NotifyOwnerMessage(...) method of the video win-
dow interface. Listing 15.3 shows other modifications we need to make to
render the output into the client area of the main window.
Despite the appearance that the video output is now in the main
windows client area, it is still being drawn by a system renderer.
In most circumstances, this is irrelevant, but if you try to cap-
ture a frame from the movie by reading pixels from the win-
dows client area, all you will get is a blank rectangle. Similarly,
if you try and copy the full screen to the clipboard then the part
of the window where the movie is rendered will again appear
blank. The reason for this is that the DirectShow renderer by-
passes the GDI drawing functions and communicates directly
with the graphics hardware so as to maximize performance.
i
i
i
i
i
i
i
i
15.3. A Movie Player 399
15.3.1 A Final Touch: Drag and Drop
As a final touch for this application, we want to show you how to make your
movie player accept files that have been dragged and dropped into the win-
dow, from Windows Explorer, for example. Drag and drop is really standard
stuff but can be hard to find out how to do it from the Windows API docu-
#include <windows.h> // necessary header files
#include <tchar.h>
#include <shlobj.h>
#include <oleidl.h>
// our class calls back to this function in the program
extern void OpenDraggedClip(char
*
);
class CDropTarget : public IDropTarget{ //
public:
STDMETHODIMP QueryInterface(REFIID riid, void
**
ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP DragEnter(IDataObject
*
, DWORD, POINTL, DWORD
*
);
STDMETHODIMP DragLeave(void);
STDMETHODIMP DragOver(DWORD, POINTL, DWORD
*
);
STDMETHODIMP Drop(IDataObject
*
,DWORD, POINTL, DWORD
*
);
CDropTarget(HWND );
˜CDropTarget();
private:
LONG m_cRef;
int m_int1;
};
CDropTarget::CDropTarget(HWND hWnd){ // constructor
if(RegisterDragDrop(hWnd,this) != S_OK)MessageBox(NULL,
"D&D setup fail",NULL,MB_OK);
}
CDropTarget::˜CDropTarget(){;} // destructor
static CDropTarget
*
pT=NULL; // This is our drag and drop object
void SetUpDragAndDrop(HWND hWnd){ // Call this method to initialize D&D
pT = new CDropTarget(hWnd);
return;
}
void CloseDragAndDrop(void){ // call at end of application
if(pT)delete pT;
}
Listing 15.4. This class implements drag and drop.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.254.28