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

@@ -118,6 +118,7 @@ protected:
" gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;" " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;"
"}"; "}";
#ifndef QT_OPENGL_ES_2_ANGLE
static const char *colorsSwapShader = static const char *colorsSwapShader =
"uniform sampler2D rgbTexture;" "uniform sampler2D rgbTexture;"
"uniform lowp float opacity;" "uniform lowp float opacity;"
@@ -137,6 +138,9 @@ protected:
default: default:
return shader; return shader;
} }
#else
return shader;
#endif
} }
virtual void initialize() { virtual void initialize() {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,333 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef EVRCUSTOMPRESENTER_H
#define EVRCUSTOMPRESENTER_H
#include <QObject>
#include <qmutex.h>
#include <qqueue.h>
#include <evr.h>
#include "mfactivate.h"
QT_BEGIN_NAMESPACE
class D3DPresentEngine;
class QAbstractVideoSurface;
class Scheduler
{
public:
enum ScheduleEvent
{
Terminate = WM_USER,
Schedule = WM_USER + 1,
Flush = WM_USER + 2
};
Scheduler();
~Scheduler();
void setCallback(QObject *cb) {
m_CB = cb;
}
void setFrameRate(const MFRatio &fps);
void setClockRate(float rate) { m_playbackRate = rate; }
const LONGLONG &lastSampleTime() const { return m_lastSampleTime; }
const LONGLONG &frameDuration() const { return m_perFrameInterval; }
HRESULT startScheduler(IMFClock *clock);
HRESULT stopScheduler();
HRESULT scheduleSample(IMFSample *sample, bool presentNow);
HRESULT processSamplesInQueue(LONG *nextSleep);
HRESULT processSample(IMFSample *sample, LONG *nextSleep);
HRESULT flush();
// ThreadProc for the scheduler thread.
static DWORD WINAPI schedulerThreadProc(LPVOID parameter);
private:
DWORD schedulerThreadProcPrivate();
QQueue<IMFSample*> m_scheduledSamples; // Samples waiting to be presented.
IMFClock *m_clock; // Presentation clock. Can be NULL.
QObject *m_CB; // Weak reference; do not delete.
DWORD m_threadID;
HANDLE m_schedulerThread;
HANDLE m_threadReadyEvent;
HANDLE m_flushEvent;
float m_playbackRate;
MFTIME m_perFrameInterval; // Duration of each frame.
LONGLONG m_perFrame_1_4th; // 1/4th of the frame duration.
MFTIME m_lastSampleTime; // Most recent sample time.
QMutex m_mutex;
};
class SamplePool
{
public:
SamplePool();
~SamplePool();
HRESULT initialize(QList<IMFSample*> &samples);
HRESULT clear();
HRESULT getSample(IMFSample **sample);
HRESULT returnSample(IMFSample *sample);
BOOL areSamplesPending();
private:
QMutex m_mutex;
QList<IMFSample*> m_videoSampleQueue;
bool m_initialized;
DWORD m_pending;
};
class EVRCustomPresenter
: public QObject
, public IMFVideoDeviceID
, public IMFVideoPresenter // Inherits IMFClockStateSink
, public IMFRateSupport
, public IMFGetService
, public IMFTopologyServiceLookupClient
{
Q_OBJECT
public:
// Defines the state of the presenter.
enum RenderState
{
RenderStarted = 1,
RenderStopped,
RenderPaused,
RenderShutdown // Initial state.
};
// Defines the presenter's state with respect to frame-stepping.
enum FrameStepState
{
FrameStepNone, // Not frame stepping.
FrameStepWaitingStart, // Frame stepping, but the clock is not started.
FrameStepPending, // Clock is started. Waiting for samples.
FrameStepScheduled, // Submitted a sample for rendering.
FrameStepComplete // Sample was rendered.
};
EVRCustomPresenter();
~EVRCustomPresenter();
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IMFGetService methods
STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject);
// IMFVideoPresenter methods
STDMETHODIMP ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param);
STDMETHODIMP GetCurrentMediaType(IMFVideoMediaType** mediaType);
// IMFClockStateSink methods
STDMETHODIMP OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset);
STDMETHODIMP OnClockStop(MFTIME systemTime);
STDMETHODIMP OnClockPause(MFTIME systemTime);
STDMETHODIMP OnClockRestart(MFTIME systemTime);
STDMETHODIMP OnClockSetRate(MFTIME systemTime, float rate);
// IMFRateSupport methods
STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate);
STDMETHODIMP GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate);
STDMETHODIMP IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate);
// IMFVideoDeviceID methods
STDMETHODIMP GetDeviceID(IID* deviceID);
// IMFTopologyServiceLookupClient methods
STDMETHODIMP InitServicePointers(IMFTopologyServiceLookup *lookup);
STDMETHODIMP ReleaseServicePointers();
void supportedFormatsChanged();
void setSurface(QAbstractVideoSurface *surface);
private Q_SLOTS:
void startSurface();
void stopSurface();
private:
HRESULT checkShutdown() const
{
if (m_renderState == RenderShutdown)
return MF_E_SHUTDOWN;
else
return S_OK;
}
// The "active" state is started or paused.
inline bool isActive() const
{
return ((m_renderState == RenderStarted) || (m_renderState == RenderPaused));
}
// Scrubbing occurs when the frame rate is 0.
inline bool isScrubbing() const { return m_playbackRate == 0.0f; }
// Send an event to the EVR through its IMediaEventSink interface.
void notifyEvent(long eventCode, LONG_PTR param1, LONG_PTR param2)
{
if (m_mediaEventSink)
m_mediaEventSink->Notify(eventCode, param1, param2);
}
float getMaxRate(bool thin);
// Mixer operations
HRESULT configureMixer(IMFTransform *mixer);
// Formats
HRESULT createOptimalVideoType(IMFMediaType* proposed, IMFMediaType **optimal);
HRESULT setMediaType(IMFMediaType *mediaType);
HRESULT isMediaTypeSupported(IMFMediaType *mediaType);
// Message handlers
HRESULT flush();
HRESULT renegotiateMediaType();
HRESULT processInputNotify();
HRESULT beginStreaming();
HRESULT endStreaming();
HRESULT checkEndOfStream();
// Managing samples
void processOutputLoop();
HRESULT processOutput();
HRESULT deliverSample(IMFSample *sample, bool repaint);
HRESULT trackSample(IMFSample *sample);
void releaseResources();
// Frame-stepping
HRESULT prepareFrameStep(DWORD steps);
HRESULT startFrameStep();
HRESULT deliverFrameStepSample(IMFSample *sample);
HRESULT completeFrameStep(IMFSample *sample);
HRESULT cancelFrameStep();
// Callback when a video sample is released.
HRESULT onSampleFree(IMFAsyncResult *result);
AsyncCallback<EVRCustomPresenter> m_sampleFreeCB;
// Holds information related to frame-stepping.
struct FrameStep
{
FrameStep()
: state(FrameStepNone)
, steps(0)
, sampleNoRef(NULL)
{
}
FrameStepState state;
QList<IMFSample*> samples;
DWORD steps;
DWORD_PTR sampleNoRef;
};
long m_refCount;
RenderState m_renderState;
FrameStep m_frameStep;
QMutex m_mutex;
// Samples and scheduling
Scheduler m_scheduler; // Manages scheduling of samples.
SamplePool m_samplePool; // Pool of allocated samples.
DWORD m_tokenCounter; // Counter. Incremented whenever we create new samples.
// Rendering state
bool m_sampleNotify; // Did the mixer signal it has an input sample?
bool m_repaint; // Do we need to repaint the last sample?
bool m_prerolled; // Have we presented at least one sample?
bool m_endStreaming; // Did we reach the end of the stream (EOS)?
MFVideoNormalizedRect m_sourceRect;
float m_playbackRate;
D3DPresentEngine *m_D3DPresentEngine; // Rendering engine. (Never null if the constructor succeeds.)
IMFClock *m_clock; // The EVR's clock.
IMFTransform *m_mixer; // The EVR's mixer.
IMediaEventSink *m_mediaEventSink; // The EVR's event-sink interface.
IMFMediaType *m_mediaType; // Output media type
QAbstractVideoSurface *m_surface;
QList<DWORD> m_supportedGLFormats;
};
class EVRCustomPresenterActivate : public MFAbstractActivate
{
public:
EVRCustomPresenterActivate();
~EVRCustomPresenterActivate()
{ }
STDMETHODIMP ActivateObject(REFIID riid, void **ppv);
STDMETHODIMP ShutdownObject();
STDMETHODIMP DetachObject();
void setSurface(QAbstractVideoSurface *surface);
void supportedFormatsChanged();
private:
EVRCustomPresenter *m_presenter;
QAbstractVideoSurface *m_surface;
QMutex m_mutex;
};
QT_END_NAMESPACE
#endif // EVRCUSTOMPRESENTER_H

View File

@@ -0,0 +1,580 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "evrd3dpresentengine.h"
#include "mfglobal.h"
#include <qtgui/qguiapplication.h>
#include <qpa/qplatformnativeinterface.h>
#include <qtgui/qopenglcontext.h>
#include <qabstractvideobuffer.h>
#include <QAbstractVideoSurface>
#include <qvideoframe.h>
#include <QDebug>
#include <qopenglcontext.h>
#include <qwindow.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <d3d9.h>
#include <dxva2api.h>
#include <WinUser.h>
#include <evr.h>
QT_USE_NAMESPACE
static const DWORD PRESENTER_BUFFER_COUNT = 3;
class TextureVideoBuffer : public QAbstractVideoBuffer
{
public:
TextureVideoBuffer(GLuint textureId)
: QAbstractVideoBuffer(GLTextureHandle)
, m_textureId(textureId)
{}
~TextureVideoBuffer() {}
MapMode mapMode() const { return NotMapped; }
uchar *map(MapMode, int*, int*) { return 0; }
void unmap() {}
QVariant handle() const
{
return QVariant::fromValue<unsigned int>(m_textureId);
}
private:
GLuint m_textureId;
};
D3DPresentEngine::D3DPresentEngine()
: QObject()
, m_mutex(QMutex::Recursive)
, m_deviceResetToken(0)
, m_D3D9(0)
, m_device(0)
, m_deviceManager(0)
, m_surface(0)
, m_glContext(0)
, m_offscreenSurface(0)
, m_eglDisplay(0)
, m_eglConfig(0)
, m_eglSurface(0)
, m_glTexture(0)
, m_texture(0)
{
ZeroMemory(&m_displayMode, sizeof(m_displayMode));
HRESULT hr = initializeD3D();
if (SUCCEEDED(hr)) {
hr = createD3DDevice();
if (FAILED(hr))
qWarning("Failed to create D3D device");
} else {
qWarning("Failed to initialize D3D");
}
}
D3DPresentEngine::~D3DPresentEngine()
{
qt_wmf_safeRelease(&m_texture);
qt_wmf_safeRelease(&m_device);
qt_wmf_safeRelease(&m_deviceManager);
qt_wmf_safeRelease(&m_D3D9);
if (m_eglSurface) {
eglReleaseTexImage(m_eglDisplay, m_eglSurface, EGL_BACK_BUFFER);
eglDestroySurface(m_eglDisplay, m_eglSurface);
m_eglSurface = NULL;
}
if (m_glTexture)
glDeleteTextures(1, &m_glTexture);
delete m_glContext;
delete m_offscreenSurface;
}
void D3DPresentEngine::start()
{
QMutexLocker locker(&m_mutex);
if (!m_surfaceFormat.isValid())
return;
if (!m_texture)
createOffscreenTexture();
if (m_surface && !m_surface->isActive())
m_surface->start(m_surfaceFormat);
}
void D3DPresentEngine::stop()
{
QMutexLocker locker(&m_mutex);
if (m_surface && m_surface->isActive())
m_surface->stop();
}
HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
{
HRESULT hr = S_OK;
if (riid == __uuidof(IDirect3DDeviceManager9)) {
if (m_deviceManager == NULL) {
hr = MF_E_UNSUPPORTED_SERVICE;
} else {
*ppv = m_deviceManager;
m_deviceManager->AddRef();
}
} else {
hr = MF_E_UNSUPPORTED_SERVICE;
}
return hr;
}
HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
{
HRESULT hr = S_OK;
UINT uAdapter = D3DADAPTER_DEFAULT;
D3DDEVTYPE type = D3DDEVTYPE_HAL;
D3DDISPLAYMODE mode;
D3DDEVICE_CREATION_PARAMETERS params;
// Our shared D3D/EGL surface only supports RGB32,
// reject all other formats
if (format != D3DFMT_X8R8G8B8)
return MF_E_INVALIDMEDIATYPE;
if (m_device) {
hr = m_device->GetCreationParameters(&params);
if (FAILED(hr))
return hr;
uAdapter = params.AdapterOrdinal;
type = params.DeviceType;
}
hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
if (FAILED(hr))
return hr;
return m_D3D9->CheckDeviceType(uAdapter, type, mode.Format, format, TRUE);
}
HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue)
{
if (!format)
return MF_E_UNEXPECTED;
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS pp;
IDirect3DSwapChain9 *swapChain = NULL;
IMFSample *videoSample = NULL;
QMutexLocker locker(&m_mutex);
releaseResources();
// Get the swap chain parameters from the media type.
hr = getSwapChainPresentParameters(format, &pp);
if (FAILED(hr))
goto done;
// Create the video samples.
for (int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
// Create a new swap chain.
hr = m_device->CreateAdditionalSwapChain(&pp, &swapChain);
if (FAILED(hr))
goto done;
// Create the video sample from the swap chain.
hr = createD3DSample(swapChain, &videoSample);
if (FAILED(hr))
goto done;
// Add it to the list.
videoSample->AddRef();
videoSampleQueue.append(videoSample);
// Set the swap chain pointer as a custom attribute on the sample. This keeps
// a reference count on the swap chain, so that the swap chain is kept alive
// for the duration of the sample's lifetime.
hr = videoSample->SetUnknown(MFSamplePresenter_SampleSwapChain, swapChain);
if (FAILED(hr))
goto done;
qt_wmf_safeRelease(&videoSample);
qt_wmf_safeRelease(&swapChain);
}
done:
if (FAILED(hr))
releaseResources();
qt_wmf_safeRelease(&swapChain);
qt_wmf_safeRelease(&videoSample);
return hr;
}
void D3DPresentEngine::releaseResources()
{
}
void D3DPresentEngine::presentSample(void *opaque, qint64)
{
HRESULT hr = S_OK;
IMFSample *sample = reinterpret_cast<IMFSample*>(opaque);
IMFMediaBuffer* buffer = NULL;
IDirect3DSurface9* surface = NULL;
if (sample) {
// Get the buffer from the sample.
hr = sample->GetBufferByIndex(0, &buffer);
if (FAILED(hr))
goto done;
// Get the surface from the buffer.
hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface));
if (FAILED(hr))
goto done;
}
if (surface && updateTexture(surface)) {
m_surface->present(QVideoFrame(new TextureVideoBuffer(m_glTexture),
m_surfaceFormat.frameSize(),
m_surfaceFormat.pixelFormat()));
}
done:
qt_wmf_safeRelease(&surface);
qt_wmf_safeRelease(&buffer);
qt_wmf_safeRelease(&sample);
}
void D3DPresentEngine::setSurface(QAbstractVideoSurface *surface)
{
QMutexLocker locker(&m_mutex);
m_surface = surface;
}
void D3DPresentEngine::setSurfaceFormat(const QVideoSurfaceFormat &format)
{
QMutexLocker locker(&m_mutex);
m_surfaceFormat = format;
}
void D3DPresentEngine::createOffscreenTexture()
{
// First, check if we have a context on this thread
QOpenGLContext *currentContext = QOpenGLContext::currentContext();
if (!currentContext) {
//Create OpenGL context and set share context from surface
QOpenGLContext *shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
if (!shareContext)
return;
m_offscreenSurface = new QWindow;
m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
//Needs geometry to be a valid surface, but size is not important
m_offscreenSurface->setGeometry(-1, -1, 1, 1);
m_offscreenSurface->create();
m_glContext = new QOpenGLContext;
m_glContext->setFormat(m_offscreenSurface->requestedFormat());
m_glContext->setShareContext(shareContext);
if (!m_glContext->create()) {
delete m_glContext;
delete m_offscreenSurface;
m_glContext = 0;
m_offscreenSurface = 0;
return;
}
currentContext = m_glContext;
}
if (m_glContext)
m_glContext->makeCurrent(m_offscreenSurface);
QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
m_eglDisplay = static_cast<EGLDisplay*>(
nativeInterface->nativeResourceForContext("eglDisplay", currentContext));
m_eglConfig = static_cast<EGLDisplay*>(
nativeInterface->nativeResourceForContext("eglConfig", currentContext));
glGenTextures(1, &m_glTexture);
int w = m_surfaceFormat.frameWidth();
int h = m_surfaceFormat.frameHeight();
EGLint attribs[] = {
EGL_WIDTH, w,
EGL_HEIGHT, h,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE
};
EGLSurface pbuffer = eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, attribs);
HANDLE share_handle = 0;
PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE =
reinterpret_cast<PFNEGLQUERYSURFACEPOINTERANGLEPROC>(eglGetProcAddress("eglQuerySurfacePointerANGLE"));
eglQuerySurfacePointerANGLE(
m_eglDisplay,
pbuffer,
EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle);
m_device->CreateTexture(w, h, 1,
D3DUSAGE_RENDERTARGET,
D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&m_texture,
&share_handle);
m_eglSurface = pbuffer;
if (m_glContext)
m_glContext->doneCurrent();
}
bool D3DPresentEngine::updateTexture(IDirect3DSurface9 *src)
{
if (!m_texture)
return false;
if (m_glContext)
m_glContext->makeCurrent(m_offscreenSurface);
glBindTexture(GL_TEXTURE_2D, m_glTexture);
IDirect3DSurface9 *dest = NULL;
// Copy the sample surface to the shared D3D/EGL surface
HRESULT hr = m_texture->GetSurfaceLevel(0, &dest);
if (FAILED(hr))
goto done;
hr = m_device->StretchRect(src, NULL, dest, NULL, D3DTEXF_NONE);
if (FAILED(hr))
qWarning("Failed to copy D3D surface");
if (hr == S_OK)
eglBindTexImage(m_eglDisplay, m_eglSurface, EGL_BACK_BUFFER);
done:
qt_wmf_safeRelease(&dest);
if (m_glContext)
m_glContext->doneCurrent();
return SUCCEEDED(hr);
}
HRESULT D3DPresentEngine::initializeD3D()
{
HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_D3D9);
if (SUCCEEDED(hr))
hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, &m_deviceManager);
return hr;
}
HRESULT D3DPresentEngine::createD3DDevice()
{
HRESULT hr = S_OK;
HWND hwnd = NULL;
UINT uAdapterID = D3DADAPTER_DEFAULT;
DWORD vp = 0;
D3DCAPS9 ddCaps;
ZeroMemory(&ddCaps, sizeof(ddCaps));
IDirect3DDevice9Ex* device = NULL;
// Hold the lock because we might be discarding an existing device.
QMutexLocker locker(&m_mutex);
if (!m_D3D9 || !m_deviceManager)
return MF_E_NOT_INITIALIZED;
hwnd = ::GetShellWindow();
// Note: The presenter creates additional swap chains to present the
// video frames. Therefore, it does not use the device's implicit
// swap chain, so the size of the back buffer here is 1 x 1.
D3DPRESENT_PARAMETERS pp;
ZeroMemory(&pp, sizeof(pp));
pp.BackBufferWidth = 1;
pp.BackBufferHeight = 1;
pp.BackBufferFormat = D3DFMT_UNKNOWN;
pp.BackBufferCount = 1;
pp.Windowed = TRUE;
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
pp.BackBufferFormat = D3DFMT_UNKNOWN;
pp.hDeviceWindow = hwnd;
pp.Flags = D3DPRESENTFLAG_VIDEO;
pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
hr = m_D3D9->GetDeviceCaps(uAdapterID, D3DDEVTYPE_HAL, &ddCaps);
if (FAILED(hr))
goto done;
if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
hr = m_D3D9->CreateDeviceEx(
uAdapterID,
D3DDEVTYPE_HAL,
pp.hDeviceWindow,
vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&pp,
NULL,
&device
);
if (FAILED(hr))
goto done;
hr = m_D3D9->GetAdapterDisplayMode(uAdapterID, &m_displayMode);
if (FAILED(hr))
goto done;
hr = m_deviceManager->ResetDevice(device, m_deviceResetToken);
if (FAILED(hr))
goto done;
qt_wmf_safeRelease(&m_device);
m_device = device;
m_device->AddRef();
done:
qt_wmf_safeRelease(&device);
return hr;
}
HRESULT D3DPresentEngine::createD3DSample(IDirect3DSwapChain9 *swapChain, IMFSample **videoSample)
{
D3DCOLOR clrBlack = D3DCOLOR_ARGB(0xFF, 0x00, 0x00, 0x00);
IDirect3DSurface9* surface = NULL;
IMFSample* sample = NULL;
// Get the back buffer surface.
HRESULT hr = swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &surface);
if (FAILED(hr))
goto done;
// Fill it with black.
hr = m_device->ColorFill(surface, NULL, clrBlack);
if (FAILED(hr))
goto done;
hr = MFCreateVideoSampleFromSurface(surface, &sample);
if (FAILED(hr))
goto done;
*videoSample = sample;
(*videoSample)->AddRef();
done:
qt_wmf_safeRelease(&surface);
qt_wmf_safeRelease(&sample);
return hr;
}
HRESULT D3DPresentEngine::getSwapChainPresentParameters(IMFMediaType *type, D3DPRESENT_PARAMETERS* pp)
{
ZeroMemory(pp, sizeof(D3DPRESENT_PARAMETERS));
// Get some information about the video format.
UINT32 width = 0, height = 0;
HRESULT hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
return hr;
DWORD d3dFormat = 0;
hr = qt_wmf_getFourCC(type, &d3dFormat);
if (FAILED(hr))
return hr;
ZeroMemory(pp, sizeof(D3DPRESENT_PARAMETERS));
pp->BackBufferWidth = width;
pp->BackBufferHeight = height;
pp->Windowed = TRUE;
pp->SwapEffect = D3DSWAPEFFECT_DISCARD;
pp->BackBufferFormat = (D3DFORMAT)d3dFormat;
pp->hDeviceWindow = ::GetShellWindow();
pp->Flags = D3DPRESENTFLAG_VIDEO;
pp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
D3DDEVICE_CREATION_PARAMETERS params;
hr = m_device->GetCreationParameters(&params);
if (FAILED(hr))
return hr;
if (params.DeviceType != D3DDEVTYPE_HAL)
pp->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
return S_OK;
}

View File

@@ -0,0 +1,130 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef EVRD3DPRESENTENGINE_H
#define EVRD3DPRESENTENGINE_H
#include <QObject>
#include <EGL/egl.h>
#include <QMutex>
#include <d3d9types.h>
#include <QVideoSurfaceFormat>
struct IDirect3D9Ex;
struct IDirect3DDevice9;
struct IDirect3DDevice9Ex;
struct IDirect3DDeviceManager9;
struct IDirect3DSurface9;
struct IDirect3DTexture9;
struct IMFSample;
struct IMFMediaType;
struct IDirect3DSwapChain9;
// Randomly generated GUIDs
static const GUID MFSamplePresenter_SampleCounter =
{ 0xb0bb83cc, 0xf10f, 0x4e2e, { 0xaa, 0x2b, 0x29, 0xea, 0x5e, 0x92, 0xef, 0x85 } };
static const GUID MFSamplePresenter_SampleSwapChain =
{ 0xad885bd1, 0x7def, 0x414a, { 0xb5, 0xb0, 0xd3, 0xd2, 0x63, 0xd6, 0xe9, 0x6d } };
QT_BEGIN_NAMESPACE
class QAbstractVideoSurface;
class QOpenGLContext;
class D3DPresentEngine : public QObject
{
Q_OBJECT
public:
D3DPresentEngine();
virtual ~D3DPresentEngine();
void start();
void stop();
HRESULT getService(REFGUID guidService, REFIID riid, void** ppv);
HRESULT checkFormat(D3DFORMAT format);
HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue);
void releaseResources();
UINT refreshRate() const { return m_displayMode.RefreshRate; }
void setSurface(QAbstractVideoSurface *surface);
void setSurfaceFormat(const QVideoSurfaceFormat &format);
void createOffscreenTexture();
bool updateTexture(IDirect3DSurface9 *src);
public Q_SLOTS:
void presentSample(void* sample, qint64 llTarget);
private:
HRESULT initializeD3D();
HRESULT getSwapChainPresentParameters(IMFMediaType *type, D3DPRESENT_PARAMETERS *pp);
HRESULT createD3DDevice();
HRESULT createD3DSample(IDirect3DSwapChain9 *swapChain, IMFSample **videoSample);
QMutex m_mutex;
UINT m_deviceResetToken;
D3DDISPLAYMODE m_displayMode;
IDirect3D9Ex *m_D3D9;
IDirect3DDevice9Ex *m_device;
IDirect3DDeviceManager9 *m_deviceManager;
QVideoSurfaceFormat m_surfaceFormat;
QAbstractVideoSurface *m_surface;
QOpenGLContext *m_glContext;
QWindow *m_offscreenSurface;
EGLDisplay *m_eglDisplay;
EGLConfig *m_eglConfig;
EGLSurface m_eglSurface;
unsigned int m_glTexture;
IDirect3DTexture9 *m_texture;
};
QT_END_NAMESPACE
#endif // EVRD3DPRESENTENGINE_H

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "mfactivate.h"
#include <mfapi.h>
MFAbstractActivate::MFAbstractActivate()
: m_attributes(0)
, m_cRef(1)
{
MFCreateAttributes(&m_attributes, 0);
}
MFAbstractActivate::~MFAbstractActivate()
{
if (m_attributes)
m_attributes->Release();
}
HRESULT MFAbstractActivate::QueryInterface(REFIID riid, LPVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
if (riid == IID_IMFActivate) {
*ppvObject = static_cast<IMFActivate*>(this);
} else if (riid == IID_IMFAttributes) {
*ppvObject = static_cast<IMFAttributes*>(this);
} else if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this));
} else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG MFAbstractActivate::AddRef(void)
{
return InterlockedIncrement(&m_cRef);
}
ULONG MFAbstractActivate::Release(void)
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (cRef == 0)
delete this;
return m_cRef;
}

View File

@@ -0,0 +1,216 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MFACTIVATE_H
#define MFACTIVATE_H
#include "mfglobal.h"
#include <mfidl.h>
class MFAbstractActivate : public IMFActivate
{
public:
explicit MFAbstractActivate();
virtual ~MFAbstractActivate();
//from IUnknown
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
//from IMFAttributes
STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue)
{
return m_attributes->GetItem(guidKey, pValue);
}
STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType)
{
return m_attributes->GetItemType(guidKey, pType);
}
STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult)
{
return m_attributes->CompareItem(guidKey, Value, pbResult);
}
STDMETHODIMP Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult)
{
return m_attributes->Compare(pTheirs, MatchType, pbResult);
}
STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32 *punValue)
{
return m_attributes->GetUINT32(guidKey, punValue);
}
STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64 *punValue)
{
return m_attributes->GetUINT64(guidKey, punValue);
}
STDMETHODIMP GetDouble(REFGUID guidKey, double *pfValue)
{
return m_attributes->GetDouble(guidKey, pfValue);
}
STDMETHODIMP GetGUID(REFGUID guidKey, GUID *pguidValue)
{
return m_attributes->GetGUID(guidKey, pguidValue);
}
STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32 *pcchLength)
{
return m_attributes->GetStringLength(guidKey, pcchLength);
}
STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength)
{
return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
}
STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength)
{
return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
}
STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize)
{
return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
}
STDMETHODIMP GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize)
{
return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
}
STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize)
{
return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
}
STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv)
{
return m_attributes->GetUnknown(guidKey, riid, ppv);
}
STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value)
{
return m_attributes->SetItem(guidKey, Value);
}
STDMETHODIMP DeleteItem(REFGUID guidKey)
{
return m_attributes->DeleteItem(guidKey);
}
STDMETHODIMP DeleteAllItems()
{
return m_attributes->DeleteAllItems();
}
STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue)
{
return m_attributes->SetUINT32(guidKey, unValue);
}
STDMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue)
{
return m_attributes->SetUINT64(guidKey, unValue);
}
STDMETHODIMP SetDouble(REFGUID guidKey, double fValue)
{
return m_attributes->SetDouble(guidKey, fValue);
}
STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue)
{
return m_attributes->SetGUID(guidKey, guidValue);
}
STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue)
{
return m_attributes->SetString(guidKey, wszValue);
}
STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize)
{
return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
}
STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown *pUnknown)
{
return m_attributes->SetUnknown(guidKey, pUnknown);
}
STDMETHODIMP LockStore()
{
return m_attributes->LockStore();
}
STDMETHODIMP UnlockStore()
{
return m_attributes->UnlockStore();
}
STDMETHODIMP GetCount(UINT32 *pcItems)
{
return m_attributes->GetCount(pcItems);
}
STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue)
{
return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
}
STDMETHODIMP CopyAllItems(IMFAttributes *pDest)
{
return m_attributes->CopyAllItems(pDest);
}
private:
IMFAttributes *m_attributes;
ULONG m_cRef;
};
#endif // MFACTIVATE_H

View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "mfglobal.h"
HRESULT qt_wmf_getFourCC(IMFMediaType *type, DWORD *fourCC)
{
if (!fourCC)
return E_POINTER;
HRESULT hr = S_OK;
GUID guidSubType = GUID_NULL;
if (SUCCEEDED(hr))
hr = type->GetGUID(MF_MT_SUBTYPE, &guidSubType);
if (SUCCEEDED(hr))
*fourCC = guidSubType.Data1;
return hr;
}
MFRatio qt_wmf_getPixelAspectRatio(IMFMediaType *type)
{
MFRatio ratio = { 0, 0 };
HRESULT hr = S_OK;
hr = MFGetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&ratio.Numerator, (UINT32*)&ratio.Denominator);
if (FAILED(hr)) {
ratio.Numerator = 1;
ratio.Denominator = 1;
}
return ratio;
}
bool qt_wmf_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
{
if (!type1 && !type2)
return true;
else if (!type1 || !type2)
return false;
DWORD dwFlags = 0;
HRESULT hr = type1->IsEqual(type2, &dwFlags);
return (hr == S_OK);
}
HRESULT qt_wmf_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
{
float fOffsetX = qt_wmf_MFOffsetToFloat(area.OffsetX);
float fOffsetY = qt_wmf_MFOffsetToFloat(area.OffsetY);
if ( ((LONG)fOffsetX + area.Area.cx > (LONG)width) ||
((LONG)fOffsetY + area.Area.cy > (LONG)height) )
return MF_E_INVALIDMEDIATYPE;
else
return S_OK;
}
bool qt_wmf_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
{
if (!sample || !clock)
return false;
HRESULT hr = S_OK;
MFTIME hnsTimeNow = 0;
MFTIME hnsSystemTime = 0;
MFTIME hnsSampleStart = 0;
MFTIME hnsSampleDuration = 0;
hr = clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
if (SUCCEEDED(hr))
hr = sample->GetSampleTime(&hnsSampleStart);
if (SUCCEEDED(hr))
hr = sample->GetSampleDuration(&hnsSampleDuration);
if (SUCCEEDED(hr)) {
if (hnsSampleStart + hnsSampleDuration < hnsTimeNow)
return true;
}
return false;
}

157
src/plugins/wmf/mfglobal.h Normal file
View File

@@ -0,0 +1,157 @@
/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MFGLOBAL_H
#define MFGLOBAL_H
#include <mfapi.h>
#include <mfidl.h>
#include <Mferror.h>
template<class T>
class AsyncCallback : public IMFAsyncCallback
{
public:
typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *asyncResult);
AsyncCallback(T *parent, InvokeFn fn) : m_parent(parent), m_invokeFn(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_parent->AddRef();
}
STDMETHODIMP_(ULONG) Release() {
// Delegate to parent class.
return m_parent->Release();
}
// IMFAsyncCallback methods
STDMETHODIMP GetParameters(DWORD*, DWORD*)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
STDMETHODIMP Invoke(IMFAsyncResult* asyncResult)
{
return (m_parent->*m_invokeFn)(asyncResult);
}
T *m_parent;
InvokeFn m_invokeFn;
};
template <class T> void qt_wmf_safeRelease(T **ppT)
{
if (*ppT) {
(*ppT)->Release();
*ppT = NULL;
}
}
template <class T>
void qt_wmf_copyComPointer(T* &dest, T *src)
{
if (dest)
dest->Release();
dest = src;
if (dest)
dest->AddRef();
}
HRESULT qt_wmf_getFourCC(IMFMediaType *type, DWORD *fourCC);
MFRatio qt_wmf_getPixelAspectRatio(IMFMediaType *type);
bool qt_wmf_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2);
HRESULT qt_wmf_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height);
bool qt_wmf_isSampleTimePassed(IMFClock *clock, IMFSample *sample);
inline float qt_wmf_MFOffsetToFloat(const MFOffset& offset)
{
return offset.value + (float(offset.fract) / 65536);
}
inline MFOffset qt_wmf_makeMFOffset(float v)
{
MFOffset offset;
offset.value = short(v);
offset.fract = WORD(65536 * (v-offset.value));
return offset;
}
inline MFVideoArea qt_wmf_makeMFArea(float x, float y, DWORD width, DWORD height)
{
MFVideoArea area;
area.OffsetX = qt_wmf_makeMFOffset(x);
area.OffsetY = qt_wmf_makeMFOffset(y);
area.Area.cx = width;
area.Area.cy = height;
return area;
}
inline HRESULT qt_wmf_getFrameRate(IMFMediaType *pType, MFRatio *pRatio)
{
return MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&pRatio->Numerator, (UINT32*)&pRatio->Denominator);
}
#endif // MFGLOBAL_H

View File

@@ -40,7 +40,10 @@
****************************************************************************/ ****************************************************************************/
#include "mfvideorenderercontrol.h" #include "mfvideorenderercontrol.h"
#include <mferror.h> #include "mfglobal.h"
#ifdef QT_OPENGL_ES_2_ANGLE
#include "evrcustompresenter.h"
#endif
#include <qabstractvideosurface.h> #include <qabstractvideosurface.h>
#include <qvideosurfaceformat.h> #include <qvideosurfaceformat.h>
#include <qtcore/qtimer.h> #include <qtcore/qtimer.h>
@@ -114,67 +117,6 @@ namespace
MapMode m_mapMode; 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. // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7") MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7")
IMarker : public IUnknown IMarker : public IUnknown
@@ -2134,6 +2076,9 @@ MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
, m_surface(0) , m_surface(0)
, m_currentActivate(0) , m_currentActivate(0)
, m_callback(0) , m_callback(0)
#ifdef QT_OPENGL_ES_2_ANGLE
, m_presenterActivate(0)
#endif
{ {
} }
@@ -2147,6 +2092,14 @@ void MFVideoRendererControl::clear()
if (m_surface) if (m_surface)
m_surface->stop(); 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) { if (m_currentActivate) {
m_currentActivate->ShutdownObject(); m_currentActivate->ShutdownObject();
m_currentActivate->Release(); m_currentActivate->Release();
@@ -2174,12 +2127,22 @@ void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged())); 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) if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface); static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface);
} }
void MFVideoRendererControl::customEvent(QEvent *event) void MFVideoRendererControl::customEvent(QEvent *event)
{ {
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
return;
#endif
if (!m_currentActivate) if (!m_currentActivate)
return; return;
@@ -2203,23 +2166,48 @@ void MFVideoRendererControl::customEvent(QEvent *event)
void MFVideoRendererControl::supportedFormatsChanged() void MFVideoRendererControl::supportedFormatsChanged()
{ {
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
m_presenterActivate->supportedFormatsChanged();
else
#endif
if (m_currentActivate) if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged(); static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged();
} }
void MFVideoRendererControl::present() void MFVideoRendererControl::present()
{ {
#ifdef QT_OPENGL_ES_2_ANGLE
if (m_presenterActivate)
return;
#endif
if (m_currentActivate) if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->present(); static_cast<VideoRendererActivate*>(m_currentActivate)->present();
} }
IMFActivate* MFVideoRendererControl::createActivate() IMFActivate* MFVideoRendererControl::createActivate()
{ {
Q_ASSERT(m_surface);
clear(); 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); m_currentActivate = new VideoRendererActivate(this);
if (m_surface)
setSurface(m_surface); setSurface(m_surface);
return m_currentActivate; return m_currentActivate;
} }

View File

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

View File

@@ -16,14 +16,32 @@ HEADERS += \
mfstream.h \ mfstream.h \
sourceresolver.h \ sourceresolver.h \
samplegrabber.h \ samplegrabber.h \
mftvideo.h mftvideo.h \
mfglobal.h \
mfactivate.h
SOURCES += \ SOURCES += \
wmfserviceplugin.cpp \ wmfserviceplugin.cpp \
mfstream.cpp \ mfstream.cpp \
sourceresolver.cpp \ sourceresolver.cpp \
samplegrabber.cpp \ samplegrabber.cpp \
mftvideo.cpp mftvideo.cpp \
mfactivate.cpp \
mfglobal.cpp
contains(QT_CONFIG, angle) {
LIBS += -ld3d9 -ldxva2 -lwinmm -levr
QT += gui-private
HEADERS += \
evrcustompresenter.h \
evrd3dpresentengine.h
SOURCES += \
evrcustompresenter.cpp \
evrd3dpresentengine.cpp
}
include (player/player.pri) include (player/player.pri)
include (decoder/decoder.pri) include (decoder/decoder.pri)