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:
Andrew Knight
2014-08-21 10:55:10 +03:00
committed by Maurice Kalinowski
parent 5d04ed2a0a
commit 778b233bcd
12 changed files with 1921 additions and 1 deletions

View 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();
}