summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Knight <andrew.knight@digia.com>2014-08-21 10:55:10 +0300
committerMaurice Kalinowski <maurice.kalinowski@digia.com>2014-08-25 16:05:24 +0200
commit778b233bcdbc2942a44efd468f6bfb4c0ce5d5cf (patch)
tree69e00b0db784542badbb2a6d3200b2356f1c3d3c
parent5d04ed2a0a02e483196bd88ca059b7cfcb52a6ac (diff)
winrt: Add media playback service
This introduces a MediaPlayer service based on the MFEngineEx API. Only scene graph video rendering is supported at this time. The video renderer control is abstracted in order to provide a base for future video nodes which do not source their content from the MF engine. [ChangeLog] Media player support was added to the winrt backend. Change-Id: I8155a1030466ea352fad0a87d1ae97a88983760c Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
-rw-r--r--src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp403
-rw-r--r--src/plugins/winrt/qwinrtabstractvideorenderercontrol.h87
-rw-r--r--src/plugins/winrt/qwinrtmediaplayercontrol.cpp899
-rw-r--r--src/plugins/winrt/qwinrtmediaplayercontrol.h104
-rw-r--r--src/plugins/winrt/qwinrtmediaplayerservice.cpp111
-rw-r--r--src/plugins/winrt/qwinrtmediaplayerservice.h67
-rw-r--r--src/plugins/winrt/qwinrtplayerrenderercontrol.cpp156
-rw-r--r--src/plugins/winrt/qwinrtplayerrenderercontrol.h72
-rw-r--r--src/plugins/winrt/qwinrtserviceplugin.cpp9
-rw-r--r--src/plugins/winrt/winrt.json2
-rw-r--r--src/plugins/winrt/winrt.pro8
-rw-r--r--tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp4
12 files changed, 1921 insertions, 1 deletions
diff --git a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
new file mode 100644
index 000000000..175fec1d5
--- /dev/null
+++ b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "qwinrtabstractvideorenderercontrol.h"
+
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QGlobalStatic>
+#include <QtCore/QMetaMethod>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtMultimedia/QAbstractVideoBuffer>
+#include <QtMultimedia/QAbstractVideoSurface>
+#include <QtMultimedia/QVideoSurfaceFormat>
+
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <d3d11.h>
+#include <mfapi.h>
+#include <wrl.h>
+
+using namespace Microsoft::WRL;
+
+QT_USE_NAMESPACE
+
+#define BREAK_IF_FAILED(msg) RETURN_IF_FAILED(msg, break)
+#define CONTINUE_IF_FAILED(msg) RETURN_IF_FAILED(msg, continue)
+
+// Global D3D device to be shared between video surfaces
+struct QWinRTVideoRendererControlGlobal
+{
+ QWinRTVideoRendererControlGlobal()
+ {
+ HRESULT hr;
+
+ D3D_FEATURE_LEVEL featureLevels[] =
+ {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1
+ };
+
+ UINT flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+#ifdef _DEBUG
+ flags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+ hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags,
+ featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION,
+ &device, &featureLevel, &context);
+ if (FAILED(hr))
+ qErrnoWarning(hr, "Failed to create D3D device");
+
+ if (!device || FAILED(hr)) {
+ hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, flags,
+ featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION,
+ &device, &featureLevel, &context);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to create software D3D device");
+ return;
+ }
+ }
+
+ ComPtr<ID3D10Multithread> multithread;
+ hr = device.As(&multithread);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = multithread->SetMultithreadProtected(true);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IDXGIDevice> dxgiDevice;
+ hr = device.As(&dxgiDevice);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IDXGIAdapter> adapter;
+ hr = dxgiDevice->GetAdapter(&adapter);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = adapter->EnumOutputs(0, &output);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ComPtr<ID3D11Device> device;
+ ComPtr<ID3D11DeviceContext> context;
+ D3D_FEATURE_LEVEL featureLevel;
+ ComPtr<IDXGIOutput> output;
+};
+Q_GLOBAL_STATIC(QWinRTVideoRendererControlGlobal, g)
+
+class QWinRTVideoBuffer : public QAbstractVideoBuffer, public QOpenGLTexture
+{
+public:
+ QWinRTVideoBuffer()
+ : QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle)
+ , QOpenGLTexture(QOpenGLTexture::Target2D)
+ {
+ }
+
+ void addRef()
+ {
+ refCount.ref();
+ }
+
+ void release() Q_DECL_OVERRIDE
+ {
+ if (!refCount.deref())
+ delete this;
+ }
+
+ MapMode mapMode() const Q_DECL_OVERRIDE
+ {
+ return NotMapped;
+ }
+
+ uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(mode);
+ Q_UNUSED(numBytes);
+ Q_UNUSED(bytesPerLine);
+ return 0;
+ }
+
+ void unmap() Q_DECL_OVERRIDE
+ {
+ }
+
+ QVariant handle() const Q_DECL_OVERRIDE
+ {
+ return QVariant::fromValue(textureId());
+ }
+
+private:
+ QAtomicInt refCount;
+};
+
+enum DirtyState {
+ NotDirty, // All resources have been created
+ TextureDirty, // The shared D3D texture needs to be recreated
+ SurfaceDirty // The shared EGL surface needs to be recreated
+};
+
+class QWinRTAbstractVideoRendererControlPrivate
+{
+public:
+ QAbstractVideoSurface *surface;
+ QVideoSurfaceFormat format;
+
+ DirtyState dirtyState;
+
+ HANDLE shareHandle;
+ ComPtr<ID3D11Texture2D> texture;
+
+ EGLDisplay eglDisplay;
+ EGLConfig eglConfig;
+ EGLSurface eglSurface;
+
+ QWinRTVideoBuffer *videoBuffer;
+
+ QThread renderThread;
+ bool active;
+};
+
+ID3D11Device *QWinRTAbstractVideoRendererControl::d3dDevice()
+{
+ return g->device.Get();
+}
+
+// This is required so that subclasses can stop the render thread before deletion
+void QWinRTAbstractVideoRendererControl::shutdown()
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+ if (d->renderThread.isRunning()) {
+ d->renderThread.requestInterruption();
+ d->renderThread.wait();
+ }
+}
+
+QWinRTAbstractVideoRendererControl::QWinRTAbstractVideoRendererControl(const QSize &size, QObject *parent)
+ : QVideoRendererControl(parent), d_ptr(new QWinRTAbstractVideoRendererControlPrivate)
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+
+ d->surface = Q_NULLPTR;
+ d->format = QVideoSurfaceFormat(size, QVideoFrame::Format_BGRA32,
+ QAbstractVideoBuffer::GLTextureHandle);
+ d->dirtyState = TextureDirty;
+ d->shareHandle = 0;
+ d->eglDisplay = EGL_NO_DISPLAY;
+ d->eglConfig = 0;
+ d->eglSurface = EGL_NO_SURFACE;
+ d->active = false;
+
+ d->videoBuffer = new QWinRTVideoBuffer;
+
+ connect(&d->renderThread, &QThread::started,
+ this, &QWinRTAbstractVideoRendererControl::syncAndRender,
+ Qt::DirectConnection);
+}
+
+QWinRTAbstractVideoRendererControl::~QWinRTAbstractVideoRendererControl()
+{
+ shutdown();
+}
+
+QAbstractVideoSurface *QWinRTAbstractVideoRendererControl::surface() const
+{
+ Q_D(const QWinRTAbstractVideoRendererControl);
+ return d->surface;
+}
+
+void QWinRTAbstractVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+ d->surface = surface;
+}
+
+void QWinRTAbstractVideoRendererControl::syncAndRender()
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+
+ QThread *currentThread = QThread::currentThread();
+ const QMetaMethod present = staticMetaObject.method(staticMetaObject.indexOfMethod("present()"));
+ forever {
+ if (currentThread->isInterruptionRequested())
+ break;
+
+ HRESULT hr;
+ if (d->dirtyState == TextureDirty) {
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, d->format.frameWidth(), d->format.frameHeight(), 1, 1);
+ desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ hr = g->device->CreateTexture2D(&desc, NULL, d->texture.ReleaseAndGetAddressOf());
+ BREAK_IF_FAILED("Failed to get create video texture");
+ ComPtr<IDXGIResource> resource;
+ hr = d->texture.As(&resource);
+ BREAK_IF_FAILED("Failed to cast texture to resource");
+ hr = resource->GetSharedHandle(&d->shareHandle);
+ BREAK_IF_FAILED("Failed to get texture share handle");
+ d->dirtyState = SurfaceDirty;
+ }
+
+ hr = g->output->WaitForVBlank();
+ CONTINUE_IF_FAILED("Failed to wait for vertical blank");
+
+ if (!render(d->texture.Get()))
+ continue;
+
+ // Queue to the control's thread for presentation
+ present.invoke(this, Qt::QueuedConnection);
+ currentThread->eventDispatcher()->processEvents(QEventLoop::AllEvents);
+ }
+
+ // All done, exit render loop
+ currentThread->quit();
+}
+
+QSize QWinRTAbstractVideoRendererControl::size() const
+{
+ Q_D(const QWinRTAbstractVideoRendererControl);
+ return d->format.frameSize();
+}
+
+void QWinRTAbstractVideoRendererControl::setSize(const QSize &size)
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+
+ if (d->format.frameSize() == size)
+ return;
+
+ d->format.setFrameSize(size);
+ d->dirtyState = TextureDirty;
+}
+
+void QWinRTAbstractVideoRendererControl::setActive(bool active)
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+
+ if (d->active == active)
+ return;
+
+ d->active = active;
+ if (d->active) {
+ if (!d->surface)
+ return;
+
+ if (!d->surface->isActive())
+ d->surface->start(d->format);
+
+ d->renderThread.start();
+ return;
+ }
+
+ d->renderThread.requestInterruption();
+}
+
+void QWinRTAbstractVideoRendererControl::present()
+{
+ Q_D(QWinRTAbstractVideoRendererControl);
+
+ if (d->dirtyState == SurfaceDirty) {
+ if (!QOpenGLContext::currentContext()) {
+ qWarning("A valid OpenGL context is required for binding the video texture.");
+ return;
+ }
+
+ if (d->eglDisplay == EGL_NO_DISPLAY)
+ d->eglDisplay = eglGetCurrentDisplay();
+
+ if (d->eglDisplay == EGL_NO_DISPLAY) {
+ qWarning("Failed to get the current EGL display for video presentation: 0x%x", eglGetError());
+ return;
+ }
+
+ EGLint configAttributes[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE,
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_NONE
+ };
+ EGLint configCount;
+ if (!eglChooseConfig(d->eglDisplay, configAttributes, &d->eglConfig, 1, &configCount)) {
+ qWarning("Failed to create the texture EGL configuration for video presentation: 0x%x", eglGetError());
+ return;
+ }
+
+ if (d->eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(d->eglDisplay, d->eglSurface);
+
+ EGLint bufferAttributes[] = {
+ EGL_WIDTH, d->format.frameWidth(),
+ EGL_HEIGHT, d->format.frameHeight(),
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_NONE
+ };
+ d->eglSurface = eglCreatePbufferFromClientBuffer(d->eglDisplay,
+ EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
+ d->shareHandle, d->eglConfig, bufferAttributes);
+ if (d->eglSurface == EGL_NO_SURFACE) {
+ qWarning("Failed to create the EGL configuration for video presentation: 0x%x", eglGetError());
+ return;
+ }
+
+ d->videoBuffer->setFormat(QOpenGLTexture::RGBAFormat);
+ d->videoBuffer->setSize(d->format.frameWidth(), d->format.frameHeight());
+ if (!d->videoBuffer->isCreated())
+ d->videoBuffer->create();
+
+ // bind the pbuffer surface to the texture
+ d->videoBuffer->bind();
+ eglBindTexImage(d->eglDisplay, d->eglSurface, EGL_BACK_BUFFER);
+ static_cast<QOpenGLTexture *>(d->videoBuffer)->release();
+
+ d->dirtyState = NotDirty;
+ }
+
+ // Present the frame
+ d->videoBuffer->addRef();
+ QVideoFrame frame(d->videoBuffer, d->format.frameSize(), d->format.pixelFormat());
+ d->surface->present(frame);
+}
diff --git a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h
new file mode 100644
index 000000000..86a7b15f9
--- /dev/null
+++ b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 QWINRTABSTRACTVIDEORENDERERCONTROL_H
+#define QWINRTABSTRACTVIDEORENDERERCONTROL_H
+
+#include <QtMultimedia/QVideoRendererControl>
+
+struct ID3D11Device;
+struct ID3D11Texture2D;
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTAbstractVideoRendererControlPrivate;
+class QWinRTAbstractVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ explicit QWinRTAbstractVideoRendererControl(const QSize &size, QObject *parent = 0);
+ ~QWinRTAbstractVideoRendererControl();
+
+ QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE;
+ void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
+
+ QSize size() const;
+ void setSize(const QSize &size);
+
+ void setActive(bool active);
+
+ virtual bool render(ID3D11Texture2D *texture) = 0;
+
+ static ID3D11Device *d3dDevice();
+
+protected:
+ void shutdown();
+
+private slots:
+ void syncAndRender();
+
+private:
+ Q_INVOKABLE void present();
+
+ QScopedPointer<QWinRTAbstractVideoRendererControlPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QWinRTAbstractVideoRendererControl)
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTABSTRACTVIDEORENDERERCONTROL_H
diff --git a/src/plugins/winrt/qwinrtmediaplayercontrol.cpp b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp
new file mode 100644
index 000000000..66c0bd568
--- /dev/null
+++ b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp
@@ -0,0 +1,899 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "qwinrtmediaplayercontrol.h"
+#include "qwinrtplayerrenderercontrol.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFile>
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QSize>
+#include <QtCore/QTimerEvent>
+#include <QtCore/QUrl>
+#include <QtMultimedia/QMediaPlaylist>
+#include <QtConcurrent/QtConcurrentRun>
+
+#include <dxgi.h>
+#include <oleauto.h>
+#include <mfapi.h>
+#include <mfmediaengine.h>
+
+#include <comdef.h>
+#include <wrl.h>
+using namespace Microsoft::WRL;
+
+QT_USE_NAMESPACE
+
+#define QT_WINRT_MEDIAPLAYER_STREAM_ID "__qtmultimedia_winrt_player_stream"
+
+class MediaEngineNotify;
+class MediaEngineSources;
+class MediaEngineByteStream;
+class QWinRTMediaPlayerControlPrivate
+{
+public:
+ QMediaPlayer::State state;
+ QMediaPlayer::MediaStatus mediaStatus;
+ qint64 duration;
+ qint64 position;
+ qreal playbackRate;
+ int volume;
+ bool muted;
+ int bufferStatus;
+ bool seekable;
+ bool hasVideo;
+ bool hasAudio;
+
+ QMediaContent media;
+ QScopedPointer<QIODevice, QWinRTMediaPlayerControlPrivate> stream;
+ QWinRTPlayerRendererControl *videoRenderer;
+
+ ComPtr<MediaEngineNotify> notifier;
+ ComPtr<MediaEngineSources> sources;
+ ComPtr<MediaEngineByteStream> streamProvider;
+ ComPtr<IMFAttributes> configuration;
+ ComPtr<IMFMediaEngineEx> engine;
+
+ quint32 resetToken;
+ ComPtr<IMFDXGIDeviceManager> manager;
+
+ // Automatically delete streams created by the player
+ static inline void cleanup(QIODevice *device)
+ {
+ if (device && device->property(QT_WINRT_MEDIAPLAYER_STREAM_ID).toBool())
+ device->deleteLater();
+ }
+
+ // Allows for deferred cleanup of the engine, which tends to block on shutdown
+ static void cleanup(QWinRTMediaPlayerControlPrivate *d);
+ static void shutdown(QWinRTMediaPlayerControlPrivate *d);
+};
+
+class MediaEngineNotify : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IMFMediaEngineNotify>
+{
+public:
+ MediaEngineNotify(QWinRTMediaPlayerControl *q_ptr, QWinRTMediaPlayerControlPrivate *d_ptr)
+ : q(q_ptr), d(d_ptr)
+ {
+ }
+
+ HRESULT __stdcall EventNotify(DWORD event, DWORD_PTR param1, DWORD param2)
+ {
+ QMediaPlayer::State newState = d->state;
+ QMediaPlayer::MediaStatus newStatus = d->mediaStatus;
+
+ switch (event) {
+ // Media change events
+ case MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA: {
+ const bool hasAudio = d->engine->HasAudio();
+ if (d->hasAudio != hasAudio) {
+ d->hasAudio = hasAudio;
+ emit q->audioAvailableChanged(d->hasAudio);
+ }
+
+ const bool hasVideo = d->engine->HasVideo();
+ if (d->hasVideo != hasVideo) {
+ d->hasVideo = hasVideo;
+ emit q->audioAvailableChanged(d->hasAudio);
+ }
+
+ if (hasVideo) {
+ HRESULT hr;
+ DWORD width, height;
+ hr = d->engine->GetNativeVideoSize(&width, &height);
+ if (FAILED(hr))
+ break;
+ d->videoRenderer->setSize(QSize(width, height));
+ }
+
+ newStatus = QMediaPlayer::LoadedMedia;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_LOADSTART:
+ case MF_MEDIA_ENGINE_EVENT_PROGRESS: {
+ newStatus = QMediaPlayer::LoadingMedia;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_CANPLAY:
+ d->bufferStatus = 100; // Fired when buffering is not used
+ newStatus = d->state == QMediaPlayer::StoppedState ? QMediaPlayer::LoadedMedia
+ : QMediaPlayer::BufferedMedia;
+ break;
+ case MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED:
+ case MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED: {
+ PROPVARIANT stat;
+ HRESULT hr = d->engine->GetStatistics(MF_MEDIA_ENGINE_STATISTIC_BUFFER_PROGRESS, &stat);
+ if (SUCCEEDED(hr)) {
+ d->bufferStatus = stat.lVal;
+ PropVariantClear(&stat);
+ }
+ newStatus = d->state == QMediaPlayer::StoppedState ? QMediaPlayer::LoadedMedia
+ : (d->bufferStatus == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia);
+ break;
+ }
+ //case MF_MEDIA_ENGINE_EVENT_SUSPEND: ???
+ //case MF_MEDIA_ENGINE_EVENT_ABORT: ???
+ case MF_MEDIA_ENGINE_EVENT_EMPTIED: {
+ newState = QMediaPlayer::StoppedState;
+ break;
+ }
+ // Transport controls
+ case MF_MEDIA_ENGINE_EVENT_PLAY: {
+ // If the media is already loaded, the playing event may not occur after stop
+ if (d->mediaStatus != QMediaPlayer::LoadedMedia)
+ break;
+ // fall through
+ }
+ case MF_MEDIA_ENGINE_EVENT_PLAYING: {
+ newState = QMediaPlayer::PlayingState;
+ newStatus = d->bufferStatus < 100 ? QMediaPlayer::BufferingMedia
+ : QMediaPlayer::BufferedMedia;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_PAUSE: {
+ newState = QMediaPlayer::PausedState;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_STALLED: {
+ newStatus = QMediaPlayer::StalledMedia;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_WAITING: {
+ newStatus = QMediaPlayer::StalledMedia;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_ENDED: {
+ newState = QMediaPlayer::StoppedState;
+ newStatus = QMediaPlayer::EndOfMedia;
+ break;
+ }
+ // Media attributes
+ case MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE: {
+ double duration = d->engine->GetDuration() * 1000;
+ if (!qFuzzyCompare(d->duration, duration)) {
+ d->duration = duration;
+ emit q->durationChanged(d->duration);
+ }
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: {
+ double position = d->engine->GetCurrentTime() * 1000;
+ if (!qFuzzyCompare(d->position, position)) {
+ d->position = position;
+ emit q->positionChanged(d->position);
+ }
+ // Stopped state: paused at beginning
+ if (qFuzzyIsNull(position) && d->state == QMediaPlayer::PausedState)
+ newState = QMediaPlayer::StoppedState;
+ break;
+ }
+ case MF_MEDIA_ENGINE_EVENT_RATECHANGE: {
+ double playbackRate = d->engine->GetPlaybackRate();
+ if (!qFuzzyCompare(d->playbackRate, playbackRate)) {
+ d->playbackRate = playbackRate;
+ emit q->playbackRateChanged(d->playbackRate);
+ }
+ break;
+ }
+ // Error handling
+ case MF_MEDIA_ENGINE_EVENT_ERROR: {
+ newState = QMediaPlayer::StoppedState;
+ newStatus = QMediaPlayer::InvalidMedia;
+ switch (param1) {
+ default:
+ case MF_MEDIA_ENGINE_ERR_NOERROR:
+ newStatus = QMediaPlayer::UnknownMediaStatus;
+ emit q->error(QMediaPlayer::ResourceError, qt_error_string(param2));
+ break;
+ case MF_MEDIA_ENGINE_ERR_ABORTED:
+ if (d->mediaStatus == QMediaPlayer::StalledMedia || d->mediaStatus == QMediaPlayer::BufferingMedia)
+ d->mediaStatus = QMediaPlayer::LoadedMedia;
+ emit q->error(QMediaPlayer::ResourceError, QStringLiteral("The process of fetching the media resource was stopped at the user's request."));
+ break;
+ case MF_MEDIA_ENGINE_ERR_NETWORK:
+ if (d->mediaStatus == QMediaPlayer::StalledMedia || d->mediaStatus == QMediaPlayer::BufferingMedia)
+ d->mediaStatus = QMediaPlayer::LoadedMedia;
+ emit q->error(QMediaPlayer::NetworkError, QStringLiteral("A network error occurred while fetching the media resource."));
+ break;
+ case MF_MEDIA_ENGINE_ERR_DECODE:
+ emit q->error(QMediaPlayer::FormatError, QStringLiteral("An error occurred while decoding the media resource."));
+ break;
+ case MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED:
+ emit q->error(QMediaPlayer::FormatError, QStringLiteral("The media resource is not supported."));
+ break;
+ case MF_MEDIA_ENGINE_ERR_ENCRYPTED:
+ emit q->error(QMediaPlayer::FormatError, QStringLiteral("An error occurred while encrypting the media resource."));
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (d->state != newState) {
+ d->state = newState;
+ emit q->stateChanged(d->state);
+ }
+
+ if (d->videoRenderer)
+ d->videoRenderer->setActive(d->state == QMediaPlayer::PlayingState);
+
+ if (d->mediaStatus != newStatus) {
+ d->mediaStatus = newStatus;
+ emit q->mediaStatusChanged(d->mediaStatus);
+ }
+
+ return S_OK;
+ }
+
+private:
+ QWinRTMediaPlayerControl *q;
+ QWinRTMediaPlayerControlPrivate *d;
+};
+
+class MediaEngineSources : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IMFMediaEngineSrcElements>
+{
+public:
+ MediaEngineSources(QWinRTMediaPlayerControlPrivate *d_ptr)
+ : d(d_ptr)
+ {
+ }
+
+ DWORD __stdcall GetLength()
+ {
+ return d->media.resources().length();
+ }
+
+ HRESULT __stdcall GetURL(DWORD index, BSTR *url)
+ {
+ const QString resourceUrl = d->media.resources().value(index).url().toString();
+ *url = SysAllocString((const OLECHAR *)resourceUrl.utf16());
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetType(DWORD index, BSTR *type)
+ {
+ const QString resourceType = d->media.resources().value(index).mimeType();
+ *type = SysAllocString((const OLECHAR *)resourceType.utf16());
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetMedia(DWORD index, BSTR *media)
+ {
+ Q_UNUSED(index);
+ *media = NULL;
+ return S_OK;
+ }
+
+ HRESULT __stdcall AddElement(BSTR url, BSTR type, BSTR media)
+ {
+ Q_UNUSED(url);
+ Q_UNUSED(type);
+ Q_UNUSED(media);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall RemoveAllElements()
+ {
+ return E_NOTIMPL;
+ }
+
+private:
+ QWinRTMediaPlayerControlPrivate *d;
+};
+
+class MediaEngineReadResult : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IUnknown>
+{
+public:
+ MediaEngineReadResult(BYTE *bytes, ULONG maxLength)
+ : bytes(bytes), maxLength(maxLength) { }
+
+ void read(QIODevice *device)
+ {
+ bytesRead = device->read(reinterpret_cast<char *>(bytes), maxLength);
+ }
+
+ BYTE *bytes;
+ ULONG maxLength;
+ ULONG bytesRead;
+};
+
+class MediaEngineByteStream : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IMFByteStream>
+{
+public:
+ MediaEngineByteStream(QWinRTMediaPlayerControl *q_ptr, QWinRTMediaPlayerControlPrivate *d_ptr)
+ : q(q_ptr), d(d_ptr)
+ {
+ }
+
+ HRESULT __stdcall GetCapabilities(DWORD *capabilities)
+ {
+ *capabilities |= MFBYTESTREAM_IS_READABLE;
+ if (!d->stream->isSequential())
+ *capabilities |= MFBYTESTREAM_IS_SEEKABLE;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetLength(QWORD *length)
+ {
+ *length = QWORD(d->stream->size());
+ return S_OK;
+ }
+
+ HRESULT __stdcall SetLength(QWORD length)
+ {
+ Q_UNUSED(length);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall GetCurrentPosition(QWORD *position)
+ {
+ *position = QWORD(d->stream->pos());
+ return S_OK;
+ }
+
+ HRESULT __stdcall SetCurrentPosition(QWORD position)
+ {
+ qint64 pos(position);
+ if (pos >= d->stream->size())
+ return E_INVALIDARG;
+
+ const bool ok = d->stream->seek(pos);
+ return ok ? S_OK : S_FALSE;
+ }
+
+ HRESULT __stdcall IsEndOfStream(BOOL *endOfStream)
+ {
+ *endOfStream = d->stream->atEnd() ? TRUE : FALSE;
+ return S_OK;
+ }
+
+ HRESULT __stdcall Read(BYTE *bytes, ULONG maxlen, ULONG *bytesRead)
+ {
+ *bytesRead = d->stream->read(reinterpret_cast<char *>(bytes), maxlen);
+ return S_OK;
+ }
+
+ HRESULT __stdcall BeginRead(BYTE *bytes, ULONG maxLength, IMFAsyncCallback *callback, IUnknown *state)
+ {
+ ComPtr<MediaEngineReadResult> readResult = Make<MediaEngineReadResult>(bytes, maxLength);
+ HRESULT hr;
+ hr = MFCreateAsyncResult(readResult.Get(), callback, state, &asyncResult);
+ RETURN_HR_IF_FAILED("Failed to create read callback result");
+ QMetaObject::invokeMethod(q, "finishRead", Qt::QueuedConnection);
+ return S_OK;
+ }
+
+ HRESULT __stdcall EndRead(IMFAsyncResult *result, ULONG *bytesRead)
+ {
+ HRESULT hr;
+ ComPtr<MediaEngineReadResult> readResult;
+ hr = result->GetObject(&readResult);
+ RETURN_HR_IF_FAILED("Failed to get read result");
+
+ *bytesRead = readResult->bytesRead;
+ return S_OK;
+ }
+
+ HRESULT __stdcall Write(const BYTE *bytes, ULONG maxlen, ULONG *bytesWritten)
+ {
+ Q_UNUSED(bytes);
+ Q_UNUSED(maxlen);
+ Q_UNUSED(bytesWritten);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall BeginWrite(const BYTE *bytes, ULONG maxlen, IMFAsyncCallback *callback, IUnknown *state)
+ {
+ Q_UNUSED(bytes);
+ Q_UNUSED(maxlen);
+ Q_UNUSED(callback);
+ Q_UNUSED(state);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall EndWrite(IMFAsyncResult *result, ULONG *bytesWritten)
+ {
+ Q_UNUSED(result);
+ Q_UNUSED(bytesWritten);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Seek(MFBYTESTREAM_SEEK_ORIGIN origin, LONGLONG offset, DWORD flags, QWORD *position)
+ {
+ Q_UNUSED(flags);
+
+ qint64 pos = offset;
+ if (origin == msoCurrent)
+ pos += d->stream->pos();
+
+ const bool ok = d->stream->seek(pos);
+ *position = QWORD(d->stream->pos());
+
+ return ok ? S_OK : E_FAIL;
+ }
+
+ HRESULT __stdcall Flush()
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Close()
+ {
+ if (asyncResult)
+ finishRead();
+
+ if (d->stream->property(QT_WINRT_MEDIAPLAYER_STREAM_ID).toBool())
+ d->stream->close();
+
+ return S_OK;
+ }
+
+ void finishRead()
+ {
+ if (!asyncResult)
+ return;
+
+ HRESULT hr;
+ ComPtr<MediaEngineReadResult> readResult;
+ hr = asyncResult->GetObject(&readResult);
+ RETURN_VOID_IF_FAILED("Failed to get read result object");
+ readResult->read(d->stream.data());
+ hr = MFInvokeCallback(asyncResult.Get());
+ RETURN_VOID_IF_FAILED("Failed to invoke read callback");
+ asyncResult.Reset();
+ }
+
+private:
+ QWinRTMediaPlayerControl *q;
+ QWinRTMediaPlayerControlPrivate *d;
+
+ ComPtr<IMFAsyncResult> asyncResult;
+};
+
+void QWinRTMediaPlayerControlPrivate::cleanup(QWinRTMediaPlayerControlPrivate *d)
+{
+ QtConcurrent::run(&QWinRTMediaPlayerControlPrivate::shutdown, d);
+}
+
+void QWinRTMediaPlayerControlPrivate::shutdown(QWinRTMediaPlayerControlPrivate *d)
+{
+ d->engine->Shutdown();
+ delete d;
+}
+
+QWinRTMediaPlayerControl::QWinRTMediaPlayerControl(IMFMediaEngineClassFactory *factory, QObject *parent)
+ : QMediaPlayerControl(parent), d_ptr(new QWinRTMediaPlayerControlPrivate)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ d->state = QMediaPlayer::StoppedState;
+ d->mediaStatus = QMediaPlayer::NoMedia;
+ d->duration = 0;
+ d->position = 0;
+ d->playbackRate = 1.0;
+ d->volume = 100;
+ d->bufferStatus = 0;
+ d->muted = false;
+ d->seekable = false;
+ d->hasAudio = false;
+ d->hasVideo = false;
+ d->videoRenderer = Q_NULLPTR;
+ d->notifier = Make<MediaEngineNotify>(this, d);
+
+ HRESULT hr;
+ hr = MFCreateDXGIDeviceManager(&d->resetToken, &d->manager);
+ RETURN_VOID_IF_FAILED("Failed to create DXGI device manager");
+
+ hr = MFCreateAttributes(&d->configuration, 1);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->configuration->SetUnknown(MF_MEDIA_ENGINE_CALLBACK, d->notifier.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->configuration->SetUnknown(MF_MEDIA_ENGINE_DXGI_MANAGER, d->manager.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->configuration->SetUINT32(MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IMFMediaEngine> engine;
+ hr = factory->CreateInstance(0, d->configuration.Get(), &engine);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = engine.As(&d->engine);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = d->engine->SetVolume(1.0);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ d->sources = Make<MediaEngineSources>(d);
+ hr = d->engine->SetSourceElements(d->sources.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ d->streamProvider = Make<MediaEngineByteStream>(this, d);
+}
+
+QWinRTMediaPlayerControl::~QWinRTMediaPlayerControl()
+{
+}
+
+QMediaPlayer::State QWinRTMediaPlayerControl::state() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->state;
+}
+
+QMediaPlayer::MediaStatus QWinRTMediaPlayerControl::mediaStatus() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->mediaStatus;
+}
+
+qint64 QWinRTMediaPlayerControl::duration() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->duration;
+}
+
+qint64 QWinRTMediaPlayerControl::position() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->position;
+}
+
+void QWinRTMediaPlayerControl::setPosition(qint64 position)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->position == position)
+ return;
+
+ HRESULT hr;
+ hr = d->engine->SetCurrentTime(double(position)/1000);
+ RETURN_VOID_IF_FAILED("Failed to seek to new position");
+
+ d->position = position;
+ emit positionChanged(d->position);
+
+ if (d->mediaStatus == QMediaPlayer::EndOfMedia) {
+ d->mediaStatus = QMediaPlayer::LoadedMedia;
+ emit mediaStatusChanged(d->mediaStatus);
+ }
+}
+
+int QWinRTMediaPlayerControl::volume() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->volume;
+}
+
+void QWinRTMediaPlayerControl::setVolume(int volume)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->volume == volume)
+ return;
+
+ HRESULT hr;
+ hr = d->engine->SetVolume(double(volume)/100);
+ RETURN_VOID_IF_FAILED("Failed to set volume");
+
+ d->volume = volume;
+ emit volumeChanged(d->volume);
+}
+
+bool QWinRTMediaPlayerControl::isMuted() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->muted;
+}
+
+void QWinRTMediaPlayerControl::setMuted(bool muted)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->muted == muted)
+ return;
+
+ HRESULT hr;
+ hr = d->engine->SetMuted(muted);
+ RETURN_VOID_IF_FAILED("Failed to mute volume");
+
+ d->muted = muted;
+ emit mutedChanged(d->muted);
+}
+
+int QWinRTMediaPlayerControl::bufferStatus() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->bufferStatus;
+}
+
+bool QWinRTMediaPlayerControl::isAudioAvailable() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->hasAudio;
+}
+
+bool QWinRTMediaPlayerControl::isVideoAvailable() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->hasVideo;
+}
+
+bool QWinRTMediaPlayerControl::isSeekable() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->seekable;
+}
+
+QMediaTimeRange QWinRTMediaPlayerControl::availablePlaybackRanges() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return QMediaTimeRange(0, d->duration);
+}
+
+qreal QWinRTMediaPlayerControl::playbackRate() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->playbackRate;
+}
+
+void QWinRTMediaPlayerControl::setPlaybackRate(qreal rate)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (qFuzzyCompare(d->playbackRate, rate))
+ return;
+
+ HRESULT hr;
+ hr = d->engine->SetPlaybackRate(rate);
+ RETURN_VOID_IF_FAILED("Failed to set playback rate");
+}
+
+QMediaContent QWinRTMediaPlayerControl::media() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->media;
+}
+
+const QIODevice *QWinRTMediaPlayerControl::mediaStream() const
+{
+ Q_D(const QWinRTMediaPlayerControl);
+ return d->stream.data();
+}
+
+void QWinRTMediaPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->media == media)
+ return;
+
+ d->media = media;
+ d->stream.reset(stream);
+ if (d->hasAudio != false) {
+ d->hasAudio = false;
+ emit audioAvailableChanged(d->hasAudio);
+ }
+ if (d->hasVideo != false) {
+ d->hasVideo = false;
+ emit videoAvailableChanged(d->hasVideo);
+ }
+ if (d->seekable != false) {
+ d->seekable = false;
+ emit seekableChanged(d->seekable);
+ }
+ if (d->bufferStatus != 0) {
+ d->bufferStatus = 0;
+ emit bufferStatusChanged(d->bufferStatus);
+ }
+ if (d->position != 0) {
+ d->position = 0;
+ emit positionChanged(d->position);
+ }
+ if (d->duration != 0) {
+ d->duration = 0;
+ emit durationChanged(d->duration);
+ }
+ QMediaPlayer::MediaStatus mediaStatus = media.isNull() ? QMediaPlayer::NoMedia
+ : QMediaPlayer::LoadingMedia;
+ if (d->mediaStatus != mediaStatus) {
+ d->mediaStatus = mediaStatus;
+ emit mediaStatusChanged(d->mediaStatus);
+ }
+ emit mediaChanged(media);
+
+ QString urlString;
+ if (!d->stream) {
+ // If we can read the file via Qt, use the byte stream approach
+ foreach (const QMediaResource &resource, media.resources()) {
+ const QUrl url = resource.url();
+ if (url.isLocalFile()) {
+ urlString = url.toLocalFile();
+ QScopedPointer<QFile> file(new QFile(urlString));
+ if (file->open(QFile::ReadOnly)) {
+ file->setProperty(QT_WINRT_MEDIAPLAYER_STREAM_ID, true);
+ d->stream.reset(file.take());
+ break;
+ }
+ }
+ }
+ }
+
+ HRESULT hr;
+ if (d->stream) {
+ hr = d->engine->SetSourceFromByteStream(d->streamProvider.Get(),
+ reinterpret_cast<BSTR>(urlString.data()));
+ if (FAILED(hr))
+ emit error(QMediaPlayer::ResourceError, qt_error_string(hr));
+ return;
+ }
+
+ // Let Windows handle all other URLs
+ hr = d->engine->SetSource(Q_NULLPTR); // Resets the byte stream
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->engine->Load();
+ if (FAILED(hr))
+ emit error(QMediaPlayer::ResourceError, qt_error_string(hr));
+
+ if (d->media.isNull() && d->stream.isNull())
+ return;
+
+ // Resume play/pause/stop
+ switch (d->state) {
+ case QMediaPlayer::StoppedState:
+ stop();
+ break;
+ case QMediaPlayer::PlayingState:
+ play();
+ break;
+ case QMediaPlayer::PausedState:
+ pause();
+ break;
+ }
+}
+
+void QWinRTMediaPlayerControl::play()
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->state != QMediaPlayer::PlayingState) {
+ d->state = QMediaPlayer::PlayingState;
+ emit stateChanged(d->state);
+ }
+
+ if (d->media.isNull() && d->stream.isNull())
+ return;
+
+ if (d->videoRenderer)
+ d->videoRenderer->ensureReady();
+
+ HRESULT hr = d->engine->Play();
+ if (FAILED(hr)) {
+ emit error(QMediaPlayer::ResourceError, qt_error_string(hr));
+ return;
+ }
+}
+
+void QWinRTMediaPlayerControl::pause()
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->state != QMediaPlayer::PausedState) {
+ d->state = QMediaPlayer::PausedState;
+ emit stateChanged(d->state);
+ }
+
+ if (d->media.isNull() && d->stream.isNull())
+ return;
+
+ HRESULT hr;
+ hr = d->engine->Pause();
+ if (FAILED(hr)) {
+ emit error(QMediaPlayer::ResourceError, qt_error_string(hr));
+ return;
+ }
+}
+
+void QWinRTMediaPlayerControl::stop()
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (d->state != QMediaPlayer::StoppedState) {
+ d->state = QMediaPlayer::StoppedState;
+ emit stateChanged(d->state);
+ }
+
+ if (d->mediaStatus == QMediaPlayer::BufferedMedia
+ || d->mediaStatus == QMediaPlayer::BufferingMedia) {
+ d->mediaStatus = QMediaPlayer::LoadedMedia;
+ emit mediaStatusChanged(d->mediaStatus);
+ }
+
+ if (d->media.isNull() && d->stream.isNull())
+ return;
+
+ setPosition(0);
+
+ HRESULT hr;
+ hr = d->engine->Pause();
+ if (FAILED(hr)) {
+ emit error(QMediaPlayer::ResourceError, qt_error_string(hr));
+ return;
+ }
+}
+
+QVideoRendererControl *QWinRTMediaPlayerControl::videoRendererControl()
+{
+ Q_D(QWinRTMediaPlayerControl);
+
+ if (!d->videoRenderer) {
+ d->videoRenderer = new QWinRTPlayerRendererControl(d->engine.Get(), d->manager.Get(),
+ d->resetToken, this);
+ }
+
+ return d->videoRenderer;
+}
+
+void QWinRTMediaPlayerControl::finishRead()
+{
+ Q_D(QWinRTMediaPlayerControl);
+ d->streamProvider->finishRead();
+}
diff --git a/src/plugins/winrt/qwinrtmediaplayercontrol.h b/src/plugins/winrt/qwinrtmediaplayercontrol.h
new file mode 100644
index 000000000..b615b62d9
--- /dev/null
+++ b/src/plugins/winrt/qwinrtmediaplayercontrol.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 QWINRTMEDIAPLAYERCONTROL_H
+#define QWINRTMEDIAPLAYERCONTROL_H
+
+#include <QtMultimedia/QMediaPlayerControl>
+
+struct IMFMediaEngineClassFactory;
+
+QT_USE_NAMESPACE
+
+class QVideoRendererControl;
+
+class QWinRTMediaPlayerControlPrivate;
+class QWinRTMediaPlayerControl : public QMediaPlayerControl
+{
+ Q_OBJECT
+public:
+ QWinRTMediaPlayerControl(IMFMediaEngineClassFactory *factory, QObject *parent = 0);
+ ~QWinRTMediaPlayerControl();
+
+ QMediaPlayer::State state() const Q_DECL_OVERRIDE;
+ QMediaPlayer::MediaStatus mediaStatus() const Q_DECL_OVERRIDE;
+
+ qint64 duration() const Q_DECL_OVERRIDE;
+
+ qint64 position() const Q_DECL_OVERRIDE;
+ void setPosition(qint64 position) Q_DECL_OVERRIDE;
+
+ int volume() const Q_DECL_OVERRIDE;
+ void setVolume(int volume) Q_DECL_OVERRIDE;
+
+ bool isMuted() const Q_DECL_OVERRIDE;
+ void setMuted(bool muted) Q_DECL_OVERRIDE;
+
+ int bufferStatus() const Q_DECL_OVERRIDE;
+
+ bool isAudioAvailable() const Q_DECL_OVERRIDE;
+ bool isVideoAvailable() const Q_DECL_OVERRIDE;
+
+ bool isSeekable() const Q_DECL_OVERRIDE;
+
+ QMediaTimeRange availablePlaybackRanges() const Q_DECL_OVERRIDE;
+
+ qreal playbackRate() const Q_DECL_OVERRIDE;
+ void setPlaybackRate(qreal rate) Q_DECL_OVERRIDE;
+
+ QMediaContent media() const Q_DECL_OVERRIDE;
+ const QIODevice *mediaStream() const Q_DECL_OVERRIDE;
+ void setMedia(const QMediaContent &media, QIODevice *stream) Q_DECL_OVERRIDE;
+
+ void play() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+
+ QVideoRendererControl *videoRendererControl();
+
+private:
+ Q_INVOKABLE void finishRead();
+
+ QScopedPointer<QWinRTMediaPlayerControlPrivate, QWinRTMediaPlayerControlPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QWinRTMediaPlayerControl)
+};
+
+#endif // QWINRTMEDIAPLAYERCONTROL_H
diff --git a/src/plugins/winrt/qwinrtmediaplayerservice.cpp b/src/plugins/winrt/qwinrtmediaplayerservice.cpp
new file mode 100644
index 000000000..cc1473887
--- /dev/null
+++ b/src/plugins/winrt/qwinrtmediaplayerservice.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "qwinrtmediaplayerservice.h"
+#include "qwinrtmediaplayercontrol.h"
+
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QPointer>
+#include <QtMultimedia/QVideoRendererControl>
+
+#include <mfapi.h>
+#include <mfmediaengine.h>
+#include <wrl.h>
+
+using namespace Microsoft::WRL;
+
+QT_USE_NAMESPACE
+
+class QWinRTMediaPlayerServicePrivate
+{
+public:
+ QPointer<QWinRTMediaPlayerControl> player;
+
+ ComPtr<IMFMediaEngineClassFactory> factory;
+};
+
+QWinRTMediaPlayerService::QWinRTMediaPlayerService(QObject *parent)
+ : QMediaService(parent), d_ptr(new QWinRTMediaPlayerServicePrivate)
+{
+ Q_D(QWinRTMediaPlayerService);
+
+ d->player = Q_NULLPTR;
+
+ HRESULT hr = MFStartup(MF_VERSION);
+ Q_ASSERT(SUCCEEDED(hr));
+
+ MULTI_QI results = { &IID_IUnknown, NULL, 0 };
+ hr = CoCreateInstanceFromApp(CLSID_MFMediaEngineClassFactory, NULL,
+ CLSCTX_INPROC_SERVER, NULL, 1, &results);
+ Q_ASSERT(SUCCEEDED(hr));
+
+ hr = results.pItf->QueryInterface(d->factory.GetAddressOf());
+ Q_ASSERT(SUCCEEDED(hr));
+}
+
+QWinRTMediaPlayerService::~QWinRTMediaPlayerService()
+{
+ MFShutdown();
+}
+
+QMediaControl *QWinRTMediaPlayerService::requestControl(const char *name)
+{
+ Q_D(QWinRTMediaPlayerService);
+ if (qstrcmp(name, QMediaPlayerControl_iid) == 0) {
+ if (!d->player)
+ d->player = new QWinRTMediaPlayerControl(d->factory.Get(), this);
+ return d->player;
+ }
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
+ if (!d->player)
+ return Q_NULLPTR;
+ return d->player->videoRendererControl();
+ }
+
+ return Q_NULLPTR;
+}
+
+void QWinRTMediaPlayerService::releaseControl(QMediaControl *control)
+{
+ Q_D(QWinRTMediaPlayerService);
+ if (control == d->player)
+ d->player->deleteLater();
+}
diff --git a/src/plugins/winrt/qwinrtmediaplayerservice.h b/src/plugins/winrt/qwinrtmediaplayerservice.h
new file mode 100644
index 000000000..9a2498365
--- /dev/null
+++ b/src/plugins/winrt/qwinrtmediaplayerservice.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 QWINRTMEDIAPLAYERSERVICE_H
+#define QWINRTMEDIAPLAYERSERVICE_H
+
+#include <QtMultimedia/QMediaService>
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTMediaPlayerServicePrivate;
+class QWinRTMediaPlayerService : public QMediaService
+{
+ Q_OBJECT
+public:
+ QWinRTMediaPlayerService(QObject *parent);
+ ~QWinRTMediaPlayerService();
+
+ QMediaControl *requestControl(const char *name);
+ void releaseControl(QMediaControl *control);
+
+private:
+ QScopedPointer<QWinRTMediaPlayerServicePrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QWinRTMediaPlayerService)
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTMEDIAPLAYERSERVICE_H
diff --git a/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp b/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp
new file mode 100644
index 000000000..273a650df
--- /dev/null
+++ b/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 "qwinrtplayerrenderercontrol.h"
+
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QGlobalStatic>
+#include <QtMultimedia/QAbstractVideoSurface>
+#include <QtMultimedia/QVideoSurfaceFormat>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QOpenGLContext>
+
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <d3d11.h>
+#include <mfapi.h>
+#include <mfmediaengine.h>
+#include <wrl.h>
+using namespace Microsoft::WRL;
+
+QT_USE_NAMESPACE
+
+template <typename T>
+class D3DDeviceLocker
+{
+public:
+ D3DDeviceLocker(IMFDXGIDeviceManager *manager, HANDLE deviceHandle, T **device)
+ : m_manager(manager), m_deviceHandle(deviceHandle)
+ {
+ HRESULT hr = m_manager->LockDevice(m_deviceHandle, IID_PPV_ARGS(device), TRUE);
+ if (FAILED(hr))
+ qErrnoWarning(hr, "Failed to lock the D3D device");
+ }
+
+ ~D3DDeviceLocker()
+ {
+ HRESULT hr = m_manager->UnlockDevice(m_deviceHandle, FALSE);
+ if (FAILED(hr))
+ qErrnoWarning(hr, "Failed to unlock the D3D device");
+ }
+
+private:
+ IMFDXGIDeviceManager *m_manager;
+ HANDLE m_deviceHandle;
+};
+
+class QWinRTPlayerRendererControlPrivate
+{
+public:
+ ComPtr<IMFMediaEngineEx> engine;
+ ComPtr<IMFDXGIDeviceManager> manager;
+ quint32 resetToken;
+ HANDLE deviceHandle;
+};
+
+QWinRTPlayerRendererControl::QWinRTPlayerRendererControl(IMFMediaEngineEx *engine, IMFDXGIDeviceManager *manager, quint32 resetToken, QObject *parent)
+ : QWinRTAbstractVideoRendererControl(QSize(), parent), d_ptr(new QWinRTPlayerRendererControlPrivate)
+{
+ Q_D(QWinRTPlayerRendererControl);
+
+ d->engine = engine;
+ d->manager = manager;
+ d->resetToken = resetToken;
+ d->deviceHandle = 0;
+}
+
+QWinRTPlayerRendererControl::~QWinRTPlayerRendererControl()
+{
+ shutdown();
+}
+
+bool QWinRTPlayerRendererControl::ensureReady()
+{
+ Q_D(QWinRTPlayerRendererControl);
+
+ HRESULT hr;
+ hr = d->manager->TestDevice(d->deviceHandle);
+ if (hr == MF_E_DXGI_NEW_VIDEO_DEVICE || FAILED(hr)) {
+ d->manager->CloseDeviceHandle(d->deviceHandle);
+
+ hr = d->manager->ResetDevice(d3dDevice(), d->resetToken);
+ RETURN_FALSE_IF_FAILED("Failed to reset the DXGI manager device");
+
+ hr = d->manager->OpenDeviceHandle(&d->deviceHandle);
+ RETURN_FALSE_IF_FAILED("Failed to open device handle");
+ }
+
+ return SUCCEEDED(hr);
+}
+
+bool QWinRTPlayerRendererControl::render(ID3D11Texture2D *texture)
+{
+ Q_D(QWinRTPlayerRendererControl);
+
+ if (!ensureReady())
+ return false;
+
+ HRESULT hr;
+ LONGLONG ts;
+ hr = d->engine->OnVideoStreamTick(&ts);
+ RETURN_FALSE_IF_FAILED("Failed to obtain video stream tick");
+ if (hr == S_FALSE) // Frame not available, nothing to do
+ return false;
+
+ ComPtr<ID3D11Device> device;
+ D3DDeviceLocker<ID3D11Device> locker(d->manager.Get(), d->deviceHandle, device.GetAddressOf());
+
+ MFVideoNormalizedRect sourceRect = { 0, 0, 1, 1 };
+ const QSize frameSize = size();
+ RECT destRect = { 0, 0, frameSize.width(), frameSize.height() };
+ MFARGB borderColor = { 255, 255, 255, 255 };
+ hr = d->engine->TransferVideoFrame(texture, &sourceRect, &destRect, &borderColor);
+ RETURN_FALSE_IF_FAILED("Failed to transfer video frame to DXGI surface");
+ return true;
+}
diff --git a/src/plugins/winrt/qwinrtplayerrenderercontrol.h b/src/plugins/winrt/qwinrtplayerrenderercontrol.h
new file mode 100644
index 000000000..2d973cbfc
--- /dev/null
+++ b/src/plugins/winrt/qwinrtplayerrenderercontrol.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 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 QWINRTPLAYERRENDERERCONTROL_H
+#define QWINRTPLAYERRENDERERCONTROL_H
+
+#include "qwinrtabstractvideorenderercontrol.h"
+#include <QtMultimedia/QMediaPlayer>
+
+struct IMFMediaEngineEx;
+struct IMFDXGIDeviceManager;
+
+QT_BEGIN_NAMESPACE
+
+class QWinRTPlayerRendererControlPrivate;
+class QWinRTPlayerRendererControl : public QWinRTAbstractVideoRendererControl
+{
+ Q_OBJECT
+public:
+ explicit QWinRTPlayerRendererControl(IMFMediaEngineEx *engine, IMFDXGIDeviceManager *manager, quint32 resetToken, QObject *parent);
+ ~QWinRTPlayerRendererControl();
+
+ bool ensureReady();
+
+ bool render(ID3D11Texture2D *texture) Q_DECL_OVERRIDE;
+
+private:
+ QScopedPointer<QWinRTPlayerRendererControlPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QWinRTPlayerRendererControl)
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINRTPLAYERRENDERERCONTROL_H
diff --git a/src/plugins/winrt/qwinrtserviceplugin.cpp b/src/plugins/winrt/qwinrtserviceplugin.cpp
index 143b93547..5d49b44a4 100644
--- a/src/plugins/winrt/qwinrtserviceplugin.cpp
+++ b/src/plugins/winrt/qwinrtserviceplugin.cpp
@@ -43,9 +43,15 @@
#include <QtCore/QFile>
#include "qwinrtserviceplugin.h"
+#include "qwinrtmediaplayerservice.h"
+
+QT_USE_NAMESPACE
QMediaService *QWinRTServicePlugin::create(QString const &key)
{
+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
+ return new QWinRTMediaPlayerService(this);
+
return Q_NULLPTR;
}
@@ -57,5 +63,8 @@ void QWinRTServicePlugin::release(QMediaService *service)
QMediaServiceProviderHint::Features QWinRTServicePlugin::supportedFeatures(
const QByteArray &service) const
{
+ if (service == Q_MEDIASERVICE_MEDIAPLAYER)
+ return QMediaServiceProviderHint::StreamPlayback | QMediaServiceProviderHint::VideoSurface;
+
return QMediaServiceProviderHint::Features();
}
diff --git a/src/plugins/winrt/winrt.json b/src/plugins/winrt/winrt.json
index f4a1ceb47..b85cfeb12 100644
--- a/src/plugins/winrt/winrt.json
+++ b/src/plugins/winrt/winrt.json
@@ -1,4 +1,4 @@
{
"Keys": ["winrt"],
- "Services": []
+ "Services": ["org.qt-project.qt.mediaplayer"]
}
diff --git a/src/plugins/winrt/winrt.pro b/src/plugins/winrt/winrt.pro
index 82f97e489..0ea90d22e 100644
--- a/src/plugins/winrt/winrt.pro
+++ b/src/plugins/winrt/winrt.pro
@@ -8,9 +8,17 @@ load(qt_plugin)
LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11
HEADERS += \
+ qwinrtabstractvideorenderercontrol.h \
+ qwinrtmediaplayercontrol.h \
+ qwinrtmediaplayerservice.h \
+ qwinrtplayerrenderercontrol.h \
qwinrtserviceplugin.h
SOURCES += \
+ qwinrtabstractvideorenderercontrol.cpp \
+ qwinrtmediaplayercontrol.cpp \
+ qwinrtmediaplayerservice.cpp \
+ qwinrtplayerrenderercontrol.cpp \
qwinrtserviceplugin.cpp
OTHER_FILES += \
diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
index 7e9327a10..5109d305b 100644
--- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
+++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
@@ -764,6 +764,10 @@ void tst_QMediaPlayerBackend::playlist()
player.play();
QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000);
+
+ if (player.mediaStatus() == QMediaPlayer::InvalidMedia)
+ QSKIP("Media player does not support M3U playlists");
+
QCOMPARE(mediaSpy.count(), 2);
// sample.m3u -> sample.m3u resolved -> test.wav ->
// nested1.m3u -> nested1.m3u resolved -> test.wav ->