WMF: enabled HW-accelerated video decoding for the QML video item.

It also applies to QGraphicsVideoItem when used on a GL viewport.
We now have a new video sink that is based on Microsoft's EVR sink, we just
replace the default Presenter with our own. Frames are rendered into D3D
surfaces using DXVA, then copied into a shared D3D/EGL surface and finally
bound to a GL texture to be used by the video surface.
The shared D3D/EGL surface is a feature provided by ANGLE and therefore Qt
must be compiled with ANGLE for this new video sink to be compiled and
used.

Change-Id: I0b7b9968eed5488f9ef1a2dcca5213bd0af232ab
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
This commit is contained in:
Yoann Lopes
2013-01-14 17:44:06 +01:00
committed by The Qt Project
parent 02add40392
commit 101c78983a
12 changed files with 3769 additions and 66 deletions

View File

@@ -40,7 +40,10 @@
****************************************************************************/
#include "mfvideorenderercontrol.h"
#include <mferror.h>
#include "mfglobal.h"
#ifdef QT_OPENGL_ES_2_ANGLE
#include "evrcustompresenter.h"
#endif
#include <qabstractvideosurface.h>
#include <qvideosurfaceformat.h>
#include <qtcore/qtimer.h>
@@ -114,67 +117,6 @@ namespace
MapMode m_mapMode;
};
template<class T>
class AsyncCallback : public IMFAsyncCallback
{
public:
typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *pAsyncResult);
AsyncCallback(T *pParent, InvokeFn fn) : m_pParent(pParent), m_pInvokeFn(fn)
{
}
// IUnknown
STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
{
if (!ppv)
{
return E_POINTER;
}
if (iid == __uuidof(IUnknown))
{
*ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
}
else if (iid == __uuidof(IMFAsyncCallback))
{
*ppv = static_cast<IMFAsyncCallback*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) AddRef()
{
// Delegate to parent class.
return m_pParent->AddRef();
}
STDMETHODIMP_(ULONG) Release()
{
// Delegate to parent class.
return m_pParent->Release();
}
// IMFAsyncCallback methods
STDMETHODIMP GetParameters(DWORD*, DWORD*)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult)
{
return (m_pParent->*m_pInvokeFn)(pAsyncResult);
}
T *m_pParent;
InvokeFn m_pInvokeFn;
};
// Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7")
IMarker : public IUnknown
@@ -2134,6 +2076,9 @@ MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
, m_surface(0)
, m_currentActivate(0)
, m_callback(0)
#ifdef QT_OPENGL_ES_2_ANGLE
, m_presenterActivate(0)
#endif
{
}
@@ -2147,6 +2092,14 @@ void MFVideoRendererControl::clear()
if (m_surface)
m_surface->stop();
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate) {
m_presenterActivate->ShutdownObject();
m_presenterActivate->Release();
m_presenterActivate = NULL;
}
#endif
if (m_currentActivate) {
m_currentActivate->ShutdownObject();
m_currentActivate->Release();
@@ -2174,12 +2127,22 @@ void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
}
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
m_presenterActivate->setSurface(m_surface);
else
#endif
if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface);
}
void MFVideoRendererControl::customEvent(QEvent *event)
{
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
return;
#endif
if (!m_currentActivate)
return;
@@ -2203,23 +2166,48 @@ void MFVideoRendererControl::customEvent(QEvent *event)
void MFVideoRendererControl::supportedFormatsChanged()
{
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
m_presenterActivate->supportedFormatsChanged();
else
#endif
if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged();
}
void MFVideoRendererControl::present()
{
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
return;
#endif
if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->present();
}
IMFActivate* MFVideoRendererControl::createActivate()
{
Q_ASSERT(m_surface);
clear();
#ifdef QT_OPENGL_ES_2_ANGLE
// We can use the EVR with our custom presenter only if the surface supports OpenGL
// texture handles
if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) {
// Create the EVR media sink, but replace the presenter with our own
if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) {
m_presenterActivate = new EVRCustomPresenterActivate;
m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate);
}
}
if (!m_currentActivate)
#endif
m_currentActivate = new VideoRendererActivate(this);
if (m_surface)
setSurface(m_surface);
setSurface(m_surface);
return m_currentActivate;
}

View File

@@ -46,6 +46,14 @@
#include <mfapi.h>
#include <mfidl.h>
QT_BEGIN_NAMESPACE
#ifdef QT_OPENGL_ES_2_ANGLE
class EVRCustomPresenterActivate;
#endif
QT_END_NAMESPACE
QT_USE_NAMESPACE
class MFVideoRendererControl : public QVideoRendererControl
@@ -74,6 +82,10 @@ private:
QAbstractVideoSurface *m_surface;
IMFActivate *m_currentActivate;
IMFSampleGrabberSinkCallback *m_callback;
#ifdef QT_OPENGL_ES_2_ANGLE
EVRCustomPresenterActivate *m_presenterActivate;
#endif
};
#endif