summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Goddard <michael.goddard@nokia.com>2011-07-25 16:45:09 +1000
committerQt by Nokia <qt-info@nokia.com>2011-07-25 14:31:41 +0200
commit1e4dda971098656dad390478601ee932e9c4e3e3 (patch)
tree7da016aa40fc1a0f356f51eabc5033a32bccc2e4
parente45902822c8161c84d2148244f0ff72fff0ba515 (diff)
Add Windows Media Foundation backend for QMediaPlayer
Task-number:QTMOBILITY-1606 Reviewed-by: Michael Goddard (cherry picked from commit bdf3a9b39661cfb836245139d02e95e854646a7e) (cherry picked from commit 8d2370953eb32bf44a037244e0d9f9b484875f7a) Change-Id: I07790b8c540a04e4e19a3d018a4884a773e980bf Reviewed-on: http://codereview.qt.nokia.com/2086 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Ling Hu <ling.hu@nokia.com>
-rw-r--r--config.tests/wmf/main.cpp52
-rw-r--r--config.tests/wmf/wmf.pro10
-rw-r--r--src/plugins/directshow/directshow.pro2
-rw-r--r--src/plugins/directshow/dsserviceplugin.cpp1
-rw-r--r--src/plugins/plugins.pro1
-rw-r--r--src/plugins/wmf/player/evr9videowindowcontrol.cpp351
-rw-r--r--src/plugins/wmf/player/evr9videowindowcontrol.h111
-rw-r--r--src/plugins/wmf/player/mfaudioendpointcontrol.cpp162
-rw-r--r--src/plugins/wmf/player/mfaudioendpointcontrol.h84
-rw-r--r--src/plugins/wmf/player/mfmetadatacontrol.cpp246
-rw-r--r--src/plugins/wmf/player/mfmetadatacontrol.h80
-rw-r--r--src/plugins/wmf/player/mfplayercontrol.cpp311
-rw-r--r--src/plugins/wmf/player/mfplayercontrol.h124
-rw-r--r--src/plugins/wmf/player/mfplayerservice.cpp154
-rw-r--r--src/plugins/wmf/player/mfplayerservice.h96
-rw-r--r--src/plugins/wmf/player/mfplayersession.cpp1388
-rw-r--r--src/plugins/wmf/player/mfplayersession.h212
-rw-r--r--src/plugins/wmf/player/mfstream.cpp361
-rw-r--r--src/plugins/wmf/player/mfstream.h149
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.cpp2201
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.h76
-rw-r--r--src/plugins/wmf/player/player.pri30
-rw-r--r--src/plugins/wmf/player/sourceresolver.cpp318
-rw-r--r--src/plugins/wmf/player/sourceresolver.h106
-rw-r--r--src/plugins/wmf/wmf.pro16
-rw-r--r--src/plugins/wmf/wmfserviceplugin.cpp97
-rw-r--r--src/plugins/wmf/wmfserviceplugin.h68
-rw-r--r--sync.profile1
28 files changed, 6807 insertions, 1 deletions
diff --git a/config.tests/wmf/main.cpp b/config.tests/wmf/main.cpp
new file mode 100644
index 000000000..db361ca64
--- /dev/null
+++ b/config.tests/wmf/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mferror.h>
+#include <d3d9.h>
+#include <evr9.h>
+#include <mmdeviceapi.h>
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/config.tests/wmf/wmf.pro b/config.tests/wmf/wmf.pro
new file mode 100644
index 000000000..31b532843
--- /dev/null
+++ b/config.tests/wmf/wmf.pro
@@ -0,0 +1,10 @@
+CONFIG -= qt
+CONFIG += console
+TEMPLATE = app
+
+# Input
+SOURCES += main.cpp
+
+LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 -lMf -lMfuuid -lMfplat -lPropsys
+
+
diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro
index 513f810fb..e2a37e612 100644
--- a/src/plugins/directshow/directshow.pro
+++ b/src/plugins/directshow/directshow.pro
@@ -19,7 +19,7 @@ SOURCES += dsserviceplugin.cpp
!contains(config_test_wmsdk, yes): DEFINES += QT_NO_WMSDK
-include (player/player.pri)
+contains(config_test_wmf, no): include (player/player.pri)
include (camera/camera.pri)
target.path += $$[QT_INSTALL_PLUGINS]/$${PLUGIN_TYPE}
diff --git a/src/plugins/directshow/dsserviceplugin.cpp b/src/plugins/directshow/dsserviceplugin.cpp
index b354d2195..adfb025d8 100644
--- a/src/plugins/directshow/dsserviceplugin.cpp
+++ b/src/plugins/directshow/dsserviceplugin.cpp
@@ -47,6 +47,7 @@
#ifdef QMEDIA_DIRECTSHOW_CAMERA
+#include <dshow.h>
#include "dscameraservice.h"
#endif
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index bf747612f..6ae546f72 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -14,6 +14,7 @@ win32 {
win32 {
contains(config_test_directshow, yes): SUBDIRS += directshow
+ contains(config_test_wmf, yes) : SUBDIRS += wmf
}
simulator: SUBDIRS += simulator
diff --git a/src/plugins/wmf/player/evr9videowindowcontrol.cpp b/src/plugins/wmf/player/evr9videowindowcontrol.cpp
new file mode 100644
index 000000000..0b705b743
--- /dev/null
+++ b/src/plugins/wmf/player/evr9videowindowcontrol.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "evr9videowindowcontrol.h"
+#include <QtCore/qdebug.h>
+#include <QtCore/qglobal.h>
+
+Evr9VideoWindowControl::Evr9VideoWindowControl(QObject *parent)
+ : QVideoWindowControl(parent)
+ , m_windowId(0)
+ , m_dirtyValues(0)
+ , m_aspectRatioMode(Qt::KeepAspectRatio)
+ , m_brightness(0)
+ , m_contrast(0)
+ , m_hue(0)
+ , m_saturation(0)
+ , m_fullScreen(false)
+ , m_currentActivate(0)
+ , m_evrSink(0)
+ , m_displayControl(0)
+{
+ if (FAILED(MFCreateVideoRendererActivate(0, &m_currentActivate))) {
+ qWarning() << "Failed to create evr video renderer activate!";
+ return;
+ }
+ if (FAILED(m_currentActivate->ActivateObject(IID_IMFMediaSink, (LPVOID*)(&m_evrSink)))) {
+ qWarning() << "Failed to activate evr media sink!";
+ return;
+ }
+ if (FAILED(MFGetService(m_evrSink, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_displayControl)))) {
+ qWarning() << "Failed to get display control from evr media sink!";
+ return;
+ }
+ if (FAILED(MFGetService(m_evrSink, MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_processor)))) {
+ qWarning() << "Failed to get video processor from evr media sink!";
+ return;
+ }
+}
+
+Evr9VideoWindowControl::~Evr9VideoWindowControl()
+{
+ if (m_processor)
+ m_processor->Release();
+ if (m_displayControl)
+ m_displayControl->Release();
+ if (m_evrSink)
+ m_evrSink->Release();
+ if (m_currentActivate) {
+ m_currentActivate->ShutdownObject();
+ m_currentActivate->Release();
+ }
+}
+
+
+WId Evr9VideoWindowControl::winId() const
+{
+ return m_windowId;
+}
+
+void Evr9VideoWindowControl::setWinId(WId id)
+{
+ m_windowId = id;
+
+ if (QWidget *widget = QWidget::find(m_windowId)) {
+ const QColor color = widget->palette().color(QPalette::Window);
+
+ m_windowColor = RGB(color.red(), color.green(), color.blue());
+ }
+
+ if (m_displayControl) {
+ m_displayControl->SetVideoWindow(m_windowId);
+ }
+}
+
+QRect Evr9VideoWindowControl::displayRect() const
+{
+ return m_displayRect;
+}
+
+void Evr9VideoWindowControl::setDisplayRect(const QRect &rect)
+{
+ m_displayRect = rect;
+
+ if (m_displayControl) {
+ RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 };
+ QSize sourceSize = nativeSize();
+
+ RECT sourceRect = { 0, 0, sourceSize.width(), sourceSize.height() };
+
+ if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
+ QSize clippedSize = rect.size();
+ clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio);
+
+ sourceRect.left = (sourceRect.right - clippedSize.width()) / 2;
+ sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2;
+ sourceRect.right = sourceRect.left + clippedSize.width();
+ sourceRect.bottom = sourceRect.top + clippedSize.height();
+ }
+
+ if (sourceSize.width() > 0 && sourceSize.height() > 0) {
+ MFVideoNormalizedRect sourceNormRect;
+ sourceNormRect.left = float(sourceRect.left) / float(sourceRect.right);
+ sourceNormRect.top = float(sourceRect.top) / float(sourceRect.bottom);
+ sourceNormRect.right = float(sourceRect.right) / float(sourceRect.right);
+ sourceNormRect.bottom = float(sourceRect.bottom) / float(sourceRect.bottom);
+ m_displayControl->SetVideoPosition(&sourceNormRect, &displayRect);
+ } else {
+ m_displayControl->SetVideoPosition(NULL, &displayRect);
+ }
+ }
+}
+
+bool Evr9VideoWindowControl::isFullScreen() const
+{
+ return m_fullScreen;
+}
+
+void Evr9VideoWindowControl::setFullScreen(bool fullScreen)
+{
+ if (m_fullScreen == fullScreen)
+ return;
+ emit fullScreenChanged(m_fullScreen = fullScreen);
+}
+
+void Evr9VideoWindowControl::repaint()
+{
+ QSize size = nativeSize();
+ if (size.width() > 0 && size.height() > 0
+ && m_displayControl
+ && SUCCEEDED(m_displayControl->RepaintVideo())) {
+ return;
+ }
+
+ PAINTSTRUCT paint;
+ if (HDC dc = ::BeginPaint(m_windowId, &paint)) {
+ HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor);
+ HBRUSH brush = ::CreateSolidBrush(m_windowColor);
+ ::SelectObject(dc, pen);
+ ::SelectObject(dc, brush);
+
+ ::Rectangle(
+ dc,
+ m_displayRect.left(),
+ m_displayRect.top(),
+ m_displayRect.right() + 1,
+ m_displayRect.bottom() + 1);
+
+ ::DeleteObject(pen);
+ ::DeleteObject(brush);
+ ::EndPaint(m_windowId, &paint);
+ }
+}
+
+QSize Evr9VideoWindowControl::nativeSize() const
+{
+ QSize size;
+ if (m_displayControl) {
+ SIZE sourceSize;
+ if (SUCCEEDED(m_displayControl->GetNativeVideoSize(&sourceSize, 0)))
+ size = QSize(sourceSize.cx, sourceSize.cy);
+ }
+ return size;
+}
+
+Qt::AspectRatioMode Evr9VideoWindowControl::aspectRatioMode() const
+{
+ return m_aspectRatioMode;
+}
+
+void Evr9VideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
+{
+ m_aspectRatioMode = mode;
+
+ if (m_displayControl) {
+ switch (mode) {
+ case Qt::IgnoreAspectRatio:
+ //comment from MSDN: Do not maintain the aspect ratio of the video. Stretch the video to fit the output rectangle.
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_None);
+ break;
+ case Qt::KeepAspectRatio:
+ //comment from MSDN: Preserve the aspect ratio of the video by letterboxing or within the output rectangle.
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ //for this mode, more adjustment will be done in setDisplayRect
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
+ break;
+ default:
+ break;
+ }
+ setDisplayRect(m_displayRect);
+ }
+}
+
+int Evr9VideoWindowControl::brightness() const
+{
+ return m_brightness;
+}
+
+void Evr9VideoWindowControl::setBrightness(int brightness)
+{
+ if (m_brightness == brightness)
+ return;
+
+ m_brightness = brightness;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Brightness;
+
+ setProcAmpValues();
+
+ emit brightnessChanged(brightness);
+}
+
+int Evr9VideoWindowControl::contrast() const
+{
+ return m_contrast;
+}
+
+void Evr9VideoWindowControl::setContrast(int contrast)
+{
+ if (m_contrast == contrast)
+ return;
+
+ m_contrast = contrast;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Contrast;
+
+ setProcAmpValues();
+
+ emit contrastChanged(contrast);
+}
+
+int Evr9VideoWindowControl::hue() const
+{
+ return m_hue;
+}
+
+void Evr9VideoWindowControl::setHue(int hue)
+{
+ if (m_hue == hue)
+ return;
+
+ m_hue = hue;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Hue;
+
+ setProcAmpValues();
+
+ emit hueChanged(hue);
+}
+
+int Evr9VideoWindowControl::saturation() const
+{
+ return m_saturation;
+}
+
+void Evr9VideoWindowControl::setSaturation(int saturation)
+{
+ if (m_saturation == saturation)
+ return;
+
+ m_saturation = saturation;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Saturation;
+
+ setProcAmpValues();
+
+ emit saturationChanged(saturation);
+}
+
+IMFActivate* Evr9VideoWindowControl::currentActivate() const
+{
+ return m_currentActivate;
+}
+
+void Evr9VideoWindowControl::setProcAmpValues()
+{
+ if (m_processor) {
+ DXVA2_ProcAmpValues values;
+ if (m_dirtyValues & DXVA2_ProcAmp_Brightness) {
+ values.Brightness = scaleProcAmpValue(DXVA2_ProcAmp_Brightness, m_brightness);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Contrast) {
+ values.Contrast = scaleProcAmpValue(DXVA2_ProcAmp_Contrast, m_contrast);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Hue) {
+ values.Hue = scaleProcAmpValue(DXVA2_ProcAmp_Hue, m_hue);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Saturation) {
+ values.Saturation = scaleProcAmpValue(DXVA2_ProcAmp_Saturation, m_saturation);
+ }
+
+ if (SUCCEEDED(m_processor->SetProcAmpValues(0, &values))) {
+ m_dirtyValues = 0;
+ }
+ }
+}
+
+DXVA2_Fixed32 Evr9VideoWindowControl::scaleProcAmpValue(DWORD prop, int value) const
+{
+ float scaledValue = 0.0;
+
+ DXVA2_ValueRange range;
+ if (SUCCEEDED(m_processor->GetProcAmpRange(prop, &range))) {
+ scaledValue = DXVA2FixedToFloat(range.DefaultValue);
+ if (value > 0)
+ scaledValue += float(value) * (DXVA2FixedToFloat(range.MaxValue) - DXVA2FixedToFloat(range.DefaultValue)) / 100;
+ else if (value < 0)
+ scaledValue -= float(value) * (DXVA2FixedToFloat(range.MinValue) - DXVA2FixedToFloat(range.DefaultValue)) / 100;
+ }
+
+ return DXVA2FloatToFixed(scaledValue);
+}
diff --git a/src/plugins/wmf/player/evr9videowindowcontrol.h b/src/plugins/wmf/player/evr9videowindowcontrol.h
new file mode 100644
index 000000000..a39d75041
--- /dev/null
+++ b/src/plugins/wmf/player/evr9videowindowcontrol.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVR9VIDEOWINDOWCONTROL_H
+#define EVR9VIDEOWINDOWCONTROL_H
+
+#include "../../src/multimedia/qvideowindowcontrol.h"
+
+#include <Mfidl.h>
+#include <d3d9.h>
+#include <Evr9.h>
+
+QT_USE_NAMESPACE
+
+class Evr9VideoWindowControl : public QVideoWindowControl
+{
+ Q_OBJECT
+public:
+ Evr9VideoWindowControl(QObject *parent = 0);
+ ~Evr9VideoWindowControl();
+
+ WId winId() const;
+ void setWinId(WId id);
+
+ QRect displayRect() const;
+ void setDisplayRect(const QRect &rect);
+
+ bool isFullScreen() const;
+ void setFullScreen(bool fullScreen);
+
+ void repaint();
+
+ QSize nativeSize() const;
+
+ Qt::AspectRatioMode aspectRatioMode() const;
+ void setAspectRatioMode(Qt::AspectRatioMode mode);
+
+ int brightness() const;
+ void setBrightness(int brightness);
+
+ int contrast() const;
+ void setContrast(int contrast);
+
+ int hue() const;
+ void setHue(int hue);
+
+ int saturation() const;
+ void setSaturation(int saturation);
+
+ IMFActivate* currentActivate() const;
+
+private:
+ void setProcAmpValues();
+ DXVA2_Fixed32 scaleProcAmpValue(DWORD prop, int value) const;
+
+ WId m_windowId;
+ COLORREF m_windowColor;
+ DWORD m_dirtyValues;
+ Qt::AspectRatioMode m_aspectRatioMode;
+ QRect m_displayRect;
+ int m_brightness;
+ int m_contrast;
+ int m_hue;
+ int m_saturation;
+ bool m_fullScreen;
+
+ IMFActivate *m_currentActivate;
+ IMFMediaSink *m_evrSink;
+ IMFVideoDisplayControl *m_displayControl;
+ IMFVideoProcessor *m_processor;
+};
+
+#endif
diff --git a/src/plugins/wmf/player/mfaudioendpointcontrol.cpp b/src/plugins/wmf/player/mfaudioendpointcontrol.cpp
new file mode 100644
index 000000000..dac5a0abf
--- /dev/null
+++ b/src/plugins/wmf/player/mfaudioendpointcontrol.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "QtCore/qdebug.h"
+#include "mfaudioendpointcontrol.h"
+
+MFAudioEndpointControl::MFAudioEndpointControl(QObject *parent)
+ : QAudioEndpointSelector(parent)
+ , m_currentActivate(0)
+{
+ updateEndpoints();
+ setActiveEndpoint(m_defaultEndpoint);
+}
+
+MFAudioEndpointControl::~MFAudioEndpointControl()
+{
+ foreach (LPWSTR wstrID, m_devices)
+ CoTaskMemFree(wstrID);
+
+ if (m_currentActivate)
+ m_currentActivate->Release();
+}
+
+QList<QString> MFAudioEndpointControl::availableEndpoints() const
+{
+ return m_devices.keys();
+}
+
+QString MFAudioEndpointControl::endpointDescription(const QString &name) const
+{
+ return name.section(QLatin1Char('\\'), -1);
+}
+
+QString MFAudioEndpointControl::defaultEndpoint() const
+{
+ return m_defaultEndpoint;
+}
+
+QString MFAudioEndpointControl::activeEndpoint() const
+{
+ return m_activeEndpoint;
+}
+
+void MFAudioEndpointControl::setActiveEndpoint(const QString &name)
+{
+ if (m_activeEndpoint == name)
+ return;
+ QMap<QString, LPWSTR>::iterator it = m_devices.find(name);
+ if (it == m_devices.end())
+ return;
+
+ LPWSTR wstrID = *it;
+ IMFActivate *activate = NULL;
+ HRESULT hr = MFCreateAudioRendererActivate(&activate);
+ if (FAILED(hr)) {
+ qWarning() << "Failed to create audio renderer activate";
+ return;
+ }
+
+ if (wstrID) {
+ hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, wstrID);
+ } else {
+ //This is the default one that has been inserted in updateEndpoints(),
+ //so give the activate a hint that we want to use the device for multimedia playback
+ //then the media foundation will choose an apropriate one.
+
+ //from MSDN:
+ //The ERole enumeration defines constants that indicate the role that the system has assigned to an audio endpoint device.
+ //eMultimedia: Music, movies, narration, and live music recording.
+ hr = activate->SetUINT32(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, eMultimedia);
+ }
+
+ if (FAILED(hr)) {
+ qWarning() << "Failed to set attribute for audio device" << name;
+ return;
+ }
+
+ if (m_currentActivate)
+ m_currentActivate->Release();
+ m_currentActivate = activate;
+ m_activeEndpoint = name;
+}
+
+IMFActivate* MFAudioEndpointControl::currentActivate() const
+{
+ return m_currentActivate;
+}
+
+void MFAudioEndpointControl::updateEndpoints()
+{
+ m_defaultEndpoint = QString::fromLatin1("Default");
+ m_devices.insert(m_defaultEndpoint, NULL);
+
+ IMMDeviceEnumerator *pEnum = NULL;
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pEnum);
+ if (SUCCEEDED(hr)) {
+ IMMDeviceCollection *pDevices = NULL;
+ hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);
+ if (SUCCEEDED(hr)) {
+ UINT count;
+ hr = pDevices->GetCount(&count);
+ if (SUCCEEDED(hr)) {
+ for (UINT i = 0; i < count; ++i) {
+ IMMDevice *pDevice = NULL;
+ hr = pDevices->Item(i, &pDevice);
+ if (SUCCEEDED(hr)) {
+ LPWSTR wstrID = NULL;
+ hr = pDevice->GetId(&wstrID);
+ if (SUCCEEDED(hr)) {
+ QString deviceId = QString::fromWCharArray(wstrID);
+ m_devices.insert(deviceId, wstrID);
+ }
+ pDevice->Release();
+ }
+ }
+ }
+ pDevices->Release();
+ }
+ pEnum->Release();
+ }
+}
diff --git a/src/plugins/wmf/player/mfaudioendpointcontrol.h b/src/plugins/wmf/player/mfaudioendpointcontrol.h
new file mode 100644
index 000000000..b4aa6f797
--- /dev/null
+++ b/src/plugins/wmf/player/mfaudioendpointcontrol.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFAUDIOENDPOINTCONTROL_H
+#define MFAUDIOENDPOINTCONTROL_H
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mmdeviceapi.h>
+
+#include "../../src/multimedia/qaudioendpointselector.h"
+
+class MFPlayerService;
+
+QT_USE_NAMESPACE
+
+class MFAudioEndpointControl : public QAudioEndpointSelector
+{
+ Q_OBJECT
+public:
+ MFAudioEndpointControl(QObject *parent = 0);
+ ~MFAudioEndpointControl();
+
+ QList<QString> availableEndpoints() const;
+
+ QString endpointDescription(const QString &name) const;
+
+ QString defaultEndpoint() const;
+ QString activeEndpoint() const;
+
+ void setActiveEndpoint(const QString& name);
+
+ IMFActivate* currentActivate() const;
+
+private:
+ void updateEndpoints();
+
+ QString m_defaultEndpoint;
+ QString m_activeEndpoint;
+ QMap<QString, LPWSTR> m_devices;
+ IMFActivate *m_currentActivate;
+
+};
+
+#endif
+
diff --git a/src/plugins/wmf/player/mfmetadatacontrol.cpp b/src/plugins/wmf/player/mfmetadatacontrol.cpp
new file mode 100644
index 000000000..21cc7032b
--- /dev/null
+++ b/src/plugins/wmf/player/mfmetadatacontrol.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mfmetadatacontrol.h"
+#include "mfplayerservice.h"
+#include "Propkey.h"
+
+//#define DEBUG_MEDIAFOUNDATION
+
+MFMetaDataControl::MFMetaDataControl(QObject *parent)
+ : QMetaDataReaderControl(parent)
+ , m_metaData(0)
+ , m_content(0)
+{
+}
+
+MFMetaDataControl::~MFMetaDataControl()
+{
+ if (m_metaData)
+ m_metaData->Release();
+ if (m_content)
+ m_content->Release();
+}
+
+bool MFMetaDataControl::isMetaDataAvailable() const
+{
+ return m_content || m_metaData;
+}
+
+QVariant MFMetaDataControl::metaData(QtMultimediaKit::MetaData key) const
+{
+ QVariant value;
+ if (!isMetaDataAvailable())
+ return value;
+
+ int index = m_availableMetaDatas.indexOf(key);
+ if (index < 0)
+ return value;
+
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ HRESULT hr = S_FALSE;
+ if (m_content)
+ hr = m_content->GetValue(m_commonKeys[index], &var);
+ else if (m_metaData)
+ hr = m_metaData->GetProperty(m_commonNames[index].utf16(), &var);
+
+ if (SUCCEEDED(hr))
+ value = convertValue(var);
+
+ PropVariantClear(&var);
+ return value;
+}
+
+QVariant MFMetaDataControl::convertValue(const PROPVARIANT& var) const
+{
+ QVariant value;
+ //form MSDN: http://msdn.microsoft.com/en-us/library/ff384862%28v=VS.85%29.aspx
+ //it seems that those 4 types are enough for media foundation metadata
+ //add more later if necessary
+ switch (var.vt) {
+ case VT_LPWSTR:
+ value = QString::fromUtf16(var.pwszVal);
+ break;
+ case VT_UI4:
+ value = uint(var.ulVal);
+ break;
+ case VT_UI8:
+ value = qulonglong(var.uhVal.QuadPart);
+ break;
+ case VT_BOOL:
+ value = bool(var.boolVal);
+ break;
+ }
+ return value;
+}
+
+QList<QtMultimediaKit::MetaData> MFMetaDataControl::availableMetaData() const
+{
+ return m_availableMetaDatas;
+}
+
+QVariant MFMetaDataControl::extendedMetaData(const QString &key) const
+{
+ QVariant value;
+ HRESULT hr = S_FALSE;
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ if (m_content) {
+ int index = m_extendedMetaDatas.indexOf(key);
+ if (index >= 0) {
+ hr = m_content->GetValue(m_extendedKeys[index], &var);
+ }
+ } else if (m_metaData) {
+ hr = m_metaData->GetProperty(key.utf16(), &var);
+ }
+
+ if (SUCCEEDED(hr))
+ value = convertValue(var);
+
+ PropVariantClear(&var);
+ return value;
+}
+
+QStringList MFMetaDataControl::availableExtendedMetaData() const
+{
+ return m_extendedMetaDatas;
+}
+
+void MFMetaDataControl::updateSource(IMFPresentationDescriptor* sourcePD, IMFMediaSource* mediaSource)
+{
+ if (m_metaData) {
+ m_metaData->Release();
+ m_metaData = 0;
+ }
+
+ if (m_content) {
+ m_content->Release();
+ m_content = 0;
+ }
+
+ m_availableMetaDatas.clear();
+ m_commonKeys.clear();
+ m_commonNames.clear();
+ m_extendedMetaDatas.clear();
+ m_extendedKeys.clear();
+
+ if (SUCCEEDED(MFGetService(mediaSource, MF_PROPERTY_HANDLER_SERVICE, IID_PPV_ARGS(&m_content)))) {
+ DWORD cProps;
+ if (SUCCEEDED(m_content->GetCount(&cProps))) {
+ for (DWORD i = 0; i < cProps; i++)
+ {
+ PROPERTYKEY key;
+ if (FAILED(m_content->GetAt(i, &key)))
+ continue;
+ bool common = true;
+ if (key == PKEY_Author) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Author);
+ } else if (key == PKEY_Title) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Title);
+ } else if (key == PKEY_ParentalRating) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::ParentalRating);
+ } else if (key == PKEY_Comment) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Description);
+ } else if (key == PKEY_Copyright) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Copyright);
+ //TODO: add more common keys
+ } else {
+ common = false;
+ //TODO: add more extended keys
+ }
+ if (common)
+ m_commonKeys.push_back(key);
+ }
+ } else {
+ m_content->Release();
+ m_content = NULL;
+ }
+ }
+
+ if (!m_content) {
+ //fallback to Vista approach
+ IMFMetadataProvider *provider = NULL;
+ if (SUCCEEDED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_PPV_ARGS(&provider)))) {
+ if (SUCCEEDED(provider->GetMFMetadata(sourcePD, 0, 0, &m_metaData))) {
+ PROPVARIANT varNames;
+ PropVariantInit(&varNames);
+ if (SUCCEEDED(m_metaData->GetAllPropertyNames(&varNames)) && varNames.vt == (VT_VECTOR | VT_LPWSTR)) {
+ ULONG cElements = varNames.calpwstr.cElems;
+ for (ULONG i = 0; i < cElements; i++)
+ {
+ const WCHAR* sName = varNames.calpwstr.pElems[i];
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "metadata: " << QString::fromUtf16(sName);
+#endif
+ bool common = true;
+ if (wcscmp(sName, L"Author") == 0) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Author);
+ } else if (wcscmp(sName, L"Title") == 0) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Title);
+ } else if (wcscmp(sName, L"Rating") == 0) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::ParentalRating);
+ } else if (wcscmp(sName, L"Description") == 0) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Description);
+ } else if (wcscmp(sName, L"Copyright") == 0) {
+ m_availableMetaDatas.push_back(QtMultimediaKit::Copyright);
+ //TODO: add more common keys
+ } else {
+ common = false;
+ m_extendedMetaDatas.push_back(QString::fromUtf16(sName));
+ }
+ if (common)
+ m_commonNames.push_back(QString::fromUtf16(sName));
+ }
+ }
+ PropVariantClear(&varNames);
+ } else {
+ qWarning("Failed to get IMFMetadata");
+ }
+ provider->Release();
+ } else {
+ qWarning("Failed to get IMFMetadataProvider from source");
+ }
+ }
+
+ emit metaDataChanged();
+ emit metaDataAvailableChanged(m_metaData || m_content);
+}
diff --git a/src/plugins/wmf/player/mfmetadatacontrol.h b/src/plugins/wmf/player/mfmetadatacontrol.h
new file mode 100644
index 000000000..59eddade7
--- /dev/null
+++ b/src/plugins/wmf/player/mfmetadatacontrol.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFMETADATACONTROL_H
+#define MFMETADATACONTROL_H
+
+#include <qmetadatareadercontrol.h>
+#include "Mfidl.h"
+
+QT_USE_NAMESPACE
+
+class MFMetaDataControl : public QMetaDataReaderControl
+{
+ Q_OBJECT
+public:
+ MFMetaDataControl(QObject *parent = 0);
+ ~MFMetaDataControl();
+
+ bool isMetaDataAvailable() const;
+
+ QVariant metaData(QtMultimediaKit::MetaData key) const;
+ QList<QtMultimediaKit::MetaData> availableMetaData() const;
+
+ QVariant extendedMetaData(const QString &key) const;
+ QStringList availableExtendedMetaData() const;
+
+ void updateSource(IMFPresentationDescriptor* sourcePD, IMFMediaSource* mediaSource);
+
+private:
+ QVariant convertValue(const PROPVARIANT& var) const;
+ IPropertyStore *m_content; //for Windows7
+ IMFMetadata *m_metaData; //for Vista
+
+ QList<QtMultimediaKit::MetaData> m_availableMetaDatas;
+ QList<PROPERTYKEY> m_commonKeys; //for Windows7
+ QStringList m_commonNames; //for Vista
+
+ QStringList m_extendedMetaDatas;
+ QList<PROPERTYKEY> m_extendedKeys; //for Windows7
+};
+
+#endif
diff --git a/src/plugins/wmf/player/mfplayercontrol.cpp b/src/plugins/wmf/player/mfplayercontrol.cpp
new file mode 100644
index 000000000..3eb0764fb
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayercontrol.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "mfplayercontrol.h"
+#include <qtcore/qdebug.h>
+
+//#define DEBUG_MEDIAFOUNDATION
+
+MFPlayerControl::MFPlayerControl(MFPlayerSession *session)
+: QMediaPlayerControl(session)
+, m_state(QMediaPlayer::StoppedState)
+, m_videoAvailable(false)
+, m_audioAvailable(false)
+, m_duration(-1)
+, m_seekable(false)
+, m_session(session)
+{
+ QObject::connect(m_session, SIGNAL(statusChanged()), this, SLOT(handleStatusChanged()));
+ QObject::connect(m_session, SIGNAL(videoAvailable()), this, SLOT(handleVideoAvailable()));
+ QObject::connect(m_session, SIGNAL(audioAvailable()), this, SLOT(handleAudioAvailable()));
+ QObject::connect(m_session, SIGNAL(durationUpdate(qint64)), this, SLOT(handleDurationUpdate(qint64)));
+ QObject::connect(m_session, SIGNAL(seekableUpdate(bool)), this, SLOT(handleSeekableUpdate(bool)));
+ QObject::connect(m_session, SIGNAL(error(QMediaPlayer::Error, QString, bool)), this, SLOT(handleError(QMediaPlayer::Error, QString, bool)));
+ QObject::connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
+ QObject::connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int)));
+ QObject::connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
+ QObject::connect(m_session, SIGNAL(playbackRateChanged(qreal)), this, SIGNAL(playbackRateChanged(qreal)));
+ QObject::connect(m_session, SIGNAL(bufferStatusChanged(int)), this, SIGNAL(bufferStatusChanged(int)));
+}
+
+MFPlayerControl::~MFPlayerControl()
+{
+}
+
+void MFPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
+{
+ stop();
+ m_media = media;
+ m_stream = stream;
+ resetAudioVideoAvailable();
+ handleDurationUpdate(-1);
+ handleSeekableUpdate(false);
+ m_session->load(media, stream);
+ emit mediaChanged(m_media);
+}
+
+void MFPlayerControl::play()
+{
+ if (m_state == QMediaPlayer::PlayingState)
+ return;
+ if (QMediaPlayer::InvalidMedia == m_session->status())
+ m_session->load(m_media, m_stream);
+
+ switch (m_session->status()) {
+ case QMediaPlayer::UnknownMediaStatus:
+ case QMediaPlayer::NoMedia:
+ case QMediaPlayer::InvalidMedia:
+ return;
+ case QMediaPlayer::LoadedMedia:
+ case QMediaPlayer::BufferingMedia:
+ case QMediaPlayer::BufferedMedia:
+ case QMediaPlayer::EndOfMedia:
+ changeState(QMediaPlayer::PlayingState);
+ m_session->start();
+ break;
+ default: //Loading/Stalled
+ changeState(QMediaPlayer::PlayingState);
+ break;
+ }
+ refreshState();
+}
+
+void MFPlayerControl::pause()
+{
+ if (m_state != QMediaPlayer::PlayingState)
+ return;
+ changeState(QMediaPlayer::PausedState);
+ m_session->pause();
+ refreshState();
+}
+
+void MFPlayerControl::stop()
+{
+ if (m_state == QMediaPlayer::StoppedState)
+ return;
+ changeState(QMediaPlayer::StoppedState);
+ m_session->stop();
+ refreshState();
+}
+
+void MFPlayerControl::changeState(QMediaPlayer::State state)
+{
+ if (m_state == state)
+ return;
+ m_state = state;
+ m_stateDirty = true;
+}
+
+void MFPlayerControl::refreshState()
+{
+ if (!m_stateDirty)
+ return;
+ m_stateDirty = false;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerControl::emit stateChanged" << m_state;
+#endif
+ emit stateChanged(m_state);
+}
+
+void MFPlayerControl::handleStatusChanged()
+{
+ QMediaPlayer::MediaStatus status = m_session->status();
+ switch (status) {
+ case QMediaPlayer::EndOfMedia:
+ changeState(QMediaPlayer::StoppedState);
+ break;
+ case QMediaPlayer::InvalidMedia:
+ break;
+ case QMediaPlayer::LoadedMedia:
+ case QMediaPlayer::BufferingMedia:
+ case QMediaPlayer::BufferedMedia:
+ if (m_state == QMediaPlayer::PlayingState)
+ m_session->start();
+ break;
+ }
+ emit mediaStatusChanged(m_session->status());
+ refreshState();
+}
+
+void MFPlayerControl::handleVideoAvailable()
+{
+ if (m_videoAvailable)
+ return;
+ m_videoAvailable = true;
+ emit videoAvailableChanged(m_videoAvailable);
+}
+
+void MFPlayerControl::handleAudioAvailable()
+{
+ if (m_audioAvailable)
+ return;
+ m_audioAvailable = true;
+ emit audioAvailableChanged(m_audioAvailable);
+}
+
+void MFPlayerControl::resetAudioVideoAvailable()
+{
+ bool videoDirty = false;
+ if (m_videoAvailable) {
+ m_videoAvailable = false;
+ videoDirty = true;
+ }
+ if (m_audioAvailable) {
+ m_audioAvailable = false;
+ emit audioAvailableChanged(m_audioAvailable);
+ }
+ if (videoDirty)
+ emit videoAvailableChanged(m_videoAvailable);
+}
+
+void MFPlayerControl::handleDurationUpdate(qint64 duration)
+{
+ if (m_duration == duration)
+ return;
+ m_duration = duration;
+ emit durationChanged(m_duration);
+}
+
+void MFPlayerControl::handleSeekableUpdate(bool seekable)
+{
+ if (m_seekable == seekable)
+ return;
+ m_seekable = seekable;
+ emit seekableChanged(m_seekable);
+}
+
+QMediaPlayer::State MFPlayerControl::state() const
+{
+ return m_state;
+}
+
+QMediaPlayer::MediaStatus MFPlayerControl::mediaStatus() const
+{
+ return m_session->status();
+}
+
+qint64 MFPlayerControl::duration() const
+{
+ return m_duration;
+}
+
+qint64 MFPlayerControl::position() const
+{
+ return m_session->position();
+}
+
+void MFPlayerControl::setPosition(qint64 position)
+{
+ if (!m_seekable || position == m_session->position())
+ return;
+ m_session->setPosition(position);
+}
+
+int MFPlayerControl::volume() const
+{
+ return m_session->volume();
+}
+
+void MFPlayerControl::setVolume(int volume)
+{
+ m_session->setVolume(volume);
+}
+
+bool MFPlayerControl::isMuted() const
+{
+ return m_session->isMuted();
+}
+
+void MFPlayerControl::setMuted(bool muted)
+{
+ m_session->setMuted(muted);
+}
+
+int MFPlayerControl::bufferStatus() const
+{
+ return m_session->bufferStatus();
+}
+
+bool MFPlayerControl::isAudioAvailable() const
+{
+ return m_audioAvailable;
+}
+
+bool MFPlayerControl::isVideoAvailable() const
+{
+ return m_videoAvailable;
+}
+
+bool MFPlayerControl::isSeekable() const
+{
+ return m_seekable;
+}
+
+QMediaTimeRange MFPlayerControl::availablePlaybackRanges() const
+{
+ return m_session->availablePlaybackRanges();
+}
+
+qreal MFPlayerControl::playbackRate() const
+{
+ return m_session->playbackRate();
+}
+
+void MFPlayerControl::setPlaybackRate(qreal rate)
+{
+ m_session->setPlaybackRate(rate);
+}
+
+QMediaContent MFPlayerControl::media() const
+{
+ return m_media;
+}
+
+const QIODevice* MFPlayerControl::mediaStream() const
+{
+ return m_stream;
+}
+
+void MFPlayerControl::handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal)
+{
+ if (isFatal)
+ stop();
+ emit error(int(errorCode), errorString);
+}
diff --git a/src/plugins/wmf/player/mfplayercontrol.h b/src/plugins/wmf/player/mfplayercontrol.h
new file mode 100644
index 000000000..c2399504b
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayercontrol.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFPLAYERCONTROL_H
+#define MFPLAYERCONTROL_H
+
+#include "qmediacontent.h"
+#include "qmediaplayercontrol.h"
+
+#include <QtCore/qcoreevent.h>
+
+#include "mfplayersession.h"
+
+QT_USE_NAMESPACE
+
+class MFPlayerControl : public QMediaPlayerControl
+{
+ Q_OBJECT
+public:
+ MFPlayerControl(MFPlayerSession *session);
+ ~MFPlayerControl();
+
+ QMediaPlayer::State state() const;
+
+ QMediaPlayer::MediaStatus mediaStatus() const;
+
+ qint64 duration() const;
+
+ qint64 position() const;
+ void setPosition(qint64 position);
+
+ int volume() const;
+ void setVolume(int volume);
+
+ bool isMuted() const;
+ void setMuted(bool muted);
+
+ int bufferStatus() const;
+
+ bool isAudioAvailable() const;
+ bool isVideoAvailable() const;
+
+ bool isSeekable() const;
+
+ QMediaTimeRange availablePlaybackRanges() const;
+
+ qreal playbackRate() const;
+ void setPlaybackRate(qreal rate);
+
+ QMediaContent media() const;
+ const QIODevice *mediaStream() const;
+ void setMedia(const QMediaContent &media, QIODevice *stream);
+
+ void play();
+ void pause();
+ void stop();
+
+private Q_SLOTS:
+ void handleStatusChanged();
+ void handleVideoAvailable();
+ void handleAudioAvailable();
+ void handleDurationUpdate(qint64 duration);
+ void handleSeekableUpdate(bool seekable);
+ void handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal);
+
+private:
+ void changeState(QMediaPlayer::State state);
+ void resetAudioVideoAvailable();
+ void refreshState();
+
+ QMediaPlayer::State m_state;
+ bool m_stateDirty;
+ QMediaPlayer::MediaStatus m_status;
+ QMediaPlayer::Error m_error;
+
+ bool m_videoAvailable;
+ bool m_audioAvailable;
+ qint64 m_duration;
+ bool m_seekable;
+
+ QIODevice *m_stream;
+ QMediaContent m_media;
+ MFPlayerSession *m_session;
+};
+
+#endif
diff --git a/src/plugins/wmf/player/mfplayerservice.cpp b/src/plugins/wmf/player/mfplayerservice.cpp
new file mode 100644
index 000000000..81fdf5792
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayerservice.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qmediacontent.h"
+
+#include <QtCore/qdebug.h>
+
+#include "mfplayercontrol.h"
+#ifndef Q_WS_SIMULATOR
+#include "evr9videowindowcontrol.h"
+#endif
+#include "mfvideorenderercontrol.h"
+#include "mfaudioendpointcontrol.h"
+#include "mfplayerservice.h"
+#include "mfplayersession.h"
+#include "mfmetadatacontrol.h"
+
+MFPlayerService::MFPlayerService(QObject *parent)
+ : QMediaService(parent)
+ , m_session(0)
+#ifndef Q_WS_SIMULATOR
+ , m_videoWindowControl(0)
+#endif
+ , m_videoRendererControl(0)
+{
+ CoInitialize(NULL);
+ MFStartup(MF_VERSION);
+ m_session = new MFPlayerSession(this);
+ m_player = new MFPlayerControl(m_session);
+ m_audioEndpointControl = new MFAudioEndpointControl(this);
+ m_metaDataControl = new MFMetaDataControl(this);
+}
+
+MFPlayerService::~MFPlayerService()
+{
+
+#ifndef Q_WS_SIMULATOR
+ if (m_videoWindowControl)
+ delete m_videoWindowControl;
+#endif
+
+ if (m_videoRendererControl)
+ delete m_videoRendererControl;
+
+ delete m_session;
+
+ MFShutdown();
+ CoUninitialize();
+}
+
+QMediaControl* MFPlayerService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QMediaPlayerControl_iid) == 0) {
+ return m_player;
+ } else if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) {
+ return m_audioEndpointControl;
+ } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) {
+ return m_metaDataControl;
+ } else if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
+#ifndef Q_WS_SIMULATOR
+ if (!m_videoRendererControl && !m_videoWindowControl) {
+#else
+ if (!m_videoRendererControl) {
+#endif
+ m_videoRendererControl = new MFVideoRendererControl;
+ return m_videoRendererControl;
+ }
+#ifndef Q_WS_SIMULATOR
+ } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
+ if (!m_videoRendererControl && !m_videoWindowControl) {
+ m_videoWindowControl = new Evr9VideoWindowControl;
+ return m_videoWindowControl;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+void MFPlayerService::releaseControl(QMediaControl *control)
+{
+ if (!control) {
+ qWarning("QMediaService::releaseControl():"
+ " Attempted release of null control");
+ } else if (control == m_videoRendererControl) {
+ m_videoRendererControl->setSurface(0);
+ delete m_videoRendererControl;
+ m_videoRendererControl = 0;
+#ifndef Q_WS_SIMULATOR
+ } else if (control == m_videoWindowControl) {
+ delete m_videoWindowControl;
+ m_videoWindowControl = 0;
+#endif
+ }
+}
+
+MFAudioEndpointControl* MFPlayerService::audioEndpointControl() const
+{
+ return m_audioEndpointControl;
+}
+
+MFVideoRendererControl* MFPlayerService::videoRendererControl() const
+{
+ return m_videoRendererControl;
+}
+
+#ifndef Q_WS_SIMULATOR
+Evr9VideoWindowControl* MFPlayerService::videoWindowControl() const
+{
+ return m_videoWindowControl;
+}
+#endif
+
+MFMetaDataControl* MFPlayerService::metaDataControl() const
+{
+ return m_metaDataControl;
+}
diff --git a/src/plugins/wmf/player/mfplayerservice.h b/src/plugins/wmf/player/mfplayerservice.h
new file mode 100644
index 000000000..7fef10784
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayerservice.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFPLAYERSERVICE_H
+#define MFPLAYERSERVICE_H
+
+#include <mfapi.h>
+#include <mfidl.h>
+
+#include "qmediaplayer.h"
+#include "qmediaresource.h"
+#include "qmediaservice.h"
+#include "qmediatimerange.h"
+
+QT_BEGIN_NAMESPACE
+class QMediaContent;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+#ifndef Q_WS_SIMULATOR
+class Evr9VideoWindowControl;
+#endif
+class MFAudioEndpointControl;
+class MFVideoRendererControl;
+class MFPlayerControl;
+class MFMetaDataControl;
+class MFPlayerSession;
+
+class MFPlayerService : public QMediaService
+{
+ Q_OBJECT
+public:
+ MFPlayerService(QObject *parent = 0);
+ ~MFPlayerService();
+
+ QMediaControl* requestControl(const char *name);
+ void releaseControl(QMediaControl *control);
+
+ MFAudioEndpointControl* audioEndpointControl() const;
+ MFVideoRendererControl* videoRendererControl() const;
+#ifndef Q_WS_SIMULATOR
+ Evr9VideoWindowControl* videoWindowControl() const;
+#endif
+ MFMetaDataControl* metaDataControl() const;
+
+private:
+ MFPlayerSession *m_session;
+ MFVideoRendererControl *m_videoRendererControl;
+ MFAudioEndpointControl *m_audioEndpointControl;
+#ifndef Q_WS_SIMULATOR
+ Evr9VideoWindowControl *m_videoWindowControl;
+#endif
+ MFPlayerControl *m_player;
+ MFMetaDataControl *m_metaDataControl;
+};
+
+#endif
diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp
new file mode 100644
index 000000000..bb5b4bd2b
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayersession.cpp
@@ -0,0 +1,1388 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qmediacontent.h"
+#include "qmediaplayercontrol.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qbuffer.h>
+
+#include "mfplayercontrol.h"
+#ifndef Q_WS_SIMULATOR
+#include "evr9videowindowcontrol.h"
+#endif
+#include "mfvideorenderercontrol.h"
+#include "mfaudioendpointcontrol.h"
+
+#include "mfplayersession.h"
+#include "mfplayerservice.h"
+#include "mfmetadatacontrol.h"
+#include <Mferror.h>
+#include <nserror.h>
+#include <sourceresolver.h>
+
+//#define DEBUG_MEDIAFOUNDATION
+//#define TEST_STREAMING
+
+namespace
+{
+ //MFStream is added for supporting QIODevice type of media source.
+ //It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice.
+ class MFStream : public QObject, public IMFByteStream
+ {
+ Q_OBJECT
+ public:
+ MFStream(QIODevice *stream, bool ownStream)
+ : m_cRef(1)
+ , m_stream(stream)
+ , m_ownStream(ownStream)
+ , m_currentReadResult(0)
+ {
+ //Move to the thread of the stream object
+ //to make sure invocations on stream
+ //are happened in the same thread of stream object
+ this->moveToThread(stream->thread());
+ connect(stream, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
+ }
+
+ ~MFStream()
+ {
+ if (m_currentReadResult)
+ m_currentReadResult->Release();
+ if (m_ownStream)
+ m_stream->deleteLater();
+ }
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFByteStream) {
+ *ppvObject = static_cast<IMFByteStream*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+ }
+
+
+ //from IMFByteStream
+ STDMETHODIMP GetCapabilities(DWORD *pdwCapabilities)
+ {
+ if (!pdwCapabilities)
+ return E_INVALIDARG;
+ *pdwCapabilities = MFBYTESTREAM_IS_READABLE;
+ if (!m_stream->isSequential())
+ *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE;
+ return S_OK;
+ }
+
+ STDMETHODIMP GetLength(QWORD *pqwLength)
+ {
+ if (!pqwLength)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwLength = QWORD(m_stream->size());
+ return S_OK;
+ }
+
+ STDMETHODIMP SetLength(QWORD)
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP GetCurrentPosition(QWORD *pqwPosition)
+ {
+ if (!pqwPosition)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwPosition = m_stream->pos();
+ return S_OK;
+ }
+
+ STDMETHODIMP SetCurrentPosition(QWORD qwPosition)
+ {
+ QMutexLocker locker(&m_mutex);
+ //SetCurrentPosition may happend during the BeginRead/EndRead pair,
+ //refusing to execute SetCurrentPosition during that time seems to be
+ //the simplest workable solution
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ bool seekOK = m_stream->seek(qint64(qwPosition));
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+ }
+
+ STDMETHODIMP IsEndOfStream(BOOL *pfEndOfStream)
+ {
+ if (!pfEndOfStream)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE;
+ return S_OK;
+ }
+
+ STDMETHODIMP Read(BYTE *pb, ULONG cb, ULONG *pcbRead)
+ {
+ QMutexLocker locker(&m_mutex);
+ qint64 read = m_stream->read((char*)(pb), qint64(cb));
+ if (pcbRead)
+ *pcbRead = ULONG(read);
+ return S_OK;
+ }
+
+ STDMETHODIMP BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
+ IUnknown *punkState)
+ {
+ if (!pCallback || !pb)
+ return E_INVALIDARG;
+
+ Q_ASSERT(m_currentReadResult == NULL);
+
+ AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb);
+ if (state == NULL)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult);
+ state->Release();
+ if (FAILED(hr))
+ return hr;
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ return hr;
+ }
+
+ STDMETHODIMP EndRead(IMFAsyncResult* pResult, ULONG *pcbRead)
+ {
+ if (!pcbRead)
+ return E_INVALIDARG;
+ IUnknown *pUnk;
+ pResult->GetObject(&pUnk);
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ *pcbRead = state->bytesRead();
+ pUnk->Release();
+
+ m_currentReadResult->Release();
+ m_currentReadResult = NULL;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP Write(const BYTE *, ULONG, ULONG *)
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
+ IMFAsyncCallback *,
+ IUnknown *)
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP EndWrite(IMFAsyncResult *,
+ ULONG *)
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Seek(
+ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
+ LONGLONG llSeekOffset,
+ DWORD,
+ QWORD *pqwCurrentPosition)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ qint64 pos = qint64(llSeekOffset);
+ switch (SeekOrigin) {
+ case msoCurrent:
+ pos += m_stream->pos();
+ break;
+ }
+ bool seekOK = m_stream->seek(pos);
+ if (*pqwCurrentPosition)
+ *pqwCurrentPosition = pos;
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+ }
+
+ STDMETHODIMP Flush()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Close()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_ownStream)
+ m_stream->close();
+ return S_OK;
+ }
+
+ private:
+ //AsyncReadState is a helper class used in BeginRead for asynchronous operation
+ //to record some BeginRead parameters, so these parameters could be
+ //used later when actually executing the read operation in another thread.
+ class AsyncReadState : public IUnknown
+ {
+ public:
+ AsyncReadState(BYTE *pb, ULONG cb)
+ : m_cRef(1)
+ , m_pb(pb)
+ , m_cb(cb)
+ , m_cbRead(0)
+ {
+ }
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+ }
+
+ BYTE* pb() const { return m_pb; }
+ ULONG cb() const { return m_cb; }
+ ULONG bytesRead() const { return m_cbRead; }
+
+ void setBytesRead(ULONG cbRead) { m_cbRead = cbRead; }
+
+ private:
+ long m_cRef;
+ BYTE *m_pb;
+ ULONG m_cb;
+ ULONG m_cbRead;
+ };
+
+ long m_cRef;
+ QIODevice *m_stream;
+ bool m_ownStream;
+ DWORD m_workQueueId;
+ QMutex m_mutex;
+
+ void doRead()
+ {
+ bool readDone = true;
+ IUnknown *pUnk = NULL;
+ HRESULT hr = m_currentReadResult->GetObject(&pUnk);
+ if (SUCCEEDED(hr)) {
+ //do actual read
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ ULONG cbRead;
+ Read(state->pb(), state->cb() - state->bytesRead(), &cbRead);
+ pUnk->Release();
+
+ state->setBytesRead(cbRead + state->bytesRead());
+ if (state->cb() > state->bytesRead() && !m_stream->atEnd()) {
+ readDone = false;
+ }
+ }
+
+ if (readDone) {
+ //now inform the original caller
+ m_currentReadResult->SetStatus(hr);
+ MFInvokeCallback(m_currentReadResult);
+ }
+ }
+
+
+ private Q_SLOTS:
+ void handleReadyRead()
+ {
+ doRead();
+ }
+
+ protected:
+ void customEvent(QEvent *event)
+ {
+ if (event->type() != QEvent::User) {
+ QObject::customEvent(event);
+ return;
+ }
+ doRead();
+ }
+ IMFAsyncResult *m_currentReadResult;
+ };
+}
+
+
+MFPlayerSession::MFPlayerSession(QObject *parent)
+ : QObject(parent)
+ , m_session(0)
+ , m_presentationClock(0)
+ , m_rateControl(0)
+ , m_rateSupport(0)
+ , m_volumeControl(0)
+ , m_netsourceStatistics(0)
+ , m_hCloseEvent(0)
+ , m_pendingRate(1)
+ , m_volume(1)
+ , m_muted(false)
+ , m_status(QMediaPlayer::NoMedia)
+ , m_scrubbing(false)
+ , m_restoreRate(1)
+ , m_mediaTypes(0)
+{
+ m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ m_sourceResolver = new SourceResolver(this);
+ QObject::connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
+ QObject::connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleSourceError(long)));
+ QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent *)), this, SLOT(handleSessionEvent(IMFMediaEvent *)));
+
+ m_pendingState = NoPending;
+ ZeroMemory(&m_state, sizeof(m_state));
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_state.rate = 1.0f;
+ ZeroMemory(&m_request, sizeof(m_request));
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+ m_request.rate = 1.0f;
+
+ createSession();
+ PropVariantInit(&m_varStart);
+ m_varStart.vt = VT_I8;
+ m_varStart.uhVal.QuadPart = 0;
+}
+
+MFPlayerSession::~MFPlayerSession()
+{
+ m_sourceResolver->Release();
+ clear();
+ HRESULT hr = S_OK;
+ if (m_session) {
+ hr = m_session->Close();
+ if (SUCCEEDED(hr)) {
+ DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent, 5000);
+ if (dwWaitResult == WAIT_TIMEOUT) {
+ qWarning() << "session close time out!";
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ m_sourceResolver->shutdown();
+ if (m_session)
+ m_session->Shutdown();
+ }
+
+ if (m_session)
+ m_session->Release();
+
+ CloseHandle(m_hCloseEvent);
+}
+
+
+void MFPlayerSession::load(const QMediaContent &media, QIODevice *stream)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "load";
+#endif
+ clear();
+ QMediaResourceList resources = media.resources();
+
+ if (m_status == QMediaPlayer::LoadingMedia)
+ m_sourceResolver->cancel();
+
+ if (resources.isEmpty() && !stream) {
+ changeStatus(QMediaPlayer::NoMedia);
+ } else if (stream && (!stream->isReadable())) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ emit error(QMediaPlayer::ResourceError, tr("Invalid stream source!"), true);
+ } else {
+ changeStatus(QMediaPlayer::LoadingMedia);
+ m_sourceResolver->load(resources, stream);
+ }
+ emit positionChanged(position());
+}
+
+void MFPlayerSession::handleSourceError(long hr)
+{
+ QString errorString;
+ QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError;
+ switch (hr) {
+ case QMediaPlayer::FormatError:
+ errorCode = QMediaPlayer::FormatError;
+ errorString = tr("Attempting to play invalid Qt resource");
+ break;
+ case NS_E_FILE_NOT_FOUND:
+ errorString = tr("The system cannot find the file specified.");
+ break;
+ case NS_E_SERVER_NOT_FOUND:
+ errorString = tr("The specified server could not be found.");
+ break;
+ default:
+ errorString = tr("Failed to load source.");
+ break;
+ }
+ changeStatus(QMediaPlayer::InvalidMedia);
+ emit error(errorCode, errorString, true);
+}
+
+void MFPlayerSession::handleMediaSourceReady()
+{
+ if (QMediaPlayer::LoadingMedia != m_status)
+ return;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "handleMediaSourceReady";
+#endif
+ HRESULT hr = S_OK;
+ IMFPresentationDescriptor* sourcePD;
+ IMFMediaSource* mediaSource = m_sourceResolver->mediaSource();
+ hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
+ if (SUCCEEDED(hr)) {
+ m_duration = 0;
+ static_cast<MFPlayerService*>(this->parent())->metaDataControl()->updateSource(sourcePD, mediaSource);
+ sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
+ //convert from 100 nanosecond to milisecond
+ emit durationUpdate(qint64(m_duration / 10000));
+ setupPlaybackTopology(mediaSource, sourcePD);
+ } else {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ emit error(QMediaPlayer::ResourceError, tr("Can't create presentation descriptor!"), true);
+ }
+}
+
+void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD)
+{
+ HRESULT hr = S_OK;
+ // Get the number of streams in the media source.
+ DWORD cSourceStreams = 0;
+ hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::UnknownMediaStatus);
+ emit error(QMediaPlayer::ResourceError, tr("Failed to get stream count"), true);
+ return;
+ }
+
+ IMFTopology *topology;
+ hr = MFCreateTopology(&topology);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::UnknownMediaStatus);
+ emit error(QMediaPlayer::ResourceError, tr("Failed to create topology!"), true);
+ return;
+ }
+
+ // For each stream, create the topology nodes and add them to the topology.
+ DWORD succeededCount = 0;
+ for (DWORD i = 0; i < cSourceStreams; i++)
+ {
+ BOOL fSelected = FALSE;
+ IMFStreamDescriptor *streamDesc = NULL;
+
+ HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &fSelected, &streamDesc);
+ if (SUCCEEDED(hr)) {
+ MediaType mediaType = Unknown;
+ IMFTopologyNode *sourceNode = addSourceNode(topology, source, sourcePD, streamDesc);
+ if (sourceNode) {
+ IMFTopologyNode *outputNode = addOutputNode(streamDesc, mediaType, topology, 0);
+ if (outputNode) {
+ hr = sourceNode->ConnectOutput(0, outputNode, 0);
+ if (FAILED(hr)) {
+ emit error(QMediaPlayer::FormatError, tr("Unable to play some stream"), false);
+ }
+ else {
+ succeededCount++;
+ m_mediaTypes |= mediaType;
+ switch (mediaType) {
+ case Audio:
+ emit audioAvailable();
+ break;
+ case Video:
+ emit videoAvailable();
+ break;
+ }
+ }
+ }
+ sourceNode->Release();
+ }
+ streamDesc->Release();
+ }
+ }
+
+ if (succeededCount == 0) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ emit error(QMediaPlayer::ResourceError, tr("Unable to play"), true);
+ } else {
+ hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::UnknownMediaStatus);
+ emit error(QMediaPlayer::ResourceError, tr("Failed to set topology!"), true);
+ }
+ }
+ topology->Release();
+}
+
+IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaSource* source,
+ IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc)
+{
+ IMFTopologyNode *node = NULL;
+ HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
+ if (SUCCEEDED(hr)) {
+ hr = topology->AddNode(node);
+ if (SUCCEEDED(hr))
+ return node;
+ }
+ }
+ }
+ node->Release();
+ }
+ return NULL;
+}
+
+IMFTopologyNode* MFPlayerSession::addOutputNode(IMFStreamDescriptor *streamDesc, MediaType& mediaType, IMFTopology* topology, DWORD sinkID)
+{
+ IMFTopologyNode *node = NULL;
+ HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node);
+ if (FAILED(hr))
+ return NULL;
+ node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
+
+ mediaType = Unknown;
+ IMFMediaTypeHandler *handler = NULL;
+ hr = streamDesc->GetMediaTypeHandler(&handler);
+ if (SUCCEEDED(hr)) {
+ GUID guidMajorType;
+ hr = handler->GetMajorType(&guidMajorType);
+ if (SUCCEEDED(hr)) {
+ IMFActivate *activate = NULL;
+ MFPlayerService *service = static_cast<MFPlayerService*>(this->parent());
+ if (MFMediaType_Audio == guidMajorType) {
+ mediaType = Audio;
+ activate = service->audioEndpointControl()->currentActivate();
+ } else if (MFMediaType_Video == guidMajorType) {
+ mediaType = Video;
+ if (service->videoRendererControl()) {
+ activate = service->videoRendererControl()->currentActivate();
+#ifndef Q_WS_SIMULATOR
+ } else if (service->videoWindowControl()) {
+ activate = service->videoWindowControl()->currentActivate();
+#endif
+ } else {
+ qWarning() << "no videoWindowControl or videoRendererControl, unable to add output node for video data";
+ }
+ } else {
+ // Unknown stream type.
+ emit error(QMediaPlayer::FormatError, tr("Unknown stream type"), false);
+ }
+
+ if (activate) {
+ hr = node->SetObject(activate);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUINT32(MF_TOPONODE_STREAMID, sinkID);
+ if (SUCCEEDED(hr)) {
+ if (SUCCEEDED(topology->AddNode(node)))
+ return node;
+ }
+ }
+ }
+ }
+ }
+ node->Release();
+ return NULL;
+}
+
+void MFPlayerSession::stop()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "stop";
+#endif
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdStop);
+ } else {
+ if (m_state.command == CmdStop)
+ return;
+
+ if (m_scrubbing)
+ scrub(false);
+
+ if (SUCCEEDED(m_session->Stop())) {
+ m_state.setCommand(CmdStop);
+ m_pendingState = CmdPending;
+ if (m_status != QMediaPlayer::EndOfMedia) {
+ m_varStart.vt = VT_I8;
+ m_varStart.hVal.QuadPart = 0;
+ }
+ } else {
+ emit error(QMediaPlayer::ResourceError, tr("failed to stop"), true);
+ }
+ }
+}
+
+void MFPlayerSession::start()
+{
+ switch (m_status) {
+ case QMediaPlayer::EndOfMedia:
+ m_varStart.hVal.QuadPart = 0;
+ //since it must be loaded already, just fallthrough
+ case QMediaPlayer::LoadedMedia:
+ changeStatus(QMediaPlayer::BufferedMedia);
+ return;
+ }
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "start";
+#endif
+
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdStart);
+ } else {
+ if (m_state.command == CmdStart)
+ return;
+
+ if (m_scrubbing)
+ scrub(false);
+
+ if (SUCCEEDED(m_session->Start(&GUID_NULL, &m_varStart))) {
+ m_state.setCommand(CmdStart);
+ m_pendingState = CmdPending;
+ PropVariantInit(&m_varStart);
+ } else {
+ emit error(QMediaPlayer::ResourceError, tr("failed to start playback"), true);
+ }
+ }
+}
+
+void MFPlayerSession::pause()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "pause";
+#endif
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdPause);
+ } else {
+ if (m_state.command == CmdPause)
+ return;
+ if (SUCCEEDED(m_session->Pause())) {
+ m_state.setCommand(CmdPause);
+ m_pendingState = CmdPending;
+ } else {
+ emit error(QMediaPlayer::ResourceError, tr("failed to pause"), false);
+ }
+ }
+}
+
+void MFPlayerSession::changeStatus(QMediaPlayer::MediaStatus newStatus)
+{
+ if (m_status == newStatus)
+ return;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerSession::changeStatus" << newStatus;
+#endif
+ m_status = newStatus;
+ emit statusChanged();
+}
+
+QMediaPlayer::MediaStatus MFPlayerSession::status() const
+{
+ return m_status;
+}
+
+void MFPlayerSession::createSession()
+{
+ Q_ASSERT(m_session == NULL);
+ HRESULT hr = MFCreateMediaSession(NULL, &m_session);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::UnknownMediaStatus);
+ emit error(QMediaPlayer::ResourceError, tr("Unable to create mediasession"), true);
+ }
+
+ hr = m_session->BeginGetEvent(this, m_session);
+
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::UnknownMediaStatus);
+ emit error(QMediaPlayer::ResourceError, tr("Unable to pulling session events"), false);
+ }
+}
+
+qint64 MFPlayerSession::position()
+{
+ if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
+ return m_request.start;
+
+ if (m_pendingState == SeekPending)
+ return m_state.start;
+
+ if (m_state.command == CmdStop)
+ return qint64(m_varStart.hVal.QuadPart / 10000);
+
+ if (m_presentationClock) {
+ MFTIME time, sysTime;
+ if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
+ return 0;
+ return qint64(time / 10000);
+ }
+ return 0;
+}
+
+void MFPlayerSession::setPosition(qint64 position)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPosition";
+#endif
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdSeek);
+ m_request.start = position;
+ } else {
+ setPositionInternal(position, CmdNone);
+ }
+}
+
+void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
+{
+ if (m_status == QMediaPlayer::EndOfMedia)
+ changeStatus(QMediaPlayer::LoadedMedia);
+ if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
+ m_varStart.vt = VT_I8;
+ m_varStart.hVal.QuadPart = LONGLONG(position * 10000);
+ return;
+ }
+
+ if (m_state.command == CmdPause)
+ scrub(true);
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPositionInternal";
+#endif
+
+ PROPVARIANT varStart;
+ varStart.vt = VT_I8;
+ varStart.hVal.QuadPart = LONGLONG(position * 10000);
+ if (SUCCEEDED(m_session->Start(NULL, &varStart)))
+ {
+ PropVariantInit(&m_varStart);
+ // Store the pending state.
+ m_state.setCommand(CmdStart);
+ m_state.start = position;
+ m_pendingState = SeekPending;
+ } else {
+ emit error(QMediaPlayer::ResourceError, tr("failed to seek"), true);
+ }
+}
+
+qreal MFPlayerSession::playbackRate() const
+{
+ if (m_pendingState != NoPending)
+ return m_request.rate;
+ return m_state.rate;
+}
+
+void MFPlayerSession::setPlaybackRate(qreal rate)
+{
+ if (m_scrubbing) {
+ m_restoreRate = rate;
+ return;
+ }
+ setPlaybackRateInternal(rate);
+}
+
+void MFPlayerSession::setPlaybackRateInternal(qreal rate)
+{
+ if (rate == m_request.rate)
+ return;
+
+ m_pendingRate = rate;
+ if (!m_rateSupport)
+ return;
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPlaybackRate";
+#endif
+ BOOL isThin = FALSE;
+
+ //from MSDN http://msdn.microsoft.com/en-us/library/aa965220%28v=vs.85%29.aspx
+ //Thinning applies primarily to video streams.
+ //In thinned mode, the source drops delta frames and deliver only key frames.
+ //At very high playback rates, the source might skip some key frames (for example, deliver every other key frame).
+
+ if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
+ isThin = TRUE;
+ if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
+ qWarning() << "unable to set playbackrate = " << rate;
+ }
+ }
+ if (m_pendingState != NoPending) {
+ m_request.rate = rate;
+ m_request.isThin = isThin;
+ // Remember the current transport state (play, paused, etc), so that we
+ // can restore it after the rate change, if necessary. However, if
+ // anothercommand is already pending, that one takes precedent.
+ if (m_request.command == CmdNone)
+ m_request.setCommand(m_state.command);
+ } else {
+ //No pending operation. Commit the new rate.
+ commitRateChange(rate, isThin);
+ }
+}
+
+void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "commitRateChange";
+#endif
+ Q_ASSERT(m_pendingState == NoPending);
+ MFTIME hnsSystemTime = 0;
+ MFTIME hnsClockTime = 0;
+ Command cmdNow = m_state.command;
+ // Allowed rate transitions:
+ // Positive <-> negative: Stopped
+ // Negative <-> zero: Stopped
+ // Postive <-> zero: Paused or stopped
+ if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
+ // Transition to stopped.
+ if (cmdNow == CmdStart) {
+ // Get the current clock position. This will be the restart time.
+ m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
+ Q_ASSERT(hnsSystemTime != 0);
+
+ // Stop and set the rate
+ stop();
+
+ //Cache Request: Restart from stop.
+ m_request.setCommand(CmdSeekResume);
+ m_request.start = hnsClockTime / 10000;
+ } else if (cmdNow == CmdPause) {
+ // The current state is paused.
+ // For this rate change, the session must be stopped. However, the
+ // session cannot transition back from stopped to paused.
+ // Therefore, this rate transition is not supported while paused.
+ if (rate < 0 || m_state.rate < 0) {
+ qWarning() << "Unable to change rate from positive to negative or vice versa in paused state";
+ return;
+ }
+ }
+ } else if (rate == 0 && m_state.rate != 0) {
+ if (cmdNow != CmdPause) {
+ // Transition to paused.
+ // This transisition requires the paused state.
+ // Pause and set the rate.
+ pause();
+
+ // Request: Switch back to current state.
+ m_request.setCommand(cmdNow);
+ }
+ }
+
+ // Set the rate.
+ if (FAILED(m_rateControl->SetRate(isThin, rate))) {
+ qWarning() << "failed to set playbackrate = " << rate;
+ return;
+ }
+
+ // Adjust our current rate and requested rate.
+ m_pendingRate = m_request.rate = m_state.rate = rate;
+
+}
+
+void MFPlayerSession::scrub(bool enableScrub)
+{
+ if (m_scrubbing == enableScrub)
+ return;
+
+ m_scrubbing = enableScrub;
+
+ if (!canScrub()) {
+ if (!enableScrub)
+ m_pendingRate = m_restoreRate;
+ return;
+ }
+
+ if (enableScrub) {
+ // Enter scrubbing mode. Cache the rate.
+ m_restoreRate = m_request.rate;
+ setPlaybackRateInternal(0.0f);
+ } else {
+ // Leaving scrubbing mode. Restore the old rate.
+ setPlaybackRateInternal(m_restoreRate);
+ }
+}
+
+int MFPlayerSession::volume() const
+{
+ return m_volume;
+}
+
+void MFPlayerSession::setVolume(int volume)
+{
+ if (m_volume == volume)
+ return;
+ m_volume = volume;
+ if (m_volumeControl)
+ m_volumeControl->SetMasterVolume(m_volume * 0.01f);
+ emit volumeChanged(m_volume);
+}
+
+bool MFPlayerSession::isMuted() const
+{
+ return m_muted;
+}
+
+void MFPlayerSession::setMuted(bool muted)
+{
+ if (m_muted == muted)
+ return;
+ m_muted = muted;
+ if (m_volumeControl)
+ m_volumeControl->SetMute(BOOL(m_muted));
+ emit mutedChanged(m_muted);
+}
+
+int MFPlayerSession::bufferStatus()
+{
+ if (!m_netsourceStatistics)
+ return 0;
+ PROPVARIANT var;
+ PROPERTYKEY key;
+ key.fmtid = MFNETSOURCE_STATISTICS;
+ key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
+ int progress = -1;
+ if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
+ progress = var.lVal;
+ }
+ PropVariantClear(&var);
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "bufferStatus: progress = " << progress;
+#endif
+
+ return progress;
+}
+
+QMediaTimeRange MFPlayerSession::availablePlaybackRanges()
+{
+ if (!m_netsourceStatistics)
+ return QMediaTimeRange();
+
+ qint64 start = 0, end = 0;
+ PROPVARIANT var;
+ PROPERTYKEY key;
+ key.fmtid = MFNETSOURCE_STATISTICS;
+ key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
+ if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
+ start = qint64(var.uhVal.QuadPart / 10000);
+ key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
+ if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
+ end = qint64(var.uhVal.QuadPart / 10000);
+ }
+ }
+ PropVariantClear(&var);
+ return QMediaTimeRange(start, end);
+}
+
+HRESULT MFPlayerSession::QueryInterface(REFIID riid, void** ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFAsyncCallback) {
+ *ppvObject = static_cast<IMFAsyncCallback*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+ULONG MFPlayerSession::AddRef(void)
+{
+ return 1;
+}
+
+ULONG MFPlayerSession::Release(void)
+{
+ return 1;
+}
+
+HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult)
+{
+ if (pResult->GetStateNoAddRef() != m_session)
+ return S_OK;
+
+ IMFMediaEvent *pEvent = NULL;
+ // Get the event from the event queue.
+ HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
+ if (FAILED(hr)) {
+ return S_OK;
+ }
+
+ MediaEventType meType = MEUnknown;
+ hr = pEvent->GetType(&meType);
+ if (FAILED(hr)) {
+ pEvent->Release();
+ return S_OK;
+ }
+
+ if (meType == MESessionClosed) {
+ SetEvent(m_hCloseEvent);
+ return S_OK;
+ } else {
+ hr = m_session->BeginGetEvent(this, m_session);
+ if (FAILED(hr)) {
+ pEvent->Release();
+ return S_OK;
+ }
+ }
+
+ emit sessionEvent(pEvent);
+ return S_OK;
+}
+
+void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
+{
+ HRESULT hrStatus = S_OK;
+ HRESULT hr = sessionEvent->GetStatus(&hrStatus);
+ if (FAILED(hr)) {
+ sessionEvent->Release();
+ return;
+ }
+
+ MediaEventType meType = MEUnknown;
+ hr = sessionEvent->GetType(&meType);
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ if (FAILED(hrStatus))
+ qDebug() << "handleSessionEvent: MediaEventType = " << meType << "Failed";
+ else
+ qDebug() << "handleSessionEvent: MediaEventType = " << meType;
+#endif
+
+ switch (meType) {
+ case MENonFatalError: {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ sessionEvent->GetValue(&var);
+ qWarning() << "handleSessionEvent: non fatal error = " << var.ulVal;
+ PropVariantClear(&var);
+ emit error(QMediaPlayer::ResourceError, tr("media session non-fatal error!"), false);
+ }
+ break;
+ case MESourceUnknown:
+ changeStatus(QMediaPlayer::InvalidMedia);
+ case MEError:
+ qWarning() << "handleSessionEvent: serious error = " << hrStatus;
+ emit error(QMediaPlayer::ResourceError, tr("media session serious error!"), true);
+ break;
+ case MESessionRateChanged:
+ // If the rate change succeeded, we've already got the rate
+ // cached. If it failed, try to get the actual rate.
+ if (FAILED(hrStatus)) {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
+ m_state.rate = var.fltVal;
+ }
+ emit playbackRateChanged(playbackRate());
+ }
+ break;
+ case MESessionScrubSampleComplete :
+ if (m_scrubbing)
+ updatePendingCommands(CmdStart);
+ break;
+ case MESessionStarted:
+ if (!m_scrubbing)
+ updatePendingCommands(CmdStart);
+ break;
+ case MESessionStopped:
+ if (m_status != QMediaPlayer::EndOfMedia) {
+ m_varStart.vt = VT_I8;
+ m_varStart.hVal.QuadPart = 0;
+ changeStatus(QMediaPlayer::LoadedMedia);
+ }
+ updatePendingCommands(CmdStop);
+ break;
+ case MESessionPaused:
+ updatePendingCommands(CmdPause);
+ break;
+ case MEReconnectStart:
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MEReconnectStart" << ((hrStatus == S_OK) ? "OK" : "Failed");
+#endif
+ break;
+ case MEReconnectEnd:
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MEReconnectEnd" << ((hrStatus == S_OK) ? "OK" : "Failed");
+#endif
+ break;
+ }
+
+ if (FAILED(hrStatus)) {
+ sessionEvent->Release();
+ return;
+ }
+
+ switch (meType) {
+ case MEBufferingStarted:
+ changeStatus(QMediaPlayer::StalledMedia);
+ emit bufferStatusChanged(bufferStatus());
+ break;
+ case MEBufferingStopped:
+ changeStatus(QMediaPlayer::BufferedMedia);
+ emit bufferStatusChanged(bufferStatus());
+ break;
+ case MEEndOfPresentation:
+ stop();
+ changeStatus(QMediaPlayer::EndOfMedia);
+ m_varStart.vt = VT_I8;
+ //keep reporting the final position after end of media
+ m_varStart.hVal.QuadPart = m_duration;
+ break;
+ case MESessionEnded:
+ m_pendingState = NoPending;
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+ break;
+ case MEEndOfPresentationSegment:
+ break;
+ case MEAudioSessionVolumeChanged:
+ if (m_volumeControl) {
+ float currentVolume = 1;
+ if (SUCCEEDED(m_volumeControl->GetMasterVolume(&currentVolume))) {
+ if (currentVolume != m_volume) {
+ m_volume = currentVolume;
+ emit volumeChanged(int(m_volume * 100));
+ }
+ }
+ BOOL currentMuted = FALSE;
+ if (SUCCEEDED(m_volumeControl->GetMute(&currentMuted))) {
+ if (currentMuted != BOOL(m_muted)) {
+ m_muted = bool(currentMuted);
+ emit mutedChanged(m_muted);
+ }
+ }
+ }
+ break;
+ case MESessionTopologySet: {
+ if (SUCCEEDED(MFGetService(m_session, MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl)))) {
+ m_volumeControl->SetMasterVolume(m_volume);
+ m_volumeControl->SetMute(m_muted);
+ }
+ DWORD dwCharacteristics = 0;
+ m_sourceResolver->mediaSource()->GetCharacteristics(&dwCharacteristics);
+ emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
+ changeStatus(QMediaPlayer::LoadedMedia);
+ }
+ break;
+ case MESessionTopologyStatus: {
+ UINT32 status;
+ if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
+ if (status == MF_TOPOSTATUS_READY) {
+ IMFClock* clock;
+ if (SUCCEEDED(m_session->GetClock(&clock))) {
+ clock->QueryInterface(IID_IMFPresentationClock, (void**)(&m_presentationClock));
+ clock->Release();
+ }
+
+ if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateControl)))) {
+ if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateSupport)))) {
+ if ((m_mediaTypes & Video) == Video
+ && SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
+ m_canScrub = true;
+ }
+ BOOL isThin = FALSE;
+ float rate = 1;
+ if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
+ if (m_pendingRate != rate) {
+ m_state.rate = m_request.rate = rate;
+ setPlaybackRate(m_pendingRate);
+ }
+ }
+ }
+ MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics));
+ }
+ }
+ }
+ break;
+ }
+
+ sessionEvent->Release();
+}
+
+void MFPlayerSession::updatePendingCommands(Command command)
+{
+ emit positionChanged(position());
+ if (m_state.command != command || m_pendingState == NoPending)
+ return;
+
+ // The current pending command has completed.
+ if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
+ m_pendingState = NoPending;
+ //if we have pending seek request,
+ //then we just keep current state to paused and continue the seek request,
+ //otherwise we will restore to pause state
+ if (m_request.command == CmdSeek) {
+ m_state.setCommand(CmdPause);
+ } else {
+ pause();
+ return;
+ }
+ }
+
+ m_pendingState = NoPending;
+
+ //First look for rate changes.
+ if (m_request.rate != m_state.rate) {
+ commitRateChange(m_request.rate, m_request.isThin);
+ }
+
+ // Now look for new requests.
+ if (m_pendingState == NoPending) {
+ switch (m_request.command) {
+ case CmdStart:
+ start();
+ break;
+ case CmdPause:
+ pause();
+ break;
+ case CmdStop:
+ stop();
+ break;
+ case CmdSeek:
+ case CmdSeekResume:
+ setPositionInternal(m_request.start, m_request.command);
+ }
+ m_request.setCommand(CmdNone);
+ }
+
+}
+
+bool MFPlayerSession::canScrub() const
+{
+ return m_canScrub && m_rateSupport && m_rateControl;
+}
+
+void MFPlayerSession::clear()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerSession::clear";
+#endif
+ m_mediaTypes = 0;
+ m_canScrub = false;
+
+ m_pendingState = NoPending;
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+
+ if (m_presentationClock) {
+ m_presentationClock->Release();
+ m_presentationClock = NULL;
+ }
+ if (m_rateControl) {
+ m_rateControl->Release();
+ m_rateControl = NULL;
+ }
+ if (m_rateSupport) {
+ m_rateSupport->Release();
+ m_rateSupport = NULL;
+ }
+ if (m_volumeControl) {
+ m_volumeControl->Release();
+ m_volumeControl = NULL;
+ }
+ if (m_netsourceStatistics) {
+ m_netsourceStatistics->Release();
+ m_netsourceStatistics = NULL;
+ }
+}
diff --git a/src/plugins/wmf/player/mfplayersession.h b/src/plugins/wmf/player/mfplayersession.h
new file mode 100644
index 000000000..e6e46ada2
--- /dev/null
+++ b/src/plugins/wmf/player/mfplayersession.h
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFPLAYERSESSION_H
+#define MFPLAYERSESSION_H
+
+#include <mfapi.h>
+#include <mfidl.h>
+
+#include "qmediaplayer.h"
+#include "qmediaresource.h"
+#include "qmediaservice.h"
+#include "qmediatimerange.h"
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qwaitcondition.h>
+
+QT_BEGIN_NAMESPACE
+class QMediaContent;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class SourceResolver;
+#ifndef Q_WS_SIMULATOR
+class Evr9VideoWindowControl;
+#endif
+class MFAudioEndpointControl;
+class MFVideoRendererControl;
+class MFPlayerControl;
+class MFMetaDataControl;
+
+class MFPlayerSession : public QObject, public IMFAsyncCallback
+{
+ Q_OBJECT
+ friend class SourceResolver;
+public:
+ MFPlayerSession(QObject *parent = 0);
+ ~MFPlayerSession();
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
+
+ STDMETHODIMP_(ULONG) AddRef(void);
+
+ STDMETHODIMP_(ULONG) Release(void);
+
+ STDMETHODIMP Invoke(IMFAsyncResult *pResult);
+
+ STDMETHODIMP GetParameters(DWORD *pdwFlags, DWORD *pdwQueue)
+ {
+ Q_UNUSED(pdwFlags);
+ Q_UNUSED(pdwQueue);
+ return E_NOTIMPL;
+ }
+
+ void load(const QMediaContent &media, QIODevice *stream);
+ void stop();
+ void start();
+ void pause();
+
+ QMediaPlayer::MediaStatus status() const;
+ qint64 position();
+ void setPosition(qint64 position);
+ qreal playbackRate() const;
+ void setPlaybackRate(qreal rate);
+ int volume() const;
+ void setVolume(int volume);
+ bool isMuted() const;
+ void setMuted(bool muted);
+ int bufferStatus();
+ QMediaTimeRange availablePlaybackRanges();
+
+ void changeStatus(QMediaPlayer::MediaStatus newStatus);
+
+Q_SIGNALS:
+ void error(QMediaPlayer::Error error, QString errorString, bool isFatal);
+ void sessionEvent(IMFMediaEvent *sessionEvent);
+ void statusChanged();
+ void audioAvailable();
+ void videoAvailable();
+ void durationUpdate(qint64 duration);
+ void seekableUpdate(bool seekable);
+ void positionChanged(qint64 position);
+ void playbackRateChanged(qreal rate);
+ void volumeChanged(int volume);
+ void mutedChanged(bool muted);
+ void bufferStatusChanged(int percentFilled);
+
+private Q_SLOTS:
+ void handleMediaSourceReady();
+ void handleSessionEvent(IMFMediaEvent *sessionEvent);
+ void handleSourceError(long hr);
+
+private:
+ IMFMediaSession *m_session;
+ IMFPresentationClock *m_presentationClock;
+ IMFRateControl *m_rateControl;
+ IMFRateSupport *m_rateSupport;
+ IMFSimpleAudioVolume *m_volumeControl;
+ IPropertyStore *m_netsourceStatistics;
+ PROPVARIANT m_varStart;
+ UINT64 m_duration;
+
+ enum Command
+ {
+ CmdNone = 0,
+ CmdStop,
+ CmdStart,
+ CmdPause,
+ CmdSeek,
+ CmdSeekResume,
+ };
+
+ void clear();
+ void setPositionInternal(qint64 position, Command requestCmd);
+ void setPlaybackRateInternal(qreal rate);
+ void commitRateChange(qreal rate, BOOL isThin);
+ bool canScrub() const;
+ void scrub(bool enableScrub);
+ bool m_scrubbing;
+ float m_restoreRate;
+
+ SourceResolver *m_sourceResolver;
+ HANDLE m_hCloseEvent;
+
+ enum MediaType
+ {
+ Unknown = 0,
+ Audio = 1,
+ Video = 2,
+ };
+ DWORD m_mediaTypes;
+
+ enum PendingState
+ {
+ NoPending = 0,
+ CmdPending,
+ SeekPending,
+ };
+
+ struct SeekState
+ {
+ void setCommand(Command cmd) {
+ prevCmd = command;
+ command = cmd;
+ }
+ Command command;
+ Command prevCmd;
+ float rate; // Playback rate
+ BOOL isThin; // Thinned playback?
+ qint64 start; // Start position
+ };
+ SeekState m_state; // Current nominal state.
+ SeekState m_request; // Pending request.
+ PendingState m_pendingState;
+ float m_pendingRate;
+ void updatePendingCommands(Command command);
+
+ QMediaPlayer::MediaStatus m_status;
+ bool m_canScrub;
+ int m_volume;
+ bool m_muted;
+
+ void createSession();
+ void setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD);
+ IMFTopologyNode* addSourceNode(IMFTopology* topology, IMFMediaSource* source,
+ IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc);
+ IMFTopologyNode* addOutputNode(IMFStreamDescriptor *streamDesc, MediaType& mediaType, IMFTopology* topology, DWORD sinkID);
+};
+
+
+#endif
diff --git a/src/plugins/wmf/player/mfstream.cpp b/src/plugins/wmf/player/mfstream.cpp
new file mode 100644
index 000000000..fab854e4a
--- /dev/null
+++ b/src/plugins/wmf/player/mfstream.cpp
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mfstream.h"
+#include <QtCore/qcoreapplication.h>
+
+//MFStream is added for supporting QIODevice type of media source.
+//It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice.
+
+MFStream::MFStream(QIODevice *stream, bool ownStream)
+ : m_cRef(1)
+ , m_stream(stream)
+ , m_ownStream(ownStream)
+ , m_currentReadResult(0)
+{
+ //Move to the thread of the stream object
+ //to make sure invocations on stream
+ //are happened in the same thread of stream object
+ this->moveToThread(stream->thread());
+ connect(stream, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
+}
+
+MFStream::~MFStream()
+{
+ if (m_currentReadResult)
+ m_currentReadResult->Release();
+ if (m_ownStream)
+ m_stream->deleteLater();
+}
+
+//from IUnknown
+STDMETHODIMP MFStream::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFByteStream) {
+ *ppvObject = static_cast<IMFByteStream*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFStream::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFStream::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+}
+
+
+//from IMFByteStream
+STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities)
+{
+ if (!pdwCapabilities)
+ return E_INVALIDARG;
+ *pdwCapabilities = MFBYTESTREAM_IS_READABLE;
+ if (!m_stream->isSequential())
+ *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE;
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::GetLength(QWORD *pqwLength)
+{
+ if (!pqwLength)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwLength = QWORD(m_stream->size());
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::SetLength(QWORD)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition)
+{
+ if (!pqwPosition)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwPosition = m_stream->pos();
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition)
+{
+ QMutexLocker locker(&m_mutex);
+ //SetCurrentPosition may happend during the BeginRead/EndRead pair,
+ //refusing to execute SetCurrentPosition during that time seems to be
+ //the simplest workable solution
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ bool seekOK = m_stream->seek(qint64(qwPosition));
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+}
+
+STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream)
+{
+ if (!pfEndOfStream)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE;
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead)
+{
+ QMutexLocker locker(&m_mutex);
+ qint64 read = m_stream->read((char*)(pb), qint64(cb));
+ if (pcbRead)
+ *pcbRead = ULONG(read);
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
+ IUnknown *punkState)
+{
+ if (!pCallback || !pb)
+ return E_INVALIDARG;
+
+ Q_ASSERT(m_currentReadResult == NULL);
+
+ AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb);
+ if (state == NULL)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult);
+ state->Release();
+ if (FAILED(hr))
+ return hr;
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ return hr;
+}
+
+STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead)
+{
+ if (!pcbRead)
+ return E_INVALIDARG;
+ IUnknown *pUnk;
+ pResult->GetObject(&pUnk);
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ *pcbRead = state->bytesRead();
+ pUnk->Release();
+
+ m_currentReadResult->Release();
+ m_currentReadResult = NULL;
+
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG ,
+ IMFAsyncCallback *,
+ IUnknown *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *,
+ ULONG *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::Seek(
+ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
+ LONGLONG llSeekOffset,
+ DWORD,
+ QWORD *pqwCurrentPosition)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ qint64 pos = qint64(llSeekOffset);
+ switch (SeekOrigin) {
+ case msoCurrent:
+ pos += m_stream->pos();
+ break;
+ }
+ bool seekOK = m_stream->seek(pos);
+ if (*pqwCurrentPosition)
+ *pqwCurrentPosition = pos;
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+}
+
+STDMETHODIMP MFStream::Flush()
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::Close()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_ownStream)
+ m_stream->close();
+ return S_OK;
+}
+
+void MFStream::doRead()
+{
+ bool readDone = true;
+ IUnknown *pUnk = NULL;
+ HRESULT hr = m_currentReadResult->GetObject(&pUnk);
+ if (SUCCEEDED(hr)) {
+ //do actual read
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ ULONG cbRead;
+ Read(state->pb(), state->cb() - state->bytesRead(), &cbRead);
+ pUnk->Release();
+
+ state->setBytesRead(cbRead + state->bytesRead());
+ if (state->cb() > state->bytesRead() && !m_stream->atEnd()) {
+ readDone = false;
+ }
+ }
+
+ if (readDone) {
+ //now inform the original caller
+ m_currentReadResult->SetStatus(hr);
+ MFInvokeCallback(m_currentReadResult);
+ }
+}
+
+
+void MFStream::handleReadyRead()
+{
+ doRead();
+}
+
+void MFStream::customEvent(QEvent *event)
+{
+ if (event->type() != QEvent::User) {
+ QObject::customEvent(event);
+ return;
+ }
+ doRead();
+}
+
+//AsyncReadState is a helper class used in BeginRead for asynchronous operation
+//to record some BeginRead parameters, so these parameters could be
+//used later when actually executing the read operation in another thread.
+MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb)
+ : m_cRef(1)
+ , m_pb(pb)
+ , m_cb(cb)
+ , m_cbRead(0)
+{
+}
+
+//from IUnknown
+STDMETHODIMP MFStream::AsyncReadState::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFStream::AsyncReadState::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFStream::AsyncReadState::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+}
+
+BYTE* MFStream::AsyncReadState::pb() const
+{
+ return m_pb;
+}
+
+ULONG MFStream::AsyncReadState::cb() const
+{
+ return m_cb;
+}
+
+ULONG MFStream::AsyncReadState::bytesRead() const
+{
+ return m_cbRead;
+}
+
+void MFStream::AsyncReadState::setBytesRead(ULONG cbRead)
+{
+ m_cbRead = cbRead;
+}
diff --git a/src/plugins/wmf/player/mfstream.h b/src/plugins/wmf/player/mfstream.h
new file mode 100644
index 000000000..985d42e96
--- /dev/null
+++ b/src/plugins/wmf/player/mfstream.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef MFSTREAM_H
+#define MFSTREAM_H
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qcoreevent.h>
+
+QT_USE_NAMESPACE
+
+class MFStream : public QObject, public IMFByteStream
+{
+ Q_OBJECT
+public:
+ MFStream(QIODevice *stream, bool ownStream);
+
+ ~MFStream();
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
+
+ STDMETHODIMP_(ULONG) AddRef(void);
+
+ STDMETHODIMP_(ULONG) Release(void);
+
+
+ //from IMFByteStream
+ STDMETHODIMP GetCapabilities(DWORD *pdwCapabilities);
+
+ STDMETHODIMP GetLength(QWORD *pqwLength);
+
+ STDMETHODIMP SetLength(QWORD);
+
+ STDMETHODIMP GetCurrentPosition(QWORD *pqwPosition);
+
+ STDMETHODIMP SetCurrentPosition(QWORD qwPosition);
+
+ STDMETHODIMP IsEndOfStream(BOOL *pfEndOfStream);
+
+ STDMETHODIMP Read(BYTE *pb, ULONG cb, ULONG *pcbRead);
+
+ STDMETHODIMP BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
+ IUnknown *punkState);
+
+ STDMETHODIMP EndRead(IMFAsyncResult* pResult, ULONG *pcbRead);
+
+ STDMETHODIMP Write(const BYTE *, ULONG, ULONG *);
+
+ STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
+ IMFAsyncCallback *,
+ IUnknown *);
+
+ STDMETHODIMP EndWrite(IMFAsyncResult *,
+ ULONG *);
+
+ STDMETHODIMP Seek(
+ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
+ LONGLONG llSeekOffset,
+ DWORD,
+ QWORD *pqwCurrentPosition);
+
+ STDMETHODIMP Flush();
+
+ STDMETHODIMP Close();
+
+private:
+ class AsyncReadState : public IUnknown
+ {
+ public:
+ AsyncReadState(BYTE *pb, ULONG cb);
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
+
+ STDMETHODIMP_(ULONG) AddRef(void);
+
+ STDMETHODIMP_(ULONG) Release(void);
+
+ BYTE* pb() const;
+ ULONG cb() const;
+ ULONG bytesRead() const;
+
+ void setBytesRead(ULONG cbRead);
+
+ private:
+ long m_cRef;
+ BYTE *m_pb;
+ ULONG m_cb;
+ ULONG m_cbRead;
+ };
+
+ long m_cRef;
+ QIODevice *m_stream;
+ bool m_ownStream;
+ DWORD m_workQueueId;
+ QMutex m_mutex;
+
+ void doRead();
+
+private Q_SLOTS:
+ void handleReadyRead();
+
+protected:
+ void customEvent(QEvent *event);
+ IMFAsyncResult *m_currentReadResult;
+};
+
+#endif \ No newline at end of file
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
new file mode 100644
index 000000000..ef9ac7821
--- /dev/null
+++ b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
@@ -0,0 +1,2201 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mfvideorenderercontrol.h"
+#include <mferror.h>
+#include <qabstractvideosurface.h>
+#include <qvideosurfaceformat.h>
+#include <qtcore/qtimer.h>
+#include <qtcore/qmutex.h>
+#include <qtcore/qcoreevent.h>
+#include <qtcore/qcoreapplication.h>
+#include <qtcore/qthread.h>
+#include "guiddef.h"
+#include <qtcore/qdebug.h>
+
+//#define DEBUG_MEDIAFOUNDATION
+#define PAD_TO_DWORD(x) (((x) + 3) & ~3)
+
+namespace
+{
+ class MediaSampleVideoBuffer : public QAbstractVideoBuffer
+ {
+ public:
+ MediaSampleVideoBuffer(IMFMediaBuffer *buffer, int bytesPerLine)
+ : QAbstractVideoBuffer(NoHandle)
+ , m_buffer(buffer)
+ , m_bytesPerLine(bytesPerLine)
+ , m_mapMode(NotMapped)
+ {
+ buffer->AddRef();
+ }
+
+ ~MediaSampleVideoBuffer()
+ {
+ m_buffer->Release();
+ }
+
+ uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
+ {
+ if (m_mapMode == NotMapped && mode != NotMapped) {
+ BYTE *bytes;
+ DWORD length;
+ HRESULT hr = m_buffer->Lock(&bytes, NULL, &length);
+ if (SUCCEEDED(hr)) {
+ if (numBytes)
+ *numBytes = int(length);
+
+ if (bytesPerLine)
+ *bytesPerLine = m_bytesPerLine;
+
+ m_mapMode = mode;
+ return reinterpret_cast<uchar *>(bytes);
+ } else {
+ qWarning("Faild to lock mf buffer!");
+ }
+ }
+ return 0;
+ }
+
+ void unmap()
+ {
+ if (m_mapMode == NotMapped)
+ return;
+ m_mapMode = NotMapped;
+ m_buffer->Unlock();
+ }
+
+ MapMode mapMode() const
+ {
+ return m_mapMode;
+ }
+
+ private:
+ IMFMediaBuffer *m_buffer;
+ int m_bytesPerLine;
+ MapMode m_mapMode;
+ };
+
+ template<class T>
+ class AsyncCallback : public IMFAsyncCallback
+ {
+ public:
+ typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *pAsyncResult);
+
+ AsyncCallback(T *pParent, InvokeFn fn) : m_pParent(pParent), m_pInvokeFn(fn)
+ {
+ }
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+ {
+ if (!ppv)
+ {
+ return E_POINTER;
+ }
+ if (iid == __uuidof(IUnknown))
+ {
+ *ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
+ }
+ else if (iid == __uuidof(IMFAsyncCallback))
+ {
+ *ppv = static_cast<IMFAsyncCallback*>(this);
+ }
+ else
+ {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ // Delegate to parent class.
+ return m_pParent->AddRef();
+ }
+ STDMETHODIMP_(ULONG) Release()
+ {
+ // Delegate to parent class.
+ return m_pParent->Release();
+ }
+
+ // IMFAsyncCallback methods
+ STDMETHODIMP GetParameters(DWORD*, DWORD*)
+ {
+ // Implementation of this method is optional.
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult)
+ {
+ return (m_pParent->*m_pInvokeFn)(pAsyncResult);
+ }
+
+ T *m_pParent;
+ InvokeFn m_pInvokeFn;
+ };
+
+
+ // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
+ MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7")
+ IMarker : public IUnknown
+ {
+ virtual STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) = 0;
+ virtual STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) = 0;
+ virtual STDMETHODIMP GetContext(PROPVARIANT *pvar) = 0;
+ };
+
+ class Marker : public IMarker
+ {
+ public:
+ static HRESULT Create(
+ MFSTREAMSINK_MARKER_TYPE eMarkerType,
+ const PROPVARIANT* pvarMarkerValue, // Can be NULL.
+ const PROPVARIANT* pvarContextValue, // Can be NULL.
+ IMarker **ppMarker)
+ {
+ if (ppMarker == NULL)
+ return E_POINTER;
+
+ HRESULT hr = S_OK;
+ Marker *pMarker = new Marker(eMarkerType);
+ if (pMarker == NULL)
+ hr = E_OUTOFMEMORY;
+
+ // Copy the marker data.
+ if (SUCCEEDED(hr) && pvarMarkerValue)
+ hr = PropVariantCopy(&pMarker->m_varMarkerValue, pvarMarkerValue);
+
+ if (SUCCEEDED(hr) && pvarContextValue)
+ hr = PropVariantCopy(&pMarker->m_varContextValue, pvarContextValue);
+
+ if (SUCCEEDED(hr)) {
+ *ppMarker = pMarker;
+ (*ppMarker)->AddRef();
+ }
+
+ if (pMarker)
+ pMarker->Release();
+
+ return hr;
+ }
+
+ // IUnknown methods.
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (iid == IID_IUnknown) {
+ *ppv = static_cast<IUnknown*>(this);
+ } else if (iid == __uuidof(IMarker)) {
+ *ppv = static_cast<IMarker*>(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release()
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+ }
+
+ STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType)
+ {
+ if (pType == NULL)
+ return E_POINTER;
+ *pType = m_eMarkerType;
+ return S_OK;
+ }
+
+ STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar)
+ {
+ if (pvar == NULL)
+ return E_POINTER;
+ return PropVariantCopy(pvar, &m_varMarkerValue);
+ }
+
+ STDMETHODIMP GetContext(PROPVARIANT *pvar)
+ {
+ if (pvar == NULL)
+ return E_POINTER;
+ return PropVariantCopy(pvar, &m_varContextValue);
+ }
+
+ protected:
+ MFSTREAMSINK_MARKER_TYPE m_eMarkerType;
+ PROPVARIANT m_varMarkerValue;
+ PROPVARIANT m_varContextValue;
+
+ private:
+ long m_cRef;
+
+ Marker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_cRef(1), m_eMarkerType(eMarkerType)
+ {
+ PropVariantInit(&m_varMarkerValue);
+ PropVariantInit(&m_varContextValue);
+ }
+
+ virtual ~Marker()
+ {
+ PropVariantClear(&m_varMarkerValue);
+ PropVariantClear(&m_varContextValue);
+ }
+ };
+
+ class MediaStream : public QObject, public IMFStreamSink, public IMFMediaTypeHandler
+ {
+ Q_OBJECT
+ friend class MFVideoRendererControl;
+ public:
+ static const DWORD DEFAULT_MEDIA_STREAM_ID = 0x0;
+
+ MediaStream(IMFMediaSink *parent, MFVideoRendererControl *rendererControl)
+ : m_cRef(1)
+ , m_eventQueue(0)
+ , m_shutdown(false)
+ , m_surface(0)
+ , m_state(State_TypeNotSet)
+ , m_currentFormatIndex(-1)
+ , m_bytesPerLine(0)
+ , m_workQueueId(0)
+ , m_workQueueCB(this, &MediaStream::onDispatchWorkItem)
+ , m_finalizeResult(0)
+ , m_scheduledBuffer(0)
+ , m_presentationClock(0)
+ , m_currentMediaType(0)
+ , m_prerolling(false)
+ , m_prerollTargetTime(0)
+ , m_startTime(0)
+ , m_rendererControl(rendererControl)
+ {
+ m_sink = parent;
+
+ if (FAILED(MFCreateEventQueue(&m_eventQueue)))
+ qWarning("Failed to create mf event queue!");
+ if (FAILED(MFAllocateWorkQueue(&m_workQueueId)))
+ qWarning("Failed to allocated mf work queue!");
+ }
+
+ ~MediaStream()
+ {
+ Q_ASSERT(m_shutdown);
+ }
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFStreamSink) {
+ *ppvObject = static_cast<IMFStreamSink*>(this);
+ } else if (riid == IID_IMFMediaEventGenerator) {
+ *ppvObject = static_cast<IMFMediaEventGenerator*>(this);
+ } else if (riid == IID_IMFMediaTypeHandler) {
+ *ppvObject = static_cast<IMFMediaTypeHandler*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(static_cast<IMFStreamSink*>(this));
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+ }
+
+ //from IMFMediaEventGenerator
+ STDMETHODIMP GetEvent(
+ DWORD dwFlags,
+ IMFMediaEvent **ppEvent)
+ {
+ // GetEvent can block indefinitely, so we don't hold the lock.
+ // This requires some juggling with the event queue pointer.
+ HRESULT hr = S_OK;
+ IMFMediaEventQueue *queue = NULL;
+
+ m_mutex.lock();
+ if (m_shutdown)
+ hr = MF_E_SHUTDOWN;
+ if (SUCCEEDED(hr)) {
+ queue = m_eventQueue;
+ queue->AddRef();
+ }
+ m_mutex.unlock();
+
+ // Now get the event.
+ if (SUCCEEDED(hr)) {
+ hr = queue->GetEvent(dwFlags, ppEvent);
+ queue->Release();
+ }
+
+ return hr;
+ }
+
+ STDMETHODIMP BeginGetEvent(
+ IMFAsyncCallback *pCallback,
+ IUnknown *punkState)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_eventQueue->BeginGetEvent(pCallback, punkState);
+ }
+
+ STDMETHODIMP EndGetEvent(
+ IMFAsyncResult *pResult,
+ IMFMediaEvent **ppEvent)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_eventQueue->EndGetEvent(pResult, ppEvent);
+ }
+
+ STDMETHODIMP QueueEvent(
+ MediaEventType met,
+ REFGUID guidExtendedType,
+ HRESULT hrStatus,
+ const PROPVARIANT *pvValue)
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::QueueEvent" << met;
+#endif
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
+ }
+
+ //from IMFStreamSink
+ STDMETHODIMP GetMediaSink(
+ IMFMediaSink **ppMediaSink)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ else if (!ppMediaSink)
+ return E_INVALIDARG;
+
+ m_sink->AddRef();
+ *ppMediaSink = m_sink;
+ return S_OK;
+ }
+
+ STDMETHODIMP GetIdentifier(
+ DWORD *pdwIdentifier)
+ {
+ *pdwIdentifier = MediaStream::DEFAULT_MEDIA_STREAM_ID;
+ return S_OK;
+ }
+
+ STDMETHODIMP GetMediaTypeHandler(
+ IMFMediaTypeHandler **ppHandler)
+ {
+ LPVOID handler = NULL;
+ HRESULT hr = QueryInterface(IID_IMFMediaTypeHandler, &handler);
+ *ppHandler = (IMFMediaTypeHandler*)(handler);
+ return hr;
+ }
+
+ STDMETHODIMP ProcessSample(
+ IMFSample *pSample)
+ {
+ if (pSample == NULL)
+ return E_INVALIDARG;
+ HRESULT hr = S_OK;
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ if (!m_prerolling) {
+ hr = validateOperation(OpProcessSample);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ pSample->AddRef();
+ m_sampleQueue.push_back(pSample);
+
+ // Unless we are paused, start an async operation to dispatch the next sample.
+ if (m_state != State_Paused)
+ hr = queueAsyncOperation(OpProcessSample);
+
+ return hr;
+ }
+
+ STDMETHODIMP PlaceMarker(
+ MFSTREAMSINK_MARKER_TYPE eMarkerType,
+ const PROPVARIANT *pvarMarkerValue,
+ const PROPVARIANT *pvarContextValue)
+ {
+ HRESULT hr = S_OK;
+ QMutexLocker locker(&m_mutex);
+ IMarker *pMarker = NULL;
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ hr = validateOperation(OpPlaceMarker);
+ if (FAILED(hr))
+ return hr;
+
+ // Create a marker object and put it on the sample queue.
+ hr = Marker::Create(eMarkerType, pvarMarkerValue, pvarContextValue, &pMarker);
+ if (FAILED(hr))
+ return hr;
+
+ m_sampleQueue.push_back(pMarker);
+
+ // Unless we are paused, start an async operation to dispatch the next sample/marker.
+ if (m_state != State_Paused)
+ hr = queueAsyncOperation(OpPlaceMarker); // Increments ref count on pOp.
+ return hr;
+ }
+
+ STDMETHODIMP Flush( void)
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::Flush";
+#endif
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ // Note: Even though we are flushing data, we still need to send
+ // any marker events that were queued.
+ clearBufferCache();
+ return processSamplesFromQueue(DropSamples);
+ }
+
+ //from IMFMediaTypeHandler
+ STDMETHODIMP IsMediaTypeSupported(
+ IMFMediaType *pMediaType,
+ IMFMediaType **ppMediaType)
+ {
+ if (ppMediaType)
+ *ppMediaType = NULL;
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ int index = getMediaTypeIndex(pMediaType);
+ if (index < 0) {
+ if (ppMediaType && m_mediaTypes.size() > 0) {
+ *ppMediaType = m_mediaTypes[0];
+ (*ppMediaType)->AddRef();
+ }
+ return MF_E_INVALIDMEDIATYPE;
+ }
+
+ BOOL compressed = TRUE;
+ pMediaType->IsCompressedFormat(&compressed);
+ if (compressed) {
+ if (ppMediaType && (SUCCEEDED(MFCreateMediaType(ppMediaType)))) {
+ (*ppMediaType)->CopyAllItems(pMediaType);
+ (*ppMediaType)->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
+ (*ppMediaType)->SetUINT32(MF_MT_COMPRESSED, FALSE);
+ (*ppMediaType)->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
+ }
+ return MF_E_INVALIDMEDIATYPE;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP GetMediaTypeCount(
+ DWORD *pdwTypeCount)
+ {
+ if (pdwTypeCount == NULL)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pdwTypeCount = DWORD(m_mediaTypes.size());
+ return S_OK;
+ }
+
+ STDMETHODIMP GetMediaTypeByIndex(
+ DWORD dwIndex,
+ IMFMediaType **ppType)
+ {
+ if (ppType == NULL)
+ return E_INVALIDARG;
+ HRESULT hr = S_OK;
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ hr = MF_E_SHUTDOWN;
+
+ if (SUCCEEDED(hr)) {
+ if (dwIndex >= DWORD(m_mediaTypes.size()))
+ hr = MF_E_NO_MORE_TYPES;
+ }
+
+ if (SUCCEEDED(hr)) {
+ *ppType = m_mediaTypes[dwIndex];
+ (*ppType)->AddRef();
+ }
+ return hr;
+ }
+
+ STDMETHODIMP SetCurrentMediaType(
+ IMFMediaType *pMediaType)
+ {
+ HRESULT hr = S_OK;
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ DWORD flag = MF_MEDIATYPE_EQUAL_MAJOR_TYPES |
+ MF_MEDIATYPE_EQUAL_FORMAT_TYPES |
+ MF_MEDIATYPE_EQUAL_FORMAT_DATA;
+
+ if (m_currentMediaType && (m_currentMediaType->IsEqual(pMediaType, &flag) == S_OK))
+ return S_OK;
+
+ hr = validateOperation(OpSetMediaType);
+
+ if (SUCCEEDED(hr)) {
+ int index = getMediaTypeIndex(pMediaType);
+ if (index >= 0) {
+ UINT64 size;
+ hr = pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &size);
+ if (SUCCEEDED(hr)) {
+ m_currentFormatIndex = index;
+ int width = int(HI32(size));
+ int height = int(LO32(size));
+ QVideoSurfaceFormat format(QSize(width, height), m_pixelFormats[index]);
+ m_surfaceFormat = format;
+
+ if (FAILED(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&m_bytesPerLine))) {
+ m_bytesPerLine = getBytesPerLine(format);
+ }
+
+ m_state = State_Ready;
+ if (m_currentMediaType)
+ m_currentMediaType->Release();
+ m_currentMediaType = pMediaType;
+ pMediaType->AddRef();
+ }
+ } else {
+ hr = MF_E_INVALIDREQUEST;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHODIMP GetCurrentMediaType(
+ IMFMediaType **ppMediaType)
+ {
+ if (ppMediaType == NULL)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ if (m_currentFormatIndex < 0)
+ return MF_E_NOT_INITIALIZED;
+ *ppMediaType = m_currentMediaType;
+ (*ppMediaType)->AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP GetMajorType(
+ GUID *pguidMajorType)
+ {
+ if (pguidMajorType == NULL)
+ return E_INVALIDARG;
+ *pguidMajorType = MFMediaType_Video;
+ return S_OK;
+ }
+
+ //
+ void setSurface(QAbstractVideoSurface *surface)
+ {
+ m_mutex.lock();
+ m_surface = surface;
+ m_mutex.unlock();
+ supportedFormatsChanged();
+ }
+
+ void setClock(IMFPresentationClock *presentationClock)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_shutdown) {
+ if (m_presentationClock)
+ m_presentationClock->Release();
+ m_presentationClock = presentationClock;
+ if (m_presentationClock)
+ m_presentationClock->AddRef();
+ }
+ }
+
+ void shutdown()
+ {
+ QMutexLocker locker(&m_mutex);
+ Q_ASSERT(!m_shutdown);
+
+ if (m_currentMediaType) {
+ m_currentMediaType->Release();
+ m_currentMediaType = NULL;
+ m_currentFormatIndex = -1;
+ }
+
+ if (m_eventQueue)
+ m_eventQueue->Shutdown();
+
+ MFUnlockWorkQueue(m_workQueueId);
+
+ if (m_presentationClock) {
+ m_presentationClock->Release();
+ m_presentationClock = NULL;
+ }
+
+ if (m_scheduledBuffer) {
+ m_scheduledBuffer->Release();
+ m_scheduledBuffer = NULL;
+ }
+
+ clearMediaTypes();
+ clearSampleQueue();
+ clearBufferCache();
+
+ if (m_eventQueue) {
+ m_eventQueue->Release();
+ m_eventQueue = NULL;
+ }
+
+ m_shutdown = true;
+ }
+
+ HRESULT startPreroll(MFTIME hnsUpcomingStartTime)
+ {
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = validateOperation(OpPreroll);
+ if (SUCCEEDED(hr)) {
+ m_prerollTargetTime = hnsUpcomingStartTime;
+ hr = queueAsyncOperation(OpPreroll);
+ }
+ return hr;
+ }
+
+ HRESULT finalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
+ {
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = S_OK;
+ hr = validateOperation(OpFinalize);
+ if (SUCCEEDED(hr) && m_finalizeResult != NULL)
+ hr = MF_E_INVALIDREQUEST; // The operation is already pending.
+
+ // Create and store the async result object.
+ if (SUCCEEDED(hr))
+ hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_finalizeResult);
+
+ if (SUCCEEDED(hr)) {
+ m_state = State_Finalized;
+ hr = queueAsyncOperation(OpFinalize);
+ }
+ return hr;
+ }
+
+ HRESULT start(MFTIME start)
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::start" << start;
+#endif
+ HRESULT hr = S_OK;
+ QMutexLocker locker(&m_mutex);
+ if (m_rate != 0)
+ hr = validateOperation(OpStart);
+
+ if (SUCCEEDED(hr)) {
+ MFTIME sysTime;
+ if (start != PRESENTATION_CURRENT_POSITION)
+ m_startTime = start; // Cache the start time.
+ else
+ m_presentationClock->GetCorrelatedTime(0, &m_startTime, &sysTime);
+ m_state = State_Started;
+ hr = queueAsyncOperation(OpStart);
+ }
+ return hr;
+ }
+
+ HRESULT restart()
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::restart";
+#endif
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = validateOperation(OpRestart);
+ if (SUCCEEDED(hr)) {
+ m_state = State_Started;
+ hr = queueAsyncOperation(OpRestart);
+ }
+ return hr;
+ }
+
+ HRESULT stop()
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::stop";
+#endif
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = validateOperation(OpStop);
+ if (SUCCEEDED(hr)) {
+ m_state = State_Stopped;
+ hr = queueAsyncOperation(OpStop);
+ }
+ return hr;
+ }
+
+ HRESULT pause()
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::pause";
+#endif
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = validateOperation(OpPause);
+ if (SUCCEEDED(hr)) {
+ m_state = State_Paused;
+ hr = queueAsyncOperation(OpPause);
+ }
+ return hr;
+ }
+
+ HRESULT setRate(float rate)
+ {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MediaStream::setRate" << rate;
+#endif
+ QMutexLocker locker(&m_mutex);
+ m_rate = rate;
+ queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
+ return S_OK;
+ }
+
+ void supportedFormatsChanged()
+ {
+ QMutexLocker locker(&m_mutex);
+ m_pixelFormats.clear();
+ clearMediaTypes();
+ QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats();
+ foreach (QVideoFrame::PixelFormat format, formats) {
+ IMFMediaType *mediaType;
+ if (FAILED(MFCreateMediaType(&mediaType))) {
+ qWarning("Failed to create mf media type!");
+ continue;
+ }
+ mediaType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
+ mediaType->SetUINT32(MF_MT_COMPRESSED, FALSE);
+ mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
+ mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ switch (format) {
+ case QVideoFrame::Format_ARGB32:
+ case QVideoFrame::Format_ARGB32_Premultiplied:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
+ break;
+ case QVideoFrame::Format_RGB32:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
+ break;
+ case QVideoFrame::Format_RGB24:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24);
+ break;
+ case QVideoFrame::Format_RGB565:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565);
+ break;
+ case QVideoFrame::Format_RGB555:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555);
+ break;
+ case QVideoFrame::Format_AYUV444:
+ case QVideoFrame::Format_AYUV444_Premultiplied:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV);
+ break;
+ case QVideoFrame::Format_YUV420P:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I420);
+ break;
+ case QVideoFrame::Format_UYVY:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY);
+ break;
+ case QVideoFrame::Format_YV12:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
+ break;
+ case QVideoFrame::Format_NV12:
+ mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
+ break;
+ default:
+ mediaType->Release();
+ continue;
+ }
+ m_pixelFormats.push_back(format);
+ m_mediaTypes.push_back(mediaType);
+ }
+ }
+
+ void present()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_scheduledBuffer)
+ return;
+ m_surface->present(QVideoFrame(
+ new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine),
+ m_surfaceFormat.frameSize(),
+ m_surfaceFormat.pixelFormat()));
+ m_scheduledBuffer->Release();
+ m_scheduledBuffer = NULL;
+ if (m_rate != 0)
+ schedulePresentation(true);
+ }
+
+ enum
+ {
+ StartSurface = QEvent::User,
+ StopSurface,
+ FlushSurface,
+ PresentSurface
+ };
+
+ class PresentEvent : public QEvent
+ {
+ public:
+ PresentEvent(MFTIME targetTime)
+ : QEvent(QEvent::Type(PresentSurface))
+ , m_time(targetTime)
+ {
+ }
+
+ int targetTime()
+ {
+ return m_time;
+ }
+
+ private:
+ MFTIME m_time;
+ };
+
+ protected:
+ void customEvent(QEvent *event)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (event->type() == StartSurface) {
+ if (m_state == State_WaitForSurfaceStart) {
+ m_startResult = startSurface();
+ queueAsyncOperation(OpStart);
+ }
+ } else if (event->type() == StopSurface) {
+ stopSurface();
+ } else {
+ QObject::customEvent(event);
+ }
+ }
+ HRESULT m_startResult;
+
+ private:
+ HRESULT startSurface()
+ {
+ if (!m_surface->isFormatSupported(m_surfaceFormat))
+ return S_FALSE;
+ if (!m_surface->start(m_surfaceFormat))
+ return S_FALSE;
+ return S_OK;
+ }
+
+ void stopSurface()
+ {
+ m_surface->stop();
+ }
+
+ enum FlushState
+ {
+ DropSamples = 0,
+ WriteSamples
+ };
+
+ // State enum: Defines the current state of the stream.
+ enum State
+ {
+ State_TypeNotSet = 0, // No media type is set
+ State_Ready, // Media type is set, Start has never been called.
+ State_Started,
+ State_Paused,
+ State_Stopped,
+ State_WaitForSurfaceStart,
+ State_Finalized,
+ State_Count = State_Finalized + 1 // Number of states
+ };
+
+ // StreamOperation: Defines various operations that can be performed on the stream.
+ enum StreamOperation
+ {
+ OpSetMediaType = 0,
+ OpPreroll,
+ OpStart,
+ OpRestart,
+ OpPause,
+ OpStop,
+ OpSetRate,
+ OpProcessSample,
+ OpPlaceMarker,
+ OpFinalize,
+
+ Op_Count = OpFinalize + 1 // Number of operations
+ };
+
+ // AsyncOperation:
+ // Used to queue asynchronous operations. When we call MFPutWorkItem, we use this
+ // object for the callback state (pState). Then, when the callback is invoked,
+ // we can use the object to determine which asynchronous operation to perform.
+ class AsyncOperation : public IUnknown
+ {
+ public:
+ AsyncOperation(StreamOperation op)
+ :m_cRef(1), m_op(op)
+ {
+ }
+
+ StreamOperation m_op; // The operation to perform.
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (iid == IID_IUnknown) {
+ *ppv = static_cast<IUnknown*>(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+ STDMETHODIMP_(ULONG) AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+ STDMETHODIMP_(ULONG) Release()
+ {
+ ULONG uCount = InterlockedDecrement(&m_cRef);
+ if (uCount == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return uCount;
+ }
+
+ private:
+ long m_cRef;
+ virtual ~AsyncOperation()
+ {
+ Q_ASSERT(m_cRef == 0);
+ }
+ };
+
+ // ValidStateMatrix: Defines a look-up table that says which operations
+ // are valid from which states.
+ static BOOL ValidStateMatrix[State_Count][Op_Count];
+
+ long m_cRef;
+ QMutex m_mutex;
+
+ IMFMediaType *m_currentMediaType;
+ State m_state;
+ IMFMediaSink *m_sink;
+ IMFMediaEventQueue *m_eventQueue;
+ DWORD m_workQueueId;
+ AsyncCallback<MediaStream> m_workQueueCB;
+ QList<IUnknown*> m_sampleQueue;
+ IMFAsyncResult *m_finalizeResult; // Result object for Finalize operation.
+ MFTIME m_startTime; // Presentation time when the clock started.
+
+ bool m_shutdown;
+ QList<IMFMediaType*> m_mediaTypes;
+ QList<QVideoFrame::PixelFormat> m_pixelFormats;
+ int m_currentFormatIndex;
+ int m_bytesPerLine;
+ QVideoSurfaceFormat m_surfaceFormat;
+ QAbstractVideoSurface* m_surface;
+ MFVideoRendererControl *m_rendererControl;
+
+ void clearMediaTypes()
+ {
+ foreach (IMFMediaType* mediaType, m_mediaTypes)
+ mediaType->Release();
+ m_mediaTypes.clear();
+ }
+
+ int getMediaTypeIndex(IMFMediaType *mt)
+ {
+ GUID majorType;
+ if (FAILED(mt->GetMajorType(&majorType)))
+ return -1;
+ if (majorType != MFMediaType_Video)
+ return -1;
+
+ GUID subType;
+ if (FAILED(mt->GetGUID(MF_MT_SUBTYPE, &subType)))
+ return -1;
+
+ for (int index = 0; index < m_mediaTypes.size(); ++index) {
+ GUID st;
+ m_mediaTypes[index]->GetGUID(MF_MT_SUBTYPE, &st);
+ if (st == subType)
+ return index;
+ }
+ return -1;
+ }
+
+ int getBytesPerLine(const QVideoSurfaceFormat &format)
+ {
+ switch (format.pixelFormat()) {
+ // 32 bpp packed formats.
+ case QVideoFrame::Format_RGB32:
+ case QVideoFrame::Format_AYUV444:
+ return format.frameWidth() * 4;
+ // 24 bpp packed formats.
+ case QVideoFrame::Format_RGB24:
+ return PAD_TO_DWORD(format.frameWidth() * 3);
+ // 16 bpp packed formats.
+ case QVideoFrame::Format_RGB565:
+ case QVideoFrame::Format_RGB555:
+ case QVideoFrame::Format_YUYV:
+ case QVideoFrame::Format_UYVY:
+ return PAD_TO_DWORD(format.frameWidth() * 2);
+ // Planar formats.
+ case QVideoFrame::Format_IMC1:
+ case QVideoFrame::Format_IMC2:
+ case QVideoFrame::Format_IMC3:
+ case QVideoFrame::Format_IMC4:
+ case QVideoFrame::Format_YV12:
+ case QVideoFrame::Format_NV12:
+ case QVideoFrame::Format_YUV420P:
+ return PAD_TO_DWORD(format.frameWidth());
+ default:
+ return 0;
+ }
+ }
+
+ // Callback for MFPutWorkItem.
+ HRESULT onDispatchWorkItem(IMFAsyncResult* pAsyncResult)
+ {
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = S_OK;
+ IUnknown *pState = NULL;
+ hr = pAsyncResult->GetState(&pState);
+ if (SUCCEEDED(hr)) {
+ // The state object is an AsncOperation object.
+ AsyncOperation *pOp = (AsyncOperation*)pState;
+ StreamOperation op = pOp->m_op;
+ switch (op) {
+ case OpStart:
+ endPreroll(S_FALSE);
+ if (m_state == State_WaitForSurfaceStart) {
+ hr = m_startResult;
+ m_state = State_Started;
+ } else if (!m_surface->isActive()) {
+ if (thread() == QThread::currentThread()) {
+ hr = startSurface();
+ }
+ else {
+ m_state = State_WaitForSurfaceStart;
+ QCoreApplication::postEvent(m_rendererControl, new QChildEvent(QEvent::Type(StartSurface), this));
+ break;
+ }
+ }
+ case OpRestart:
+ endPreroll(S_FALSE);
+ if (SUCCEEDED(hr)) {
+ // Send MEStreamSinkStarted.
+ hr = queueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL);
+ // Kick things off by requesting samples...
+ schedulePresentation(true);
+ // There might be samples queue from earlier (ie, while paused).
+ if (SUCCEEDED(hr))
+ hr = processSamplesFromQueue(WriteSamples);
+ }
+ break;
+ case OpPreroll:
+ beginPreroll();
+ break;
+ case OpStop:
+ // Drop samples from queue.
+ hr = processSamplesFromQueue(DropSamples);
+ if (m_scheduledBuffer) {
+ m_scheduledBuffer->Release();
+ m_scheduledBuffer = NULL;
+ }
+ // Send the event even if the previous call failed.
+ hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL);
+ if (m_surface->isActive()) {
+ if (thread() == QThread::currentThread()) {
+ stopSurface();
+ }
+ else {
+ QCoreApplication::postEvent(m_rendererControl, new QChildEvent(QEvent::Type(StopSurface), this));
+ }
+ }
+ break;
+ case OpPause:
+ hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
+ break;
+ case OpSetRate:
+ //TODO:
+ break;
+ case OpProcessSample:
+ case OpPlaceMarker:
+ hr = dispatchProcessSample(pOp);
+ break;
+ case OpFinalize:
+ endPreroll(S_FALSE);
+ hr = dispatchFinalize(pOp);
+ break;
+ }
+ }
+
+ if (pState)
+ pState->Release();
+ return hr;
+ }
+
+
+ HRESULT queueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue)
+ {
+ HRESULT hr = S_OK;
+ if (m_shutdown)
+ hr = MF_E_SHUTDOWN;
+ if (SUCCEEDED(hr))
+ hr = m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
+ return hr;
+ }
+
+ HRESULT validateOperation(StreamOperation op)
+ {
+ Q_ASSERT(!m_shutdown);
+ if (ValidStateMatrix[m_state][op])
+ return S_OK;
+ else
+ return MF_E_INVALIDREQUEST;
+ }
+
+ HRESULT queueAsyncOperation(StreamOperation op)
+ {
+ HRESULT hr = S_OK;
+ AsyncOperation *asyncOp = new AsyncOperation(op);
+ if (asyncOp == NULL)
+ hr = E_OUTOFMEMORY;
+
+ if (SUCCEEDED(hr))
+ hr = MFPutWorkItem(m_workQueueId, &m_workQueueCB, asyncOp);
+
+ if (asyncOp)
+ asyncOp->Release();
+
+ return hr;
+ }
+
+ HRESULT processSamplesFromQueue(FlushState bFlushData)
+ {
+ HRESULT hr = S_OK;
+ QList<IUnknown*>::Iterator pos = m_sampleQueue.begin();
+ // Enumerate all of the samples/markers in the queue.
+ while (pos != m_sampleQueue.end()) {
+ IUnknown *pUnk = NULL;
+ IMarker *pMarker = NULL;
+ IMFSample *pSample = NULL;
+ pUnk = *pos;
+ // Figure out if this is a marker or a sample.
+ if (SUCCEEDED(hr)) {
+ hr = pUnk->QueryInterface(__uuidof(IMarker), (void**)&pMarker);
+ if (hr == E_NOINTERFACE)
+ hr = pUnk->QueryInterface(IID_IMFSample, (void**)&pSample);
+ }
+
+ // Now handle the sample/marker appropriately.
+ if (SUCCEEDED(hr)) {
+ if (pMarker) {
+ hr = sendMarkerEvent(pMarker, bFlushData);
+ } else {
+ Q_ASSERT(pSample != NULL); // Not a marker, must be a sample
+ if (bFlushData == WriteSamples)
+ hr = processSampleData(pSample);
+ }
+ }
+ if (pMarker)
+ pMarker->Release();
+ if (pSample)
+ pSample->Release();
+
+ if (FAILED(hr))
+ break;
+
+ pos++;
+ }
+
+ clearSampleQueue();
+ return hr;
+ }
+
+ void beginPreroll()
+ {
+ if (m_prerolling)
+ return;
+ m_prerolling = true;
+ clearSampleQueue();
+ clearBufferCache();
+ queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
+ }
+
+ void endPreroll(HRESULT hrStatus)
+ {
+ if (!m_prerolling)
+ return;
+ m_prerolling = false;
+ queueEvent(MEStreamSinkPrerolled, GUID_NULL, hrStatus, NULL);
+ }
+ MFTIME m_prerollTargetTime;
+ bool m_prerolling;
+
+ void clearSampleQueue() {
+ foreach (IUnknown* sample, m_sampleQueue)
+ sample->Release();
+ m_sampleQueue.clear();
+ }
+
+ HRESULT sendMarkerEvent(IMarker *pMarker, FlushState FlushState)
+ {
+ HRESULT hr = S_OK;
+ HRESULT hrStatus = S_OK; // Status code for marker event.
+ if (FlushState == DropSamples)
+ hrStatus = E_ABORT;
+
+ PROPVARIANT var;
+ PropVariantInit(&var);
+
+ // Get the context data.
+ hr = pMarker->GetContext(&var);
+
+ if (SUCCEEDED(hr))
+ hr = queueEvent(MEStreamSinkMarker, GUID_NULL, hrStatus, &var);
+
+ PropVariantClear(&var);
+ return hr;
+ }
+
+ HRESULT dispatchProcessSample(AsyncOperation* pOp)
+ {
+ HRESULT hr = S_OK;
+ Q_ASSERT(pOp != NULL);
+ hr = processSamplesFromQueue(WriteSamples);
+ // We are in the middle of an asynchronous operation, so if something failed, send an error.
+ if (FAILED(hr))
+ hr = queueEvent(MEError, GUID_NULL, hr, NULL);
+
+ return hr;
+ }
+
+ HRESULT dispatchFinalize(AsyncOperation*)
+ {
+ HRESULT hr = S_OK;
+ // Write any samples left in the queue...
+ hr = processSamplesFromQueue(WriteSamples);
+
+ // Set the async status and invoke the callback.
+ m_finalizeResult->SetStatus(hr);
+ hr = MFInvokeCallback(m_finalizeResult);
+ return hr;
+ }
+
+ HRESULT processSampleData(IMFSample *pSample)
+ {
+ LONGLONG time;
+ HRESULT hr = pSample->GetSampleTime(&time);
+
+ if (m_prerolling) {
+ if (SUCCEEDED(hr) && time >= m_prerollTargetTime) {
+ IMFMediaBuffer *pBuffer = NULL;
+ hr = pSample->ConvertToContiguousBuffer(&pBuffer);
+ if (SUCCEEDED(hr)) {
+ SampleBuffer sb;
+ sb.m_buffer = pBuffer;
+ sb.m_time = time;
+ m_bufferCache.push_back(sb);
+ endPreroll(S_OK);
+ }
+ } else {
+ queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
+ }
+ } else {
+ bool requestSample = true;
+ // If the plugins/multimedia/wmf/player/mfstream.cpp
+time stamp is too early, just discard this sample.
+ if (SUCCEEDED(hr) && time >= m_startTime) {
+ IMFMediaBuffer *pBuffer = NULL;
+ hr = pSample->ConvertToContiguousBuffer(&pBuffer);
+ if (SUCCEEDED(hr)) {
+ SampleBuffer sb;
+ sb.m_buffer = pBuffer;
+ sb.m_time = time;
+ m_bufferCache.push_back(sb);
+ }
+ if (m_rate == 0)
+ requestSample = false;
+ }
+ schedulePresentation(requestSample);
+ }
+ return hr;
+ }
+
+ class SampleBuffer
+ {
+ public:
+ IMFMediaBuffer *m_buffer;
+ LONGLONG m_time;
+ };
+ QList<SampleBuffer> m_bufferCache;
+ static const int BUFFER_CACHE_SIZE = 2;
+
+ void clearBufferCache()
+ {
+ foreach (SampleBuffer sb, m_bufferCache)
+ sb.m_buffer->Release();
+ m_bufferCache.clear();
+ }
+
+ void schedulePresentation(bool requestSample)
+ {
+ if (m_state == State_Paused)
+ return;
+ if (!m_scheduledBuffer) {
+ //get time from presentation time
+ MFTIME currentTime = m_startTime, sysTime;
+ bool timeOK = true;
+ if (m_rate != 0) {
+ if (FAILED(m_presentationClock->GetCorrelatedTime(0, &currentTime, &sysTime)))
+ timeOK = false;
+ }
+ while (!m_bufferCache.isEmpty()) {
+ SampleBuffer sb = m_bufferCache.first();
+ m_bufferCache.pop_front();
+ if (timeOK && currentTime > sb.m_time) {
+ sb.m_buffer->Release();
+ qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f;
+ continue;
+ }
+ m_scheduledBuffer = sb.m_buffer;
+ QCoreApplication::postEvent(m_rendererControl, new PresentEvent(sb.m_time));
+ if (m_rate == 0)
+ queueEvent(MEStreamSinkScrubSampleComplete, GUID_NULL, S_OK, NULL);
+ break;
+ }
+ }
+ if (requestSample && m_bufferCache.size() < BUFFER_CACHE_SIZE)
+ queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
+ }
+ IMFMediaBuffer *m_scheduledBuffer;
+ IMFPresentationClock *m_presentationClock;
+ float m_rate;
+ };
+
+ BOOL MediaStream::ValidStateMatrix[MediaStream::State_Count][MediaStream::Op_Count] =
+ {
+ // States: Operations:
+ // SetType Start Preroll, Restart Pause Stop SetRate Sample Marker Finalize
+ /* NotSet */ TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+
+ /* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE,
+
+ /* Start */ FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
+
+ /* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
+
+ /* Stop */ FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE,
+
+ /*WaitForSurfaceStart*/ FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE,
+
+ /* Final */ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
+
+ // Note about states:
+ // 1. OnClockRestart should only be called from paused state.
+ // 2. While paused, the sink accepts samples but does not process them.
+ };
+
+ class MediaSink : public IMFFinalizableMediaSink, public IMFClockStateSink, public IMFMediaSinkPreroll
+ {
+ public:
+ MediaSink(MFVideoRendererControl *rendererControl)
+ : m_cRef(1)
+ , m_shutdown(false)
+ , m_presentationClock(0)
+ , m_playRate(1)
+ {
+ m_stream = new MediaStream(this, rendererControl);
+ }
+
+ ~MediaSink()
+ {
+ Q_ASSERT(m_shutdown);
+ }
+
+ void setSurface(QAbstractVideoSurface *surface)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return;
+ m_stream->setSurface(surface);
+ }
+
+ void supportedFormatsChanged()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return;
+ m_stream->supportedFormatsChanged();
+ }
+
+ void present()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return;
+ m_stream->present();
+ }
+
+ MFTIME getTime()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_presentationClock)
+ return 0;
+ MFTIME time, sysTime;
+ m_presentationClock->GetCorrelatedTime(0, &time, &sysTime);
+ return time;
+ }
+
+ float getPlayRate()
+ {
+ QMutexLocker locker(&m_mutex);
+ return m_playRate;
+ }
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFMediaSink) {
+ *ppvObject = static_cast<IMFMediaSink*>(this);
+ } else if (riid == IID_IMFMediaSinkPreroll) {
+ *ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
+ } else if (riid == IID_IMFClockStateSink) {
+ *ppvObject = static_cast<IMFClockStateSink*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+ }
+
+
+
+ //IMFMediaSinkPreroll
+ STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->startPreroll(hnsUpcomingStartTime);
+ }
+
+ //from IMFFinalizableMediaSink
+ STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->finalize(pCallback, punkState);
+ }
+
+ STDMETHODIMP EndFinalize(IMFAsyncResult *pResult)
+ {
+ HRESULT hr = S_OK;
+ // Return the status code from the async result.
+ if (pResult == NULL)
+ hr = E_INVALIDARG;
+ else
+ hr = pResult->GetStatus();
+ return hr;
+ }
+
+ //from IMFMediaSink
+ STDMETHODIMP GetCharacteristics(
+ DWORD *pdwCharacteristics)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ *pdwCharacteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_CAN_PREROLL;
+ return S_OK;
+ }
+
+ STDMETHODIMP AddStreamSink(
+ DWORD,
+ IMFMediaType *,
+ IMFStreamSink **)
+ {
+ QMutexLocker locker(&m_mutex);
+ return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
+ }
+
+ STDMETHODIMP RemoveStreamSink(
+ DWORD)
+ {
+ QMutexLocker locker(&m_mutex);
+ return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
+ }
+
+ STDMETHODIMP GetStreamSinkCount(
+ DWORD *pcStreamSinkCount)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ *pcStreamSinkCount = 1;
+ return S_OK;
+ }
+
+ STDMETHODIMP GetStreamSinkByIndex(
+ DWORD dwIndex,
+ IMFStreamSink **ppStreamSink)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ if (dwIndex != 0)
+ return MF_E_INVALIDINDEX;
+
+ *ppStreamSink = m_stream;
+ m_stream->AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP GetStreamSinkById(
+ DWORD dwStreamSinkIdentifier,
+ IMFStreamSink **ppStreamSink)
+ {
+ if (ppStreamSink == NULL)
+ return E_INVALIDARG;
+ if (dwStreamSinkIdentifier != MediaStream::DEFAULT_MEDIA_STREAM_ID)
+ return MF_E_INVALIDSTREAMNUMBER;
+
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ *ppStreamSink = m_stream;
+ m_stream->AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP SetPresentationClock(
+ IMFPresentationClock *pPresentationClock)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ if (m_presentationClock) {
+ m_presentationClock->RemoveClockStateSink(this);
+ m_presentationClock->Release();
+ }
+ m_presentationClock = pPresentationClock;
+ if (m_presentationClock) {
+ m_presentationClock->AddRef();
+ m_presentationClock->AddClockStateSink(this);
+ }
+ m_stream->setClock(m_presentationClock);
+ return S_OK;
+ }
+
+ STDMETHODIMP GetPresentationClock(
+ IMFPresentationClock **ppPresentationClock)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ *ppPresentationClock = m_presentationClock;
+ if (m_presentationClock) {
+ m_presentationClock->AddRef();
+ return S_OK;
+ }
+ return MF_E_NO_CLOCK;
+ }
+
+ STDMETHODIMP Shutdown(void)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+
+ m_stream->shutdown();
+ if (m_presentationClock) {
+ m_presentationClock->Release();
+ m_presentationClock = NULL;
+ }
+ m_stream->Release();
+ m_stream = NULL;
+ m_shutdown = true;
+ return S_OK;
+ }
+
+ // IMFClockStateSink methods
+ STDMETHODIMP OnClockStart(MFTIME, LONGLONG llClockStartOffset)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->start(llClockStartOffset);
+ }
+
+ STDMETHODIMP OnClockStop(MFTIME)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->stop();
+ }
+
+ STDMETHODIMP OnClockPause(MFTIME)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->pause();
+ }
+
+ STDMETHODIMP OnClockRestart(MFTIME)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ return m_stream->restart();
+ }
+
+ STDMETHODIMP OnClockSetRate(MFTIME, float flRate)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_shutdown)
+ return MF_E_SHUTDOWN;
+ m_playRate = flRate;
+ return m_stream->setRate(flRate);
+ }
+
+ private:
+ long m_cRef;
+ QMutex m_mutex;
+ bool m_shutdown;
+ IMFPresentationClock *m_presentationClock;
+ MediaStream *m_stream;
+ float m_playRate;
+ };
+
+ class VideoRendererActivate : public IMFActivate
+ {
+ public:
+ VideoRendererActivate(MFVideoRendererControl *rendererControl)
+ : m_cRef(1)
+ , m_sink(0)
+ , m_rendererControl(rendererControl)
+ , m_attributes(0)
+ {
+ MFCreateAttributes(&m_attributes, 0);
+ m_sink = new MediaSink(rendererControl);
+ }
+
+ ~VideoRendererActivate()
+ {
+ m_attributes->Release();
+ }
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFActivate) {
+ *ppvObject = static_cast<IMFActivate*>(this);
+ } else if (riid == IID_IMFAttributes) {
+ *ppvObject = static_cast<IMFAttributes*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this));
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+ }
+
+ //from IMFActivate
+ STDMETHODIMP ActivateObject(REFIID riid, void **ppv)
+ {
+ if (!ppv)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ if (!m_sink) {
+ m_sink = new MediaSink(m_rendererControl);
+ if (m_surface)
+ m_sink->setSurface(m_surface);
+ }
+ return m_sink->QueryInterface(riid, ppv);
+ }
+
+ STDMETHODIMP ShutdownObject(void)
+ {
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = S_OK;
+ if (m_sink) {
+ hr = m_sink->Shutdown();
+ m_sink->Release();
+ m_sink = NULL;
+ }
+ return hr;
+ }
+
+ STDMETHODIMP DetachObject(void)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_sink) {
+ m_sink->Release();
+ m_sink = NULL;
+ }
+ return S_OK;
+ }
+
+ //from IMFAttributes
+ STDMETHODIMP GetItem(
+ REFGUID guidKey,
+ PROPVARIANT *pValue)
+ {
+ return m_attributes->GetItem(guidKey, pValue);
+ }
+
+ STDMETHODIMP GetItemType(
+ REFGUID guidKey,
+ MF_ATTRIBUTE_TYPE *pType)
+ {
+ return m_attributes->GetItemType(guidKey, pType);
+ }
+
+ STDMETHODIMP CompareItem(
+ REFGUID guidKey,
+ REFPROPVARIANT Value,
+ BOOL *pbResult)
+ {
+ return m_attributes->CompareItem(guidKey, Value, pbResult);
+ }
+
+ STDMETHODIMP Compare(
+ IMFAttributes *pTheirs,
+ MF_ATTRIBUTES_MATCH_TYPE MatchType,
+ BOOL *pbResult)
+ {
+ return m_attributes->Compare(pTheirs, MatchType, pbResult);
+ }
+
+ STDMETHODIMP GetUINT32(
+ REFGUID guidKey,
+ UINT32 *punValue)
+ {
+ return m_attributes->GetUINT32(guidKey, punValue);
+ }
+
+ STDMETHODIMP GetUINT64(
+ REFGUID guidKey,
+ UINT64 *punValue)
+ {
+ return m_attributes->GetUINT64(guidKey, punValue);
+ }
+
+ STDMETHODIMP GetDouble(
+ REFGUID guidKey,
+ double *pfValue)
+ {
+ return m_attributes->GetDouble(guidKey, pfValue);
+ }
+
+ STDMETHODIMP GetGUID(
+ REFGUID guidKey,
+ GUID *pguidValue)
+ {
+ return m_attributes->GetGUID(guidKey, pguidValue);
+ }
+
+ STDMETHODIMP GetStringLength(
+ REFGUID guidKey,
+ UINT32 *pcchLength)
+ {
+ return m_attributes->GetStringLength(guidKey, pcchLength);
+ }
+
+ STDMETHODIMP GetString(
+ REFGUID guidKey,
+ LPWSTR pwszValue,
+ UINT32 cchBufSize,
+ UINT32 *pcchLength)
+ {
+ return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
+ }
+
+ STDMETHODIMP GetAllocatedString(
+ REFGUID guidKey,
+ LPWSTR *ppwszValue,
+ UINT32 *pcchLength)
+ {
+ return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
+ }
+
+ STDMETHODIMP GetBlobSize(
+ REFGUID guidKey,
+ UINT32 *pcbBlobSize)
+ {
+ return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
+ }
+
+ STDMETHODIMP GetBlob(
+ REFGUID guidKey,
+ UINT8 *pBuf,
+ UINT32 cbBufSize,
+ UINT32 *pcbBlobSize)
+ {
+ return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
+ }
+
+ STDMETHODIMP GetAllocatedBlob(
+ REFGUID guidKey,
+ UINT8 **ppBuf,
+ UINT32 *pcbSize)
+ {
+ return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
+ }
+
+ STDMETHODIMP GetUnknown(
+ REFGUID guidKey,
+ REFIID riid,
+ LPVOID *ppv)
+ {
+ return m_attributes->GetUnknown(guidKey, riid, ppv);
+ }
+
+ STDMETHODIMP SetItem(
+ REFGUID guidKey,
+ REFPROPVARIANT Value)
+ {
+ return m_attributes->SetItem(guidKey, Value);
+ }
+
+ STDMETHODIMP DeleteItem(
+ REFGUID guidKey)
+ {
+ return m_attributes->DeleteItem(guidKey);
+ }
+
+ STDMETHODIMP DeleteAllItems(void)
+ {
+ return m_attributes->DeleteAllItems();
+ }
+
+ STDMETHODIMP SetUINT32(
+ REFGUID guidKey,
+ UINT32 unValue)
+ {
+ return m_attributes->SetUINT32(guidKey, unValue);
+ }
+
+ STDMETHODIMP SetUINT64(
+ REFGUID guidKey,
+ UINT64 unValue)
+ {
+ return m_attributes->SetUINT64(guidKey, unValue);
+ }
+
+ STDMETHODIMP SetDouble(
+ REFGUID guidKey,
+ double fValue)
+ {
+ return m_attributes->SetDouble(guidKey, fValue);
+ }
+
+ STDMETHODIMP SetGUID(
+ REFGUID guidKey,
+ REFGUID guidValue)
+ {
+ return m_attributes->SetGUID(guidKey, guidValue);
+ }
+
+ STDMETHODIMP SetString(
+ REFGUID guidKey,
+ LPCWSTR wszValue)
+ {
+ return m_attributes->SetString(guidKey, wszValue);
+ }
+
+ STDMETHODIMP SetBlob(
+ REFGUID guidKey,
+ const UINT8 *pBuf,
+ UINT32 cbBufSize)
+ {
+ return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
+ }
+
+ STDMETHODIMP SetUnknown(
+ REFGUID guidKey,
+ IUnknown *pUnknown)
+ {
+ return m_attributes->SetUnknown(guidKey, pUnknown);
+ }
+
+ STDMETHODIMP LockStore(void)
+ {
+ return m_attributes->LockStore();
+ }
+
+ STDMETHODIMP UnlockStore(void)
+ {
+ return m_attributes->UnlockStore();
+ }
+
+ STDMETHODIMP GetCount(
+ UINT32 *pcItems)
+ {
+ return m_attributes->GetCount(pcItems);
+ }
+
+ STDMETHODIMP GetItemByIndex(
+ UINT32 unIndex,
+ GUID *pguidKey,
+ PROPVARIANT *pValue)
+ {
+ return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
+ }
+
+ STDMETHODIMP CopyAllItems(
+ IMFAttributes *pDest)
+ {
+ return m_attributes->CopyAllItems(pDest);
+ }
+
+ /////////////////////////////////
+ void setSurface(QAbstractVideoSurface *surface)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_surface == surface)
+ return;
+
+ m_surface = surface;
+
+ if (!m_sink)
+ return;
+ m_sink->setSurface(m_surface);
+ }
+
+ void supportedFormatsChanged()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_sink)
+ return;
+ m_sink->supportedFormatsChanged();
+ }
+
+ void present()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (!m_sink)
+ return;
+ m_sink->present();
+ }
+
+ MFTIME getTime()
+ {
+ if (m_sink)
+ return m_sink->getTime();
+ return 0;
+ }
+
+ float getPlayRate()
+ {
+ if (m_sink)
+ return m_sink->getPlayRate();
+ return 1;
+ }
+
+ private:
+ long m_cRef;
+ bool m_shutdown;
+ MediaSink *m_sink;
+ MFVideoRendererControl *m_rendererControl;
+ IMFAttributes *m_attributes;
+ QAbstractVideoSurface *m_surface;
+ QMutex m_mutex;
+ };
+}
+
+MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_surface(0)
+ , m_callback(0)
+{
+ m_currentActivate = new VideoRendererActivate(this);
+}
+
+MFVideoRendererControl::~MFVideoRendererControl()
+{
+ if (m_currentActivate) {
+ m_currentActivate->ShutdownObject();
+ m_currentActivate->Release();
+ }
+}
+
+QAbstractVideoSurface *MFVideoRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface == surface)
+ return;
+
+ if (m_surface)
+ disconnect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
+ m_surface = surface;
+
+ if (m_surface) {
+ connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
+ }
+ static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface);
+}
+
+void MFVideoRendererControl::customEvent(QEvent *event)
+{
+ if (event->type() == MediaStream::PresentSurface) {
+ MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
+ MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
+ float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
+ if (playRate > 0.0001f && targetTime > currentTime)
+ QTimer::singleShot(int((float)((targetTime - currentTime) / 10000) / playRate), this, SLOT(present()));
+ else
+ present();
+ return;
+ }
+ QChildEvent *childEvent = dynamic_cast<QChildEvent*>(event);
+ if (!childEvent) {
+ QObject::customEvent(event);
+ return;
+ }
+ static_cast<MediaStream*>(childEvent->child())->customEvent(event);
+}
+
+void MFVideoRendererControl::supportedFormatsChanged()
+{
+ static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged();
+}
+
+void MFVideoRendererControl::present()
+{
+ static_cast<VideoRendererActivate*>(m_currentActivate)->present();
+}
+
+IMFActivate* MFVideoRendererControl::currentActivate() const
+{
+ return m_currentActivate;
+}
+
+#include "moc_mfvideorenderercontrol.cpp"
+#include "mfvideorenderercontrol.moc"
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.h b/src/plugins/wmf/player/mfvideorenderercontrol.h
new file mode 100644
index 000000000..f34b5bf95
--- /dev/null
+++ b/src/plugins/wmf/player/mfvideorenderercontrol.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFVIDEORENDERERCONTROL_H
+#define MFVIDEORENDERERCONTROL_H
+
+#include "../../src/multimedia/qvideorenderercontrol.h"
+#include <mfapi.h>
+#include <mfidl.h>
+
+QT_USE_NAMESPACE
+
+class MFVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ MFVideoRendererControl(QObject *parent = 0);
+ ~MFVideoRendererControl();
+
+ QAbstractVideoSurface *surface() const;
+ void setSurface(QAbstractVideoSurface *surface);
+
+ IMFActivate* currentActivate() const;
+
+protected:
+ void customEvent(QEvent *event);
+
+private Q_SLOTS:
+ void supportedFormatsChanged();
+ void present();
+
+private:
+ QAbstractVideoSurface *m_surface;
+ IMFActivate *m_currentActivate;
+ IMFSampleGrabberSinkCallback *m_callback;
+};
+
+#endif
diff --git a/src/plugins/wmf/player/player.pri b/src/plugins/wmf/player/player.pri
new file mode 100644
index 000000000..58375ba44
--- /dev/null
+++ b/src/plugins/wmf/player/player.pri
@@ -0,0 +1,30 @@
+INCLUDEPATH += $$PWD
+
+LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 -lMf -lMfuuid -lMfplat -lPropsys
+
+DEFINES += QMEDIA_MEDIAFOUNDATION_PLAYER
+
+HEADERS += \
+ $$PWD/mfplayerservice.h \
+ $$PWD/mfplayersession.h \
+ $$PWD/mfstream.h \
+ $$PWD/sourceresolver.h \
+ $$PWD/mfplayercontrol.h \
+ $$PWD/mfvideorenderercontrol.h \
+ $$PWD/mfaudioendpointcontrol.h \
+ $$PWD/mfmetadatacontrol.h
+
+SOURCES += \
+ $$PWD/mfplayerservice.cpp \
+ $$PWD/mfplayersession.cpp \
+ $$PWD/mfstream.cpp \
+ $$PWD/sourceresolver.cpp \
+ $$PWD/mfplayercontrol.cpp \
+ $$PWD/mfvideorenderercontrol.cpp \
+ $$PWD/mfaudioendpointcontrol.cpp \
+ $$PWD/mfmetadatacontrol.cpp
+
+!simulator {
+ HEADERS += $$PWD/evr9videowindowcontrol.h
+ SOURCES += $$PWD/evr9videowindowcontrol.cpp
+}
diff --git a/src/plugins/wmf/player/sourceresolver.cpp b/src/plugins/wmf/player/sourceresolver.cpp
new file mode 100644
index 000000000..1b98365b5
--- /dev/null
+++ b/src/plugins/wmf/player/sourceresolver.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mfplayersession.h"
+#include "mfstream.h"
+#include "sourceresolver.h"
+#include <Mferror.h>
+#include <nserror.h>
+#include <QtCore/qfile.h>
+
+/*
+ SourceResolver is separated from MFPlayerSession to handle the work of resolving a media source
+ asynchronously. You call SourceResolver::load to request resolving a media source asynchronously,
+ and it will emit mediaSourceReady() when resolving is done. You can call SourceResolver::cancel to
+ stop the previous load operation if there is any.
+*/
+
+SourceResolver::SourceResolver(QObject *parent)
+ : QObject(parent)
+ , m_cRef(1)
+ , m_cancelCookie(0)
+ , m_sourceResolver(0)
+ , m_mediaSource(0)
+ , m_stream(0)
+{
+}
+
+SourceResolver::~SourceResolver()
+{
+ shutdown();
+ if (m_cancelCookie)
+ m_cancelCookie->Release();
+ if (m_sourceResolver)
+ m_sourceResolver->Release();
+}
+
+STDMETHODIMP SourceResolver::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else if (riid == IID_IMFAsyncCallback) {
+ *ppvObject = static_cast<IMFAsyncCallback*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ this->deleteLater();
+ return cRef;
+}
+
+HRESULT STDMETHODCALLTYPE SourceResolver::Invoke(IMFAsyncResult *pAsyncResult)
+{
+ QMutexLocker locker(&m_mutex);
+ MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
+ IUnknown* pSource = NULL;
+ State *state = static_cast<State*>(pAsyncResult->GetStateNoAddRef());
+
+ HRESULT hr = S_OK;
+ if (state->fromStream())
+ hr = m_sourceResolver->EndCreateObjectFromByteStream(pAsyncResult, &ObjectType, &pSource);
+ else
+ hr = m_sourceResolver->EndCreateObjectFromURL(pAsyncResult, &ObjectType, &pSource);
+
+ if (state->sourceResolver() != m_sourceResolver) {
+ //This is a cancelled one
+ return S_OK;
+ }
+
+ if (m_cancelCookie) {
+ m_cancelCookie->Release();
+ m_cancelCookie = NULL;
+ }
+
+ if (FAILED(hr)) {
+ emit error(hr);
+ return S_OK;
+ }
+
+ if (m_mediaSource) {
+ m_mediaSource->Release();
+ m_mediaSource = NULL;
+ }
+
+ hr = pSource->QueryInterface(IID_PPV_ARGS(&m_mediaSource));
+ if (FAILED(hr)) {
+ emit error(hr);
+ return S_OK;
+ }
+
+ emit mediaSourceReady();
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SourceResolver::GetParameters(DWORD*, DWORD*)
+{
+ return E_NOTIMPL;
+}
+
+void SourceResolver::load(QMediaResourceList& resources, QIODevice* stream)
+{
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = S_OK;
+ if (!m_sourceResolver)
+ hr = MFCreateSourceResolver(&m_sourceResolver);
+
+ if (m_stream) {
+ m_stream->Release();
+ m_stream = NULL;
+ }
+
+ if (FAILED(hr)) {
+ qWarning() << "Failed to create Source Resolver!";
+ emit error(hr);
+ } else if (stream) {
+ if (resources.count() > 0) {
+ QMediaResource resource = resources.takeFirst();
+ QUrl url = resource.url();
+ m_stream = new MFStream(stream, false);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE, NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+ hr = MF_E_UNSUPPORTED_BYTESTREAM_TYPE;
+ qWarning() << "Can't load stream without a hint of MIME type in a url";
+ emit error(hr);
+ }
+ } else {
+ QMediaResource resource = resources.takeFirst();
+ QUrl url = resource.url();
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "loading :" << url;
+ qDebug() << "url path =" << url.path().mid(1);
+#endif
+#ifdef TEST_STREAMING
+ //Testing stream function
+ if (url.scheme() == QLatin1String("file")) {
+ stream = new QFile(url.path().mid(1), this);
+ if (stream->open(QIODevice::ReadOnly)) {
+ m_stream = new MFStream(stream, true);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE, NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+ delete stream;
+ emit error(QMediaPlayer::FormatError);
+ }
+ } else
+#endif
+ if (url.scheme() == QLatin1String("qrc")) {
+ // If the canonical URL refers to a Qt resource, open with QFile and use
+ // the stream playback capability to play.
+ stream = new QFile(QLatin1Char(':') + url.path(), this);
+ if (stream->open(QIODevice::ReadOnly)) {
+ m_stream = new MFStream(stream, true);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE, NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+ delete stream;
+ emit error(QMediaPlayer::FormatError);
+ }
+ } else {
+ hr = m_sourceResolver->BeginCreateObjectFromURL(reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE, NULL, &m_cancelCookie, this, new State(m_sourceResolver, false));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported url scheme!";
+ emit error(hr);
+ }
+ }
+ }
+}
+
+void SourceResolver::cancel()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_cancelCookie) {
+ m_sourceResolver->CancelObjectCreation(m_cancelCookie);
+ m_cancelCookie->Release();
+ m_cancelCookie = NULL;
+ m_sourceResolver->Release();
+ m_sourceResolver = NULL;
+ }
+}
+
+void SourceResolver::shutdown()
+{
+ if (m_mediaSource) {
+ m_mediaSource->Shutdown();
+ m_mediaSource->Release();
+ m_mediaSource = NULL;
+ }
+
+ if (m_stream) {
+ m_stream->Release();
+ m_stream = NULL;
+ }
+}
+
+IMFMediaSource* SourceResolver::mediaSource() const
+{
+ return m_mediaSource;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream)
+ : m_cRef(1)
+ , m_sourceResolver(sourceResolver)
+ , m_fromStream(fromStream)
+{
+ sourceResolver->AddRef();
+}
+
+SourceResolver::State::~State()
+{
+ m_sourceResolver->Release();
+}
+
+STDMETHODIMP SourceResolver::State::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::State::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::State::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+}
+
+IMFSourceResolver* SourceResolver::State::sourceResolver() const
+{
+ return m_sourceResolver;
+}
+
+bool SourceResolver::State::fromStream() const
+{
+ return m_fromStream;
+}
+
diff --git a/src/plugins/wmf/player/sourceresolver.h b/src/plugins/wmf/player/sourceresolver.h
new file mode 100644
index 000000000..60bee69f1
--- /dev/null
+++ b/src/plugins/wmf/player/sourceresolver.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SOURCERESOLVER_H
+#define SOURCERESOLVER_H
+
+#include "mfstream.h"
+#include "qmediaresource.h"
+
+class SourceResolver: public QObject, public IMFAsyncCallback
+{
+ Q_OBJECT
+public:
+ SourceResolver(QObject *parent);
+
+ ~SourceResolver();
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
+ STDMETHODIMP_(ULONG) AddRef(void);
+ STDMETHODIMP_(ULONG) Release(void);
+
+ HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult *pAsyncResult);
+
+ HRESULT STDMETHODCALLTYPE GetParameters(DWORD*, DWORD*);
+
+ void load(QMediaResourceList& resources, QIODevice* stream);
+
+ void cancel();
+
+ void shutdown();
+
+ IMFMediaSource* mediaSource() const;
+
+Q_SIGNALS:
+ void error(long hr);
+ void mediaSourceReady();
+
+private:
+ class State : public IUnknown
+ {
+ public:
+ State(IMFSourceResolver *sourceResolver, bool fromStream);
+ ~State();
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
+
+ STDMETHODIMP_(ULONG) AddRef(void);
+
+ STDMETHODIMP_(ULONG) Release(void);
+
+ IMFSourceResolver* sourceResolver() const;
+ bool fromStream() const;
+
+ private:
+ long m_cRef;
+ IMFSourceResolver *m_sourceResolver;
+ bool m_fromStream;
+ };
+
+ long m_cRef;
+ IUnknown *m_cancelCookie;
+ IMFSourceResolver *m_sourceResolver;
+ IMFMediaSource *m_mediaSource;
+ MFStream *m_stream;
+ QMutex m_mutex;
+};
+
+#endif
diff --git a/src/plugins/wmf/wmf.pro b/src/plugins/wmf/wmf.pro
new file mode 100644
index 000000000..5eccc80d5
--- /dev/null
+++ b/src/plugins/wmf/wmf.pro
@@ -0,0 +1,16 @@
+load(qt_module)
+
+TARGET = wmfengine
+QT += multimediakit-private network
+PLUGIN_TYPE=mediaservice
+
+load(qt_plugin)
+
+DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE}
+
+DEPENDPATH += .
+
+HEADERS += wmfserviceplugin.h
+SOURCES += wmfserviceplugin.cpp
+
+include (player/player.pri)
diff --git a/src/plugins/wmf/wmfserviceplugin.cpp b/src/plugins/wmf/wmfserviceplugin.cpp
new file mode 100644
index 000000000..31910fd3a
--- /dev/null
+++ b/src/plugins/wmf/wmfserviceplugin.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/QFile>
+
+#include "wmfserviceplugin.h"
+#ifdef QMEDIA_MEDIAFOUNDATION_PLAYER
+#include "mfplayerservice.h"
+#endif
+#include <qmediaserviceprovider.h>
+
+QStringList WMFServicePlugin::keys() const
+{
+ return QStringList()
+#ifdef QMEDIA_MEDIAFOUNDATION_PLAYER
+ << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)
+#endif
+ ;
+}
+
+QMediaService* WMFServicePlugin::create(QString const& key)
+{
+#ifdef QMEDIA_MEDIAFOUNDATION_PLAYER
+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
+ return new MFPlayerService;
+#endif
+
+ //qDebug() << "unsupported key:" << key;
+ return 0;
+}
+
+void WMFServicePlugin::release(QMediaService *service)
+{
+ delete service;
+}
+
+QMediaServiceProviderHint::Features WMFServicePlugin::supportedFeatures(
+ const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_MEDIAPLAYER)
+ return QMediaServiceProviderHint::StreamPlayback;
+ else
+ return QMediaServiceProviderHint::Features();
+}
+
+QList<QByteArray> WMFServicePlugin::devices(const QByteArray &service) const
+{
+ return QList<QByteArray>();
+}
+
+QString WMFServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
+{
+ return QString();
+}
+
+Q_EXPORT_PLUGIN2(qtmedia_wmfengine, WMFServicePlugin);
+
diff --git a/src/plugins/wmf/wmfserviceplugin.h b/src/plugins/wmf/wmfserviceplugin.h
new file mode 100644
index 000000000..cfaaef58c
--- /dev/null
+++ b/src/plugins/wmf/wmfserviceplugin.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WMFSERVICEPLUGIN_H
+#define WMFSERVICEPLUGIN_H
+
+#include "qmediaserviceproviderplugin.h"
+
+QT_USE_NAMESPACE
+
+class WMFServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+ , public QMediaServiceFeaturesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ Q_INTERFACES(QMediaServiceFeaturesInterface)
+public:
+ QStringList keys() const;
+ QMediaService* create(QString const& key);
+ void release(QMediaService *service);
+
+ QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const;
+
+ QList<QByteArray> devices(const QByteArray &service) const;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device);
+};
+
+#endif // DSSERVICEPLUGIN_H
diff --git a/sync.profile b/sync.profile
index 6370cb1cf..9939e4638 100644
--- a/sync.profile
+++ b/sync.profile
@@ -38,6 +38,7 @@
"directshow" => {},
"wmsdk" => {},
"wmp" => {},
+ "wmf" => {},
"evr" => {},
# Linux tests