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>
This commit is contained in:
committed by
Maurice Kalinowski
parent
5d04ed2a0a
commit
778b233bcd
403
src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
Normal file
403
src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp
Normal file
@@ -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);
|
||||
}
|
||||
87
src/plugins/winrt/qwinrtabstractvideorenderercontrol.h
Normal file
87
src/plugins/winrt/qwinrtabstractvideorenderercontrol.h
Normal file
@@ -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
|
||||
899
src/plugins/winrt/qwinrtmediaplayercontrol.cpp
Normal file
899
src/plugins/winrt/qwinrtmediaplayercontrol.cpp
Normal file
@@ -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();
|
||||
}
|
||||
104
src/plugins/winrt/qwinrtmediaplayercontrol.h
Normal file
104
src/plugins/winrt/qwinrtmediaplayercontrol.h
Normal file
@@ -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
|
||||
111
src/plugins/winrt/qwinrtmediaplayerservice.cpp
Normal file
111
src/plugins/winrt/qwinrtmediaplayerservice.cpp
Normal file
@@ -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();
|
||||
}
|
||||
67
src/plugins/winrt/qwinrtmediaplayerservice.h
Normal file
67
src/plugins/winrt/qwinrtmediaplayerservice.h
Normal file
@@ -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
|
||||
156
src/plugins/winrt/qwinrtplayerrenderercontrol.cpp
Normal file
156
src/plugins/winrt/qwinrtplayerrenderercontrol.cpp
Normal file
@@ -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;
|
||||
}
|
||||
72
src/plugins/winrt/qwinrtplayerrenderercontrol.h
Normal file
72
src/plugins/winrt/qwinrtplayerrenderercontrol.h
Normal file
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"Keys": ["winrt"],
|
||||
"Services": []
|
||||
"Services": ["org.qt-project.qt.mediaplayer"]
|
||||
}
|
||||
|
||||
@@ -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 += \
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
Reference in New Issue
Block a user