QtウィジェットとDirectshowを使用するとビデオがちらつく

現在、Windows用のビデオプレーヤーを開発しています。 そして、タスク上でしばらく「ハング」しました。Qtに切り替えた後、プレーヤーのビデオが点滅して消え始めます(ビデオを参照)。



QWidget :: paintEventの前にQtが(https://qt-project.org/doc/qt-4.8/qwidget.html#autoFillBackground-prop)を埋めるため、QWidget :: paintEventをオーバーライドすることはできません。

QWidget :: winEventでWM_PAINTとWM_ERASEBACKGOUNDをオーバーライドする試みも失敗しました。 paintEventは、WM_PAINTからだけでなく、私が知らないアルゴリズムを使用する他のサービスからも呼び出すことができます。

したがって、この状況から抜け出した方法を以下に示します。





解決策は次のとおりです。

ホイールを再発明するのではなく、ネイティブウィジェットをQtで使用することにしました。 ネイティブウィジェット自体はQWidget内にあります。 概略的に、プレーヤーのウィンドウでは、これは次のように表すことができます。



まず、ウィンドウハンドラーを作成します。 彼は、ウィンドウクラスの登録とウィジェットへのメッセージの転送を処理します。

また、QApplicationがネイティブウィジェットのメッセージを処理しないようにする必要があります。 このプロジェクトではlibqxtを使用しているため、QxtApplication :: installNativeEventFilterを使用してフィルターを追加する必要があります。 別のオプションは、QCoreApplication :: winEventFilterをオーバーライドすることです。



最初に、HWNDをオブジェクトにマップするためにWindowProcMapperクラスを作成しました。

nativewidgetimpl.h

namespace Native { class NativeWndFilter : public QxtNativeEventFilter { public: inline NativeWndFilter() { } inline void insert(HWND h) { m_wnds.insert(h); } inline void remove(HWND h) { m_wnds.remove(h); } inline bool contains(HWND h) { return m_wnds.contains(h); } bool winEventFilter(MSG * msg, long *result) override; private: QSet<HWND> m_wnds; }; template<typename T> class WindowProcMapper { public: WindowProcMapper(const wchar_t *className); ~WindowProcMapper(); inline T *getWindow(HWND hwnd) const; inline void insertWindow(HWND hwnd, T *ptr); inline void removeWindow(HWND hwnd); ATOM getRegisterResult(); bool registerWindowClass(); inline static WindowProcMapper<T> *instance() { return self; } private: QHash<HWND, T*> m_hash; LPCWSTR m_className; ATOM m_registerResult; static WindowProcMapper *self; static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); NativeWndFilter *m_filter; }; }
      
      





ネイティブウィジェットのラッパーを作成します。

nativewidget.h

 namespace Native { struct NativeWidgetPrivate; class NativeWidget : public QWidget { Q_OBJECT public: NativeWidget(QWidget *parent = nullptr); NativeWidget(const QString &wndName, QWidget *parent = nullptr); ~NativeWidget(); WId nativeHWND() const; HDC getNativeDC() const; void releaseNativeDC(HDC hdc) const; QString wndName() const; protected: NativeWidget(NativeWidgetPrivate *p, const QString &wndName, QWidget *parent = nullptr); void paintEvent(QPaintEvent *ev) override; void resizeEvent(QResizeEvent *ev) override; virtual bool nativeWinEvent(MSG *msg, long *result); QScopedPointer<NativeWidgetPrivate> d_ptr; private: void init(); Q_DECLARE_PRIVATE(NativeWidget); }; void NativeWidget::paintEvent(QPaintEvent *ev) { Q_D(NativeWidget); SendMessage(d->m_hwnd, WM_PAINT, 0, 0); ev->accept(); } void NativeWidget::resizeEvent(QResizeEvent *ev) { Q_D(NativeWidget); const QSize &sz = ev->size(); SetWindowPos(d->m_hwnd, NULL, 0, 0, sz.width(), sz.height(), 0); QWidget::resizeEvent(ev); } }
      
      







ネイティブウィジェットの動作については、resizeEventおよびpaintEvent関数を再定義します。 QWidgetが変更されると、ネイティブウィジェットにイベントを送信します。

プライベートクラスは、ネイティブウィジェットを管理する責任を負います。 windowsEventメソッドを使用してメッセージを受け取り、NativeWidget :: nativeWinEventに渡します。これは派生クラスで簡単にオーバーライドできます。

 namespace Native { struct NativeWidgetPrivate { NativeWidgetPrivate(NativeWidget *q); ~NativeWidgetPrivate(); inline HWND winID() const; HDC getDC() const; void releaseDC(HDC hdc) const; QRect windowPlacement() const; virtual bool createWindow(QWidget *parent); void sendEvent(QEvent *ev); inline bool isCreating() const { return m_creating; } LRESULT windowsEvent(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam); HWND m_hwnd; mutable HDC m_hdc; bool m_creating; void sendToWidget(uint msg, WPARAM wparam, LPARAM lparam); QString m_wndName; NativeWidget *const q_ptr; Q_DECLARE_PUBLIC(NativeWidget); }; }
      
      





ビデオウィジェットを実装するには、WM_PAINTおよびWM_ERASEBACKGROUNDメッセージをインターセプトする必要があります。

実装例を以下に示します。

 bool MovieScreen::nativeWinEvent(MSG *msg, long *result) { Q_D(MovieScreen); //qDebug() << __FUNCTION__; switch (msg->message) { case WM_ERASEBKGND: case WM_PAINT: d->updateMovie(); break; case WM_SHOWWINDOW: { auto r = NativeWidget::nativeWinEvent(msg, result); if (msg->wParam == TRUE) d->updateMovie(); return r; } case WM_SIZE: case WM_MOVE: case WM_MOVING: case WM_SIZING: { auto r = NativeWidget::nativeWinEvent(msg, result); d->resizeVideo(); return r; } } return NativeWidget::nativeWinEvent(msg, result); }
      
      





サンプル実装:

 IVMRWindowlessControl9 *m_pVideoRenderer9; MovieScreenPrivate:: updateMovie() { If (isPaused()) { HDC hdc = getHDC(); m_pVideoRenderer9->Repaint_Video(winID(), hdc); releaseHDC(hdc); } } MovieScreenPrivate::resizeVideo() { long lWidth, lHeight; HRESULT hr = m_pVideoRenderer9->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(winID(), &rcDest); // Set the destination rectangle. SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom); m_pVideoRenderer9->SetVideoPosition(&rcSrc, &rcDest); } }
      
      





さて、最終的には次のようになります。



ソースはここからダウンロードできます。



All Articles