PDA

View Full Version : VMR9 (DirectShow) Windowless and QAxWidget



Tabgok
2nd June 2011, 00:18
I've gone through examples online, searched Google, and browsed these forums; however, I can't figure out how to get my code to work. Unfortunately, I am limited to using DirectShow.

Essentially, I am using a QAxWidget to build a directshow filtergraph with video playback handled by a vmr9 filter.

- The graph is completely connected
- Running the program without windowless mode on results in a separate window opening and playing information properly
- Running the program in windowless mode results in no video (although the frame the QAxWidget is in does appear)
- The video (trying a simple file atm) does play in windowless mode (I can check via an ffdshow utility), I just can't see it.


I've attached the files, and included them below for convenience.


Main.cpp


#include <qapplication.h>
#include "playerwindow.h"
int main(int argc, char *argv[])
{
RedirectIOToConsole();

QApplication app(argc, argv);
PlayerWindow playerWin;
playerWin.show();
return app.exec();
}



playerwindow.cpp

#include "playerwindow.h"
#include <ActiveQt\qaxbase.h>
#include <ActiveQt\qaxwidget.h>
#include <QtGui>
PlayerWindow::PlayerWindow()
{
long lWidth, lHeight;
IMediaEventEx *pMediaEvent;
IMediaControl *pControl;
IGraphBuilder *gBuilder;
IVMRWindowlessControl9* pWindowlessControl;
HRESULT hr = 0;
DWORD reg;
HDC hdc;

CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );

setWindowTitle(tr("Capture Preview"));
setMouseTracking(true);

wmp = new QAxWidget(this);
wmp->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
wmp->setGeometry( QRect( 0, 0, 640, 480 ) );

QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(wmp);
setLayout(mainLayout);

hr = wmp->setControl( QUuid( CLSID_FilterGraph ) );
hr = wmp->queryInterface(QUuid( IID_IGraphBuilder ), (void**) &gBuilder );

hr = gBuilder->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = gBuilder->QueryInterface(IID_IMediaEventEx, (void **)&pMediaEvent);

hr = InitWindowlessVMR((HWND) wmp->winId(), gBuilder, &pWindowlessControl );


hr = gBuilder->RenderFile(L"C:/AVI_DivX.avi", NULL);
hr = pWindowlessControl->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
if ( SUCCEEDED( hr ) )
{
RECT rcSrc, rcDest;
SetRect( &rcSrc, 0, 0, lWidth, lHeight );

GetClientRect( (HWND) wmp->winId( ), &rcDest );
SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom);

hr = pWindowlessControl->SetVideoPosition( &rcSrc, &rcDest );

PAINTSTRUCT ps;
RECT rcClient;
GetClientRect( (HWND) wmp->winId( ), &rcClient );
hdc = BeginPaint( (HWND) wmp->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) wmp->winId( ), hdc );
}
}

hr = pControl->Run();
OAFilterState pfs;
hr = pControl->GetState(INFINITE, &pfs);
reg;
AddGraphToRot(gBuilder, &reg);

wmp->repaint();
//while(true){
//hr = pWindowlessControl->RepaintVideo((HWND) wmp->winId( ), hdc);
//Sleep(1000);
//}
}



void PlayerWindow::timerEvent(QTimerEvent *event)
{
if (event->timerId() == updateTimer) {
double curPos = wmp->property("CurrentPosition").toDouble();
onPositionChange(-1, curPos);
} else {
QWidget::timerEvent(event);
}
}

HRESULT PlayerWindow::InitWindowlessVMR(
HWND hwndApp, // Window to hold the video.
IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager.
IVMRWindowlessControl9** ppWc // Receives a pointer to the VMR.
)
{
if (!pGraph || !ppWc) return E_POINTER;
pVmr = NULL;
IVMRWindowlessControl9* pWc = NULL;
// Create the VMR.
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (FAILED(hr))
{
return hr;
}

// Add the VMR to the filter graph.
hr = pGraph->AddFilter(pVmr, L"Teague Bick");
if (FAILED(hr))
{
pVmr->Release();
return hr;
}
// Set the rendering mode.
IVMRFilterConfig9* pConfig;
hr = pVmr->QueryInterface(IID_IVMRFilterConfig9, (void**)&pConfig);
if (SUCCEEDED(hr))
{
hr = pConfig->SetNumberOfStreams( 1 );
hr = pConfig->SetRenderingMode(VMR9Mode_Windowless);
pConfig->Release();
}
if (SUCCEEDED(hr))
{
// Set the window.
hr = pVmr->QueryInterface(IID_IVMRWindowlessControl9, (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;
}

void PlayerWindow::onPlayStateChange(int, int newState)
{
cout << " Stop hit\n";
}

void PlayerWindow::onReadyStateChange(ReadyStateConstan ts ready)
{
if (ready == Complete) {
double duration = 60 * wmp->property("Duration").toDouble();
seekSlider->setMinimum(0);
seekSlider->setMaximum(int(duration));
seekSlider->setEnabled(true);
}
}

void PlayerWindow::onPositionChange(double, double newPos)
{
seekSlider->blockSignals(true);
seekSlider->setValue(int(newPos * 60));
seekSlider->blockSignals(false);
}
void PlayerWindow::sliderValueChanged(int newValue)
{
seekSlider->blockSignals(true);
wmp->setProperty("CurrentPosition", double(newValue) / 60);
seekSlider->blockSignals(false);
}

void PlayerWindow::stopFile(){
cout << "Stopping record\n";

card->stopRecording();
}

void PlayerWindow::openFile()
{


cout << "Starting to record\n";
card->startRecording();

//QString fileName = QFileDialog::getOpenFileName(this,
// tr("Select File"), ".", fileFilters);
//if (!fileName.isEmpty())
// wmp->setProperty("FileName",
// QDir::toNativeSeparators(fileName));
}


playerwindow.h


#pragma once
#ifndef PLAYERWINDOW_H
#define PLAYERWINDOW_H

#ifndef _M_IX86
#define _M_IX86 600
#endif


#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif
#include <d3d9.h>
#include <QWidget>
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#include <dshow.h>
#include "Video_Header.h"
#include <conio.h>
#include <vmr9.h>


class QAxWidget;
class QSlider;
class QToolButton;

class PlayerWindow : public QWidget
{
Q_OBJECT
Q_ENUMS(ReadyStateConstants)

public:
enum PlayStateConstants { Stopped = 0, Paused = 1, Playing = 2 };
enum ReadyStateConstants { Uninitialized = 0, Loading = 1,
Interactive = 3, Complete = 4 };

PlayerWindow();

protected:
void timerEvent(QTimerEvent *event);

private:
HRESULT InitWindowlessVMR(HWND hwndApp, IGraphBuilder* pGraph,
IVMRWindowlessControl9** ppWc) ;
QAxWidget *wmp;
QToolButton *playPauseButton;
QToolButton *openButton;
QSlider *seekSlider;
QToolButton *stopButton;
QString fileFilters;
int updateTimer;
VideoCard *card;
IBaseFilter* pVmr;
};


#ifdef _DEBUG

// maximum mumber of lines the output console should have
static const WORD MAX_CONSOLE_LINES = 500;
static void RedirectIOToConsole(){
int hConHandle;
long lStdHandle;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
FILE *fp;

// allocate a console for this app
AllocConsole();

// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT _HANDLE),&coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT _HANDLE),coninfo.dwSize);

// redirect unbuffered STDOUT to the console
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );

// redirect unbuffered STDIN to the console
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );

// redirect unbuffered STDERR to the console
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );

// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
// point to console as well
ios::sync_with_stdio();
}
#endif
#endif

Tabgok
6th June 2011, 17:31
After several more hours of banging my head against the wall, I discovered that using the setControl method for the QAxWidget has to be IGNORED in order to display the VMR9 output in the widget. setControl(FilterGraph) just gives the widget control of the filtergraph, not the widget's window.

hootener
20th July 2011, 15:25
Thank you for this. The code samples were helpful, and I got DirectShow video to play inside a QAxWidget. Currently I am using the IVMRWindowlessControl versus the IVMRWindowlessControl9, though. However, I think I'll change this.

There are a couple of problems with my implementation that I wanted to bring up here to see if you (or anyone else) encountered them.

First, I have the video playing in a QAxWidget that is placed into a QVBoxLayout. Like so:


DirectShowWidget dShowWidget; //my subclassed QAxWidget that is responsible for rendering the video.
QVBoxLayout * pLayout = new QVBoxLayout();
pLayout->addWidget(&dShowWidget);
QWidget * pWidget = new QWidget();
pWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy:: Expanding);
pWidget->setLayout(pLayout);

dShowWidget.OpenFile(); //gets a video file name and stores to a member variable
dShowWidget.CreateVideo(); //creates and plays the video.

pWidget->show();

The problem is that the layout does not resize to fit the entire video. This isn't a big problem, though, I'm sure I can putz around with the layout itself and get it to work.
A larger problem is that when resizing the window (while the application is running, not in code), the video disappears and only reappears after I finish resizing and then single click on the window again. Also, clicking on another application window (i.e., a console, browser window, etc) causes the video to disappear. Basically, the video disappears when the application window loses focus. It only reappears when the window regains focus.

Do these issues have something to do with the QWidget's paintEvent perhaps overriding the video on a repaint? Will I need to subclass QWidget and implement my own paintEvent to make sure the video is always repainted correctly on the widget's repaint? Any thoughts?

Thanks,
EH