I finally got the video player to work! It took a while but I thought I would post the code so if other people want to use Qt to make a Windows video player with frame stepping capabilities they could see what I have done.
These are the pointers in my .h file.
// DirectShow interfaces
IGraphBuilder *pGraphBuilder;
IMediaControl *pMediaControl;
IMediaEventEx *pMediaEvent;
IVideoWindow *pVideoWindow;
IBasicAudio *pBasicAudio;
IBasicVideo *pBasicVideo;
IMediaSeeking *pMediaSeeking;
IMediaPosition *pMediaPosition;
IVideoFrameStep *pFrameStep;
IBaseFilter *pBaseFilter;
IVMRWindowlessControl* pWindowlessControl;
// DirectShow interfaces
IGraphBuilder *pGraphBuilder;
IMediaControl *pMediaControl;
IMediaEventEx *pMediaEvent;
IVideoWindow *pVideoWindow;
IBasicAudio *pBasicAudio;
IBasicVideo *pBasicVideo;
IMediaSeeking *pMediaSeeking;
IMediaPosition *pMediaPosition;
IVideoFrameStep *pFrameStep;
IBaseFilter *pBaseFilter;
IVMRWindowlessControl* pWindowlessControl;
To copy to clipboard, switch view to plain text mode
The openFile function:
void VideoViewerWindow::openFile( )
{
QString fileFilter
= "Video files (*.mpg *.avi *.mpeg);;All files (*.*)";
mDirectoryStr, fileFilter );
if ( fileName.isEmpty( ) == false )
{
mDirectoryStr = info.absolutePath( );
emit updateFilename( fileName );
if( !createVideo( fileName ) )
{
//ERROR CREATING VIDEO
"There was an error loading the video" );
}
}
}
void VideoViewerWindow::openFile( )
{
QString fileFilter = "Video files (*.mpg *.avi *.mpeg);;All files (*.*)";
QString fileName = QFileDialog::getOpenFileName( this, "Select video file to view",
mDirectoryStr, fileFilter );
if ( fileName.isEmpty( ) == false )
{
QFileInfo info( fileName );
mDirectoryStr = info.absolutePath( );
emit updateFilename( fileName );
if( !createVideo( fileName ) )
{
//ERROR CREATING VIDEO
QMessageBox::warning( this, "Video Creation Error",
"There was an error loading the video" );
}
}
}
To copy to clipboard, switch view to plain text mode
The createVideo function:
bool VideoViewerWindow
::createVideo( QString filename
) {
HRESULT hr;
int length = filename.size( ) + 1;
WCHAR* wfile = new WCHAR[length];
int i = 0;
for( i = 0 ; i < filename.size( ) ; i++ )
{
wfile[i] = filename[i].toAscii( );
}
wfile[i] = '\0';
hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
if( FAILED( hr ) )
{
return false;
}
hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **) &pGraphBuilder );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaControl, (void**) &pMediaControl );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaEvent, (void **) &pMediaEvent);
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaSeeking, (void **) &pMediaSeeking );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaPosition, (void **) &pMediaPosition);
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IVideoWindow, (void **) &pVideoWindow );
if( FAILED( hr ) )
{
return false;
}
if( pVideoWindow )
{
pVideoWindow->put_Owner( (OAHWND) mpVideoPlayer->winId( ) );
pVideoWindow->put_WindowStyle( WS_CHILD | WS_CLIPCHILDREN );
}
hr = InitWindowlessVMR( (HWND) mpVideoPlayer->winId( ), pGraphBuilder, &pWindowlessControl );
if( SUCCEEDED( hr ) )
{
// Build the graph. For example:
pGraphBuilder->RenderFile( wfile, NULL );
}
// Find the native video size.
long lWidth, lHeight;
hr = pWindowlessControl->GetNativeVideoSize( &lWidth, &lHeight, NULL, NULL );
if ( SUCCEEDED( hr ) )
{
RECT rcSrc, rcDest;
// Set the source rectangle.
SetRect( &rcSrc, 0, 0, lWidth, lHeight );
// Get the window client area.
GetClientRect( (HWND) mpVideoPlayer->winId( ), &rcDest );
// Set the destination rectangle.
SetRect(&rcDest, 1, 1, 558, 349);
// Set the video position.
hr = pWindowlessControl->SetVideoPosition( &rcSrc, &rcDest );
// Add this code to make the video initially show up once opening the file
PAINTSTRUCT ps;
HDC hdc;
RECT rcClient;
GetClientRect( (HWND) mpVideoPlayer->winId( ), &rcClient );
hdc = BeginPaint( (HWND) mpVideoPlayer->winId( ), &ps );
if( pWindowlessControl != NULL )
{
// Find the region where the application can paint by subtracting
// the video destination rectangle from the client area.
// (Assume that g_rcDest was calculated previously.)
HRGN rgnClient = CreateRectRgnIndirect( &rcClient );
HRGN rgnVideo = CreateRectRgnIndirect( &rcDest );
CombineRgn( rgnClient, rgnClient, rgnVideo, RGN_DIFF );
// Paint on window.
HBRUSH hbr = GetSysColorBrush( COLOR_BTNFACE );
FillRgn( hdc, rgnClient, hbr );
// Clean up.
DeleteObject( hbr );
DeleteObject( rgnClient );
DeleteObject( rgnVideo );
// Request the VMR to paint the video.
HRESULT hr = pWindowlessControl->RepaintVideo( (HWND) mpVideoPlayer->winId( ), hdc );
}
}
getFrameStepInterface( );
pMediaControl->Run( );
return true;
}
bool VideoViewerWindow::createVideo( QString filename )
{
HRESULT hr;
int length = filename.size( ) + 1;
WCHAR* wfile = new WCHAR[length];
int i = 0;
for( i = 0 ; i < filename.size( ) ; i++ )
{
wfile[i] = filename[i].toAscii( );
}
wfile[i] = '\0';
hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
if( FAILED( hr ) )
{
return false;
}
hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **) &pGraphBuilder );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaControl, (void**) &pMediaControl );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaEvent, (void **) &pMediaEvent);
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaSeeking, (void **) &pMediaSeeking );
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IMediaPosition, (void **) &pMediaPosition);
if( FAILED( hr ) )
{
return false;
}
hr = pGraphBuilder->QueryInterface( IID_IVideoWindow, (void **) &pVideoWindow );
if( FAILED( hr ) )
{
return false;
}
if( pVideoWindow )
{
pVideoWindow->put_Owner( (OAHWND) mpVideoPlayer->winId( ) );
pVideoWindow->put_WindowStyle( WS_CHILD | WS_CLIPCHILDREN );
}
hr = InitWindowlessVMR( (HWND) mpVideoPlayer->winId( ), pGraphBuilder, &pWindowlessControl );
if( SUCCEEDED( hr ) )
{
// Build the graph. For example:
pGraphBuilder->RenderFile( wfile, NULL );
}
// Find the native video size.
long lWidth, lHeight;
hr = pWindowlessControl->GetNativeVideoSize( &lWidth, &lHeight, NULL, NULL );
if ( SUCCEEDED( hr ) )
{
RECT rcSrc, rcDest;
// Set the source rectangle.
SetRect( &rcSrc, 0, 0, lWidth, lHeight );
// Get the window client area.
GetClientRect( (HWND) mpVideoPlayer->winId( ), &rcDest );
// Set the destination rectangle.
SetRect(&rcDest, 1, 1, 558, 349);
// Set the video position.
hr = pWindowlessControl->SetVideoPosition( &rcSrc, &rcDest );
// Add this code to make the video initially show up once opening the file
PAINTSTRUCT ps;
HDC hdc;
RECT rcClient;
GetClientRect( (HWND) mpVideoPlayer->winId( ), &rcClient );
hdc = BeginPaint( (HWND) mpVideoPlayer->winId( ), &ps );
if( pWindowlessControl != NULL )
{
// Find the region where the application can paint by subtracting
// the video destination rectangle from the client area.
// (Assume that g_rcDest was calculated previously.)
HRGN rgnClient = CreateRectRgnIndirect( &rcClient );
HRGN rgnVideo = CreateRectRgnIndirect( &rcDest );
CombineRgn( rgnClient, rgnClient, rgnVideo, RGN_DIFF );
// Paint on window.
HBRUSH hbr = GetSysColorBrush( COLOR_BTNFACE );
FillRgn( hdc, rgnClient, hbr );
// Clean up.
DeleteObject( hbr );
DeleteObject( rgnClient );
DeleteObject( rgnVideo );
// Request the VMR to paint the video.
HRESULT hr = pWindowlessControl->RepaintVideo( (HWND) mpVideoPlayer->winId( ), hdc );
}
}
getFrameStepInterface( );
pMediaControl->Run( );
return true;
}
To copy to clipboard, switch view to plain text mode
The initWindowlessVMR function:
// Initializes the video window for windowless viewing so it will appear inside
// our Qt GUI instead of its own separate window.
HRESULT InitWindowlessVMR(
HWND hwndApp, // Window to hold the video.
IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager.
IVMRWindowlessControl** ppWc // Receives a pointer to the VMR.
)
{
if (!pGraph || !ppWc) return E_POINTER;
IBaseFilter* pVmr = NULL;
IVMRWindowlessControl* pWc = NULL;
// Create the VMR.
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (FAILED(hr))
{
return hr;
}
// Add the VMR to the filter graph.
hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer");
if (FAILED(hr))
{
pVmr->Release();
return hr;
}
// Set the rendering mode.
IVMRFilterConfig* pConfig;
hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);
if (SUCCEEDED(hr))
{
hr = pConfig->SetRenderingMode(VMRMode_Windowless);
pConfig->Release();
}
if (SUCCEEDED(hr))
{
// Set the window.
hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);
if( SUCCEEDED(hr))
{
hr = pWc->SetVideoClippingWindow(hwndApp);
if (SUCCEEDED(hr))
{
*ppWc = pWc; // Return this as an AddRef'd pointer.
}
else
{
// An error occurred, so release the interface.
pWc->Release();
}
}
}
pVmr->Release();
return hr;
}
// Initializes the video window for windowless viewing so it will appear inside
// our Qt GUI instead of its own separate window.
HRESULT InitWindowlessVMR(
HWND hwndApp, // Window to hold the video.
IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager.
IVMRWindowlessControl** ppWc // Receives a pointer to the VMR.
)
{
if (!pGraph || !ppWc) return E_POINTER;
IBaseFilter* pVmr = NULL;
IVMRWindowlessControl* pWc = NULL;
// Create the VMR.
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (FAILED(hr))
{
return hr;
}
// Add the VMR to the filter graph.
hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer");
if (FAILED(hr))
{
pVmr->Release();
return hr;
}
// Set the rendering mode.
IVMRFilterConfig* pConfig;
hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);
if (SUCCEEDED(hr))
{
hr = pConfig->SetRenderingMode(VMRMode_Windowless);
pConfig->Release();
}
if (SUCCEEDED(hr))
{
// Set the window.
hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);
if( SUCCEEDED(hr))
{
hr = pWc->SetVideoClippingWindow(hwndApp);
if (SUCCEEDED(hr))
{
*ppWc = pWc; // Return this as an AddRef'd pointer.
}
else
{
// An error occurred, so release the interface.
pWc->Release();
}
}
}
pVmr->Release();
return hr;
}
To copy to clipboard, switch view to plain text mode
And the getFrameStepInterface function:
//
// Some video renderers support stepping media frame by frame with the
// IVideoFrameStep interface. See the interface documentation for more
// details on frame stepping.
//
bool VideoViewerWindow::getFrameStepInterface( )
{
HRESULT hr;
IVideoFrameStep *pFrameStepTest = NULL;
// Get the frame step interface, if supported
hr = pGraphBuilder->QueryInterface( __uuidof(IVideoFrameStep),
(void **) &pFrameStepTest );
if ( FAILED( hr ) )
return false;
// Check if this decoder can step
hr = pFrameStepTest->CanStep( 0L, NULL );
if ( hr == S_OK )
{
pFrameStep = pFrameStepTest; // Save interface to global variable for later use
return true;
}
else
{
pFrameStepTest->Release( );
return false;
}
}
//
// Some video renderers support stepping media frame by frame with the
// IVideoFrameStep interface. See the interface documentation for more
// details on frame stepping.
//
bool VideoViewerWindow::getFrameStepInterface( )
{
HRESULT hr;
IVideoFrameStep *pFrameStepTest = NULL;
// Get the frame step interface, if supported
hr = pGraphBuilder->QueryInterface( __uuidof(IVideoFrameStep),
(void **) &pFrameStepTest );
if ( FAILED( hr ) )
return false;
// Check if this decoder can step
hr = pFrameStepTest->CanStep( 0L, NULL );
if ( hr == S_OK )
{
pFrameStep = pFrameStepTest; // Save interface to global variable for later use
return true;
}
else
{
pFrameStepTest->Release( );
return false;
}
}
To copy to clipboard, switch view to plain text mode
I hope this code helps other people trying to do the same things we have done. This video plays in the Qt GUI inside our frame. We were having problems with it playing in a separate window but realized there is a windowless mode that displays the video in the window you specify instead of its own window. This hasn't made me a DirectShow guru by any means, but at least we got this. Thanks again for all your help!!!
Bookmarks