PDA

View Full Version : DirectShow VMR9 Drawing on QWidget Subclass Resulting in Repaint Flicker



MichaelQuigley
24th September 2012, 22:11
I apologize in advance if there's a thread lurking somewhere that contains the information I'm looking for. I searched. Honestly, I did.

I have a QWidget subclass which is meant to be the area where a DirectShow VMR9 draws its video output. The application is working perfectly--all of the DirectShow stuff is wired up happily and the VMR9 is drawing on my QWidget subclass. The problem is that when resizing the window, I'm getting a lot of flicker in the redraw. It looks to me like Qt and DirectShow are having a race during repainting. It looks like Qt is clearing the widget, and then DirectShow is repainting afterward. In my QWidget subclass I have a paintEvent() method something like this:



void VideoWidget::paintEvent(QPaintEvent*) {
RECT pos;
pos.top = 0;
pos.left = 0;
pos.bottom = height() - 1;
pos.right = width() - 1;
_impl->vmrWc->SetVideoPosition(NULL, &pos);
HWND hWnd = (HWND) winId();
HDC hDC = GetDC(hWnd);
_impl->vmrWc->RepaintVideo(hWnd, hDC);
}

"_impl->vmrWc" refers to the IVMRWindowlessControl9 for my VMR9 renderer.

Is there something I should be doing differently with my QWidget subclass to provoke it to not try to redraw the widget's contents?

Thanks in advance...
Michael

ChrisW67
24th September 2012, 22:54
I am not sure if this will help but you could look at the Qt::WA_NoSystemBackground and Qt::WA_OpaquePaintEvent QWidget attribute

See Transparency and Double Buffering and QWidget::setAttribute())

MichaelQuigley
25th September 2012, 00:01
From reading the docs, that really sounds like it should have done the trick. I'm now setting Qt::WA_OpaquePaintEvent in the constructor of my QWidget subclass. That makes the flicker happen in black, instead of in the default widget background color. It looks a tiny bit better, but it's still not right.

I've also split the work I was doing up into two methods now:



void VideoViewer::paintEvent(QPaintEvent*) {
HWND hWnd = (HWND) winId();
HDC hDC = GetDC(hWnd);
_impl->vmrWc->RepaintVideo(hWnd, hDC);
}

void VideoViewer::resizeEvent(QResizeEvent*) {
RECT pos;
pos.top = 0;
pos.left = 0;
pos.bottom = height() - 1;
pos.right = width() - 1;
_impl->vmrWc->SetVideoPosition(NULL, &pos);
}


From reading the DirectShow docs, it seems like something is going on with the windows messages, and possibly causing DirectShow to think it needs to repaint more than it should?

This code is being ported from wxWidgets--things worked well there. I have a feeling it's got to be something minor and silly.

MichaelQuigley
25th September 2012, 20:11
Ok... I managed to get this sorted out.

in my QWidget subclass, I had to override the QWidget::paintEngine method to return NULL. I also had to set the following widget properties:



setAttribute(Qt::WA_OpaquePaintEvent, true);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_PaintOnScreen, true);
setAutoFillBackground(false);


I also adjusted my calls to the IVMRWindowlessControl9 like the following:



void WindowsVideoViewer::paintEvent(QPaintEvent*) {
HWND hWnd = (HWND) winId();
HDC hDC = GetDC(hWnd);
_impl->vmrWc->RepaintVideo(hWnd, hDC);
}

void WindowsVideoViewer::resizeEvent(QResizeEvent*) {
RECT pos;
pos.top = 0;
pos.left = 0;
pos.bottom = height();
pos.right = width();
_impl->vmrWc->SetVideoPosition(NULL, &pos);
}


Everything seems happy now.

Ok... I managed to get this sorted out.

in my QWidget subclass, I had to override the QWidget::paintEngine method to return NULL. I also had to set the following widget properties:



setAttribute(Qt::WA_OpaquePaintEvent, true);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_PaintOnScreen, true);
setAutoFillBackground(false);


I also adjusted my calls to the IVMRWindowlessControl9 like the following:



void WindowsVideoViewer::paintEvent(QPaintEvent*) {
HWND hWnd = (HWND) winId();
HDC hDC = GetDC(hWnd);
_impl->vmrWc->RepaintVideo(hWnd, hDC);
}

void WindowsVideoViewer::resizeEvent(QResizeEvent*) {
RECT pos;
pos.top = 0;
pos.left = 0;
pos.bottom = height();
pos.right = width();
_impl->vmrWc->SetVideoPosition(NULL, &pos);
}


Everything seems happy now.

Added after 5 minutes:

ChrisW67's tips pointed me in the right direction, but the chunk of code that pushed it over the edge was in the Qt 4.8 source distribution at the location:


qt-everywhere-opensource-src-4.8.3/src/3rdparty/phonon/ds9

Specifically, the VideoWidget class.