PDA

View Full Version : [SOLVED] DirectShow Video Player Inside Qt



ToddAtWSU
3rd April 2006, 15:21
I am trying to develop a video player using Qt and DirectShow. When I use queryInterface( ) from mpVideoPlayer which is a QAxWidget, I get NULL pointers. Whenever I use CoCreateInstance to create the first pointer, I get valid pointers for all my subsequent pointers, but this creates the video playing window in a separate window instead of inside my Qt GUI. So how do I get the UUID for a DirectShow inside Qt? How do I use setControl to act like CoCreateInstance( )? :confused: Any help and/or examples would be greatly appreciated. Below is my code for setting up the different pointers and running the video. Thanks!!!


bool VideoViewerWindow::createVideo( QString filename )
{
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';

//mpVideoPlayer->setControl( "DirectShow" );

// Get the interface for DirectShow's GraphBuilder
//CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
// IID_IGraphBuilder, (void **)&pGraphBuilder);
mpVideoPlayer->queryInterface( QUuid( IID_IGraphBuilder ), (void**) &pGraphBuilder );

pGraphBuilder->RenderFile( wfile, NULL );

// Query for the media control interfaces
//mpVideoPlayer->queryInterface( QUuid( IID_IMediaControl ), (void **)&pMediaControl );
pGraphBuilder->QueryInterface( IID_IMediaControl, (void **)&pMediaControl );
pGraphBuilder->QueryInterface( IID_IMediaEventEx, (void **)&pMediaEvent );
pGraphBuilder->QueryInterface( IID_IMediaSeeking, (void **)&pMediaSeeking );
pGraphBuilder->QueryInterface( IID_IMediaPosition, (void **)&pMediaPosition );

// Query for video interfaces, which may not be relevant for audio files
pGraphBuilder->QueryInterface( IID_IVideoWindow, (void **)&pVideoWindow );
pGraphBuilder->QueryInterface( IID_IBasicVideo, (void **)&pBasicVideo );

// Query for audio interfaces, which may not be relevant for video-only files
pGraphBuilder->QueryInterface( IID_IBasicAudio, (void **)&pBasicAudio );

getFrameStepInterface( );

pMediaControl->Run( );

return true;
}

jacek
3rd April 2006, 17:31
Maybe you should use something other than "DirectShow" as a parameter of setControl() method?

http://doc.trolltech.com/4.1/qaxbase.html#control-prop

Rayven
3rd April 2006, 20:44
I am working with ToddAtWSU on this problem as well. We have tried a variety of UUID numbers found on the internet, in setControl(), each returns NULL. We cannot find a specific (UUID that works, there may be multiple I am not sure) or a class name for "DirectShow" like the MSCalendar example. The problem is that all the sample code for DirectShow is written using MFC, and each forum post that we have read only asks specific questions about after the video/audio is being played, not how they got it play initially. Essentially what we are looking for, is how to setup (we are assuming using setControl, but that may not be the correct method) the IGraphBuilder using a QAxWidget. We can get the code using the CoCreateInstance() from MFC, but that just draws the video outside of the QAxWidget. After we get the video setup, the rest of the code I understand (I HOPE!! :o ) . Thanks for the frequent responses jacek!!

wysota
3rd April 2006, 20:50
You should start with Windows Media Player. It's GUUIDs are easy to find (it has more than one of them), so you should be able to get it running in a short time. If you have trouble finding the right number, you might want to take a look at your OS registry to look which COM objects have been registered at all. There are their id there too, it should ease your job.

Rayven
4th April 2006, 14:40
WMP will not work (I dont think...) for our needs. Perhaps a bit of information is needed. We are developing an application to frameskip through an mpeg video. Very rarely will the video be just played through. When we were looking into the frameskipping of Media Player, many of those features are locked out when developing your own front end. When using DirectShow, at least in the MFC exmples we have ran, we have access to the frame properties of the video. We could be wrong in our asessment, we dont have much expirience with COM objects, please let me know. Also, the reason we are not using MFC for the interface, since that seems to be the faster route, is eventually this system may have to work in a UNIX environment. I know DirectShow is not portable, but I have looked into other UNIX solutions and do not want to re-develop the GUI.
Thanks

jacek
4th April 2006, 15:09
How about this?
mpVideoPlayer->setControl( QUuid( CLSID_FilterGraph ) );
mpVideoPlayer->queryInterface( QUuid( IID_IGraphBuilder ), (void**) &pGraphBuilder );

wysota
4th April 2006, 15:14
WMP will not work (I dont think...) for our needs.

But it's a good start to see if your approach is correct.

Rayven
7th April 2006, 15:41
I got the WMP code to play the video, however, there is no access to the frame properties to directly control WMP.

Jacek, the code you gave me does work, but I think there is something else I am missing. The video plays now, but it does not play within the Qt structure but outside in a new window. This is definatly one step closer to the solution though. Here is the code for what we have so far:


mpFrame = new QFrame( this );
mpFrame->setGeometry( QRect( 10, 41, 561, 351 ) );
mpFrame->setFrameShape( QFrame::WinPanel );
mpFrame->setFrameShadow( QFrame::Sunken );

mpVideoPlayer = new QAxWidget( mpFrame );
mpVideoPlayer->setGeometry( QRect( 15, 46, 551, 341 ) );

:
:

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';

// Get the interface for DirectShow's GraphBuilder
mpVideoPlayer->setControl( QUuid( CLSID_FilterGraph ) );
mpVideoPlayer->queryInterface( QUuid( IID_IGraphBuilder ), (void**) &pGraphBuilder );

// Query for the media control interfaces
mpVideoPlayer->queryInterface( QUuid( IID_IMediaControl ), (void **)&pMediaControl );
mpVideoPlayer->queryInterface( QUuid( IID_IMediaEventEx ), (void**)&pMediaEvent );
mpVideoPlayer->queryInterface( QUuid( IID_IMediaSeeking ), (void **)&pMediaSeeking );
mpVideoPlayer->queryInterface( QUuid( IID_IMediaPosition ), (void **)&pMediaPosition );

pGraphBuilder->RenderFile( wfile, NULL );

pMediaControl->Run( );

Is there something I am missing to place the video in the Qt window? I am thinking it is the RenderFile() call made by pGraphBuilder but I can not find a QAxWidget call that uses the RenderFile functionality.

jacek
7th April 2006, 17:26
Jacek, the code you gave me does work, but I think there is something else I am missing. The video plays now, but it does not play within the Qt structure but outside in a new window.
Unfortunately I can't help you much, as I have no access to ActiveQt.

IMO setControl() should take care of everything, unless that window doesn't come from FilterGraph but some of its sub-objects.

ToddAtWSU
24th April 2006, 17:01
I finally got the video player to work! :D 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;

The openFile function:

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" );
}
}
}

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;
}

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;
}

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;
}
}

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!!! :D :cool:

trskel
5th June 2007, 22:43
Hi Todd,
I am trying to do this same thing but I don't manage to make it work. I tried using your code but there is a thing I don't understand::confused: Is VideoViewerWindow a class derived from QAxWidget or something at a higher level? Then what is mpVideoPlayer? I thought that it was a QAxWidget too. Is there any chance that you could post the whole class code or send it to me?

Thanks

trskel
11th June 2007, 22:44
I made some progress. I implemented VideoViewerWindow as a QFrame and mpVideoPlayer is a QAxWidget that is a member of VideoViewerWindow. With this I am able to play a movie and hear the sound, but I can't see the image. Probably I am trying to mix the two approaches here (the one using QAxWidget that got the video playing in a separate window and the later one, that I think didn't even use Active Qt). Please shed a litle bit of light since I don't know much about COM programming.

ToddAtWSU
12th June 2007, 18:39
I don't know much about COM programming either. A lot of my questions I got answered from looking online and asking questions in Microsoft's DirectShow message boards. I wish I could provide all the code but I do not have permission to give out the code. I believe VideoViewerWindow is a class derived from a QMainWindow and mpVideoPlayer is a QAxWidget that is a member of the VideoViewerWindow class. I have not looked at the code since about December so I am a bit foggy about it but based on how I do all my naming convention a class ending in the word Window is a QMainWindow subclass. Hopefully this will help you out and if you need more help I can try to get back to the code and look through it. But my initial guess would be since you can hear the sound is that you have initialized it correctly but need to issue redraws in your code. Here is a link to a good Microsoft DirectShow forum that could also help you. http://msdn.microsoft.com/newsgroups/default.aspx?dg=microsoft.public.win32.programmer. directx.video&lang=en&cr=US I posted some questions on here so maybe you can search or post some things here too. I will try to remember to check this forum page again, but if I don't respond please send me another PM as I get an e-mail when I receive a PM where I don't get any e-mails when someone responds to this message. Good luck on your project.

trskel
12th June 2007, 18:57
Thanks a lot.
I will look into the redraw issue and look in that forum. I will ask you again if I cannot get it to work.

ToddAtWSU
13th June 2007, 21:28
Sounds good and I will try to look at that code again, too.

trskel
28th June 2007, 15:39
Still no luck. I have been trying different things but I still cannot see the video play in the window.
I can make it work with no problems if it displays in its own external window, but whenever use the InitWindowlessVMR() finction, I don't see anything (only sound), and my application window looks exactly like when the video is displayed in window mode.

Do you use mpVideoPlayer->setControl( QUuid( CLSID_FilterGraph ) ); at any point?

If you don't have more suggestions I will post my code here.

chenchaoyin
3rd November 2011, 07:47
i am a new in directshow, i use mfc create a player, but the user interface is very ugly and i don't like mfc, so i want to use qt make a user interface, i hope you can help me. thank you

i include dshow.h in my header, but a few variable don't use and tips not find