Introduction
This project will show a case to developers on the use of DirectShow to take screen shots of a video playback and dump the image to a folder. The function "InitWindowlessVMR
" is picked up from MSDN (I did not see any reason to rewrite it).
Background
All of us have faced problems to take screen shots of video being played back using WMP (in Windows XP), this might help. The reason is that video play back uses an overlay method. (It bypasses the Windows standard rendering method (GDI) which is why print screen does not work.)
Using the Code
Please use the code along with the document. We start by creating the object: IGraphBuilder
, we use CComPtr
, it saves us the trouble of releasing the pointer (and does other few smart things for us).
CComPtr<IGraphBuilder> pGraph=NULL;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
We then require to create a window using CreateWindow
(no explanation is required for this). We must then set the object to windowless control and target the video rendering to it. This is done by calling "InitWindowlessVMR
" and set the clipping window to the handle of the window we have just created by calling:
pWc->SetVideoClippingWindow(hwndApp);
We must then select the video file for which we are going to take screen shots:
pGraph->RenderFile(L"MyMovie.avi", NULL);
We will also require objects to control the playback (using IMediaControl
). You may even want to use "IMediaSeeking
" to jump to the frame for which screen shot is required (please refer to code).
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaSeeking, (void**)&pSeek);
After this, we start the movie play back using the pControl
interface (of the type IMediaControl
).
pControl->Run();
Now comes the part to take snap shots
After adjusting the frame number for which the screen shot is being taken using pSeek
(of the type IMediaSeeking
), we use the windowless control interface (we get this object by calling InitWindowlessVMR
) to get the current image.
pWc->GetCurrentImage(&pb)
BITMAPINFOHEADER *pBMIH = (BITMAPINFOHEADER*) pb;
DWORD d=pBMIH->biSize+pBMIH->biSizeImage;
We then make use of DirectX9 to convert this image to a BMP or any other format supported by "D3DXSaveTextureToFile
". DX9 is fast and a very efficient way of doing graphics manipulation (though my code does not optimize by creating DX9 objects outside the function scope so as to minimize DX9 creation calls).
Please refer to the attached code.
Notice that I create a texture object on the fly and then save it to the file.
hr=D3DXCreateTextureFromFileInMemory(pd3dDevice,pb,d,&pTexture_Movie);
D3DXSaveTextureToFile(_bstr_t("asif.bmp"),D3DXIFF_BMP ,pTexture_Movie,0);
Points of Interest
With this, we learn a bit of DirectShow and a tad bit of DirectX9. We can do the same using DX10, DX11 based interfaces (but then that would make it a Windows Vista/7 code only).
You can try something even funkier by using movie textures in your game engines (for those who write DirectX9/10/11 code).
History
- 10th August, 2010: Initial post