diff options
166 files changed, 3755 insertions, 5984 deletions
diff --git a/config.tests/linux_v4l/linux_v4l.pro b/config.tests/linux_v4l/linux_v4l.pro new file mode 100644 index 000000000..28dcadcbf --- /dev/null +++ b/config.tests/linux_v4l/linux_v4l.pro @@ -0,0 +1 @@ +SOURCES += main.cpp diff --git a/src/plugins/qt7/qt7backend.mm b/config.tests/linux_v4l/main.cpp index 136220cef..0a3040be5 100644 --- a/src/plugins/qt7/qt7backend.mm +++ b/config.tests/linux_v4l/main.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Toolkit. @@ -39,22 +39,9 @@ ** ****************************************************************************/ -#include "qt7backend.h" +#include <linux/videodev2.h> -#import <Foundation/NSAutoreleasePool.h> -#include <CoreFoundation/CFBase.h> - - -QT_BEGIN_NAMESPACE - -AutoReleasePool::AutoReleasePool() +int main(int argc, char** argv) { - pool = (void*)[[NSAutoreleasePool alloc] init]; + return 0; } - -AutoReleasePool::~AutoReleasePool() -{ - [(NSAutoreleasePool*)pool release]; -} - -QT_END_NAMESPACE diff --git a/config.tests/wmp/main.cpp b/config.tests/wmp/main.cpp index b06037ca0..0eb86cfe2 100644 --- a/config.tests/wmp/main.cpp +++ b/config.tests/wmp/main.cpp @@ -30,8 +30,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +#ifndef _WIN32_WCE #include <wmp.h> +#else +#include <wmpcore.h> +#endif int main(int, char**) { diff --git a/config.tests/wmp/wmp.pro b/config.tests/wmp/wmp.pro index b16509cc4..563de1453 100644 --- a/config.tests/wmp/wmp.pro +++ b/config.tests/wmp/wmp.pro @@ -3,4 +3,5 @@ CONFIG += console SOURCES += main.cpp -LIBS += -lstrmiids -lole32 -lOleaut32 -luser32 -lgdi32 +LIBS += -lstrmiids -lole32 -lOleaut32 +!wince*:LIBS += -luser32 -lgdi32 diff --git a/dist/changes-5.3.2 b/dist/changes-5.3.2 new file mode 100644 index 000000000..b427bb809 --- /dev/null +++ b/dist/changes-5.3.2 @@ -0,0 +1,65 @@ +Qt 5.3.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.3.0 and 5.3.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.3 + +The Qt version 5.3 series is binary compatible with the 5.2.x series. +Applications compiled for 5.2 will continue to run with 5.3. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Android +------- + + - Fixed regression causing videos recorded with the camera not to be registered with the Android + media scanner, making them invisible to media browsing apps. + - Fixed crash when unloading a QCamera while a recording is active. + - [QTBUG-39307] Setting camera parameters on the QML Camera type (e.g. digitalZoom, flashMode) + now works correctly when set before the camera is loaded. + - [QTBUG-40208] QAudioOutput::setNotifyInterval() can now be used when the output is active. + - [QTBUG-40274] Fixed metadata not being loaded by the MediaPlayer when playing a remote media, + from a qrc file or from assets. + +iOS +--- + + - [QTBUG-39036] Audio played using SoundEffect or QAudioOutput is now correctly muted when the + device is set to silent mode or when the screen is locked. + - [QTBUG-39385] The last video frame displayed in a QML VideoOutput doesn't remain on screen + anymore after destroying the VideoOutput. + +Linux +----- + + - MediaPlayer's loops property now works correctly when playing a media from a qrc file. + - [QTBUG-29742] Fixed Qt apps hanging when audio APIs are used and PulseAudio is not running. + - [QTBUG-39949] Fixed QMediaRecorder::setOutputLocation() not working with QUrl::fromLocalFile(). + +OS X +---- + + - Application doesn't freeze anymore when changing the system audio output device while audio + is being played with QSoundEffect or QAudioOutput. + - [QTBUG-38666] Video frames are now correctly positioned on screen when playing a live stream + in a QVideoWidget. This also applies to iOS. + - [QTBUG-38668] Fixed crash when setting QMediaRecorder's output location to a URL containing + nonstandard characters. + +Windows +------- + + - The DirectShow camera backend has been almost entirely rewritten. It doesn't provide any new + feature but it now works as it should. diff --git a/examples/multimedia/audioinput/audioinput.cpp b/examples/multimedia/audioinput/audioinput.cpp index 09f571b0c..87d7963aa 100644 --- a/examples/multimedia/audioinput/audioinput.cpp +++ b/examples/multimedia/audioinput/audioinput.cpp @@ -315,20 +315,11 @@ void InputTest::initializeAudio() void InputTest::createAudioInput() { m_audioInput = new QAudioInput(m_device, m_format, this); - connect(m_audioInput, SIGNAL(notify()), SLOT(notified())); - connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); m_volumeSlider->setValue(m_audioInput->volume() * 100); m_audioInfo->start(); m_audioInput->start(m_audioInfo); } -void InputTest::notified() -{ - qWarning() << "bytesReady = " << m_audioInput->bytesReady() - << ", " << "elapsedUSecs = " <<m_audioInput->elapsedUSecs() - << ", " << "processedUSecs = "<<m_audioInput->processedUSecs(); -} - void InputTest::readMore() { if (!m_audioInput) @@ -364,27 +355,19 @@ void InputTest::toggleSuspend() { // toggle suspend/resume if (m_audioInput->state() == QAudio::SuspendedState) { - qWarning() << "status: Suspended, resume()"; m_audioInput->resume(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } else if (m_audioInput->state() == QAudio::ActiveState) { - qWarning() << "status: Active, suspend()"; m_audioInput->suspend(); m_suspendResumeButton->setText(tr(RESUME_LABEL)); } else if (m_audioInput->state() == QAudio::StoppedState) { - qWarning() << "status: Stopped, resume()"; m_audioInput->resume(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } else if (m_audioInput->state() == QAudio::IdleState) { - qWarning() << "status: IdleState"; + // no-op } } -void InputTest::handleStateChanged(QAudio::State state) -{ - qWarning() << "state = " << state; -} - void InputTest::refreshDisplay() { m_canvas->setLevel(m_audioInfo->level()); diff --git a/examples/multimedia/audioinput/audioinput.h b/examples/multimedia/audioinput/audioinput.h index 82def563b..df26104b9 100644 --- a/examples/multimedia/audioinput/audioinput.h +++ b/examples/multimedia/audioinput/audioinput.h @@ -110,11 +110,9 @@ private: private slots: void refreshDisplay(); - void notified(); void readMore(); void toggleMode(); void toggleSuspend(); - void handleStateChanged(QAudio::State state); void deviceChanged(int index); void sliderChanged(int value); diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp index daa7bfdf6..7911b580a 100644 --- a/examples/multimedia/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -247,8 +247,6 @@ void AudioTest::createAudioOutput() delete m_audioOutput; m_audioOutput = 0; m_audioOutput = new QAudioOutput(m_device, m_format, this); - connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); - connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); m_generator->start(); m_audioOutput->start(m_generator); m_volumeSlider->setValue(int(m_audioOutput->volume()*100.0f)); @@ -275,13 +273,6 @@ void AudioTest::volumeChanged(int value) m_audioOutput->setVolume(qreal(value/100.0f)); } -void AudioTest::notified() -{ - qWarning() << "bytesFree = " << m_audioOutput->bytesFree() - << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() - << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); -} - void AudioTest::pullTimerExpired() { if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) { @@ -319,23 +310,16 @@ void AudioTest::toggleMode() void AudioTest::toggleSuspendResume() { if (m_audioOutput->state() == QAudio::SuspendedState) { - qWarning() << "status: Suspended, resume()"; m_audioOutput->resume(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } else if (m_audioOutput->state() == QAudio::ActiveState) { - qWarning() << "status: Active, suspend()"; m_audioOutput->suspend(); m_suspendResumeButton->setText(tr(RESUME_LABEL)); } else if (m_audioOutput->state() == QAudio::StoppedState) { - qWarning() << "status: Stopped, resume()"; m_audioOutput->resume(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } else if (m_audioOutput->state() == QAudio::IdleState) { - qWarning() << "status: IdleState"; + // no-op } } -void AudioTest::handleStateChanged(QAudio::State state) -{ - qWarning() << "state = " << state; -} diff --git a/examples/multimedia/audiooutput/audiooutput.h b/examples/multimedia/audiooutput/audiooutput.h index 30cf35369..80d6d3279 100644 --- a/examples/multimedia/audiooutput/audiooutput.h +++ b/examples/multimedia/audiooutput/audiooutput.h @@ -110,11 +110,9 @@ private: QByteArray m_buffer; private slots: - void notified(); void pullTimerExpired(); void toggleMode(); void toggleSuspendResume(); - void handleStateChanged(QAudio::State state); void deviceChanged(int index); void volumeChanged(int); }; diff --git a/examples/multimedia/audiorecorder/audiorecorder.cpp b/examples/multimedia/audiorecorder/audiorecorder.cpp index 2291fed52..7d942255a 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.cpp +++ b/examples/multimedia/audiorecorder/audiorecorder.cpp @@ -114,6 +114,8 @@ AudioRecorder::AudioRecorder(QWidget *parent) : SLOT(updateProgress(qint64))); connect(audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SLOT(updateStatus(QMediaRecorder::Status))); + connect(audioRecorder, SIGNAL(stateChanged(QMediaRecorder::State)), + this, SLOT(onStateChanged(QMediaRecorder::State))); connect(audioRecorder, SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage())); } @@ -138,47 +140,44 @@ void AudioRecorder::updateStatus(QMediaRecorder::Status status) switch (status) { case QMediaRecorder::RecordingStatus: - if (audioLevels.count() != audioRecorder->audioSettings().channelCount()) { - qDeleteAll(audioLevels); - audioLevels.clear(); - for (int i = 0; i < audioRecorder->audioSettings().channelCount(); ++i) { - QAudioLevel *level = new QAudioLevel(ui->centralwidget); - audioLevels.append(level); - ui->levelsLayout->addWidget(level); - } - } - - ui->recordButton->setText(tr("Stop")); - ui->pauseButton->setText(tr("Pause")); - if (audioRecorder->outputLocation().isEmpty()) - statusMessage = tr("Recording"); - else - statusMessage = tr("Recording to %1").arg( - audioRecorder->outputLocation().toString()); + statusMessage = tr("Recording to %1").arg(audioRecorder->actualLocation().toString()); break; case QMediaRecorder::PausedStatus: clearAudioLevels(); - ui->recordButton->setText(tr("Stop")); - ui->pauseButton->setText(tr("Resume")); statusMessage = tr("Paused"); break; case QMediaRecorder::UnloadedStatus: case QMediaRecorder::LoadedStatus: clearAudioLevels(); - ui->recordButton->setText(tr("Record")); - ui->pauseButton->setText(tr("Pause")); statusMessage = tr("Stopped"); default: break; } - ui->pauseButton->setEnabled(audioRecorder->state() - != QMediaRecorder::StoppedState); - if (audioRecorder->error() == QMediaRecorder::NoError) ui->statusbar->showMessage(statusMessage); } +void AudioRecorder::onStateChanged(QMediaRecorder::State state) +{ + switch (state) { + case QMediaRecorder::RecordingState: + ui->recordButton->setText(tr("Stop")); + ui->pauseButton->setText(tr("Pause")); + break; + case QMediaRecorder::PausedState: + ui->recordButton->setText(tr("Stop")); + ui->pauseButton->setText(tr("Resume")); + break; + case QMediaRecorder::StoppedState: + ui->recordButton->setText(tr("Record")); + ui->pauseButton->setText(tr("Pause")); + break; + } + + ui->pauseButton->setEnabled(audioRecorder->state() != QMediaRecorder::StoppedState); +} + static QVariant boxValue(const QComboBox *box) { int idx = box->currentIndex(); @@ -347,6 +346,16 @@ QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels) void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { + if (audioLevels.count() != buffer.format().channelCount()) { + qDeleteAll(audioLevels); + audioLevels.clear(); + for (int i = 0; i < buffer.format().channelCount(); ++i) { + QAudioLevel *level = new QAudioLevel(ui->centralwidget); + audioLevels.append(level); + ui->levelsLayout->addWidget(level); + } + } + QVector<qreal> levels = getBufferLevels(buffer); for (int i = 0; i < levels.count(); ++i) audioLevels.at(i)->setLevel(levels.at(i)); diff --git a/examples/multimedia/audiorecorder/audiorecorder.h b/examples/multimedia/audiorecorder/audiorecorder.h index 2c0a1e5ed..9d013ae7c 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.h +++ b/examples/multimedia/audiorecorder/audiorecorder.h @@ -71,6 +71,7 @@ private slots: void toggleRecord(); void updateStatus(QMediaRecorder::Status); + void onStateChanged(QMediaRecorder::State); void updateProgress(qint64 pos); void displayErrorMessage(); diff --git a/examples/multimedia/video/doc/images/qmlvideo-menu.jpg b/examples/multimedia/video/doc/images/qmlvideo-menu.jpg Binary files differnew file mode 100644 index 000000000..54ab877a1 --- /dev/null +++ b/examples/multimedia/video/doc/images/qmlvideo-menu.jpg diff --git a/examples/multimedia/video/doc/images/qmlvideo-menu.png b/examples/multimedia/video/doc/images/qmlvideo-menu.png Binary files differdeleted file mode 100644 index b2d773319..000000000 --- a/examples/multimedia/video/doc/images/qmlvideo-menu.png +++ /dev/null diff --git a/examples/multimedia/video/doc/images/qmlvideo-overlay.jpg b/examples/multimedia/video/doc/images/qmlvideo-overlay.jpg Binary files differnew file mode 100644 index 000000000..6a0d48ae6 --- /dev/null +++ b/examples/multimedia/video/doc/images/qmlvideo-overlay.jpg diff --git a/examples/multimedia/video/doc/images/qmlvideo-overlay.png b/examples/multimedia/video/doc/images/qmlvideo-overlay.png Binary files differdeleted file mode 100644 index f5dc8390d..000000000 --- a/examples/multimedia/video/doc/images/qmlvideo-overlay.png +++ /dev/null diff --git a/examples/multimedia/video/doc/src/qmlvideo.qdoc b/examples/multimedia/video/doc/src/qmlvideo.qdoc index 1e80cd176..5ffb542fd 100644 --- a/examples/multimedia/video/doc/src/qmlvideo.qdoc +++ b/examples/multimedia/video/doc/src/qmlvideo.qdoc @@ -46,7 +46,7 @@ The following image shows the application executing the video-overlay scene, which creates a dummy overlay item (just a semi-transparent \l{Rectangle}), which moves across the \l{VideoOutput} item. -\image qmlvideo-overlay.png +\image qmlvideo-overlay.jpg \include examples-run.qdocinc @@ -67,7 +67,7 @@ the following items: average over the past second. \endlist -\image qmlvideo-menu.png +\image qmlvideo-menu.jpg Each scene in the flickable list is implemented in its own QML file - for example the video-basic scene (which just displays a static \l{VideoOutput} diff --git a/examples/multimedia/video/qmlvideo/images/close.png b/examples/multimedia/video/qmlvideo/images/close.png Binary files differdeleted file mode 100644 index 6904df0e4..000000000 --- a/examples/multimedia/video/qmlvideo/images/close.png +++ /dev/null diff --git a/examples/multimedia/video/qmlvideo/images/folder.png b/examples/multimedia/video/qmlvideo/images/folder.png Binary files differindex e53e2ad46..62d97004f 100644 --- a/examples/multimedia/video/qmlvideo/images/folder.png +++ b/examples/multimedia/video/qmlvideo/images/folder.png diff --git a/examples/multimedia/video/qmlvideo/images/progress_handle.svg b/examples/multimedia/video/qmlvideo/images/progress_handle.svg deleted file mode 100644 index 7ad9014e3..000000000 --- a/examples/multimedia/video/qmlvideo/images/progress_handle.svg +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="10px" - height="46px" - version="1.1"> - <g> - <defs> - <linearGradient id="MyGradient1" x1="0%" y1="0%" x2="100%" y2="100%"> - <stop offset="0%" stop-color="lightcyan" /> - <stop offset="100%" stop-color="dodgerblue" /> - </linearGradient> - </defs> - <rect - stroke="white" - fill="url(#MyGradient1)" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - width="8" - height="44" - x="1" - y="1" - rx="4" - ry="4"/> - </g> -</svg> diff --git a/examples/multimedia/video/qmlvideo/images/progress_handle_pressed.svg b/examples/multimedia/video/qmlvideo/images/progress_handle_pressed.svg deleted file mode 100644 index c9c6c486c..000000000 --- a/examples/multimedia/video/qmlvideo/images/progress_handle_pressed.svg +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="10px" - height="46px" - version="1.1"> - <g> - <defs> - <linearGradient id="MyGradient1" x1="0%" y1="0%" x2="100%" y2="100%"> - <stop offset="0%" stop-color="skyblue" /> - <stop offset="100%" stop-color="darkblue" /> - </linearGradient> - </defs> - <rect - stroke="white" - fill="url(#MyGradient1)" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - width="8" - height="44" - x="1" - y="1" - rx="4" - ry="4"/> - </g> -</svg> diff --git a/examples/multimedia/video/qmlvideo/images/titlebar.png b/examples/multimedia/video/qmlvideo/images/titlebar.png Binary files differdeleted file mode 100644 index 51c90082d..000000000 --- a/examples/multimedia/video/qmlvideo/images/titlebar.png +++ /dev/null diff --git a/examples/multimedia/video/qmlvideo/images/titlebar.sci b/examples/multimedia/video/qmlvideo/images/titlebar.sci deleted file mode 100644 index 0418d94cd..000000000 --- a/examples/multimedia/video/qmlvideo/images/titlebar.sci +++ /dev/null @@ -1,5 +0,0 @@ -border.left: 10 -border.top: 12 -border.bottom: 12 -border.right: 10 -source: titlebar.png diff --git a/examples/multimedia/video/qmlvideo/images/up.png b/examples/multimedia/video/qmlvideo/images/up.png Binary files differindex b05f8025d..6823de004 100644 --- a/examples/multimedia/video/qmlvideo/images/up.png +++ b/examples/multimedia/video/qmlvideo/images/up.png diff --git a/examples/multimedia/video/qmlvideo/main.cpp b/examples/multimedia/video/qmlvideo/main.cpp index 5d5b8b33d..3edee5ab6 100644 --- a/examples/multimedia/video/qmlvideo/main.cpp +++ b/examples/multimedia/video/qmlvideo/main.cpp @@ -123,13 +123,14 @@ int main(int argc, char *argv[]) const QStringList moviesLocation = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation); const QUrl videoPath = - QUrl::fromLocalFile(moviesLocation.isEmpty() ? - app.applicationDirPath() : - moviesLocation.front()); + QUrl::fromLocalFile(moviesLocation.isEmpty() ? + app.applicationDirPath() : + moviesLocation.front()); viewer.rootContext()->setContextProperty("videoPath", videoPath); QMetaObject::invokeMethod(rootObject, "init"); + viewer.setMinimumSize(QSize(640, 360)); viewer.show(); return app.exec(); diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml index ea686ea3f..4f5cbada4 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml @@ -33,30 +33,31 @@ import QtQuick 2.0 -Rectangle { +Item { id: root - color: textColor - radius: 0.25 * height property string text - property color bgColor: "white" - property color bgColorSelected: "red" - property color textColor: "black" + property color bgColor: "#757575" + property color bgColorSelected: "#bdbdbd" + property color textColor: "white" + property color textColorSelected: "black" property alias enabled: mouseArea.enabled + property alias radius: bgr.radius signal clicked Rectangle { - anchors { fill: parent; margins: 1 } + id: bgr + anchors.fill: parent color: mouseArea.pressed ? bgColorSelected : bgColor - radius: 0.25 * height + radius: height / 15 Text { id: text anchors.centerIn: parent text: root.text - font.pixelSize: 0.5 * parent.height - color: mouseArea.pressed ? bgColor : textColor + font.pixelSize: 0.4 * parent.height + color: mouseArea.pressed ? textColorSelected : textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml index 30b1e32b8..3094c1a3b 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml @@ -97,21 +97,16 @@ Rectangle { } function initialize() { - console.log("[qmlvideo] Content.initialize: contentType " + contentType) if ("video" == contentType) { - console.log("[qmlvideo] Content.initialize: loading VideoItem.qml") contentLoader.source = "VideoItem.qml" if (Loader.Error == contentLoader.status) { - console.log("[qmlvideo] Content.initialize: loading VideoDummy.qml") contentLoader.source = "VideoDummy.qml" dummy = true } contentLoader.item.volume = volume } else if ("camera" == contentType) { - console.log("[qmlvideo] Content.initialize: loading CameraItem.qml") contentLoader.source = "CameraItem.qml" if (Loader.Error == contentLoader.status) { - console.log("[qmlvideo] Content.initialize: loading CameraDummy.qml") contentLoader.source = "CameraDummy.qml" dummy = true } @@ -127,12 +122,10 @@ Rectangle { if (root.autoStart) root.start() } - console.log("[qmlvideo] Content.initialize: complete") root.initialized() } function start() { - console.log("[qmlvideo] Content.start") if (contentLoader.item) { if (root.contentType == "video") contentLoader.item.mediaSource = root.source @@ -142,7 +135,6 @@ Rectangle { } function stop() { - console.log("[qmlvideo] Content.stop") if (contentLoader.item) { contentLoader.item.stop() if (root.contentType == "video") diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/DisableScreenSaver.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/DisableScreenSaver.qml deleted file mode 100644 index 74f951b13..000000000 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/DisableScreenSaver.qml +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Mobility Components. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtSystemInfo 5.0 -// NOTE: The QtSystemInfo module is not yet part of Qt 5 - -Item { - ScreenSaver { - screenSaverInhibited: true - } -} diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml index c9e7cd109..33a55ebd7 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml @@ -38,6 +38,8 @@ Rectangle { color: "transparent" opacity: 0.0 property alias enabled: mouseArea.enabled + property int dialogWidth: 300 + property int dialogHeight: 200 state: enabled ? "on" : "baseState" states: [ @@ -70,9 +72,9 @@ Rectangle { Rectangle { anchors.centerIn: parent - width: 300 - height: 200 - radius: 10 + width: dialogWidth + height: dialogHeight + radius: 5 color: "white" Text { diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml index 466ea8b97..33109bd34 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml @@ -40,6 +40,9 @@ Rectangle { property string folder + property int itemHeight: Math.min(parent.width, parent.height) / 15 + property int buttonHeight: Math.min(parent.width, parent.height) / 12 + signal fileSelected(string file) function selectFile(file) { @@ -66,12 +69,12 @@ Rectangle { Rectangle { id: root - color: "white" + color: "black" property bool showFocusHighlight: false property variant folders: folders1 property variant view: view1 property alias folder: folders1.folder - property color textColor: "black" + property color textColor: "white" FolderListModel { id: folders1 @@ -103,34 +106,39 @@ Rectangle { fileBrowser.selectFile(path) } width: root.width - height: 52 + height: folderImage.height color: "transparent" Rectangle { - id: highlight; visible: false + id: highlight + visible: false anchors.fill: parent - color: palette.highlight - gradient: Gradient { - GradientStop { id: t1; position: 0.0; color: palette.highlight } - GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) } - } + anchors.leftMargin: 5 + anchors.rightMargin: 5 + color: "#212121" } Item { - width: 48; height: 48 + id: folderImage + width: itemHeight + height: itemHeight Image { + id: folderPicture source: "qrc:/folder.png" - anchors.centerIn: parent + width: itemHeight * 0.9 + height: itemHeight * 0.9 + anchors.left: parent.left + anchors.margins: 5 visible: folders.isFolder(index) } } Text { id: nameText - anchors.fill: parent; verticalAlignment: Text.AlignVCenter + anchors.fill: parent; + verticalAlignment: Text.AlignVCenter text: fileName - anchors.leftMargin: 54 - font.pixelSize: 32 + anchors.leftMargin: itemHeight + 10 color: (wrapper.ListView.isCurrentItem && root.showFocusHighlight) ? palette.highlightedText : textColor elide: Text.ElideRight } @@ -142,7 +150,7 @@ Rectangle { root.showFocusHighlight = false; wrapper.ListView.view.currentIndex = index; } - onClicked: { if (folders == wrapper.ListView.view.model) launch() } + onClicked: { if (folders === wrapper.ListView.view.model) launch() } } states: [ @@ -160,17 +168,12 @@ Rectangle { id: view1 anchors.top: titleBar.bottom anchors.bottom: cancelButton.top - x: 0 width: parent.width model: folders1 delegate: folderDelegate highlight: Rectangle { - color: palette.highlight + color: "#212121" visible: root.showFocusHighlight && view1.count != 0 - gradient: Gradient { - GradientStop { id: t1; position: 0.0; color: palette.highlight } - GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) } - } width: view1.currentItem == null ? 0 : view1.currentItem.width } highlightMoveVelocity: 1000 @@ -215,12 +218,8 @@ Rectangle { model: folders2 delegate: folderDelegate highlight: Rectangle { - color: palette.highlight + color: "#212121" visible: root.showFocusHighlight && view2.count != 0 - gradient: Gradient { - GradientStop { id: t1; position: 0.0; color: palette.highlight } - GradientStop { id: t2; position: 1.0; color: Qt.lighter(palette.highlight) } - } width: view1.currentItem == null ? 0 : view1.currentItem.width } highlightMoveVelocity: 1000 @@ -254,19 +253,29 @@ Rectangle { } Rectangle { - id: cancelButton - width: 100 - height: titleBar.height - 7 + width: parent.width + height: buttonHeight + 10 + anchors.bottom: parent.bottom color: "black" - anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter } + } + + Rectangle { + id: cancelButton + width: parent.width + height: buttonHeight + color: "#212121" + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 5 + radius: buttonHeight / 15 Text { - anchors { fill: parent; margins: 4 } + anchors.fill: parent text: "Cancel" color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - font.pixelSize: 20 } MouseArea { @@ -277,55 +286,66 @@ Rectangle { Keys.onPressed: { root.keyPressed(event.key); - if (event.key == Qt.Key_Return || event.key == Qt.Key_Select || event.key == Qt.Key_Right) { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Select || event.key === Qt.Key_Right) { view.currentItem.launch(); event.accepted = true; - } else if (event.key == Qt.Key_Left) { + } else if (event.key === Qt.Key_Left) { up(); } } - BorderImage { - source: "qrc:/titlebar.sci"; - width: parent.width; - height: 52 - y: -7 + + Rectangle { id: titleBar + width: parent.width + height: buttonHeight + 10 + anchors.top: parent.top + color: "black" Rectangle { - id: upButton - width: 48 - height: titleBar.height - 7 - color: "transparent" - Image { anchors.centerIn: parent; source: "qrc:/up.png" } - MouseArea { id: upRegion; anchors.centerIn: parent - width: 56 - height: 56 - onClicked: up() - } - states: [ - State { - name: "pressed" - when: upRegion.pressed - PropertyChanges { target: upButton; color: palette.highlight } - } - ] - } + width: parent.width; + height: buttonHeight + color: "#212121" + anchors.margins: 5 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + radius: buttonHeight / 15 - Rectangle { - color: "gray" - x: 48 - width: 1 - height: 44 - } + Rectangle { + id: upButton + width: buttonHeight + height: buttonHeight + color: "transparent" + Image { + width: itemHeight + height: itemHeight + anchors.centerIn: parent + source: "qrc:/up.png" + } + MouseArea { id: upRegion; anchors.centerIn: parent + width: buttonHeight + height: buttonHeight + onClicked: up() + } + states: [ + State { + name: "pressed" + when: upRegion.pressed + PropertyChanges { target: upButton; color: palette.highlight } + } + ] + } - Text { - anchors.left: upButton.right; anchors.right: parent.right; height: parent.height - anchors.leftMargin: 4; anchors.rightMargin: 4 - text: folders.folder - color: "white" - elide: Text.ElideLeft; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter - font.pixelSize: 32 + Text { + anchors.left: upButton.right; anchors.right: parent.right; height: parent.height + anchors.leftMargin: 5; anchors.rightMargin: 5 + text: folders.folder + color: "white" + elide: Text.ElideLeft; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter + } } } @@ -366,14 +386,14 @@ Rectangle { function keyPressed(key) { switch (key) { - case Qt.Key_Up: - case Qt.Key_Down: - case Qt.Key_Left: - case Qt.Key_Right: - root.showFocusHighlight = true; + case Qt.Key_Up: + case Qt.Key_Down: + case Qt.Key_Left: + case Qt.Key_Right: + root.showFocusHighlight = true; break; - default: - // do nothing + default: + // do nothing break; } } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml index 5443dc846..04c852a96 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml @@ -39,9 +39,9 @@ Rectangle { property alias buttonHeight: closeButton.height property string source1 property string source2 - property int contentWidth: 250 + property int contentWidth: parent.width / 2 property real volume: 0.25 - property int margins: 10 + property int margins: 5 property QtObject content signal close @@ -54,9 +54,12 @@ Rectangle { right: parent.right margins: root.margins } - width: 50 - height: 30 + width: Math.max(parent.width, parent.height) / 12 + height: Math.min(parent.width, parent.height) / 12 z: 2.0 + bgColor: "#212121" + bgColorSelected: "#757575" + textColorSelected: "white" text: "Back" onClicked: root.close() } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml index 560262db2..dd2dfaf54 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml @@ -59,15 +59,13 @@ Scene { } text: content.started ? "Tap the screen to stop content" : "Tap the screen to start content" - color: "yellow" - font.pixelSize: 20 + color: "#e0e0e0" z: 2.0 } MouseArea { anchors.fill: parent onClicked: { - console.log("[qmlvideo] SceneBasic.onClicked, started = " + content.started) if (content.started) content.stop() else diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml index aa2b42678..63f94de28 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml @@ -86,8 +86,7 @@ Scene { margins: 20 } text: "Tap on the content to toggle full-screen mode" - color: "yellow" - font.pixelSize: 20 + color: "#e0e0e0" z: 2.0 } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml index 7824589e0..99159591d 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml @@ -91,8 +91,7 @@ Scene { margins: 20 } text: "Tap on the content to toggle full-screen mode" - color: "yellow" - font.pixelSize: 20 + color: "#e0e0e0" z: 2.0 } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml index f31cc9580..042c609d3 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml @@ -65,8 +65,7 @@ Scene { margins: 20 } text: content() ? content().started ? "Tap to stop" : "Tap to start" : "" - color: "yellow" - font.pixelSize: 20 + color: "#e0e0e0" } MouseArea { diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml index 1f4559a5f..bdeff4e19 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml @@ -53,7 +53,7 @@ Scene { y: 0.5 * parent.height width: content.width height: content.height - color: "yellow" + color: "#e0e0e0" opacity: 0.5 SequentialAnimation on x { diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml index cfba508a2..2ad65f606 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml @@ -56,7 +56,7 @@ Scene { bottom: rotateNegativeButton.top margins: parent.margins } - width: 90 + width: Math.max(parent.width, parent.height) / 10 height: root.buttonHeight text: "Rotate +" + delta onClicked: content.rotation = content.rotation + delta @@ -69,7 +69,7 @@ Scene { verticalCenter: parent.verticalCenter margins: parent.margins } - width: 90 + width: Math.max(parent.width, parent.height) / 10 height: root.buttonHeight text: "Rotate -" + delta onClicked: content.rotation = content.rotation - delta @@ -82,7 +82,7 @@ Scene { verticalCenter: parent.verticalCenter margins: parent.margins } - width: 30 + width: Math.max(parent.width, parent.height) / 25 height: root.buttonHeight enabled: false text: content.rotation % 360 diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml index 976644b4b..8e6d11a86 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml @@ -39,44 +39,64 @@ Rectangle { property string sceneSource: "" ListModel { - id: list - ListElement { name: "multi"; source: "SceneMulti.qml" } - ListElement { name: "video"; source: "VideoBasic.qml" } - ListElement { name: "video-drag"; source: "VideoDrag.qml" } - ListElement { name: "video-fillmode"; source: "VideoFillMode.qml" } - ListElement { name: "video-fullscreen"; source: "VideoFullScreen.qml" } - ListElement { name: "video-fullscreen-inverted"; source: "VideoFullScreenInverted.qml" } - ListElement { name: "video-metadata"; source: "VideoMetadata.qml" } - ListElement { name: "video-move"; source: "VideoMove.qml" } - ListElement { name: "video-overlay"; source: "VideoOverlay.qml" } - ListElement { name: "video-playbackrate"; source: "VideoPlaybackRate.qml" } - ListElement { name: "video-resize"; source: "VideoResize.qml" } - ListElement { name: "video-rotate"; source: "VideoRotate.qml" } - ListElement { name: "video-spin"; source: "VideoSpin.qml" } - ListElement { name: "video-seek"; source: "VideoSeek.qml" } - ListElement { name: "camera"; source: "CameraBasic.qml" } - ListElement { name: "camera-drag"; source: "CameraDrag.qml" } - ListElement { name: "camera-fullscreen"; source: "CameraFullScreen.qml" } - ListElement { name: "camera-fullscreen-inverted"; source: "CameraFullScreenInverted.qml" } - ListElement { name: "camera-move"; source: "CameraMove.qml" } - ListElement { name: "camera-overlay"; source: "CameraOverlay.qml" } - ListElement { name: "camera-resize"; source: "CameraResize.qml" } - ListElement { name: "camera-rotate"; source: "CameraRotate.qml" } - ListElement { name: "camera-spin"; source: "CameraSpin.qml" } + id: videolist + ListElement { name: "Multi"; source: "SceneMulti.qml" } + ListElement { name: "Video"; source: "VideoBasic.qml" } + ListElement { name: "Drag"; source: "VideoDrag.qml" } + ListElement { name: "Fillmode"; source: "VideoFillMode.qml" } + ListElement { name: "Fullscreen"; source: "VideoFullScreen.qml" } + ListElement { name: "Fullscreen-inverted"; source: "VideoFullScreenInverted.qml" } + ListElement { name: "Metadata"; source: "VideoMetadata.qml" } + ListElement { name: "Move"; source: "VideoMove.qml" } + ListElement { name: "Overlay"; source: "VideoOverlay.qml" } + ListElement { name: "Playback Rate"; source: "VideoPlaybackRate.qml" } + ListElement { name: "Resize"; source: "VideoResize.qml" } + ListElement { name: "Rotate"; source: "VideoRotate.qml" } + ListElement { name: "Spin"; source: "VideoSpin.qml" } + ListElement { name: "Seek"; source: "VideoSeek.qml" } + } + + ListModel { + id: cameralist + ListElement { name: "Camera"; source: "CameraBasic.qml" } + ListElement { name: "Drag"; source: "CameraDrag.qml" } + ListElement { name: "Fullscreen"; source: "CameraFullScreen.qml" } + ListElement { name: "Fullscreen-inverted"; source: "CameraFullScreenInverted.qml" } + ListElement { name: "Move"; source: "CameraMove.qml" } + ListElement { name: "Overlay"; source: "CameraOverlay.qml" } + ListElement { name: "Resize"; source: "CameraResize.qml" } + ListElement { name: "Rotate"; source: "CameraRotate.qml" } + ListElement { name: "Spin"; source: "CameraSpin.qml" } + } + + Component { + id: leftDelegate + Item { + width: root.width / 2 + height: 0.8 * itemHeight + + Button { + anchors.fill: parent + anchors.margins: 5 + anchors.rightMargin: 2.5 + anchors.bottomMargin: 0 + text: name + onClicked: root.sceneSource = source + } + } } Component { - id: delegate + id: rightDelegate Item { - id: delegateItem - width: root.width - height: itemHeight + width: root.width / 2 + height: 0.8 * itemHeight Button { - id: selectorItem - anchors.centerIn: parent - width: 0.9 * parent.width - height: 0.8 * itemHeight + anchors.fill: parent + anchors.margins: 5 + anchors.leftMargin: 2.5 + anchors.bottomMargin: 0 text: name onClicked: root.sceneSource = source } @@ -85,20 +105,29 @@ Rectangle { Flickable { anchors.fill: parent - contentHeight: (itemHeight * list.count) + layout.anchors.topMargin + layout.spacing + contentHeight: (itemHeight * videolist.count) + 10 clip: true - Column { + Row { id: layout - anchors { fill: parent - topMargin: 10 + topMargin: 5 + bottomMargin: 5 + } + + Column { + Repeater { + model: videolist + delegate: leftDelegate + } } - Repeater { - model: list - delegate: delegate + Column { + Repeater { + model: cameralist + delegate: rightDelegate + } } } } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml index 9c3810134..f14fa402c 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml @@ -35,7 +35,7 @@ import QtQuick 2.0 Item { id: seekControl - height: 46 + height: Math.min(parent.width, parent.height) / 20 property int duration: 0 property int playPosition: 0 property int seekPosition: 0 @@ -45,8 +45,9 @@ Item { Rectangle { id: background anchors.fill: parent - color: "black" + color: "white" opacity: 0.3 + radius: parent.height / 15 } Rectangle { @@ -60,7 +61,6 @@ Item { Text { width: 90 anchors { left: parent.left; top: parent.top; bottom: parent.bottom; leftMargin: 10 } - font { family: "Nokia Sans S60"; pixelSize: 24 } horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter color: "white" @@ -71,7 +71,6 @@ Item { Text { width: 90 anchors { right: parent.right; top: parent.top; bottom: parent.bottom; rightMargin: 10 } - font { family: "Nokia Sans S60"; pixelSize: 24 } horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter color: "white" @@ -79,35 +78,36 @@ Item { text: formatTime(duration) } - Image { + Rectangle { id: progressHandle - height: 46 - width: 10 - source: mouseArea.pressed ? "qrc:/images/progress_handle_pressed.svg" : "qrc:/images/progress_handle.svg" + height: parent.height + width: parent.height / 2 + color: "white" + opacity: 0.5 anchors.verticalCenter: progressBar.verticalCenter - x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * 630 + x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * background.width MouseArea { id: mouseArea anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom } - height: 46+16 - width: height + height: parent.height + width: parent.height * 2 enabled: seekControl.enabled drag { target: progressHandle axis: Drag.XAxis minimumX: 0 - maximumX: 631 + maximumX: background.width } onPressed: { seekControl.seeking = true; } onCanceled: { - seekControl.seekPosition = progressHandle.x * seekControl.duration / 630 + seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width seekControl.seeking = false } onReleased: { - seekControl.seekPosition = progressHandle.x * seekControl.duration / 630 + seekControl.seekPosition = progressHandle.x * seekControl.duration / background.width seekControl.seeking = false mouse.accepted = true } @@ -120,7 +120,7 @@ Item { interval: 300 running: seekControl.seeking onTriggered: { - seekControl.seekPosition = progressHandle.x*seekControl.duration/630 + seekControl.seekPosition = progressHandle.x*seekControl.duration / background.width } } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml index f623aa412..b114d5bb4 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml @@ -54,16 +54,16 @@ Scene { verticalCenter: parent.verticalCenter margins: parent.margins } - width: 150 + width: Math.max(parent.width, parent.height) / 5 height: root.buttonHeight text: "PreserveAspectFit" onClicked: { if (!content.dummy) { var video = content.contentItem() - if (video.fillMode == VideoOutput.Stretch) { + if (video.fillMode === VideoOutput.Stretch) { video.fillMode = VideoOutput.PreserveAspectFit text = "PreserveAspectFit" - } else if (video.fillMode == VideoOutput.PreserveAspectFit) { + } else if (video.fillMode === VideoOutput.PreserveAspectFit) { video.fillMode = VideoOutput.PreserveAspectCrop text = "PreserveAspectCrop" } else { diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml index 00580f782..05c6dd76c 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml @@ -56,55 +56,55 @@ Scene { Column { anchors.fill: parent Text { - color: "yellow" + color: "#e0e0e0" text: "Title:" + content.contentItem().metaData.title } Text { - color: "yellow" + color: "#e0e0e0" text: "Size:" + content.contentItem().metaData.size } Text { - color: "yellow" + color: "#e0e0e0" text: "Resolution:" + content.contentItem().metaData.resolution } Text { - color: "yellow" + color: "#e0e0e0" text: "Media type:" + content.contentItem().metaData.mediaType } Text { - color: "yellow" + color: "#e0e0e0" text: "Video codec:" + content.contentItem().metaData.videoCodec } Text { - color: "yellow" + color: "#e0e0e0" text: "Video bit rate:" + content.contentItem().metaData.videoBitRate } Text { - color: "yellow" + color: "#e0e0e0" text: "Video frame rate:" +content.contentItem().metaData.videoFrameRate } Text { - color: "yellow" + color: "#e0e0e0" text: "Audio codec:" + content.contentItem().metaData.audioCodec } Text { - color: "yellow" + color: "#e0e0e0" text: "Audio bit rate:" + content.contentItem().metaData.audioBitRate } Text { - color: "yellow" + color: "#e0e0e0" text: "Date:" + content.contentItem().metaData.date } Text { - color: "yellow" + color: "#e0e0e0" text: "Description:" + content.contentItem().metaData.description } Text { - color: "yellow" + color: "#e0e0e0" text: "Copyright:" + content.contentItem().metaData.copyright } Text { - color: "yellow" + color: "#e0e0e0" text: "Seekable:" + content.contentItem().metaData.seekable } } diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml index 5c1e6ab17..45d599b34 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml @@ -56,12 +56,12 @@ Scene { bottom: decreaseButton.top margins: parent.margins } - width: 90 + width: Math.max(parent.width, parent.height) / 10 height: root.buttonHeight text: "Increase" onClicked: { var video = content.contentItem() - video.playbackRate = video.playbackRate + delta + video.playbackRate += delta } } @@ -72,12 +72,12 @@ Scene { verticalCenter: parent.verticalCenter margins: parent.margins } - width: 90 + width: Math.max(parent.width, parent.height) / 10 height: root.buttonHeight text: "Decrease" onClicked: { var video = content.contentItem() - video.playbackRate = video.playbackRate - delta + video.playbackRate -= delta } } @@ -88,7 +88,7 @@ Scene { verticalCenter: parent.verticalCenter margins: parent.margins } - width: 50 + width: Math.max(parent.width, parent.height) / 25 height: root.buttonHeight enabled: false text: Math.round(10 * content.contentItem().playbackRate) / 10 diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml index 2d43bdf6e..05a312e37 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml @@ -36,6 +36,7 @@ import QtQuick 2.0 Scene { id: root property string contentType: "video" + contentWidth: parent.width Content { id: content @@ -51,13 +52,12 @@ Scene { anchors { left: parent.left right: parent.right - leftMargin: 100 - rightMargin: 140 + margins: 10 bottom: parent.bottom } duration: content.contentItem() ? content.contentItem().duration : 0 playPosition: content.contentItem() ? content.contentItem().position : 0 - onSeekPositionChanged: { content.contentItem().seek(seekPosition); } + onSeekPositionChanged: content.contentItem().seek(seekPosition); } Component.onCompleted: root.content = content diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml index 4422648c0..96acb4bc4 100644 --- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml +++ b/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml @@ -35,28 +35,21 @@ import QtQuick 2.0 Rectangle { id: root - width: 640 - height: 360 + anchors.fill: parent color: "black" property string source1 property string source2 - property color bgColor: "#002244" + property color bgColor: "black" property real volume: 0.25 property bool perfMonitorsLogging: false property bool perfMonitorsVisible: false QtObject { id: d - property int itemHeight: 40 + property int itemHeight: root.height > root.width ? root.width / 10 : root.height / 10 property int buttonHeight: 0.8 * itemHeight - property int margins: 10 - } - - // Create ScreenSaver element via Loader, so this app will still run if the - // SystemInfo module is not available - Loader { - source: "DisableScreenSaver.qml" + property int margins: 5 } Loader { @@ -71,7 +64,6 @@ Rectangle { } function init() { - console.log("[qmlvideo] performanceLoader.init logging " + root.perfMonitorsLogging + " visible " + root.perfMonitorsVisible) var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible source = enabled ? "../performancemonitor/PerformanceItem.qml" : "" } @@ -99,6 +91,9 @@ Rectangle { right: exitButton.left margins: d.margins } + bgColor: "#212121" + bgColorSelected: "#757575" + textColorSelected: "white" height: d.buttonHeight text: (root.source1 == "") ? "Select file 1" : root.source1 onClicked: fileBrowser1.show() @@ -112,6 +107,9 @@ Rectangle { right: exitButton.left margins: d.margins } + bgColor: "#212121" + bgColorSelected: "#757575" + textColorSelected: "white" height: d.buttonHeight text: (root.source2 == "") ? "Select file 2" : root.source2 onClicked: fileBrowser2.show() @@ -124,26 +122,58 @@ Rectangle { right: parent.right margins: d.margins } - width: 50 + bgColor: "#212121" + bgColorSelected: "#757575" + textColorSelected: "white" + width: parent.width / 10 height: d.buttonHeight text: "Exit" onClicked: Qt.quit() } + Row { + id: modes + anchors.top: openFile2Button.bottom + anchors.margins: 0 + anchors.topMargin: 5 + Button { + width: root.width / 2 + height: 0.8 * d.itemHeight + bgColor: "#212121" + radius: 0 + text: "Video Modes" + enabled: false + } + Button { + width: root.width / 2 + height: 0.8 * d.itemHeight + bgColor: "#212121" + radius: 0 + text: "Camera Modes" + enabled: false + } + } + + Rectangle { + id: divider + height: 1 + width: parent.width + color: "black" + anchors.top: modes.bottom + } + SceneSelectionPanel { id: sceneSelectionPanel itemHeight: d.itemHeight - color: "#004444" + color: "#212121" anchors { - top: openFile2Button.bottom + top: divider.bottom left: parent.left right: parent.right bottom: parent.bottom - margins: d.margins } - radius: 10 + radius: 0 onSceneSourceChanged: { - console.log("[qmlvideo] main.onSceneSourceChanged source " + sceneSource) sceneLoader.source = sceneSource var scene = null var innerVisible = true @@ -213,7 +243,9 @@ Rectangle { ErrorDialog { id: errorDialog - anchors.fill: parent + anchors.fill: root + dialogWidth: d.itemHeight * 5 + dialogHeight: d.itemHeight * 3 enabled: false } @@ -230,7 +262,6 @@ Rectangle { } function closeScene() { - console.log("[qmlvideo] main.closeScene") sceneSelectionPanel.sceneSource = "" } } diff --git a/examples/multimedia/video/qmlvideo/qmlvideo.qrc b/examples/multimedia/video/qmlvideo/qmlvideo.qrc index 5bf2df8af..6418215d0 100644 --- a/examples/multimedia/video/qmlvideo/qmlvideo.qrc +++ b/examples/multimedia/video/qmlvideo/qmlvideo.qrc @@ -1,13 +1,8 @@ <RCC> <qresource prefix="/"> <file alias="leaves.jpg">images/leaves.jpg</file> - <file alias="close.png">images/close.png</file> <file alias="folder.png">images/folder.png</file> - <file alias="titlebar.png">images/titlebar.png</file> - <file alias="titlebar.sci">images/titlebar.sci</file> <file alias="up.png">images/up.png</file> - <file alias="progress_handle.svg">images/progress_handle.svg</file> - <file alias="progress_handle_pressed.svg">images/progress_handle_pressed.svg</file> <file>qml/qmlvideo/Button.qml</file> <file>qml/qmlvideo/CameraBasic.qml</file> <file>qml/qmlvideo/CameraDrag.qml</file> @@ -21,7 +16,6 @@ <file>qml/qmlvideo/CameraRotate.qml</file> <file>qml/qmlvideo/CameraSpin.qml</file> <file>qml/qmlvideo/Content.qml</file> - <file>qml/qmlvideo/DisableScreenSaver.qml</file> <file>qml/qmlvideo/ErrorDialog.qml</file> <file>qml/qmlvideo/FileBrowser.qml</file> <file>qml/qmlvideo/main.qml</file> diff --git a/examples/multimedia/video/qmlvideofx/filereader.cpp b/examples/multimedia/video/qmlvideofx/filereader.cpp index 5f5066f12..714c0914d 100644 --- a/examples/multimedia/video/qmlvideofx/filereader.cpp +++ b/examples/multimedia/video/qmlvideofx/filereader.cpp @@ -32,7 +32,6 @@ ****************************************************************************/ #include "filereader.h" -#include "trace.h" #include <QCoreApplication> #include <QDir> @@ -42,7 +41,6 @@ QString FileReader::readFile(const QString &fileName) { - qtTrace() << "FileReader::readFile" << "fileName" << fileName; QString content; QFile file(fileName); if (file.open(QIODevice::ReadOnly)) { diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml index 0a2a1584f..19785ce86 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml @@ -84,7 +84,6 @@ Rectangle { } onEffectSourceChanged: { - console.log("[qmlvideofx] Content.onEffectSourceChanged " + effectSource) effectLoader.source = effectSource effectLoader.item.parent = root effectLoader.item.targetWidth = root.width @@ -96,7 +95,6 @@ Rectangle { } function init() { - console.log("[qmlvideofx] Content.init") openImage("qrc:/images/qt-logo.png") root.effectSource = "EffectPassThrough.qml" } @@ -107,7 +105,6 @@ Rectangle { } function updateSource() { - console.log("[qmlvideofx] Content.updateSource") if (contentLoader.item) { contentLoader.item.parent = root contentLoader.item.anchors.fill = root @@ -118,7 +115,6 @@ Rectangle { } function openImage(path) { - console.log("[qmlvideofx] Content.openImage \"" + path + "\"") stop() contentLoader.source = "ContentImage.qml" videoFramePaintedConnection.target = null @@ -127,7 +123,6 @@ Rectangle { } function openVideo(path) { - console.log("[qmlvideofx] Content.openVideo \"" + path + "\"") stop() contentLoader.source = "ContentVideo.qml" videoFramePaintedConnection.target = contentLoader.item @@ -138,7 +133,6 @@ Rectangle { } function openCamera() { - console.log("[qmlvideofx] Content.openCamera") stop() contentLoader.source = "ContentCamera.qml" videoFramePaintedConnection.target = contentLoader.item @@ -146,7 +140,6 @@ Rectangle { } function stop() { - console.log("[qmlvideofx] Content.stop") if (contentLoader.source == "ContentVideo.qml") contentLoader.item.stop() theSource.sourceItem = null diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml deleted file mode 100644 index fcf43f4b6..000000000 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Mobility Components. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtMultimedia 5.0 - -VideoOutput { - source: camera - - Camera { - id: camera - } -} diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/DisableScreenSaver.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/DisableScreenSaver.qml deleted file mode 100644 index ccd852bff..000000000 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/DisableScreenSaver.qml +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Mobility Components. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtMobility.systeminfo 1.1 -// NOTE: The QtSystemInfo module is not yet part of Qt 5 - -Item { - ScreenSaver { - screenSaverInhibited: true - } -} - diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectMagnify.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectMagnify.qml index 65c019cf9..42af673f5 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectMagnify.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectMagnify.qml @@ -31,7 +31,8 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.1 +import QtQuick.Window 2.1 Effect { id: root @@ -49,6 +50,7 @@ Effect { property real posX: -1 property real posY: -1 + property real pixDens: Screen.pixelDensity QtObject { id: d diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectRipple.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectRipple.qml index ce301d6d9..c6c60bc29 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectRipple.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectRipple.qml @@ -31,7 +31,8 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.1 +import QtQuick.Window 2.1 Effect { parameters: ListModel { @@ -48,6 +49,7 @@ Effect { // Transform slider values, and bind result to shader uniforms property real amplitude: parameters.get(0).value * 0.03 property real n: parameters.get(1).value * 7 + property real pixDens: Screen.pixelDensity property real time: 0 NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 } diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectSelectionList.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectSelectionList.qml index ce1987320..58530eb4f 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectSelectionList.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/EffectSelectionList.qml @@ -43,7 +43,7 @@ ListModel { ListElement { name: "Emboss"; source: "EffectEmboss.qml" } ListElement { name: "Glow"; source: "EffectGlow.qml" } ListElement { name: "Isolate"; source: "EffectIsolate.qml" } - //ListElement { name: "Magnify"; source: "EffectMagnify.qml" } + ListElement { name: "Magnify"; source: "EffectMagnify.qml" } ListElement { name: "Page curl"; source: "EffectPageCurl.qml" } ListElement { name: "Pixelate"; source: "EffectPixelate.qml" } ListElement { name: "Posterize"; source: "EffectPosterize.qml" } diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Main.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Main.qml index 039e6c36f..b724ee3bd 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Main.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/Main.qml @@ -160,7 +160,6 @@ Rectangle { Loader { id: performanceLoader function init() { - console.log("[qmlvideofx] performanceLoader.init logging " + root.perfMonitorsLogging + " visible " + root.perfMonitorsVisible) var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible source = enabled ? "../performancemonitor/PerformanceItem.qml" : "" } @@ -249,11 +248,6 @@ Rectangle { height = windowHeight width = windowWidth - console.log("[qmlvideofx] root.init") - console.log("Height: ", Screen.desktopAvailableHeight) - console.log("Width: ", Screen.desktopAvailableWidth) - console.log("Pixels per mm: ", Math.ceil(Screen.pixelDensity)) - console.log("Orientation: ", Screen.orientation) imageFileBrowser.folder = imagePath videoFileBrowser.folder = videoPath content.init() diff --git a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc index e7a361246..e7978e39f 100644 --- a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc +++ b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc @@ -3,10 +3,8 @@ <file>images/qt-logo.png</file> <file>qml/qmlvideofx/Button.qml</file> <file>qml/qmlvideofx/Content.qml</file> - <file>qml/qmlvideofx/ContentCamera.qml</file> <file>qml/qmlvideofx/ContentImage.qml</file> <file>qml/qmlvideofx/ContentVideo.qml</file> - <file>qml/qmlvideofx/DisableScreenSaver.qml</file> <file>qml/qmlvideofx/Divider.qml</file> <file>qml/qmlvideofx/Effect.qml</file> <file>qml/qmlvideofx/EffectBillboard.qml</file> diff --git a/examples/multimedia/video/qmlvideofx/shaders/magnify.fsh b/examples/multimedia/video/qmlvideofx/shaders/magnify.fsh index 0387d25d6..fb7e2a047 100644 --- a/examples/multimedia/video/qmlvideofx/shaders/magnify.fsh +++ b/examples/multimedia/video/qmlvideofx/shaders/magnify.fsh @@ -50,12 +50,15 @@ uniform float targetWidth; uniform float targetHeight; uniform float posX; uniform float posY; +uniform float pixDens; void main() { vec2 tc = qt_TexCoord0; vec2 center = vec2(posX, posY); vec2 xy = gl_FragCoord.xy - center.xy; + xy.x -= (pixDens * 14.0); + xy.y -= (pixDens * 29.0); float r = sqrt(xy.x * xy.x + xy.y * xy.y); if (r < radius) { float h = diffractionIndex * 0.5 * radius; diff --git a/examples/multimedia/video/qmlvideofx/shaders/ripple.fsh b/examples/multimedia/video/qmlvideofx/shaders/ripple.fsh index b70f36d92..428c041c7 100644 --- a/examples/multimedia/video/qmlvideofx/shaders/ripple.fsh +++ b/examples/multimedia/video/qmlvideofx/shaders/ripple.fsh @@ -55,12 +55,13 @@ const int ITER = 7; const float RATE = 0.1; uniform float amplitude; uniform float n; +uniform float pixDens; void main() { vec2 uv = qt_TexCoord0.xy; vec2 tc = uv; - vec2 p = vec2(-1.0 + 2.0 * gl_FragCoord.x / targetWidth, -(-1.0 + 2.0 * gl_FragCoord.y / targetHeight)); + vec2 p = vec2(-1.0 + 2.0 * (gl_FragCoord.x - (pixDens * 14.0)) / targetWidth, -(-1.0 + 2.0 * (gl_FragCoord.y - (pixDens * 29.0)) / targetHeight)); float diffx = 0.0; float diffy = 0.0; vec4 col; diff --git a/examples/multimedia/video/snippets/frequencymonitor/frequencymonitor.cpp b/examples/multimedia/video/snippets/frequencymonitor/frequencymonitor.cpp index 747ea6bdf..b165adada 100644 --- a/examples/multimedia/video/snippets/frequencymonitor/frequencymonitor.cpp +++ b/examples/multimedia/video/snippets/frequencymonitor/frequencymonitor.cpp @@ -125,7 +125,6 @@ void FrequencyMonitorPrivate::calculateAverageFrequency() void FrequencyMonitorPrivate::stalled() { if (m_instantaneousFrequency) { - qtVerboseTrace() << "FrequencyMonitor::stalled"; m_instantaneousFrequency = 0; emit q_ptr->instantaneousFrequencyChanged(m_instantaneousFrequency); emit q_ptr->frequencyChanged(); @@ -136,7 +135,6 @@ FrequencyMonitor::FrequencyMonitor(QObject *parent) : QObject(parent) { d_ptr = new FrequencyMonitorPrivate(this); - qtTrace() << "FrequencyMonitor::FrequencyMonitor"; } FrequencyMonitor::~FrequencyMonitor() diff --git a/examples/multimediawidgets/camera/camera.cpp b/examples/multimediawidgets/camera/camera.cpp index 3afe59324..714de7a4d 100644 --- a/examples/multimediawidgets/camera/camera.cpp +++ b/examples/multimediawidgets/camera/camera.cpp @@ -46,6 +46,7 @@ #include <QMediaService> #include <QMediaRecorder> #include <QCameraViewfinder> +#include <QCameraInfo> #include <QMediaMetaData> #include <QMessageBox> @@ -53,6 +54,8 @@ #include <QtWidgets> +Q_DECLARE_METATYPE(QCameraInfo) + Camera::Camera(QWidget *parent) : QMainWindow(parent), ui(new Ui::Camera), @@ -65,26 +68,23 @@ Camera::Camera(QWidget *parent) : ui->setupUi(this); //Camera devices: - QByteArray cameraDevice; QActionGroup *videoDevicesGroup = new QActionGroup(this); videoDevicesGroup->setExclusive(true); - foreach(const QByteArray &deviceName, QCamera::availableDevices()) { - QString description = camera->deviceDescription(deviceName); - QAction *videoDeviceAction = new QAction(description, videoDevicesGroup); + foreach (const QCameraInfo &cameraInfo, QCameraInfo::availableCameras()) { + QAction *videoDeviceAction = new QAction(cameraInfo.description(), videoDevicesGroup); videoDeviceAction->setCheckable(true); - videoDeviceAction->setData(QVariant(deviceName)); - if (cameraDevice.isEmpty()) { - cameraDevice = deviceName; + videoDeviceAction->setData(QVariant::fromValue(cameraInfo)); + if (cameraInfo == QCameraInfo::defaultCamera()) videoDeviceAction->setChecked(true); - } + ui->menuDevices->addAction(videoDeviceAction); } connect(videoDevicesGroup, SIGNAL(triggered(QAction*)), SLOT(updateCameraDevice(QAction*))); connect(ui->captureWidget, SIGNAL(currentChanged(int)), SLOT(updateCaptureMode())); - setCamera(cameraDevice); + setCamera(QCameraInfo::defaultCamera()); } Camera::~Camera() @@ -94,16 +94,13 @@ Camera::~Camera() delete camera; } -void Camera::setCamera(const QByteArray &cameraDevice) +void Camera::setCamera(const QCameraInfo &cameraInfo) { delete imageCapture; delete mediaRecorder; delete camera; - if (cameraDevice.isEmpty()) - camera = new QCamera; - else - camera = new QCamera(cameraDevice); + camera = new QCamera(cameraInfo); connect(camera, SIGNAL(stateChanged(QCamera::State)), this, SLOT(updateCameraState(QCamera::State))); connect(camera, SIGNAL(error(QCamera::Error)), this, SLOT(displayCameraError())); @@ -398,7 +395,7 @@ void Camera::displayCameraError() void Camera::updateCameraDevice(QAction *action) { - setCamera(action->data().toByteArray()); + setCamera(qvariant_cast<QCameraInfo>(action->data())); } void Camera::displayViewfinder() diff --git a/examples/multimediawidgets/camera/camera.h b/examples/multimediawidgets/camera/camera.h index 52f03cbd3..faa02ccd7 100644 --- a/examples/multimediawidgets/camera/camera.h +++ b/examples/multimediawidgets/camera/camera.h @@ -60,7 +60,7 @@ public: ~Camera(); private slots: - void setCamera(const QByteArray &cameraDevice); + void setCamera(const QCameraInfo &cameraInfo); void startCamera(); void stopCamera(); diff --git a/qtmultimedia.pro b/qtmultimedia.pro index c7f093ccc..3cec526e8 100644 --- a/qtmultimedia.pro +++ b/qtmultimedia.pro @@ -12,10 +12,6 @@ win32 { qtCompileTest(evr) } else:mac { qtCompileTest(avfoundation) -} else:android:!android-no-sdk { - SDK_ROOT = $$(ANDROID_SDK_ROOT) - isEmpty(SDK_ROOT): SDK_ROOT = $$DEFAULT_ANDROID_SDK_ROOT - !exists($$SDK_ROOT/platforms/android-11/android.jar): error("QtMultimedia for Android requires API level 11") } else:qnx { qtCompileTest(mmrenderer) } else { @@ -25,6 +21,7 @@ win32 { qtCompileTest(gstreamer_photography) qtCompileTest(gstreamer_encodingprofiles) qtCompileTest(gstreamer_appsrc) + qtCompileTest(linux_v4l) } qtCompileTest(resourcepolicy) qtCompileTest(gpu_vivante) diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro index 15edd04d2..7c809a777 100644 --- a/src/gsttools/gsttools.pro +++ b/src/gsttools/gsttools.pro @@ -100,6 +100,8 @@ config_gstreamer_appsrc { LIBS_PRIVATE += -lgstapp-0.10 } +config_linux_v4l: DEFINES += USE_V4L + HEADERS += $$PRIVATE_HEADERS DESTDIR = $$QT.multimedia.libs diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp index 9740b9a09..8d484aa6d 100644 --- a/src/gsttools/qgstutils.cpp +++ b/src/gsttools/qgstutils.cpp @@ -42,8 +42,10 @@ #include <QtCore/qstringlist.h> #include <qaudioformat.h> -#include <private/qcore_unix_p.h> -#include <linux/videodev2.h> +#ifdef USE_V4L +# include <private/qcore_unix_p.h> +# include <linux/videodev2.h> +#endif #include "qgstreamervideoinputdevicecontrol_p.h" @@ -469,6 +471,7 @@ QVector<QGstUtils::CameraInfo> QGstUtils::enumerateCameras(GstElementFactory *fa } } +#ifdef USE_V4L QDir devDir(QStringLiteral("/dev")); devDir.setFilter(QDir::System); @@ -516,6 +519,7 @@ QVector<QGstUtils::CameraInfo> QGstUtils::enumerateCameras(GstElementFactory *fa } qt_safe_close(fd); } +#endif // USE_V4L return devices; } diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 36f8c049b..f3e2d884c 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -56,7 +56,6 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( : m_surface(surface) , m_pool(0) , m_renderReturn(GST_FLOW_ERROR) - , m_lastPrerolledBuffer(0) , m_bytesPerLine(0) , m_startCanceled(false) { @@ -74,7 +73,6 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() { - setLastPrerolledBuffer(0); } QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const @@ -209,23 +207,6 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) return m_renderReturn; } -void QVideoSurfaceGstDelegate::setLastPrerolledBuffer(GstBuffer *prerolledBuffer) -{ - // discard previously stored buffer - if (m_lastPrerolledBuffer) { - gst_buffer_unref(m_lastPrerolledBuffer); - m_lastPrerolledBuffer = 0; - } - - if (!prerolledBuffer) - return; - - // store a reference to the buffer - Q_ASSERT(!m_lastPrerolledBuffer); - m_lastPrerolledBuffer = prerolledBuffer; - gst_buffer_ref(m_lastPrerolledBuffer); -} - void QVideoSurfaceGstDelegate::queuedStart() { if (!m_startCanceled) { @@ -397,8 +378,6 @@ QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *su sink->delegate = new QVideoSurfaceGstDelegate(surface); - g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); - return sink; } @@ -434,16 +413,15 @@ void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class)); + GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class); + video_sink_class->show_frame = QVideoSurfaceGstSink::show_frame; + GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps; base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps; base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; base_sink_class->start = QVideoSurfaceGstSink::start; base_sink_class->stop = QVideoSurfaceGstSink::stop; - // base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented. - base_sink_class->event = QVideoSurfaceGstSink::event; - base_sink_class->preroll = QVideoSurfaceGstSink::preroll; - base_sink_class->render = QVideoSurfaceGstSink::render; GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); element_class->change_state = QVideoSurfaceGstSink::change_state; @@ -709,27 +687,6 @@ void QVideoSurfaceGstSink::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buf } } -void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>(d); - - gboolean value = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(sink), "show-preroll-frame", &value, NULL); - - GstBuffer *buffer = sink->delegate->lastPrerolledBuffer(); - // Render the stored prerolled buffer if requested. - // e.g. player is in stopped mode, then seek operation is requested, - // surface now stores a prerolled frame, but doesn't display it until - // "show-preroll-frame" property is set to "true" - // when switching to pause or playing state. - if (value && buffer) { - sink->delegate->render(buffer); - sink->delegate->setLastPrerolledBuffer(0); - } -} - GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) { @@ -842,44 +799,9 @@ gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) return TRUE; } -gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) -{ - Q_UNUSED(base); - - return TRUE; -} - -gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event) -{ - // discard prerolled frame - if (event->type == GST_EVENT_FLUSH_START) { - VO_SINK(base); - sink->delegate->setLastPrerolledBuffer(0); - } - - return TRUE; -} - -GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - - gboolean value = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(base), "show-preroll-frame", &value, NULL); - if (value) { - sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer - return sink->delegate->render(buffer); // display frame - } - - // otherwise keep a reference to the buffer to display it later - sink->delegate->setLastPrerolledBuffer(buffer); - return GST_FLOW_OK; -} - -GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer) +GstFlowReturn QVideoSurfaceGstSink::show_frame(GstVideoSink *base, GstBuffer *buffer) { VO_SINK(base); - sink->delegate->setLastPrerolledBuffer(0); // discard prerolled buffer return sink->delegate->render(buffer); } diff --git a/src/imports/multimedia/qdeclarativeradiodata.cpp b/src/imports/multimedia/qdeclarativeradiodata.cpp index e8673905f..6cc829ae9 100644 --- a/src/imports/multimedia/qdeclarativeradiodata.cpp +++ b/src/imports/multimedia/qdeclarativeradiodata.cpp @@ -281,6 +281,9 @@ void QDeclarativeRadioData::_q_availabilityChanged(QMultimedia::AvailabilityStat void QDeclarativeRadioData::connectSignals() { + if (!m_radioData) + return; + connect(m_radioData, SIGNAL(programTypeChanged(QRadioData::ProgramType)), this, SLOT(_q_programTypeChanged(QRadioData::ProgramType))); diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp index c9f12aeb6..38052de32 100644 --- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp +++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp @@ -45,9 +45,6 @@ #include "qsoundeffect_qaudio_p.h" #include <QtCore/qcoreapplication.h> -#include <QtCore/qthread.h> -#include <QtCore/qmutex.h> -#include <QtCore/qwaitcondition.h> #include <QtCore/qiodevice.h> //#include <QDebug> diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h index d152e2a44..11b305d27 100644 --- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h @@ -84,9 +84,6 @@ public: GstFlowReturn render(GstBuffer *buffer); - GstBuffer *lastPrerolledBuffer() const { return m_lastPrerolledBuffer; } - void setLastPrerolledBuffer(GstBuffer *lastPrerolledBuffer); // set prerolledBuffer to 0 to discard prerolled buffer - private slots: void queuedStart(); void queuedStop(); @@ -108,8 +105,6 @@ private: QVideoSurfaceFormat m_format; QVideoFrame m_frame; GstFlowReturn m_renderReturn; - // this pointer is not 0 when there is a prerolled buffer waiting to be displayed - GstBuffer *m_lastPrerolledBuffer; int m_bytesPerLine; bool m_started; bool m_startCanceled; @@ -126,8 +121,6 @@ public: QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); static void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); - static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); - private: static GType get_type(); static void class_init(gpointer g_class, gpointer class_data); @@ -147,11 +140,7 @@ private: static gboolean start(GstBaseSink *sink); static gboolean stop(GstBaseSink *sink); - static gboolean unlock(GstBaseSink *sink); - - static gboolean event(GstBaseSink *sink, GstEvent *event); - static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); - static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); private: QVideoSurfaceGstDelegate *delegate; diff --git a/src/multimedia/playback/playlistfileparser.cpp b/src/multimedia/playback/playlistfileparser.cpp index ac556c25c..a3aba1cab 100644 --- a/src/multimedia/playback/playlistfileparser.cpp +++ b/src/multimedia/playback/playlistfileparser.cpp @@ -51,6 +51,30 @@ public: virtual void parseLine(int lineIndex, const QString& line, const QUrl& root) = 0; +protected: + QUrl expandToFullPath(const QUrl &root, const QString &line) + { + // On Linux, backslashes are not converted to forward slashes :/ + if (line.startsWith(QLatin1String("//")) || line.startsWith(QLatin1String("\\\\"))) { + // Network share paths are not resolved + return QUrl::fromLocalFile(line); + } + + QUrl url(line); + if (url.scheme().isEmpty()) { + // Resolve it relative to root + if (root.isLocalFile()) + return root.resolved(QUrl::fromLocalFile(line)); + else + return root.resolved(url); + } else if (url.scheme().length() == 1) { + // Assume it's a drive letter for a Windows path + url = QUrl::fromLocalFile(line); + } + + return url; + } + Q_SIGNALS: void newItem(const QVariant& content); void finished(); @@ -138,29 +162,6 @@ public: return -1; } - QUrl expandToFullPath(const QUrl& root, const QString& line) - { - // On Linux, backslashes are not converted to forward slashes :/ - if (line.startsWith(QLatin1String("//")) || line.startsWith(QLatin1String("\\\\"))) { - // Network share paths are not resolved - return QUrl::fromLocalFile(line); - } - - QUrl url(line); - if (url.scheme().isEmpty()) { - // Resolve it relative to root - if (root.isLocalFile()) - return root.resolved(QUrl::fromLocalFile(line)); - else - return root.resolved(url); - } else if (url.scheme().length() == 1) { - // Assume it's a drive letter for a Windows path - url = QUrl::fromLocalFile(line); - } - - return url; - } - private: bool m_extendedFormat; QVariantMap m_extraInfo; @@ -172,27 +173,9 @@ class PLSParser : public ParserBase public: PLSParser(QObject *parent) : ParserBase(parent) - , m_state(Header) - , m_count(0) - , m_readFlags(0) { } - enum ReadFlags - { - FileRead = 0x1, - TitleRead = 0x2, - LengthRead = 0x4, - All = FileRead | TitleRead | LengthRead - }; - - enum State - { - Header, - Track, - Footer - }; - /* * The format is essentially that of an INI file structured as follows: @@ -231,89 +214,25 @@ NumberOfEntries=2 Version=2 */ - inline bool containsFlag(const ReadFlags& flag) + void parseLine(int, const QString &line, const QUrl &root) { - return (m_readFlags & int(flag)) == flag; - } + // We ignore everything but 'File' entries, since that's the only thing we care about. + if (!line.startsWith(QLatin1String("File"))) + return; - inline void setFlag(const ReadFlags& flag) - { - m_readFlags |= int(flag); - } + QString value = getValue(line); + if (value.isEmpty()) + return; - void parseLine(int lineIndex, const QString& line, const QUrl&) - { - switch (m_state) { - case Header: - if (line == QLatin1String("[playlist]")) { - m_state = Track; - setCount(1); - } - break; - case Track: - if (!containsFlag(FileRead) && line.startsWith(m_fileName)) { - m_item[QLatin1String("url")] = getValue(lineIndex, line); - setFlag(FileRead); - } else if (!containsFlag(TitleRead) && line.startsWith(m_titleName)) { - m_item[QMediaMetaData::Title] = getValue(lineIndex, line); - setFlag(TitleRead); - } else if (!containsFlag(LengthRead) && line.startsWith(m_lengthName)) { - //convert from seconds to miliseconds - int length = getValue(lineIndex, line).toInt(); - if (length > 0) - m_item[QMediaMetaData::Duration] = length * 1000; - setFlag(LengthRead); - } else if (line.startsWith(QLatin1String("NumberOfEntries"))) { - m_state = Footer; - int entries = getValue(lineIndex, line).toInt(); - int count = m_readFlags == 0 ? (m_count - 1) : m_count; - if (entries != count) { - emit error(QPlaylistFileParser::FormatError, tr("Error parsing playlist: %1, expected count = %2"). - arg(line, QString::number(count))); - } - break; - } - if (m_readFlags == int(All)) { - emit newItem(m_item); - setCount(m_count + 1); - } - break; - case Footer: - if (line.startsWith(QLatin1String("Version"))) { - int version = getValue(lineIndex, line).toInt(); - if (version != 2) - emit error(QPlaylistFileParser::FormatError, QString(tr("Error parsing playlist at line[%1], expected version = 2")).arg(line)); - } - break; - } + emit newItem(expandToFullPath(root, value)); } - QString getValue(int lineIndex, const QString& line) { + QString getValue(const QString& line) { int start = line.indexOf('='); - if (start < 0) { - emit error(QPlaylistFileParser::FormatError, QString(tr("Error parsing playlist at line[%1]:%2")).arg(QString::number(lineIndex), line)); + if (start < 0) return QString(); - } return line.midRef(start + 1).trimmed().toString(); } - - void setCount(int count) { - m_count = count; - m_fileName = QStringLiteral("File%1").arg(count); - m_titleName = QStringLiteral("Title%1").arg(count); - m_lengthName = QStringLiteral("Length%1").arg(count); - m_item.clear(); - m_readFlags = 0; - } - -private: - State m_state; - int m_count; - QString m_titleName; - QString m_fileName; - QString m_lengthName; - QVariantMap m_item; - int m_readFlags; }; } diff --git a/src/multimedia/qmediaopenglhelper_p.h b/src/multimedia/qmediaopenglhelper_p.h index d42dfba42..0dbd79d47 100644 --- a/src/multimedia/qmediaopenglhelper_p.h +++ b/src/multimedia/qmediaopenglhelper_p.h @@ -66,7 +66,7 @@ inline bool QMediaOpenGLHelper::isANGLE() #else bool isANGLE = false; -# if defined(Q_OS_WIN) && (defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC)) +# if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && (defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC)) if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { // Although unlikely, technically LibGLES could mean a non-ANGLE EGL/GLES2 implementation too. // Verify that it is indeed ANGLE. @@ -98,7 +98,7 @@ inline bool QMediaOpenGLHelper::isANGLE() # endif // QT_OPENGL_ES_2_ANGLE_STATIC } -# endif // Q_OS_WIN && (QT_OPENGL_ES_2 || QT_OPENGL_DYNAMIC) +# endif // Q_OS_WIN && !Q_OS_WINCE && (QT_OPENGL_ES_2 || QT_OPENGL_DYNAMIC) return isANGLE; #endif // Q_OS_WINRT diff --git a/src/plugins/alsa/qalsaaudioinput.cpp b/src/plugins/alsa/qalsaaudioinput.cpp index bb0182a3e..adaa41c6e 100644 --- a/src/plugins/alsa/qalsaaudioinput.cpp +++ b/src/plugins/alsa/qalsaaudioinput.cpp @@ -760,7 +760,7 @@ qint64 QAlsaAudioInput::elapsedUSecs() const if (deviceState == QAudio::StoppedState) return 0; - return clockStamp.elapsed()*1000; + return clockStamp.elapsed() * qint64(1000); } void QAlsaAudioInput::reset() diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp index 07dd17362..fde2bc9bf 100644 --- a/src/plugins/alsa/qalsaaudiooutput.cpp +++ b/src/plugins/alsa/qalsaaudiooutput.cpp @@ -774,7 +774,7 @@ qint64 QAlsaAudioOutput::elapsedUSecs() const if (deviceState == QAudio::StoppedState) return 0; - return clockStamp.elapsed()*1000; + return clockStamp.elapsed() * qint64(1000); } void QAlsaAudioOutput::reset() diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java index ade2517d2..5e6630de8 100644 --- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java @@ -83,23 +83,6 @@ public class QtAndroidMediaPlayer private volatile int mState = State.Uninitialized; - private class ProgressWatcher - implements Runnable - { - @Override - public void run() - { - try { - while ((mState & (State.Started)) != 0) { - onProgressUpdateNative(getCurrentPosition(), mID); - Thread.sleep(1000); - } - } catch (final InterruptedException e) { - // Ignore - } - } - } - /** * MediaPlayer OnErrorListener */ @@ -257,8 +240,6 @@ public class QtAndroidMediaPlayer try { mMediaPlayer.start(); setState(State.Started); - Thread progressThread = new Thread(new ProgressWatcher()); - progressThread.start(); } catch (final IllegalStateException e) { Log.d(TAG, "" + e.getMessage()); } @@ -309,7 +290,6 @@ public class QtAndroidMediaPlayer try { mMediaPlayer.seekTo(msec); - onProgressUpdateNative(msec, mID); } catch (final IllegalStateException e) { Log.d(TAG, "" + e.getMessage()); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 47dd94ccd..eae09c64f 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -37,6 +37,36 @@ QT_BEGIN_NAMESPACE +class StateChangeNotifier +{ +public: + StateChangeNotifier(QAndroidMediaPlayerControl *mp) + : mControl(mp) + , mPreviousState(mp->state()) + , mPreviousMediaStatus(mp->mediaStatus()) + { + ++mControl->mActiveStateChangeNotifiers; + } + + ~StateChangeNotifier() + { + if (--mControl->mActiveStateChangeNotifiers) + return; + + if (mPreviousState != mControl->state()) + Q_EMIT mControl->stateChanged(mControl->state()); + + if (mPreviousMediaStatus != mControl->mediaStatus()) + Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus()); + } + +private: + QAndroidMediaPlayerControl *mControl; + QMediaPlayer::State mPreviousState; + QMediaPlayer::MediaStatus mPreviousMediaStatus; +}; + + QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent) : QMediaPlayerControl(parent), mMediaPlayer(new AndroidMediaPlayer), @@ -55,7 +85,9 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent) mPendingPosition(-1), mPendingSetMedia(false), mPendingVolume(-1), - mPendingMute(-1) + mPendingMute(-1), + mReloadingMedia(false), + mActiveStateChangeNotifiers(0) { connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)), this,SLOT(onBufferingChanged(qint32))); @@ -107,17 +139,14 @@ qint64 QAndroidMediaPlayerControl::position() const if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia) return duration(); - if ((mState & (AndroidMediaPlayer::Idle - | AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Prepared + if ((mState & (AndroidMediaPlayer::Prepared | AndroidMediaPlayer::Started | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::Stopped - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - return (mPendingPosition == -1) ? 0 : mPendingPosition; + | AndroidMediaPlayer::PlaybackCompleted))) { + return mMediaPlayer->getCurrentPosition(); } - return (mCurrentState == QMediaPlayer::StoppedState) ? 0 : mMediaPlayer->getCurrentPosition(); + return (mPendingPosition == -1) ? 0 : mPendingPosition; } void QAndroidMediaPlayerControl::setPosition(qint64 position) @@ -127,24 +156,25 @@ void QAndroidMediaPlayerControl::setPosition(qint64 position) const int seekPosition = (position > INT_MAX) ? INT_MAX : position; - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - if (mPendingPosition != seekPosition) { - mPendingPosition = seekPosition; - Q_EMIT positionChanged(seekPosition); - } + if (seekPosition == this->position()) return; - } + + StateChangeNotifier notifier(this); if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia) setMediaStatus(QMediaPlayer::LoadedMedia); - mMediaPlayer->seekTo(seekPosition); + if ((mState & (AndroidMediaPlayer::Prepared + | AndroidMediaPlayer::Started + | AndroidMediaPlayer::Paused + | AndroidMediaPlayer::PlaybackCompleted)) == 0) { + mPendingPosition = seekPosition; + } else { + mMediaPlayer->seekTo(seekPosition); - if (mPendingPosition != -1) { - mPendingPosition = -1; + if (mPendingPosition != -1) { + mPendingPosition = -1; + } } Q_EMIT positionChanged(seekPosition); @@ -275,9 +305,11 @@ const QIODevice *QAndroidMediaPlayerControl::mediaStream() const void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, QIODevice *stream) { - const bool reloading = (mMediaContent == mediaContent); + StateChangeNotifier notifier(this); + + mReloadingMedia = (mMediaContent == mediaContent); - if (!reloading) { + if (!mReloadingMedia) { mMediaContent = mediaContent; mMediaStream = stream; } @@ -286,41 +318,45 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0) mMediaPlayer->release(); + QString mediaPath; + if (mediaContent.isNull()) { setMediaStatus(QMediaPlayer::NoMedia); - return; - } - - if (mVideoOutput && !mVideoOutput->isReady()) { - // if a video output is set but the video texture is not ready, delay loading the media - // since it can cause problems on some hardware - mPendingSetMedia = true; - return; - } - - const QUrl url = mediaContent.canonicalUrl(); - QString mediaPath; - if (url.scheme() == QLatin1String("qrc")) { - const QString path = url.toString().mid(3); - mTempFile.reset(QTemporaryFile::createNativeFile(path)); - if (!mTempFile.isNull()) - mediaPath = QStringLiteral("file://") + mTempFile->fileName(); } else { - mediaPath = url.toString(); - } + if (mVideoOutput && !mVideoOutput->isReady()) { + // if a video output is set but the video texture is not ready, delay loading the media + // since it can cause problems on some hardware + mPendingSetMedia = true; + return; + } - if (mVideoSize.isValid() && mVideoOutput) - mVideoOutput->setVideoSize(mVideoSize); + const QUrl url = mediaContent.canonicalUrl(); + if (url.scheme() == QLatin1String("qrc")) { + const QString path = url.toString().mid(3); + mTempFile.reset(QTemporaryFile::createNativeFile(path)); + if (!mTempFile.isNull()) + mediaPath = QStringLiteral("file://") + mTempFile->fileName(); + } else { + mediaPath = url.toString(); + } - if ((mMediaPlayer->display() == 0) && mVideoOutput) - mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - mMediaPlayer->setDataSource(mediaPath); - mMediaPlayer->prepareAsync(); + if (mVideoSize.isValid() && mVideoOutput) + mVideoOutput->setVideoSize(mVideoSize); - if (!reloading) + if ((mMediaPlayer->display() == 0) && mVideoOutput) + mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); + mMediaPlayer->setDataSource(mediaPath); + mMediaPlayer->prepareAsync(); + } + + if (!mReloadingMedia) { Q_EMIT mediaChanged(mMediaContent); + Q_EMIT actualMediaLocationChanged(mediaPath); + } resetBufferingProgress(); + + mReloadingMedia = false; } void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) @@ -344,6 +380,8 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) void QAndroidMediaPlayerControl::play() { + StateChangeNotifier notifier(this); + // We need to prepare the mediaplayer again. if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isNull()) { setMedia(mMediaContent, mMediaStream); @@ -364,6 +402,8 @@ void QAndroidMediaPlayerControl::play() void QAndroidMediaPlayerControl::pause() { + StateChangeNotifier notifier(this); + setState(QMediaPlayer::PausedState); if ((mState & (AndroidMediaPlayer::Started @@ -378,6 +418,8 @@ void QAndroidMediaPlayerControl::pause() void QAndroidMediaPlayerControl::stop() { + StateChangeNotifier notifier(this); + setState(QMediaPlayer::StoppedState); if ((mState & (AndroidMediaPlayer::Prepared @@ -395,6 +437,8 @@ void QAndroidMediaPlayerControl::stop() void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra) { + StateChangeNotifier notifier(this); + Q_UNUSED(extra); switch (what) { case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN: @@ -426,6 +470,8 @@ void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra) void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra) { + StateChangeNotifier notifier(this); + QString errorString; QMediaPlayer::Error error = QMediaPlayer::ResourceError; @@ -478,6 +524,8 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra) void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent) { + StateChangeNotifier notifier(this); + mBuffering = percent != 100; mBufferPercent = percent; @@ -509,6 +557,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state) return; } + StateChangeNotifier notifier(this); + mState = state; switch (mState) { case AndroidMediaPlayer::Idle: @@ -516,7 +566,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state) case AndroidMediaPlayer::Initialized: break; case AndroidMediaPlayer::Preparing: - setMediaStatus(QMediaPlayer::LoadingMedia); + if (!mReloadingMedia) + setMediaStatus(QMediaPlayer::LoadingMedia); break; case AndroidMediaPlayer::Prepared: setMediaStatus(QMediaPlayer::LoadedMedia); @@ -537,6 +588,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state) } else { setMediaStatus(QMediaPlayer::BufferedMedia); } + Q_EMIT positionChanged(position()); break; case AndroidMediaPlayer::Paused: setState(QMediaPlayer::PausedState); @@ -545,27 +597,32 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state) setState(QMediaPlayer::StoppedState); setMediaStatus(QMediaPlayer::UnknownMediaStatus); mMediaPlayer->release(); + Q_EMIT positionChanged(0); break; case AndroidMediaPlayer::Stopped: setState(QMediaPlayer::StoppedState); setMediaStatus(QMediaPlayer::LoadedMedia); - setPosition(0); + Q_EMIT positionChanged(0); break; case AndroidMediaPlayer::PlaybackCompleted: setState(QMediaPlayer::StoppedState); - setPosition(0); setMediaStatus(QMediaPlayer::EndOfMedia); break; case AndroidMediaPlayer::Uninitialized: - // reset some properties - resetBufferingProgress(); - mPendingPosition = -1; - mPendingSetMedia = false; - mPendingState = -1; - - setAudioAvailable(false); - setVideoAvailable(false); - setSeekable(true); + // reset some properties (unless we reload the same media) + if (!mReloadingMedia) { + resetBufferingProgress(); + mPendingPosition = -1; + mPendingSetMedia = false; + mPendingState = -1; + + Q_EMIT durationChanged(0); + Q_EMIT positionChanged(0); + + setAudioAvailable(false); + setVideoAvailable(false); + setSeekable(true); + } break; default: break; @@ -597,7 +654,6 @@ void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state) return; mCurrentState = state; - Q_EMIT stateChanged(mCurrentState); } void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status) @@ -605,14 +661,13 @@ void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status if (mCurrentMediaStatus == status) return; + mCurrentMediaStatus = status; + if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) Q_EMIT durationChanged(0); if (status == QMediaPlayer::EndOfMedia) - Q_EMIT durationChanged(duration()); - - mCurrentMediaStatus = status; - Q_EMIT mediaStatusChanged(mCurrentMediaStatus); + Q_EMIT positionChanged(position()); updateBufferStatus(); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index 86d99e822..64b88f49e 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -72,6 +72,7 @@ public: Q_SIGNALS: void metaDataUpdated(); + void actualMediaLocationChanged(const QString &url); public Q_SLOTS: void setPosition(qint64 position) Q_DECL_OVERRIDE; @@ -110,7 +111,9 @@ private: bool mPendingSetMedia; int mPendingVolume; int mPendingMute; + bool mReloadingMedia; QScopedPointer<QTemporaryFile> mTempFile; + int mActiveStateChangeNotifiers; void setState(QMediaPlayer::State state); void setMediaStatus(QMediaPlayer::MediaStatus status); @@ -121,6 +124,8 @@ private: void resetBufferingProgress(); void flushPendingStates(); void updateBufferStatus(); + + friend class StateChangeNotifier; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 00e8b6184..05cee3b9d 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) { mMediaControl = new QAndroidMediaPlayerControl; mMetadataControl = new QAndroidMetaDataReaderControl; - connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)), - mMetadataControl, SLOT(onMediaChanged(QMediaContent))); + connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)), + mMetadataControl, SLOT(onMediaChanged(QString))); connect(mMediaControl, SIGNAL(metaDataUpdated()), mMetadataControl, SLOT(onUpdateMetaData())); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index a3598228c..a9c87488a 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const return m_metadata.keys(); } -void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media) +void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url) { if (!m_retriever) return; - m_mediaContent = media; + m_mediaLocation = url; updateData(); } void QAndroidMetaDataReaderControl::onUpdateMetaData() { - if (!m_retriever || m_mediaContent.isNull()) + if (!m_retriever || m_mediaLocation.isEmpty()) return; updateData(); @@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData() { m_metadata.clear(); - if (!m_mediaContent.isNull()) { - if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) { + if (!m_mediaLocation.isEmpty()) { + if (m_retriever->setDataSource(m_mediaLocation)) { QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType); if (!mimeType.isNull()) m_metadata.insert(QMediaMetaData::MediaType, mimeType); diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h index 72df6eecc..f8720b77a 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -54,13 +54,13 @@ public: QStringList availableMetaData() const Q_DECL_OVERRIDE; public Q_SLOTS: - void onMediaChanged(const QMediaContent &media); + void onMediaChanged(const QString &url); void onUpdateMetaData(); private: void updateData(); - QMediaContent m_mediaContent; + QString m_mediaLocation; bool m_available; QVariantMap m_metadata; diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp index a01aacf5e..df5345053 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp @@ -35,9 +35,24 @@ #include <QtCore/private/qjnihelpers_p.h> #include <QtCore/private/qjni_p.h> +#include <QtCore/QUrl> +#include <qdebug.h> QT_BEGIN_NAMESPACE +static bool exceptionCheckAndClear(JNIEnv *env) +{ + if (Q_UNLIKELY(env->ExceptionCheck())) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif // QT_DEBUG + env->ExceptionClear(); + return true; + } + + return false; +} + AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever() { m_metadataRetriever = QJNIObjectPrivate("android/media/MediaMetadataRetriever"); @@ -68,55 +83,105 @@ void AndroidMediaMetadataRetriever::release() m_metadataRetriever.callMethod<void>("release"); } -bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url) +bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) { if (!m_metadataRetriever.isValid()) return false; QJNIEnvironmentPrivate env; + QUrl url(urlString); - bool loaded = false; + if (url.isLocalFile()) { // also includes qrc files (copied to a temp file) + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path()); + QJNIObjectPrivate fileInputStream("java/io/FileInputStream", + "(Ljava/lang/String;)V", + string.object()); - QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString()); + if (exceptionCheckAndClear(env)) + return false; + + QJNIObjectPrivate fd = fileInputStream.callObjectMethod("getFD", + "()Ljava/io/FileDescriptor;"); + if (exceptionCheckAndClear(env)) { + fileInputStream.callMethod<void>("close"); + exceptionCheckAndClear(env); + return false; + } - QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri", - "parse", - "(Ljava/lang/String;)Landroid/net/Uri;", - string.object()); - if (env->ExceptionCheck()) { - env->ExceptionClear(); - } else { m_metadataRetriever.callMethod<void>("setDataSource", - "(Landroid/content/Context;Landroid/net/Uri;)V", - QtAndroidPrivate::activity(), - uri.object()); - if (env->ExceptionCheck()) - env->ExceptionClear(); - else - loaded = true; - } + "(Ljava/io/FileDescriptor;)V", + fd.object()); + + bool ok = !exceptionCheckAndClear(env); + + fileInputStream.callMethod<void>("close"); + exceptionCheckAndClear(env); + + if (!ok) + return false; + } else if (url.scheme() == QLatin1String("assets")) { + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path().mid(1)); // remove first '/' + QJNIObjectPrivate activity(QtAndroidPrivate::activity()); + QJNIObjectPrivate assetManager = activity.callObjectMethod("getAssets", + "()Landroid/content/res/AssetManager;"); + QJNIObjectPrivate assetFd = assetManager.callObjectMethod("openFd", + "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;", + string.object()); + if (exceptionCheckAndClear(env)) + return false; + + QJNIObjectPrivate fd = assetFd.callObjectMethod("getFileDescriptor", + "()Ljava/io/FileDescriptor;"); + if (exceptionCheckAndClear(env)) { + assetFd.callMethod<void>("close"); + exceptionCheckAndClear(env); + return false; + } - return loaded; -} + m_metadataRetriever.callMethod<void>("setDataSource", + "(Ljava/io/FileDescriptor;JJ)V", + fd.object(), + assetFd.callMethod<jlong>("getStartOffset"), + assetFd.callMethod<jlong>("getLength")); -bool AndroidMediaMetadataRetriever::setDataSource(const QString &path) -{ - if (!m_metadataRetriever.isValid()) - return false; + bool ok = !exceptionCheckAndClear(env); - QJNIEnvironmentPrivate env; + assetFd.callMethod<void>("close"); + exceptionCheckAndClear(env); - bool loaded = false; + if (!ok) + return false; + } else if (QtAndroidPrivate::androidSdkVersion() >= 14) { + // On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate hash("java/util/HashMap"); - m_metadataRetriever.callMethod<void>("setDataSource", - "(Ljava/lang/String;)V", - QJNIObjectPrivate::fromString(path).object()); - if (env->ExceptionCheck()) - env->ExceptionClear(); - else - loaded = true; + m_metadataRetriever.callMethod<void>("setDataSource", + "(Ljava/lang/String;Ljava/util/Map;)V", + string.object(), + hash.object()); + if (exceptionCheckAndClear(env)) + return false; + } else { + // While on API levels < 14, only setDataSource(Context, Uri) is available and works for + // remote media... + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri", + "parse", + "(Ljava/lang/String;)Landroid/net/Uri;", + string.object()); + if (exceptionCheckAndClear(env)) + return false; + + m_metadataRetriever.callMethod<void>("setDataSource", + "(Landroid/content/Context;Landroid/net/Uri;)V", + QtAndroidPrivate::activity(), + uri.object()); + if (exceptionCheckAndClear(env)) + return false; + } - return loaded; + return true; } QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h index 1cf0bcc91..c1633580f 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h @@ -35,7 +35,6 @@ #define ANDROIDMEDIAMETADATARETRIEVER_H #include <QtCore/private/qjni_p.h> -#include <qurl.h> QT_BEGIN_NAMESPACE @@ -73,8 +72,7 @@ public: QString extractMetadata(MetadataKey key); void release(); - bool setDataSource(const QUrl &url); - bool setDataSource(const QString &path); + bool setDataSource(const QString &url); private: QJNIObjectPrivate m_metadataRetriever; diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index 20792000a..77a145ba3 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -142,9 +142,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name) void AVFCameraService::releaseControl(QMediaControl *control) { if (m_videoOutput == control) { - m_videoOutput = 0; m_session->setVideoOutput(0); - delete control; + delete m_videoOutput; + m_videoOutput = 0; } AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control); if (videoProbe) { diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm index e5549803f..bb75adb8b 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm @@ -118,14 +118,15 @@ void AVFMediaPlayerService::releaseControl(QMediaControl *control) #ifdef QT_DEBUG_AVF qDebug() << Q_FUNC_INFO << control; #endif -#if defined(Q_OS_OSX) if (m_videoOutput == control) { +#if defined(Q_OS_OSX) AVFVideoRendererControl *renderControl = qobject_cast<AVFVideoRendererControl*>(m_videoOutput); if (renderControl) renderControl->setSurface(0); +#endif m_videoOutput = 0; m_session->setVideoOutput(0); + delete control; } -#endif } diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h index 09ac4b5b3..5157a8571 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h @@ -97,8 +97,6 @@ public Q_SLOTS: void processPositionChange(); void processMediaLoadError(); - void processCurrentItemChanged(); - Q_SIGNALS: void positionChanged(qint64 position); void durationChanged(qint64 duration); @@ -148,6 +146,9 @@ private: QByteArray rawData; }; + void setAudioAvailable(bool available); + void setVideoAvailable(bool available); + AVFMediaPlayerService *m_service; AVFVideoOutput *m_videoOutput; diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm index a73974ccd..73e9d764e 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -70,15 +70,11 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe AVPlayerItem *m_playerItem; AVPlayerLayer *m_playerLayer; NSURL *m_URL; - bool m_audioAvailable; - bool m_videoAvailable; } @property (readonly, getter=player) AVPlayer* m_player; @property (readonly, getter=playerItem) AVPlayerItem* m_playerItem; @property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer; -@property (readonly, getter=audioAvailable) bool m_audioAvailable; -@property (readonly, getter=videoAvailable) bool m_videoAvailable; @property (readonly, getter=session) AVFMediaPlayerSession* m_session; - (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session; @@ -96,7 +92,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe @implementation AVFMediaPlayerSessionObserver -@synthesize m_player, m_playerItem, m_playerLayer, m_audioAvailable, m_videoAvailable, m_session; +@synthesize m_player, m_playerItem, m_playerLayer, m_session; - (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session { @@ -186,18 +182,6 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe return; } - m_audioAvailable = false; - m_videoAvailable = false; - - //Check each track of asset for audio and video content - NSArray *tracks = [asset tracks]; - for (AVAssetTrack *track in tracks) { - if ([track hasMediaCharacteristic:AVMediaCharacteristicAudible]) - m_audioAvailable = true; - if ([track hasMediaCharacteristic:AVMediaCharacteristicVisual]) - m_videoAvailable = true; - } - //At this point we're ready to set up for playback of the asset. //Stop observing our prior AVPlayerItem, if we have one. if (m_playerItem) @@ -258,18 +242,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player]; [m_playerLayer retain]; m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - - //Get the native size of the new item, and reset the bounds of the player layer - AVAsset *asset = m_playerItem.asset; - if (asset) { - NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count]) { - AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; - m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); - m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height); - } - } - + m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); } //Observe the AVPlayer "currentItem" property to find out when any @@ -322,7 +295,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe //AVPlayerItem "status" property value observer. if (context == AVFMediaPlayerSessionObserverStatusObservationContext) { - AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; + AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue]; switch (status) { //Indicates that the status of the player is not yet known because @@ -366,24 +339,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe { AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey]; if (m_playerItem != newPlayerItem) - { m_playerItem = newPlayerItem; - - //Get the native size of the new item, and reset the bounds of the player layer - //AVAsset *asset = m_playerItem.asset; - AVAsset *asset = [m_playerItem asset]; - if (asset) { - NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count]) { - AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; - m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); - m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height); - } - } - - } - if (self.session) - QMetaObject::invokeMethod(m_session, "processCurrentItemChanged", Qt::AutoConnection); } else { @@ -513,6 +469,9 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st m_resources = content; m_mediaStream = stream; + setAudioAvailable(false); + setVideoAvailable(false); + QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus; if (content.isNull() || content.canonicalUrl().isEmpty()) { @@ -582,14 +541,32 @@ bool AVFMediaPlayerSession::isMuted() const return m_muted; } +void AVFMediaPlayerSession::setAudioAvailable(bool available) +{ + if (m_audioAvailable == available) + return; + + m_audioAvailable = available; + Q_EMIT audioAvailableChanged(available); +} + bool AVFMediaPlayerSession::isAudioAvailable() const { - return [(AVFMediaPlayerSessionObserver*)m_observer audioAvailable]; + return m_audioAvailable; +} + +void AVFMediaPlayerSession::setVideoAvailable(bool available) +{ + if (m_videoAvailable == available) + return; + + m_videoAvailable = available; + Q_EMIT videoAvailableChanged(available); } bool AVFMediaPlayerSession::isVideoAvailable() const { - return [(AVFMediaPlayerSessionObserver*)m_observer videoAvailable]; + return m_videoAvailable; } bool AVFMediaPlayerSession::isSeekable() const @@ -802,16 +779,41 @@ void AVFMediaPlayerSession::processLoadStateChange() bool isPlaying = (m_state != QMediaPlayer::StoppedState); if (currentStatus == AVPlayerStatusReadyToPlay) { + AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem]; + if (playerItem) { + // Check each track for audio and video content + AVAssetTrack *videoTrack = nil; + NSArray *tracks = playerItem.tracks; + for (AVPlayerItemTrack *track in tracks) { + AVAssetTrack *assetTrack = track.assetTrack; + if (assetTrack) { + if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) + setAudioAvailable(true); + if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) { + setVideoAvailable(true); + if (!videoTrack) + videoTrack = assetTrack; + } + } + } + + // Get the native size of the video, and reset the bounds of the player layer + AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer]; + if (videoTrack && playerLayer) { + playerLayer.bounds = CGRectMake(0.0f, 0.0f, + videoTrack.naturalSize.width, + videoTrack.naturalSize.height); + + if (m_videoOutput && m_state != QMediaPlayer::StoppedState) { + m_videoOutput->setLayer(playerLayer); + } + } + } + qint64 currentDuration = duration(); if (m_duration != currentDuration) Q_EMIT durationChanged(m_duration = currentDuration); - if (m_audioAvailable != isAudioAvailable()) - Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable); - - if (m_videoAvailable != isVideoAvailable()) - Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable); - newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia; if (m_state == QMediaPlayer::PlayingState && [(AVFMediaPlayerSessionObserver*)m_observer player]) { @@ -835,17 +837,3 @@ void AVFMediaPlayerSession::processMediaLoadError() Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia); Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState); } - -void AVFMediaPlayerSession::processCurrentItemChanged() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - - AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer]; - - if (m_videoOutput && m_state != QMediaPlayer::StoppedState) { - m_videoOutput->setLayer(playerLayer); - } - -} diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm index d4fa7c4c6..2893921f3 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm @@ -64,8 +64,10 @@ AVFVideoWidget::~AVFVideoWidget() qDebug() << Q_FUNC_INFO; #endif - if (m_playerLayer) + if (m_playerLayer) { + [m_playerLayer removeFromSuperlayer]; [m_playerLayer release]; + } } QSize AVFVideoWidget::sizeHint() const diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm index 17fc94de7..8e96d732f 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm @@ -61,8 +61,10 @@ AVFVideoWindowControl::AVFVideoWindowControl(QObject *parent) AVFVideoWindowControl::~AVFVideoWindowControl() { - if (m_playerLayer) + if (m_playerLayer) { + [m_playerLayer removeFromSuperlayer]; [m_playerLayer release]; + } } WId AVFVideoWindowControl::winId() const diff --git a/src/plugins/coreaudio/coreaudiodeviceinfo.mm b/src/plugins/coreaudio/coreaudiodeviceinfo.mm index ac41a310c..2faeac8c9 100644 --- a/src/plugins/coreaudio/coreaudiodeviceinfo.mm +++ b/src/plugins/coreaudio/coreaudiodeviceinfo.mm @@ -45,6 +45,7 @@ # include "coreaudiosessionmanager.h" #endif +#include <QtCore/QDataStream> #include <QtCore/QDebug> #include <QtCore/QSet> diff --git a/src/plugins/coreaudio/coreaudioinput.h b/src/plugins/coreaudio/coreaudioinput.h index 64691eb36..533b7fef4 100644 --- a/src/plugins/coreaudio/coreaudioinput.h +++ b/src/plugins/coreaudio/coreaudioinput.h @@ -38,6 +38,7 @@ #include <CoreAudio/CoreAudioTypes.h> #include <AudioToolbox/AudioToolbox.h> +#include <QtCore/QIODevice> #include <QtCore/QWaitCondition> #include <QtCore/QMutex> #include <QtCore/QTimer> diff --git a/src/plugins/coreaudio/coreaudioinput.mm b/src/plugins/coreaudio/coreaudioinput.mm index a0b9e9d25..972f959f5 100644 --- a/src/plugins/coreaudio/coreaudioinput.mm +++ b/src/plugins/coreaudio/coreaudioinput.mm @@ -52,6 +52,7 @@ #endif #include <QtMultimedia/private/qaudiohelpers_p.h> +#include <QtCore/QDataStream> #include <QtCore/QDebug> QT_BEGIN_NAMESPACE diff --git a/src/plugins/coreaudio/coreaudiooutput.h b/src/plugins/coreaudio/coreaudiooutput.h index 84083acea..23b229379 100644 --- a/src/plugins/coreaudio/coreaudiooutput.h +++ b/src/plugins/coreaudio/coreaudiooutput.h @@ -41,6 +41,7 @@ #include <AudioUnit/AudioUnit.h> #include <CoreAudio/CoreAudioTypes.h> +#include <QtCore/QIODevice> #include <QtCore/QWaitCondition> #include <QtCore/QMutex> diff --git a/src/plugins/coreaudio/coreaudiooutput.mm b/src/plugins/coreaudio/coreaudiooutput.mm index e5e1c65e5..7bb5c0e15 100644 --- a/src/plugins/coreaudio/coreaudiooutput.mm +++ b/src/plugins/coreaudio/coreaudiooutput.mm @@ -43,6 +43,7 @@ #include "coreaudiodeviceinfo.h" #include "coreaudioutils.h" +#include <QtCore/QDataStream> #include <QtCore/QTimer> #include <QtCore/QDebug> @@ -698,14 +699,14 @@ void CoreAudioOutput::audioThreadStop() { stopTimers(); if (m_audioThreadState.testAndSetAcquire(Running, Stopped)) - m_threadFinished.wait(&m_mutex); + m_threadFinished.wait(&m_mutex, 500); } void CoreAudioOutput::audioThreadDrain() { stopTimers(); if (m_audioThreadState.testAndSetAcquire(Running, Draining)) - m_threadFinished.wait(&m_mutex); + m_threadFinished.wait(&m_mutex, 500); } void CoreAudioOutput::audioDeviceStop() diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp index fe8098a02..865a764a8 100644 --- a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp @@ -117,4 +117,28 @@ GstEncodingProfile *CameraBinAudioEncoder::createProfile() return profile; } +void CameraBinAudioEncoder::applySettings(GstElement *encoder) +{ + GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder); + const char * const name = gst_plugin_feature_get_name( + GST_PLUGIN_FEATURE(gst_element_get_factory(encoder))); + + const bool isVorbis = qstrcmp(name, "vorbisenc") == 0; + + const int bitRate = m_actualAudioSettings.bitRate(); + if (!isVorbis && bitRate == -1) { + // Bit rate is invalid, don't evaluate the remaining conditions unless the encoder is + // vorbisenc which is known to accept -1 as an unspecified bitrate. + } else if (g_object_class_find_property(objectClass, "bitrate")) { + g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL); + } else if (g_object_class_find_property(objectClass, "target-bitrate")) { + g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL); + } + + if (isVorbis) { + static const double qualities[] = { 0.1, 0.3, 0.5, 0.7, 1.0 }; + g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualAudioSettings.quality()], NULL); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h index 46f812a67..f71436552 100644 --- a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h +++ b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h @@ -78,6 +78,8 @@ public: GstEncodingProfile *createProfile(); + void applySettings(GstElement *element); + Q_SIGNALS: void settingsChanged(); diff --git a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp index 4e7630b72..51024b7d9 100644 --- a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp @@ -38,13 +38,9 @@ #include "camerabinserviceplugin.h" - #include "camerabinservice.h" #include <private/qgstutils_p.h> -#include <private/qcore_unix_p.h> -#include <linux/videodev2.h> - QT_BEGIN_NAMESPACE template <typename T, int N> static int lengthOf(const T(&)[N]) { return N; } diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index 420ce5853..019783971 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -98,6 +98,8 @@ #define CAPTURE_START "start-capture" #define CAPTURE_STOP "stop-capture" +#define FILESINK_BIN_NAME "videobin-filesink" + #define CAMERABIN_IMAGE_MODE 1 #define CAMERABIN_VIDEO_MODE 2 @@ -133,6 +135,7 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa m_capsFilter(0), m_fileSink(0), m_audioEncoder(0), + m_videoEncoder(0), m_muxer(0) { if (m_sourceFactory) @@ -140,6 +143,8 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa m_camerabin = gst_element_factory_make("camerabin2", "camerabin2"); g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this); + g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this); + g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this); qt_gst_object_ref_sink(m_camerabin); m_bus = gst_element_get_bus(m_camerabin); @@ -344,6 +349,9 @@ void CameraBinSession::setupCaptureResolution() } else { g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL); } + + if (m_videoEncoder) + m_videoEncodeControl->applySettings(m_videoEncoder); } void CameraBinSession::setAudioCaptureCaps() @@ -370,6 +378,9 @@ void CameraBinSession::setAudioCaptureCaps() GstCaps *caps = gst_caps_new_full(structure, NULL); g_object_set(G_OBJECT(m_camerabin), AUDIO_CAPTURE_CAPS_PROPERTY, caps, NULL); gst_caps_unref(caps); + + if (m_audioEncoder) + m_audioEncodeControl->applySettings(m_audioEncoder); } GstElement *CameraBinSession::buildCameraSource() @@ -712,13 +723,19 @@ void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d) qint64 CameraBinSession::duration() const { - GstFormat format = GST_FORMAT_TIME; - gint64 duration = 0; + if (m_camerabin) { + GstElement *fileSink = gst_bin_get_by_name(GST_BIN(m_camerabin), FILESINK_BIN_NAME); + if (fileSink) { + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + bool ret = gst_element_query_position(fileSink, &format, &duration); + gst_object_unref(GST_OBJECT(fileSink)); + if (ret) + return duration / 1000000; + } + } - if ( m_camerabin && gst_element_query_position(m_camerabin, &format, &duration)) - return duration / 1000000; - else - return 0; + return 0; } bool CameraBinSession::isMuted() const @@ -1293,4 +1310,32 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate, return res; } +void CameraBinSession::elementAdded(GstBin *, GstElement *element, CameraBinSession *session) +{ + GstElementFactory *factory = gst_element_get_factory(element); + + if (GST_IS_BIN(element)) { + g_signal_connect(G_OBJECT(element), "element-added", G_CALLBACK(elementAdded), session); + g_signal_connect(G_OBJECT(element), "element-removed", G_CALLBACK(elementRemoved), session); + } else if (!factory) { + // no-op + } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER)) { + session->m_audioEncoder = element; + + session->m_audioEncodeControl->applySettings(element); + } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER)) { + session->m_videoEncoder = element; + + session->m_videoEncodeControl->applySettings(element); + } +} + +void CameraBinSession::elementRemoved(GstBin *, GstElement *element, CameraBinSession *session) +{ + if (element == session->m_audioEncoder) + session->m_audioEncoder = 0; + else if (element == session->m_videoEncoder) + session->m_videoEncoder = 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h index 42ed0e988..7e8a46dee 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.h +++ b/src/plugins/gstreamer/camerabin/camerabinsession.h @@ -190,6 +190,9 @@ private: void setAudioCaptureCaps(); static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d); + static void elementAdded(GstBin *bin, GstElement *element, CameraBinSession *session); + static void elementRemoved(GstBin *bin, GstElement *element, CameraBinSession *session); + QUrl m_sink; QUrl m_actualSink; bool m_recordingActive; @@ -241,6 +244,7 @@ private: GstElement *m_capsFilter; GstElement *m_fileSink; GstElement *m_audioEncoder; + GstElement *m_videoEncoder; GstElement *m_muxer; public: diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp index 228054318..554437047 100644 --- a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp @@ -175,4 +175,46 @@ GstEncodingProfile *CameraBinVideoEncoder::createProfile() return (GstEncodingProfile *)profile; } +void CameraBinVideoEncoder::applySettings(GstElement *encoder) +{ + GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder); + const char * const name = gst_plugin_feature_get_name( + GST_PLUGIN_FEATURE(gst_element_get_factory(encoder))); + + const int bitRate = m_actualVideoSettings.bitRate(); + if (bitRate == -1) { + // Bit rate is invalid, don't evaluate the remaining conditions. + } else if (g_object_class_find_property(objectClass, "bitrate")) { + g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL); + } else if (g_object_class_find_property(objectClass, "target-bitrate")) { + g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL); + } + + if (qstrcmp(name, "theoraenc") == 0) { + static const int qualities[] = { 8, 16, 32, 45, 60 }; + g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualVideoSettings.quality()], NULL); + } else if (qstrncmp(name, "avenc_", 6) == 0) { + if (g_object_class_find_property(objectClass, "pass")) { + static const int modes[] = { 0, 2, 512, 1024 }; + g_object_set(G_OBJECT(encoder), "pass", modes[m_actualVideoSettings.encodingMode()], NULL); + } + if (g_object_class_find_property(objectClass, "quantizer")) { + static const double qualities[] = { 20, 8.0, 3.0, 2.5, 2.0 }; + g_object_set(G_OBJECT(encoder), "quantizer", qualities[m_actualVideoSettings.quality()], NULL); + } + } else if (qstrncmp(name, "omx", 3) == 0) { + if (!g_object_class_find_property(objectClass, "control-rate")) { + } else switch (m_actualVideoSettings.encodingMode()) { + case QMultimedia::ConstantBitRateEncoding: + g_object_set(G_OBJECT(encoder), "control-rate", 2, NULL); + break; + case QMultimedia::AverageBitRateEncoding: + g_object_set(G_OBJECT(encoder), "control-rate", 1, NULL); + break; + default: + g_object_set(G_OBJECT(encoder), "control-rate", 0, NULL); + } + } +} + QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h index 0838ba651..dabe098ab 100644 --- a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h +++ b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h @@ -76,6 +76,8 @@ public: GstEncodingProfile *createProfile(); + void applySettings(GstElement *encoder); + Q_SIGNALS: void settingsChanged(); diff --git a/src/plugins/gstreamer/mediacapture/mediacapture.pro b/src/plugins/gstreamer/mediacapture/mediacapture.pro index e8d039f8d..5baa0fd8f 100644 --- a/src/plugins/gstreamer/mediacapture/mediacapture.pro +++ b/src/plugins/gstreamer/mediacapture/mediacapture.pro @@ -15,7 +15,6 @@ HEADERS += $$PWD/qgstreamercaptureservice.h \ $$PWD/qgstreamerrecordercontrol.h \ $$PWD/qgstreamermediacontainercontrol.h \ $$PWD/qgstreamercameracontrol.h \ - $$PWD/qgstreamerv4l2input.h \ $$PWD/qgstreamercapturemetadatacontrol.h \ $$PWD/qgstreamerimagecapturecontrol.h \ $$PWD/qgstreamerimageencode.h \ @@ -28,7 +27,6 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \ $$PWD/qgstreamerrecordercontrol.cpp \ $$PWD/qgstreamermediacontainercontrol.cpp \ $$PWD/qgstreamercameracontrol.cpp \ - $$PWD/qgstreamerv4l2input.cpp \ $$PWD/qgstreamercapturemetadatacontrol.cpp \ $$PWD/qgstreamerimagecapturecontrol.cpp \ $$PWD/qgstreamerimageencode.cpp \ @@ -37,13 +35,18 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \ # Camera usage with gstreamer needs to have #CONFIG += use_gstreamer_camera -use_gstreamer_camera { -DEFINES += USE_GSTREAMER_CAMERA +use_gstreamer_camera:config_linux_v4l { + DEFINES += USE_GSTREAMER_CAMERA + + OTHER_FILES += \ + mediacapturecamera.json + + HEADERS += \ + $$PWD/qgstreamerv4l2input.h + SOURCES += \ + $$PWD/qgstreamerv4l2input.cpp -OTHER_FILES += \ - mediacapturecamera.json } else { -OTHER_FILES += \ - mediacapture.json + OTHER_FILES += \ + mediacapture.json } - diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp index 2e73797ee..97a165dca 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp @@ -40,9 +40,12 @@ #include "qgstreamerimageencode.h" #include "qgstreamercameracontrol.h" #include <private/qgstreamerbushelper_p.h> -#include "qgstreamerv4l2input.h" #include "qgstreamercapturemetadatacontrol.h" +#if defined(USE_GSTREAMER_CAMERA) +#include "qgstreamerv4l2input.h" +#endif + #include "qgstreamerimagecapturecontrol.h" #include <private/qgstreameraudioinputselector_p.h> #include <private/qgstreamervideoinputdevicecontrol_p.h> @@ -66,7 +69,9 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje m_cameraControl = 0; m_metaDataControl = 0; +#if defined(USE_GSTREAMER_CAMERA) m_videoInput = 0; +#endif m_audioInputSelector = 0; m_videoInputDevice = 0; @@ -82,6 +87,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this); } +#if defined(USE_GSTREAMER_CAMERA) if (service == Q_MEDIASERVICE_CAMERA) { m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this); m_cameraControl = new QGstreamerCameraControl(m_captureSession); @@ -103,6 +109,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje #endif m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession); } +#endif m_audioInputSelector = new QGstreamerAudioInputSelector(this); connect(m_audioInputSelector, SIGNAL(activeInputChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString))); diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h index 2b1a0dcca..7ff8ce253 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h @@ -70,7 +70,9 @@ private: QGstreamerCaptureSession *m_captureSession; QGstreamerCameraControl *m_cameraControl; +#if defined(USE_GSTREAMER_CAMERA) QGstreamerV4L2Input *m_videoInput; +#endif QGstreamerCaptureMetaDataControl *m_metaDataControl; QAudioInputSelectorControl *m_audioInputSelector; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp index e9c922e28..8ae3a78de 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp @@ -76,8 +76,10 @@ static const QGstreamerMetaDataKeyLookup *qt_gstreamerMetaDataKeys() // Music metadataKeys->insert(GST_TAG_ALBUM, QMediaMetaData::AlbumTitle); - metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::AlbumArtist); - metadataKeys->insert(GST_TAG_PERFORMER, QMediaMetaData::ContributingArtist); +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 25) + metadataKeys->insert(GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist); +#endif + metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::ContributingArtist); #if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) metadataKeys->insert(GST_TAG_COMPOSER, QMediaMetaData::Composer); #endif @@ -164,6 +166,11 @@ void QGstreamerMetaDataProvider::updateTags() } } + if (oldTags.isEmpty() != m_tags.isEmpty()) { + emit metaDataAvailableChanged(isMetaDataAvailable()); + changed = true; + } + if (changed) emit metaDataChanged(); } diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp index 2f7047f97..fed756ac9 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -60,7 +60,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio , m_currentState(QMediaPlayer::StoppedState) , m_mediaStatus(QMediaPlayer::NoMedia) , m_bufferProgress(-1) - , m_seekToStartPending(false) , m_pendingSeekPosition(-1) , m_setMediaPending(false) , m_stream(0) @@ -69,7 +68,7 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio Q_ASSERT(m_resources); connect(m_session, SIGNAL(positionChanged(qint64)), - this, SLOT(updatePosition(qint64))); + this, SIGNAL(positionChanged(qint64))); connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); connect(m_session, SIGNAL(mutedStateChanged(bool)), @@ -94,8 +93,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio this, SLOT(handleInvalidMedia())); connect(m_session, SIGNAL(playbackRateChanged(qreal)), this, SIGNAL(playbackRateChanged(qreal))); - connect(m_session, SIGNAL(seekableChanged(bool)), - this, SLOT(applyPendingSeek(bool))); connect(m_resources, SIGNAL(resourcesGranted()), SLOT(handleResourcesGranted())); //denied signal should be queued to have correct state update process, @@ -117,7 +114,7 @@ QMediaPlayerResourceSetInterface* QGstreamerPlayerControl::resources() const qint64 QGstreamerPlayerControl::position() const { - return m_seekToStartPending ? 0 : m_session->position(); + return m_pendingSeekPosition != -1 ? m_pendingSeekPosition : m_session->position(); } qint64 QGstreamerPlayerControl::duration() const @@ -183,15 +180,21 @@ void QGstreamerPlayerControl::setPosition(qint64 pos) if (m_mediaStatus == QMediaPlayer::EndOfMedia) { m_mediaStatus = QMediaPlayer::LoadedMedia; - m_seekToStartPending = true; } - if (m_session->isSeekable() && m_session->seek(pos)) { - m_seekToStartPending = false; - } else { + if (m_currentState == QMediaPlayer::StoppedState) { m_pendingSeekPosition = pos; - //don't display the first video frame since it's not what user requested. - m_session->showPrerollFrames(false); + emit positionChanged(m_pendingSeekPosition); + } else if (m_session->isSeekable()) { + m_session->showPrerollFrames(true); + m_session->seek(pos); + m_pendingSeekPosition = -1; + } else if (m_session->state() == QMediaPlayer::StoppedState) { + m_pendingSeekPosition = pos; + emit positionChanged(m_pendingSeekPosition); + } else if (m_pendingSeekPosition != -1) { + m_pendingSeekPosition = -1; + emit positionChanged(m_pendingSeekPosition); } popAndNotifyState(); @@ -239,26 +242,30 @@ void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState) } #endif + if (m_mediaStatus == QMediaPlayer::EndOfMedia && m_pendingSeekPosition == -1) { + m_pendingSeekPosition = 0; + } + if (!m_resources->isGranted()) m_resources->acquire(); if (m_resources->isGranted()) { - if (m_seekToStartPending) { + // show prerolled frame if switching from stopped state + if (m_pendingSeekPosition == -1) { + m_session->showPrerollFrames(true); + } else if (m_session->state() == QMediaPlayer::StoppedState) { + // Don't evaluate the next two conditions. + } else if (m_session->isSeekable()) { m_session->pause(); - if (!m_session->seek(0)) { - m_bufferProgress = -1; - m_session->stop(); - m_mediaStatus = QMediaPlayer::LoadingMedia; - } - m_seekToStartPending = false; + m_session->showPrerollFrames(true); + m_session->seek(m_pendingSeekPosition); + m_pendingSeekPosition = -1; + } else { + m_pendingSeekPosition = -1; } bool ok = false; - // show prerolled frame if switching from stopped state - if (newState != QMediaPlayer::StoppedState && m_currentState == QMediaPlayer::StoppedState && m_pendingSeekPosition == -1) - m_session->showPrerollFrames(true); - //To prevent displaying the first video frame when playback is resumed //the pipeline is paused instead of playing, seeked to requested position, //and after seeking is finished (position updated) playback is restarted @@ -305,7 +312,7 @@ void QGstreamerPlayerControl::stop() m_session->pause(); if (m_mediaStatus != QMediaPlayer::EndOfMedia) { - m_seekToStartPending = true; + m_pendingSeekPosition = 0; emit positionChanged(position()); } } @@ -343,7 +350,7 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * m_currentState = QMediaPlayer::StoppedState; QMediaContent oldMedia = m_currentResource; - m_pendingSeekPosition = -1; + m_pendingSeekPosition = 0; m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called m_setMediaPending = false; @@ -390,7 +397,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * m_currentResource = content; m_stream = stream; - m_seekToStartPending = false; QNetworkRequest request; @@ -462,8 +468,21 @@ void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state) { pushState(); - if (state == QMediaPlayer::StoppedState) + if (state == QMediaPlayer::StoppedState) { + m_session->showPrerollFrames(false); m_currentState = QMediaPlayer::StoppedState; + } + + if (state == QMediaPlayer::PausedState && m_currentState != QMediaPlayer::StoppedState) { + if (m_pendingSeekPosition != -1 && m_session->isSeekable()) { + m_session->showPrerollFrames(true); + m_session->seek(m_pendingSeekPosition); + } + m_pendingSeekPosition = -1; + + if (m_currentState == QMediaPlayer::PlayingState) + m_session->play(); + } updateMediaStatus(); @@ -512,7 +531,6 @@ void QGstreamerPlayerControl::processEOS() m_mediaStatus = QMediaPlayer::EndOfMedia; emit positionChanged(position()); m_session->endOfMediaReset(); - m_setMediaPending = true; if (m_currentState != QMediaPlayer::StoppedState) { m_currentState = QMediaPlayer::StoppedState; @@ -549,17 +567,12 @@ void QGstreamerPlayerControl::setBufferProgress(int progress) emit bufferStatusChanged(m_bufferProgress); } -void QGstreamerPlayerControl::applyPendingSeek(bool isSeekable) -{ - if (isSeekable && m_pendingSeekPosition != -1) - setPosition(m_pendingSeekPosition); -} - void QGstreamerPlayerControl::handleInvalidMedia() { pushState(); m_mediaStatus = QMediaPlayer::InvalidMedia; m_currentState = QMediaPlayer::StoppedState; + m_setMediaPending = true; popAndNotifyState(); } @@ -636,24 +649,4 @@ void QGstreamerPlayerControl::popAndNotifyState() } } -void QGstreamerPlayerControl::updatePosition(qint64 pos) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << pos/1000.0 << "pending:" << m_pendingSeekPosition/1000.0; -#endif - - if (m_pendingSeekPosition != -1) { - //seek request is complete, it's safe to resume playback - //with prerolled frame displayed - m_pendingSeekPosition = -1; - if (m_currentState != QMediaPlayer::StoppedState) - m_session->showPrerollFrames(true); - if (m_currentState == QMediaPlayer::PlayingState) { - m_session->play(); - } - } - - emit positionChanged(pos); -} - QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h index 94f7b7019..0a5f8af83 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -103,8 +103,6 @@ private Q_SLOTS: void updateMediaStatus(); void processEOS(); void setBufferProgress(int progress); - void applyPendingSeek(bool isSeekable); - void updatePosition(qint64 pos); void handleInvalidMedia(); @@ -127,7 +125,6 @@ private: QStack<QMediaPlayer::MediaStatus> m_mediaStatusStack; int m_bufferProgress; - bool m_seekToStartPending; qint64 m_pendingSeekPosition; bool m_setMediaPending; QMediaContent m_currentResource; diff --git a/src/plugins/opensles/qopenslesaudioinput.cpp b/src/plugins/opensles/qopenslesaudioinput.cpp index 9e344b463..cd592a7d6 100644 --- a/src/plugins/opensles/qopenslesaudioinput.cpp +++ b/src/plugins/opensles/qopenslesaudioinput.cpp @@ -474,7 +474,7 @@ qint64 QOpenSLESAudioInput::elapsedUSecs() const if (m_deviceState == QAudio::StoppedState) return 0; - return m_clockStamp.elapsed() * 1000; + return m_clockStamp.elapsed() * qint64(1000); } void QOpenSLESAudioInput::setVolume(qreal vol) diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp index f055796b5..c45fbd3c5 100644 --- a/src/plugins/opensles/qopenslesaudiooutput.cpp +++ b/src/plugins/opensles/qopenslesaudiooutput.cpp @@ -70,7 +70,9 @@ QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device) m_periodSize(0), m_elapsedTime(0), m_processedBytes(0), - m_availableBuffers(BUFFER_COUNT) + m_availableBuffers(BUFFER_COUNT), + m_eventMask(SL_PLAYEVENT_HEADATEND), + m_startRequiresInit(true) { #ifndef ANDROID m_streamType = -1; @@ -98,13 +100,10 @@ QAudio::State QOpenSLESAudioOutput::state() const void QOpenSLESAudioOutput::start(QIODevice *device) { Q_ASSERT(device); - destroyPlayer(); - - m_pullMode = true; - if (!preparePlayer()) return; + m_pullMode = true; m_audioSource = device; setState(QAudio::ActiveState); setError(QAudio::NoError); @@ -125,29 +124,20 @@ void QOpenSLESAudioOutput::start(QIODevice *device) // Change the state to playing. // We need to do this after filling the buffers or processedBytes might get corrupted. - if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { - setError(QAudio::FatalError); - destroyPlayer(); - } + startPlayer(); } QIODevice *QOpenSLESAudioOutput::start() { - destroyPlayer(); - - m_pullMode = false; - if (!preparePlayer()) return Q_NULLPTR; + m_pullMode = false; m_audioSource = new SLIODevicePrivate(this); m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); // Change the state to playing - if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { - setError(QAudio::FatalError); - destroyPlayer(); - } + startPlayer(); setState(QAudio::IdleState); return m_audioSource; @@ -158,7 +148,7 @@ void QOpenSLESAudioOutput::stop() if (m_state == QAudio::StoppedState) return; - destroyPlayer(); + stopPlayer(); setError(QAudio::NoError); } @@ -180,6 +170,7 @@ void QOpenSLESAudioOutput::setBufferSize(int value) if (m_state != QAudio::StoppedState) return; + m_startRequiresInit = true; m_bufferSize = value; } @@ -190,7 +181,33 @@ int QOpenSLESAudioOutput::bufferSize() const void QOpenSLESAudioOutput::setNotifyInterval(int ms) { - m_notifyInterval = ms > 0 ? ms : 0; + const int newInterval = ms > 0 ? ms : 0; + + if (newInterval == m_notifyInterval) + return; + + const SLuint32 newEvenMask = newInterval == 0 ? m_eventMask & ~SL_PLAYEVENT_HEADATNEWPOS + : m_eventMask & SL_PLAYEVENT_HEADATNEWPOS; + + if (m_state == QAudio::StoppedState) { + m_eventMask = newEvenMask; + m_notifyInterval = newInterval; + return; + } + + if (newEvenMask != m_eventMask + && SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, newEvenMask)) { + return; + } + + m_eventMask = newEvenMask; + + if (newInterval && SL_RESULT_SUCCESS != (*m_playItf)->SetPositionUpdatePeriod(m_playItf, + newInterval)) { + return; + } + + m_notifyInterval = newInterval; } int QOpenSLESAudioOutput::notifyInterval() const @@ -227,6 +244,7 @@ void QOpenSLESAudioOutput::resume() void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format) { + m_startRequiresInit = true; m_format = format; } @@ -255,7 +273,7 @@ qint64 QOpenSLESAudioOutput::elapsedUSecs() const if (m_state == QAudio::StoppedState) return 0; - return m_clockStamp.elapsed() * 1000; + return m_clockStamp.elapsed() * qint64(1000); } void QOpenSLESAudioOutput::reset() @@ -298,6 +316,7 @@ void QOpenSLESAudioOutput::setCategory(const QString &category) return; } + m_startRequiresInit = true; m_streamType = streamType; m_category = category; #endif // ANDROID @@ -376,6 +395,11 @@ void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, voi bool QOpenSLESAudioOutput::preparePlayer() { + if (m_startRequiresInit) + destroyPlayer(); + else + return true; + SLEngineItf engine = QOpenSLESEngine::instance()->slEngine(); if (!engine) { qWarning() << "No engine"; @@ -480,13 +504,12 @@ bool QOpenSLESAudioOutput::preparePlayer() return false; } - SLuint32 mask = SL_PLAYEVENT_HEADATEND; if (m_notifyInterval && SL_RESULT_SUCCESS == (*m_playItf)->SetPositionUpdatePeriod(m_playItf, m_notifyInterval)) { - mask |= SL_PLAYEVENT_HEADATNEWPOS; + m_eventMask |= SL_PLAYEVENT_HEADATNEWPOS; } - if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, mask)) { + if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, m_eventMask)) { setError(QAudio::FatalError); return false; } @@ -517,20 +540,15 @@ bool QOpenSLESAudioOutput::preparePlayer() m_clockStamp.restart(); setError(QAudio::NoError); + m_startRequiresInit = false; return true; } void QOpenSLESAudioOutput::destroyPlayer() { - setState(QAudio::StoppedState); - - // We need to change the state manually... - if (m_playItf) - (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); - - if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) - qWarning() << "Unable to clear buffer"; + if (m_state != QAudio::StoppedState) + stopPlayer(); if (m_playerObject) { (*m_playerObject)->Destroy(m_playerObject); @@ -556,6 +574,27 @@ void QOpenSLESAudioOutput::destroyPlayer() m_playItf = Q_NULLPTR; m_volumeItf = Q_NULLPTR; m_bufferQueueItf = Q_NULLPTR; + m_startRequiresInit = true; +} + +void QOpenSLESAudioOutput::stopPlayer() +{ + setState(QAudio::StoppedState); + + // We need to change the state manually... + if (m_playItf) + (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); + + if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) + qWarning() << "Unable to clear buffer"; +} + +void QOpenSLESAudioOutput::startPlayer() +{ + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + } } qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len) diff --git a/src/plugins/opensles/qopenslesaudiooutput.h b/src/plugins/opensles/qopenslesaudiooutput.h index 60c8cfa86..200b4a3cc 100644 --- a/src/plugins/opensles/qopenslesaudiooutput.h +++ b/src/plugins/opensles/qopenslesaudiooutput.h @@ -86,6 +86,8 @@ private: bool preparePlayer(); void destroyPlayer(); + void stopPlayer(); + void startPlayer(); qint64 writeData(const char *data, qint64 len); void setState(QAudio::State state); @@ -112,6 +114,8 @@ private: qint64 m_elapsedTime; qint64 m_processedBytes; QAtomicInt m_availableBuffers; + SLuint32 m_eventMask; + bool m_startRequiresInit; qint32 m_streamType; QTime m_clockStamp; diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 0020203c3..aa28bdd1b 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -44,15 +44,15 @@ unix:!mac:!android { config_alsa: SUBDIRS += alsa # v4l is turned off because it is not supported in Qt 5 - # !maemo*:SUBDIRS += v4l + # config_linux_v4l { + # !maemo*:SUBDIRS += v4l + # } } mac:!simulator { SUBDIRS += audiocapture coreaudio config_avfoundation: SUBDIRS += avfoundation - - contains(QT_CONFIG, opengl.*):!ios: SUBDIRS += qt7 } config_resourcepolicy { diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp index b97cd6c4f..ea053578f 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudioinput_pulse.cpp @@ -684,7 +684,7 @@ qint64 QPulseAudioInput::elapsedUSecs() const if (m_deviceState == QAudio::StoppedState) return 0; - return m_clockStamp.elapsed() * 1000; + return m_clockStamp.elapsed() * qint64(1000); } void QPulseAudioInput::reset() diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp index c55cfe44c..b979450b6 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp @@ -596,7 +596,7 @@ qint64 QPulseAudioOutput::elapsedUSecs() const if (m_deviceState == QAudio::StoppedState) return 0; - return m_clockStamp.elapsed() * 1000; + return m_clockStamp.elapsed() * qint64(1000); } void QPulseAudioOutput::reset() diff --git a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp b/src/plugins/qnx-audio/audio/qnxaudioinput.cpp index ea9ea5abf..3f60dfc29 100644 --- a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp +++ b/src/plugins/qnx-audio/audio/qnxaudioinput.cpp @@ -186,7 +186,7 @@ qint64 QnxAudioInput::elapsedUSecs() const if (m_state == QAudio::StoppedState) return 0; - return m_clockStamp.elapsed() * 1000; + return m_clockStamp.elapsed() * qint64(1000); } QAudio::Error QnxAudioInput::error() const diff --git a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp b/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp index 4604ed542..e7733276e 100644 --- a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp +++ b/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp @@ -172,7 +172,7 @@ qint64 QnxAudioOutput::elapsedUSecs() const if (m_state == QAudio::StoppedState) return 0; else - return m_startTimeStamp.elapsed() * 1000; + return m_startTimeStamp.elapsed() * qint64(1000); } QAudio::Error QnxAudioOutput::error() const diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp index 217f8b3bb..503c5beb8 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp @@ -538,6 +538,7 @@ void MmRendererMediaPlayerControl::play() return; } + m_stopEventsToIgnore = 0; // once playing, stop events must be proccessed setState( QMediaPlayer::PlayingState); } diff --git a/src/plugins/qt7/mediaplayer/mediaplayer.pri b/src/plugins/qt7/mediaplayer/mediaplayer.pri deleted file mode 100644 index 2edb1d2c7..000000000 --- a/src/plugins/qt7/mediaplayer/mediaplayer.pri +++ /dev/null @@ -1,16 +0,0 @@ -INCLUDEPATH += $$PWD - -DEFINES += QMEDIA_QT7_PLAYER - -HEADERS += \ - $$PWD/qt7playercontrol.h \ - $$PWD/qt7playermetadata.h \ - $$PWD/qt7playerservice.h \ - $$PWD/qt7playersession.h - -OBJECTIVE_SOURCES += \ - $$PWD/qt7playercontrol.mm \ - $$PWD/qt7playermetadata.mm \ - $$PWD/qt7playerservice.mm \ - $$PWD/qt7playersession.mm - diff --git a/src/plugins/qt7/mediaplayer/qt7playercontrol.h b/src/plugins/qt7/mediaplayer/qt7playercontrol.h deleted file mode 100644 index 61fd0a456..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playercontrol.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7PLAYERCONTROL_H -#define QT7PLAYERCONTROL_H - -#include <QtCore/qobject.h> - -#include <qmediaplayercontrol.h> -#include <qmediaplayer.h> - -QT_BEGIN_NAMESPACE - -class QT7PlayerSession; -class QT7PlayerService; -class QMediaPlaylist; -class QMediaPlaylistNavigator; - -class QT7PlayerControl : public QMediaPlayerControl -{ -Q_OBJECT -public: - QT7PlayerControl(QObject *parent = 0); - ~QT7PlayerControl(); - - void setSession(QT7PlayerSession *session); - - QMediaPlayer::State state() const; - QMediaPlayer::MediaStatus mediaStatus() const; - - QMediaContent media() const; - const QIODevice *mediaStream() const; - void setMedia(const QMediaContent &content, QIODevice *stream); - - qint64 position() const; - qint64 duration() const; - - int bufferStatus() const; - - int volume() const; - bool isMuted() const; - - bool isAudioAvailable() const; - bool isVideoAvailable() const; - - bool isSeekable() const; - QMediaTimeRange availablePlaybackRanges() const; - - qreal playbackRate() const; - void setPlaybackRate(qreal rate); - -public Q_SLOTS: - void setPosition(qint64 pos); - - void play(); - void pause(); - void stop(); - - void setVolume(int volume); - void setMuted(bool muted); - -private: - QT7PlayerSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playercontrol.mm b/src/plugins/qt7/mediaplayer/qt7playercontrol.mm deleted file mode 100644 index c48660778..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playercontrol.mm +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qt7playercontrol.h" -#include "qt7playersession.h" - -#include <private/qmediaplaylistnavigator_p.h> - -#include <QtCore/qurl.h> -#include <QtCore/qdebug.h> - -QT_USE_NAMESPACE - -QT7PlayerControl::QT7PlayerControl(QObject *parent) - : QMediaPlayerControl(parent) -{ -} - -QT7PlayerControl::~QT7PlayerControl() -{ -} - -void QT7PlayerControl::setSession(QT7PlayerSession *session) -{ - m_session = session; - - connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64))); - connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); - connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), - this, SIGNAL(stateChanged(QMediaPlayer::State))); - connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), - this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); - connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int))); - connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); - connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool))); - connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool))); - connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString))); -} - -qint64 QT7PlayerControl::position() const -{ - return m_session->position(); -} - -qint64 QT7PlayerControl::duration() const -{ - return m_session->duration(); -} - -QMediaPlayer::State QT7PlayerControl::state() const -{ - return m_session->state(); -} - -QMediaPlayer::MediaStatus QT7PlayerControl::mediaStatus() const -{ - return m_session->mediaStatus(); -} - -int QT7PlayerControl::bufferStatus() const -{ - return m_session->bufferStatus(); -} - -int QT7PlayerControl::volume() const -{ - return m_session->volume(); -} - -bool QT7PlayerControl::isMuted() const -{ - return m_session->isMuted(); -} - -bool QT7PlayerControl::isSeekable() const -{ - return m_session->isSeekable(); -} - -QMediaTimeRange QT7PlayerControl::availablePlaybackRanges() const -{ - return m_session->availablePlaybackRanges(); -} - -qreal QT7PlayerControl::playbackRate() const -{ - return m_session->playbackRate(); -} - -void QT7PlayerControl::setPlaybackRate(qreal rate) -{ - m_session->setPlaybackRate(rate); -} - -void QT7PlayerControl::setPosition(qint64 pos) -{ - m_session->setPosition(pos); -} - -void QT7PlayerControl::play() -{ - m_session->play(); -} - -void QT7PlayerControl::pause() -{ - m_session->pause(); -} - -void QT7PlayerControl::stop() -{ - m_session->stop(); -} - -void QT7PlayerControl::setVolume(int volume) -{ - m_session->setVolume(volume); -} - -void QT7PlayerControl::setMuted(bool muted) -{ - m_session->setMuted(muted); -} - -QMediaContent QT7PlayerControl::media() const -{ - return m_session->media(); -} - -const QIODevice *QT7PlayerControl::mediaStream() const -{ - return m_session->mediaStream(); -} - -void QT7PlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) -{ - m_session->setMedia(content, stream); - - Q_EMIT mediaChanged(content); -} - -bool QT7PlayerControl::isAudioAvailable() const -{ - return m_session->isAudioAvailable(); -} - -bool QT7PlayerControl::isVideoAvailable() const -{ - return m_session->isVideoAvailable(); -} - - -#include "moc_qt7playercontrol.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playermetadata.h b/src/plugins/qt7/mediaplayer/qt7playermetadata.h deleted file mode 100644 index 9f0ceae16..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playermetadata.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7PLAYERMETADATACONTROL_H -#define QT7PLAYERMETADATACONTROL_H - -#include <qmetadatareadercontrol.h> - -QT_BEGIN_NAMESPACE - -class QT7PlayerSession; - -class QT7PlayerMetaDataControl : public QMetaDataReaderControl -{ - Q_OBJECT -public: - QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent); - virtual ~QT7PlayerMetaDataControl(); - - bool isMetaDataAvailable() const; - bool isWritable() const; - - QVariant metaData(const QString &key) const; - QStringList availableMetaData() const; - -private Q_SLOTS: - void updateTags(); - -private: - QT7PlayerSession *m_session; - QMap<QString, QVariant> m_tags; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playermetadata.mm b/src/plugins/qt7/mediaplayer/qt7playermetadata.mm deleted file mode 100644 index 84d434be2..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playermetadata.mm +++ /dev/null @@ -1,250 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qt7backend.h" -#include "qt7playermetadata.h" -#include "qt7playersession.h" -#include <QtCore/qvarlengtharray.h> -#include <QtMultimedia/qmediametadata.h> - -#import <QTKit/QTMovie.h> - -#ifdef QUICKTIME_C_API_AVAILABLE - #include <QuickTime/QuickTime.h> - #undef check // avoid name clash; -#endif - -QT_USE_NAMESPACE - -QT7PlayerMetaDataControl::QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent) - :QMetaDataReaderControl(parent), m_session(session) -{ -} - -QT7PlayerMetaDataControl::~QT7PlayerMetaDataControl() -{ -} - -bool QT7PlayerMetaDataControl::isMetaDataAvailable() const -{ - return !m_tags.isEmpty(); -} - -bool QT7PlayerMetaDataControl::isWritable() const -{ - return false; -} - -QVariant QT7PlayerMetaDataControl::metaData(const QString &key) const -{ - return m_tags.value(key); -} - -QStringList QT7PlayerMetaDataControl::availableMetaData() const -{ - return m_tags.keys(); -} - -#ifdef QUICKTIME_C_API_AVAILABLE - -static QString stripCopyRightSymbol(const QString &key) -{ - return key.right(key.length()-1); -} - -static QString convertQuickTimeKeyToUserKey(const QString &key) -{ - if (key == QLatin1String("com.apple.quicktime.displayname")) - return QLatin1String("nam"); - else if (key == QLatin1String("com.apple.quicktime.album")) - return QLatin1String("alb"); - else if (key == QLatin1String("com.apple.quicktime.artist")) - return QLatin1String("ART"); - else - return QLatin1String("???"); -} - -static OSStatus readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass, - QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size) -{ - QTPropertyValueType type; - ByteCount propSize; - UInt32 propFlags; - OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags); - - if (err == noErr) { - *value = malloc(propSize); - if (*value != 0) { - err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size); - - if (err == noErr && (type == 'code' || type == 'itsk' || type == 'itlk')) { - // convert from native endian to big endian - OSTypePtr pType = (OSTypePtr)*value; - *pType = EndianU32_NtoB(*pType); - } - } - else - return -1; - } - - return err; -} - -static UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item) -{ - QTPropertyValuePtr value = 0; - ByteCount ignore = 0; - OSStatus err = readMetaValue( - metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore); - - if (err == noErr) { - UInt32 type = *((UInt32 *) value); - if (value) - free(value); - return type; - } - - return 0; -} - -static QString cFStringToQString(CFStringRef str) -{ - if(!str) - return QString(); - CFIndex length = CFStringGetLength(str); - const UniChar *chars = CFStringGetCharactersPtr(str); - if (chars) - return QString(reinterpret_cast<const QChar *>(chars), length); - - QVarLengthArray<UniChar> buffer(length); - CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); - return QString(reinterpret_cast<const QChar *>(buffer.constData()), length); -} - - -static QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id) -{ - QTPropertyValuePtr value = 0; - ByteCount size = 0; - OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size); - QString string; - - if (err == noErr) { - UInt32 dataType = getMetaType(metaDataRef, item); - switch (dataType){ - case kQTMetaDataTypeUTF8: - case kQTMetaDataTypeMacEncodedText: - string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false)); - break; - case kQTMetaDataTypeUTF16BE: - string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false)); - break; - default: - break; - } - - if (value) - free(value); - } - - return string; -} - - -static void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result) -{ - QTMetaDataItem item = kQTMetaDataItemUninitialized; - OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); - while (err == noErr){ - QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key); - if (format == kQTMetaDataStorageFormatQuickTime) - key = convertQuickTimeKeyToUserKey(key); - else - key = stripCopyRightSymbol(key); - - if (!result.contains(key)){ - QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value); - result.insert(key, val); - } - err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); - } -} -#endif - - -void QT7PlayerMetaDataControl::updateTags() -{ - bool wasEmpty = m_tags.isEmpty(); - m_tags.clear(); - - QTMovie *movie = (QTMovie*)m_session->movie(); - - if (movie) { - QMultiMap<QString, QString> metaMap; - -#ifdef QUICKTIME_C_API_AVAILABLE - QTMetaDataRef metaDataRef; - OSStatus err = QTCopyMovieMetaData([movie quickTimeMovie], &metaDataRef); - if (err == noErr) { - readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap); - readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap); - readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap); - } -#else - AutoReleasePool pool; - NSString *name = [movie attributeForKey:@"QTMovieDisplayNameAttribute"]; - metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String])); -#endif // QUICKTIME_C_API_AVAILABLE - - m_tags.insert(QMediaMetaData::AlbumArtist, metaMap.value(QLatin1String("ART"))); - m_tags.insert(QMediaMetaData::AlbumTitle, metaMap.value(QLatin1String("alb"))); - m_tags.insert(QMediaMetaData::Title, metaMap.value(QLatin1String("nam"))); - m_tags.insert(QMediaMetaData::Date, metaMap.value(QLatin1String("day"))); - m_tags.insert(QMediaMetaData::Genre, metaMap.value(QLatin1String("gnre"))); - m_tags.insert(QMediaMetaData::TrackNumber, metaMap.value(QLatin1String("trk"))); - m_tags.insert(QMediaMetaData::Description, metaMap.value(QLatin1String("des"))); - } - - if (!wasEmpty || !m_tags.isEmpty()) - Q_EMIT metaDataChanged(); -} - -#include "moc_qt7playermetadata.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playerservice.h b/src/plugins/qt7/mediaplayer/qt7playerservice.h deleted file mode 100644 index 15c1d2cb7..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playerservice.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7PLAYERSERVICE_H -#define QT7PLAYERSERVICE_H - -#include <QtCore/qobject.h> -#include <QtCore/qset.h> -#include <qmediaservice.h> - - -QT_BEGIN_NAMESPACE -class QMediaPlayerControl; -class QMediaPlaylist; -class QMediaPlaylistNavigator; -class QT7PlayerControl; -class QT7PlayerMetaDataControl; -class QT7VideoWindowControl; -class QT7VideoWidgetControl; -class QT7VideoRendererControl; -class QT7VideoOutput; -class QT7PlayerSession; - -class QT7PlayerService : public QMediaService -{ -Q_OBJECT -public: - QT7PlayerService(QObject *parent = 0); - ~QT7PlayerService(); - - QMediaControl* requestControl(const char *name); - void releaseControl(QMediaControl *control); - -private: - QT7PlayerSession *m_session; - QT7PlayerControl *m_control; - QMediaControl * m_videoOutput; - QT7PlayerMetaDataControl *m_playerMetaDataControl; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playerservice.mm b/src/plugins/qt7/mediaplayer/qt7playerservice.mm deleted file mode 100644 index 46be5475f..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playerservice.mm +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qvariant.h> -#include <QtCore/qdebug.h> - -#include "qt7backend.h" -#include "qt7playerservice.h" -#include "qt7playercontrol.h" -#include "qt7playersession.h" -#include "qt7videooutput.h" -#include "qt7movieviewoutput.h" -#include "qt7movieviewrenderer.h" -#include "qt7movierenderer.h" -#ifndef QT_NO_WIDGETS -#include "qt7movievideowidget.h" -#endif -#include "qt7playermetadata.h" - -#include <private/qmediaplaylistnavigator_p.h> -#include <qmediaplaylist.h> - -QT_USE_NAMESPACE - -QT7PlayerService::QT7PlayerService(QObject *parent): - QMediaService(parent), - m_videoOutput(0) -{ - m_session = new QT7PlayerSession(this); - - m_control = new QT7PlayerControl(this); - m_control->setSession(m_session); - - m_playerMetaDataControl = new QT7PlayerMetaDataControl(m_session, this); - connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags())); -} - -QT7PlayerService::~QT7PlayerService() -{ -} - -QMediaControl *QT7PlayerService::requestControl(const char *name) -{ - if (qstrcmp(name, QMediaPlayerControl_iid) == 0) - return m_control; - - if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) - return m_playerMetaDataControl; - -#ifndef QT_NO_OPENGL - if (!m_videoOutput) { - if (qstrcmp(name, QVideoWindowControl_iid) == 0) { - m_videoOutput = new QT7MovieViewOutput(this); - } - - if (qstrcmp(name, QVideoRendererControl_iid) == 0) { -#ifndef QT_NO_WIDGETS - m_videoOutput = new QT7MovieViewRenderer(this); -#endif - } - -#ifndef QT_NO_WIDGETS - if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { -#ifdef QUICKTIME_C_API_AVAILABLE - m_videoOutput = new QT7MovieVideoWidget(this); -#endif - } -#endif - - if (m_videoOutput) { - QT7VideoOutput *videoOutput = qobject_cast<QT7VideoOutput*>(m_videoOutput); - m_session->setVideoOutput(videoOutput); - return m_videoOutput; - } - } -#endif // !defined(QT_NO_OPENGL) - - return 0; -} - -void QT7PlayerService::releaseControl(QMediaControl *control) -{ - if (m_videoOutput == control) { - m_videoOutput = 0; - m_session->setVideoOutput(0); - delete control; - } -} - -#include "moc_qt7playerservice.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playersession.h b/src/plugins/qt7/mediaplayer/qt7playersession.h deleted file mode 100644 index 58ea76a01..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playersession.h +++ /dev/null @@ -1,183 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7PLAYERSESSION_H -#define QT7PLAYERSESSION_H - -#include <QtCore/qobject.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qset.h> -#include <QtCore/qresource.h> - -#include <qmediaplayercontrol.h> -#include <qmediaplayer.h> - -QT_BEGIN_NAMESPACE - -class QT7PlayerControl; -class QMediaPlaylist; -class QMediaPlaylistNavigator; -class QT7VideoOutput; -class QT7PlayerSession; -class QT7PlayerService; - - -class QT7PlayerSession : public QObject -{ - Q_OBJECT -public: - QT7PlayerSession(QObject *parent = 0); - ~QT7PlayerSession(); - - void *movie() const; - - void setControl(QT7PlayerControl *control); - - void setVideoOutput(QT7VideoOutput *output); - - QMediaPlayer::State state() const; - QMediaPlayer::MediaStatus mediaStatus() const; - - QMediaContent media() const; - const QIODevice *mediaStream() const; - void setMedia(const QMediaContent &content, QIODevice *stream); - - qint64 position() const; - qint64 duration() const; - - int bufferStatus() const; - - int volume() const; - bool isMuted() const; - - bool isAudioAvailable() const; - bool isVideoAvailable() const; - - bool isSeekable() const; - QMediaTimeRange availablePlaybackRanges() const; - - qreal playbackRate() const; - -public Q_SLOTS: - void setPlaybackRate(qreal rate); - - void setPosition(qint64 pos); - - void play(); - void pause(); - void stop(); - - void setVolume(int volume); - void setMuted(bool muted); - - void processEOS(); - void processLoadStateChange(); - void processVolumeChange(); - void processNaturalSizeChange(); - void processPositionChange(); - -Q_SIGNALS: - void positionChanged(qint64 position); - void durationChanged(qint64 duration); - void stateChanged(QMediaPlayer::State newState); - void mediaStatusChanged(QMediaPlayer::MediaStatus status); - void volumeChanged(int volume); - void mutedChanged(bool muted); - void audioAvailableChanged(bool audioAvailable); - void videoAvailableChanged(bool videoAvailable); - void error(int error, const QString &errorString); - -private: - class ResourceHandler { - public: - ResourceHandler():resource(0) {} - ~ResourceHandler() { clear(); } - void setResourceFile(const QString &file) { - if (resource) { - if (resource->fileName() == file) - return; - delete resource; - rawData.clear(); - } - resource = new QResource(file); - } - bool isValid() const { return resource && resource->isValid() && resource->data() != 0; } - const uchar *data() { - if (!isValid()) - return 0; - if (resource->isCompressed()) { - if (rawData.size() == 0) - rawData = qUncompress(resource->data(), resource->size()); - return (const uchar *)rawData.constData(); - } - return resource->data(); - } - qint64 size() { - if (data() == 0) - return 0; - return resource->isCompressed() ? rawData.size() : resource->size(); - } - void clear() { - delete resource; - rawData.clear(); - } - QResource *resource; - QByteArray rawData; - }; - - void openMovie(bool tryAsync); - - void *m_QTMovie; - void *m_movieObserver; - - QMediaPlayer::State m_state; - QMediaPlayer::MediaStatus m_mediaStatus; - QIODevice *m_mediaStream; - QMediaContent m_resources; - ResourceHandler m_resourceHandler; - - QT7VideoOutput * m_videoOutput; - - bool m_muted; - bool m_tryingAsync; - int m_volume; - qreal m_rate; - - qint64 m_duration; - bool m_videoAvailable; - bool m_audioAvailable; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playersession.mm b/src/plugins/qt7/mediaplayer/qt7playersession.mm deleted file mode 100644 index 4ec015cbf..000000000 --- a/src/plugins/qt7/mediaplayer/qt7playersession.mm +++ /dev/null @@ -1,751 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#import <QTKit/QTDataReference.h> -#import <QTKit/QTMovie.h> - -#include "qt7backend.h" - -#include "qt7playersession.h" -#include "qt7playercontrol.h" -#include "qt7videooutput.h" - -#include <QtNetwork/qnetworkcookie.h> -#include <private/qmediaplaylistnavigator_p.h> - -#include <CoreFoundation/CoreFoundation.h> -#include <Foundation/Foundation.h> - -#include <QtCore/qdatetime.h> -#include <QtCore/qurl.h> - -#include <QtCore/qdebug.h> - -QT_USE_NAMESPACE - -//#define QT_DEBUG_QT7 - -@interface QTMovieObserver : NSObject -{ -@private - QT7PlayerSession *m_session; - QTMovie *m_movie; -} - -- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session; -- (void) setMovie:(QTMovie *)movie; -- (void) processEOS:(NSNotification *)notification; -- (void) processLoadStateChange:(NSNotification *)notification; -- (void) processVolumeChange:(NSNotification *)notification; -- (void) processNaturalSizeChange :(NSNotification *)notification; -- (void) processPositionChange :(NSNotification *)notification; -@end - -@implementation QTMovieObserver - -- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session -{ - if (!(self = [super init])) - return nil; - - self->m_session = session; - return self; -} - -- (void) setMovie:(QTMovie *)movie -{ - if (m_movie == movie) - return; - - if (m_movie) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [m_movie release]; - } - - m_movie = movie; - - if (movie) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processEOS:) - name:QTMovieDidEndNotification - object:m_movie]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processLoadStateChange:) - name:QTMovieLoadStateDidChangeNotification - object:m_movie]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processVolumeChange:) - name:QTMovieVolumeDidChangeNotification - object:m_movie]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processPositionChange:) - name:QTMovieTimeDidChangeNotification - object:m_movie]; - - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processNaturalSizeChange:) - name:@"QTMovieNaturalSizeDidChangeNotification" - object:m_movie]; - - } - else { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processNaturalSizeChange:) - name:QTMovieEditedNotification - object:m_movie]; - } - - [movie retain]; - } -} - -- (void) processEOS:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection); -} - -- (void) processLoadStateChange:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); -} - -- (void) processVolumeChange:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processVolumeChange", Qt::AutoConnection); -} - -- (void) processNaturalSizeChange :(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processNaturalSizeChange", Qt::AutoConnection); -} - -- (void) processPositionChange :(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection); -} - -@end - -static inline NSString *qString2CFStringRef(const QString &string) -{ - return [NSString stringWithCharacters:reinterpret_cast<const UniChar *>(string.unicode()) length:string.length()]; -} - -QT7PlayerSession::QT7PlayerSession(QObject *parent) - : QObject(parent) - , m_QTMovie(0) - , m_state(QMediaPlayer::StoppedState) - , m_mediaStatus(QMediaPlayer::NoMedia) - , m_mediaStream(0) - , m_videoOutput(0) - , m_muted(false) - , m_tryingAsync(false) - , m_volume(100) - , m_rate(1.0) - , m_duration(0) - , m_videoAvailable(false) - , m_audioAvailable(false) -{ - m_movieObserver = [[QTMovieObserver alloc] initWithPlayerSession:this]; -} - -QT7PlayerSession::~QT7PlayerSession() -{ - if (m_videoOutput) - m_videoOutput->setMovie(0); - - [(QTMovieObserver*)m_movieObserver setMovie:nil]; - [(QTMovieObserver*)m_movieObserver release]; - [(QTMovie*)m_QTMovie release]; -} - -void *QT7PlayerSession::movie() const -{ - return m_QTMovie; -} - -void QT7PlayerSession::setVideoOutput(QT7VideoOutput *output) -{ - if (m_videoOutput == output) - return; - - if (m_videoOutput) - m_videoOutput->setMovie(0); - - m_videoOutput = output; - - if (m_videoOutput && m_state != QMediaPlayer::StoppedState) - m_videoOutput->setMovie(m_QTMovie); -} - -qint64 QT7PlayerSession::position() const -{ - if (!m_QTMovie) - return 0; - - QTTime qtTime = [(QTMovie*)m_QTMovie currentTime]; - - return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); -} - -qint64 QT7PlayerSession::duration() const -{ - if (!m_QTMovie) - return 0; - - QTTime qtTime = [(QTMovie*)m_QTMovie duration]; - - return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); -} - -QMediaPlayer::State QT7PlayerSession::state() const -{ - return m_state; -} - -QMediaPlayer::MediaStatus QT7PlayerSession::mediaStatus() const -{ - return m_mediaStatus; -} - -int QT7PlayerSession::bufferStatus() const -{ - return 100; -} - -int QT7PlayerSession::volume() const -{ - return m_volume; -} - -bool QT7PlayerSession::isMuted() const -{ - return m_muted; -} - -bool QT7PlayerSession::isSeekable() const -{ - return true; -} - -#ifndef QUICKTIME_C_API_AVAILABLE -@interface QTMovie(QtExtensions) -- (NSArray*)loadedRanges; -- (QTTime)maxTimeLoaded; -@end -#endif - -QMediaTimeRange QT7PlayerSession::availablePlaybackRanges() const -{ - QTMovie *movie = (QTMovie*)m_QTMovie; -#ifndef QUICKTIME_C_API_AVAILABLE - AutoReleasePool pool; - if ([movie respondsToSelector:@selector(loadedRanges)]) { - QMediaTimeRange rc; - NSArray *r = [movie loadedRanges]; - for (NSValue *tr in r) { - QTTimeRange timeRange = [tr QTTimeRangeValue]; - qint64 startTime = qint64(float(timeRange.time.timeValue) / timeRange.time.timeScale * 1000.0); - rc.addInterval(startTime, startTime + qint64(float(timeRange.duration.timeValue) / timeRange.duration.timeScale * 1000.0)); - } - return rc; - } - else if ([movie respondsToSelector:@selector(maxTimeLoaded)]) { - QTTime loadedTime = [movie maxTimeLoaded]; - return QMediaTimeRange(0, qint64(float(loadedTime.timeValue) / loadedTime.timeScale * 1000.0)); - } -#else - TimeValue loadedTime; - TimeScale scale; - Movie m = [movie quickTimeMovie]; - if (GetMaxLoadedTimeInMovie(m, &loadedTime) == noErr) { - scale = GetMovieTimeScale(m); - return QMediaTimeRange(0, qint64(float(loadedTime) / scale * 1000.0f)); - } -#endif - return QMediaTimeRange(0, duration()); -} - -qreal QT7PlayerSession::playbackRate() const -{ - return m_rate; -} - -void QT7PlayerSession::setPlaybackRate(qreal rate) -{ - if (qFuzzyCompare(m_rate, rate)) - return; - - m_rate = rate; - - if (m_QTMovie != 0 && m_state == QMediaPlayer::PlayingState) { - AutoReleasePool pool; - float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; - [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; - } -} - -void QT7PlayerSession::setPosition(qint64 pos) -{ - if ( !isSeekable() || pos == position()) - return; - - if (duration() > 0) - pos = qMin(pos, duration()); - - QTTime newQTTime = [(QTMovie*)m_QTMovie currentTime]; - newQTTime.timeValue = (pos / 1000.0f) * newQTTime.timeScale; - [(QTMovie*)m_QTMovie setCurrentTime:newQTTime]; - - //reset the EndOfMedia status position is changed after playback is finished - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - processLoadStateChange(); -} - -void QT7PlayerSession::play() -{ - if (m_state == QMediaPlayer::PlayingState) - return; - - m_state = QMediaPlayer::PlayingState; - - if (m_videoOutput) - m_videoOutput->setMovie(m_QTMovie); - - //reset the EndOfMedia status if the same file is played again - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - processLoadStateChange(); - - AutoReleasePool pool; - float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; - [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; - - processLoadStateChange(); - Q_EMIT stateChanged(m_state); -} - -void QT7PlayerSession::pause() -{ - if (m_state == QMediaPlayer::PausedState) - return; - - m_state = QMediaPlayer::PausedState; - - if (m_videoOutput) - m_videoOutput->setMovie(m_QTMovie); - - //reset the EndOfMedia status if the same file is played again - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - processLoadStateChange(); - - [(QTMovie*)m_QTMovie setRate:0]; - - processLoadStateChange(); - Q_EMIT stateChanged(m_state); -} - -void QT7PlayerSession::stop() -{ - if (m_state == QMediaPlayer::StoppedState) - return; - - m_state = QMediaPlayer::StoppedState; - - [(QTMovie*)m_QTMovie setRate:0]; - setPosition(0); - - if (m_videoOutput) - m_videoOutput->setMovie(0); - - processLoadStateChange(); - Q_EMIT stateChanged(m_state); - Q_EMIT positionChanged(position()); -} - -void QT7PlayerSession::setVolume(int volume) -{ - if (m_volume == volume) - return; - - m_volume = volume; - - if (m_QTMovie != 0) - [(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f]; - - Q_EMIT volumeChanged(m_volume); -} - -void QT7PlayerSession::setMuted(bool muted) -{ - if (m_muted == muted) - return; - - m_muted = muted; - - if (m_QTMovie != 0) - [(QTMovie*)m_QTMovie setMuted:m_muted]; - - Q_EMIT mutedChanged(muted); -} - -QMediaContent QT7PlayerSession::media() const -{ - return m_resources; -} - -const QIODevice *QT7PlayerSession::mediaStream() const -{ - return m_mediaStream; -} - -void QT7PlayerSession::setMedia(const QMediaContent &content, QIODevice *stream) -{ - AutoReleasePool pool; - -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO << content.canonicalUrl(); -#endif - - if (m_QTMovie) { - [(QTMovieObserver*)m_movieObserver setMovie:nil]; - - if (m_videoOutput) - m_videoOutput->setMovie(0); - - [(QTMovie*)m_QTMovie release]; - m_QTMovie = 0; - m_resourceHandler.clear(); - } - - m_resources = content; - m_mediaStream = stream; - QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus; - - if (content.isNull()) { - m_mediaStatus = QMediaPlayer::NoMedia; - if (m_state != QMediaPlayer::StoppedState) - Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState); - - if (m_mediaStatus != oldMediaStatus) - Q_EMIT mediaStatusChanged(m_mediaStatus); - Q_EMIT positionChanged(position()); - return; - } - - m_mediaStatus = QMediaPlayer::LoadingMedia; - if (m_mediaStatus != oldMediaStatus) - Q_EMIT mediaStatusChanged(m_mediaStatus); - - QNetworkRequest request = content.canonicalResource().request(); - - QVariant cookies = request.header(QNetworkRequest::CookieHeader); - if (cookies.isValid()) { - NSHTTPCookieStorage *store = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - QList<QNetworkCookie> cookieList = cookies.value<QList<QNetworkCookie> >(); - - Q_FOREACH (const QNetworkCookie &requestCookie, cookieList) { - NSMutableDictionary *p = [NSMutableDictionary dictionaryWithObjectsAndKeys: - qString2CFStringRef(requestCookie.name()), NSHTTPCookieName, - qString2CFStringRef(requestCookie.value()), NSHTTPCookieValue, - qString2CFStringRef(requestCookie.domain()), NSHTTPCookieDomain, - qString2CFStringRef(requestCookie.path()), NSHTTPCookiePath, - nil - ]; - if (requestCookie.isSessionCookie()) - [p setObject:[NSString stringWithUTF8String:"TRUE"] forKey:NSHTTPCookieDiscard]; - else - [p setObject:[NSDate dateWithTimeIntervalSince1970:requestCookie.expirationDate().toTime_t()] forKey:NSHTTPCookieExpires]; - - [store setCookie:[NSHTTPCookie cookieWithProperties:p]]; - } - } - - // Attempt multiple times to open the movie. - // First try - attempt open in async mode - openMovie(true); - - Q_EMIT positionChanged(position()); -} - -void QT7PlayerSession::openMovie(bool tryAsync) -{ - QUrl requestUrl = m_resources.canonicalResource().request().url(); - if (requestUrl.scheme().isEmpty()) - requestUrl.setScheme(QLatin1String("file")); - -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO << requestUrl; -#endif - - NSError *err = 0; - NSString *urlString = [NSString stringWithUTF8String:requestUrl.toEncoded().constData()]; - - NSMutableDictionary *attr = [NSMutableDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, - [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute, - [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute, - [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, - nil]; - - - if (requestUrl.scheme() == QLatin1String("qrc")) { - // Load from Qt resource - m_resourceHandler.setResourceFile(QLatin1Char(':') + requestUrl.path()); - if (!m_resourceHandler.isValid()) { - Q_EMIT error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource")); - return; - } - - CFDataRef resourceData = - CFDataCreateWithBytesNoCopy(0, m_resourceHandler.data(), m_resourceHandler.size(), kCFAllocatorNull); - - QTDataReference *dataReference = - [QTDataReference dataReferenceWithReferenceToData:(NSData*)resourceData - name:qString2CFStringRef(requestUrl.path()) - MIMEType:nil]; - - [attr setObject:dataReference forKey:QTMovieDataReferenceAttribute]; - - CFRelease(resourceData); - } else { - [attr setObject:[NSURL URLWithString:urlString] forKey:QTMovieURLAttribute]; - } - - if (tryAsync && QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { - [attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenAsyncRequiredAttribute"]; -// XXX: This is disabled for now. causes some problems with video playback for some formats -// [attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; - m_tryingAsync = true; - } - else - m_tryingAsync = false; - - m_QTMovie = [QTMovie movieWithAttributes:attr error:&err]; - if (err != nil) { - // First attempt to test for inability to perform async -// if ([err code] == QTErrorMovieOpeningCannotBeAsynchronous) { XXX: error code unknown! - if (m_tryingAsync) { - m_tryingAsync = false; - err = nil; - [attr removeObjectForKey:@"QTMovieOpenAsyncRequiredAttribute"]; - m_QTMovie = [QTMovie movieWithAttributes:attr error:&err]; - } - } - - if (err != nil) { - m_QTMovie = 0; - QString description = QString::fromUtf8([[err localizedDescription] UTF8String]); - Q_EMIT error(QMediaPlayer::FormatError, description); - -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO << description; -#endif - } - else { - [(QTMovie*)m_QTMovie retain]; - - [(QTMovieObserver*)m_movieObserver setMovie:(QTMovie*)m_QTMovie]; - - if (m_state != QMediaPlayer::StoppedState && m_videoOutput) - m_videoOutput->setMovie(m_QTMovie); - - processLoadStateChange(); - - [(QTMovie*)m_QTMovie setMuted:m_muted]; - [(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f]; - } -} - -bool QT7PlayerSession::isAudioAvailable() const -{ - if (!m_QTMovie) - return false; - - AutoReleasePool pool; - return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES; -} - -bool QT7PlayerSession::isVideoAvailable() const -{ - if (!m_QTMovie) - return false; - - AutoReleasePool pool; - return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES; -} - -void QT7PlayerSession::processEOS() -{ -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO; -#endif - Q_EMIT positionChanged(position()); - m_mediaStatus = QMediaPlayer::EndOfMedia; - if (m_videoOutput) - m_videoOutput->setMovie(0); - Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState); - Q_EMIT mediaStatusChanged(m_mediaStatus); -} - -void QT7PlayerSession::processLoadStateChange() -{ - if (!m_QTMovie) - return; - - AutoReleasePool pool; - - long state = [[(QTMovie*)m_QTMovie attributeForKey:QTMovieLoadStateAttribute] longValue]; - -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO << state; -#endif - -#ifndef QUICKTIME_C_API_AVAILABLE - enum { - kMovieLoadStateError = -1L, - kMovieLoadStateLoading = 1000, - kMovieLoadStateLoaded = 2000, - kMovieLoadStatePlayable = 10000, - kMovieLoadStatePlaythroughOK = 20000, - kMovieLoadStateComplete = 100000 - }; -#endif - - if (state == kMovieLoadStateError) { - if (m_tryingAsync) { - NSError *error = [(QTMovie*)m_QTMovie attributeForKey:@"QTMovieLoadStateErrorAttribute"]; - if ([error code] == componentNotThreadSafeErr) { - // Last Async check, try again with no such flag - openMovie(false); - } - } - else { - if (m_videoOutput) - m_videoOutput->setMovie(0); - - Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media")); - Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia); - Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState); - } - - return; - } - - QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia; - bool isPlaying = (m_state != QMediaPlayer::StoppedState); - - if (state >= kMovieLoadStatePlaythroughOK) { - newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia; - } else if (state >= kMovieLoadStatePlayable) - newStatus = isPlaying ? QMediaPlayer::BufferingMedia : QMediaPlayer::LoadingMedia; - else if (state >= kMovieLoadStateLoading) { - if (!isPlaying) - newStatus = QMediaPlayer::LoadingMedia; - else if (m_mediaStatus >= QMediaPlayer::LoadedMedia) - newStatus = QMediaPlayer::StalledMedia; - else - newStatus = QMediaPlayer::LoadingMedia; - } - - if (state >= kMovieLoadStatePlayable && - m_state == QMediaPlayer::PlayingState && - [(QTMovie*)m_QTMovie rate] == 0) { - - float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; - - [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; - } - - if (state >= kMovieLoadStateLoaded) { - qint64 currentDuration = duration(); - if (m_duration != currentDuration) - Q_EMIT durationChanged(m_duration = currentDuration); - - if (m_audioAvailable != isAudioAvailable()) - Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable); - - if (m_videoAvailable != isVideoAvailable()) - Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable); - } - - if (newStatus != m_mediaStatus) - Q_EMIT mediaStatusChanged(m_mediaStatus = newStatus); -} - -void QT7PlayerSession::processVolumeChange() -{ - if (!m_QTMovie) - return; - - int newVolume = qRound(100.0f * [((QTMovie*)m_QTMovie) volume]); - - if (newVolume != m_volume) { - Q_EMIT volumeChanged(m_volume = newVolume); - } -} - -void QT7PlayerSession::processNaturalSizeChange() -{ - AutoReleasePool pool; - NSSize size = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; -#ifdef QT_DEBUG_QT7 - qDebug() << Q_FUNC_INFO << QSize(size.width, size.height); -#endif - - if (m_videoOutput) - m_videoOutput->updateNaturalSize(QSize(size.width, size.height)); -} - -void QT7PlayerSession::processPositionChange() -{ - Q_EMIT positionChanged(position()); -} - -#include "moc_qt7playersession.cpp" diff --git a/src/plugins/qt7/qcvdisplaylink.h b/src/plugins/qt7/qcvdisplaylink.h deleted file mode 100644 index ec24a0aa9..000000000 --- a/src/plugins/qt7/qcvdisplaylink.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QCVDISPLAYLINK_H -#define QCVDISPLAYLINK_H - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> - -#include <qtmultimediadefs.h> - -#include <QuartzCore/CVDisplayLink.h> - -QT_BEGIN_NAMESPACE - -class QCvDisplayLink : public QObject -{ -Q_OBJECT -public: - QCvDisplayLink(QObject *parent = 0); - virtual ~QCvDisplayLink(); - - bool isValid(); - bool isActive() const; - -public Q_SLOTS: - void start(); - void stop(); - -Q_SIGNALS: - void tick(const CVTimeStamp &ts); - -public: - void displayLinkEvent(const CVTimeStamp *); - -protected: - virtual bool event(QEvent *); - -private: - CVDisplayLinkRef m_displayLink; - QMutex m_displayLinkMutex; - bool m_pendingDisplayLinkEvent; - bool m_isActive; - CVTimeStamp m_frameTimeStamp; -}; - -QT_END_NAMESPACE - -#endif - diff --git a/src/plugins/qt7/qcvdisplaylink.mm b/src/plugins/qt7/qcvdisplaylink.mm deleted file mode 100644 index 890955492..000000000 --- a/src/plugins/qt7/qcvdisplaylink.mm +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcvdisplaylink.h" - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> - -QT_USE_NAMESPACE - -static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink, - const CVTimeStamp *inNow, - const CVTimeStamp *inOutputTime, - CVOptionFlags flagsIn, - CVOptionFlags *flagsOut, - void *displayLinkContext) -{ - Q_UNUSED(displayLink); - Q_UNUSED(inNow); - Q_UNUSED(flagsIn); - Q_UNUSED(flagsOut); - - QCvDisplayLink *link = (QCvDisplayLink *)displayLinkContext; - - link->displayLinkEvent(inOutputTime); - return kCVReturnSuccess; -} - - -QCvDisplayLink::QCvDisplayLink(QObject *parent) - :QObject(parent), - m_pendingDisplayLinkEvent(false), - m_isActive(false) -{ - // create display link for the main display - CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); - if (m_displayLink) { - // set the current display of a display link. - CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); - - // set the renderer output callback function - CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this); - } -} - -QCvDisplayLink::~QCvDisplayLink() -{ - if (m_displayLink) { - CVDisplayLinkStop(m_displayLink); - CVDisplayLinkRelease(m_displayLink); - m_displayLink = NULL; - } -} - -bool QCvDisplayLink::isValid() -{ - return m_displayLink != 0; -} - -bool QCvDisplayLink::isActive() const -{ - return m_isActive; -} - -void QCvDisplayLink::start() -{ - if (m_displayLink && !m_isActive) { - CVDisplayLinkStart(m_displayLink); - m_isActive = true; - } -} - -void QCvDisplayLink::stop() -{ - if (m_displayLink && m_isActive) { - CVDisplayLinkStop(m_displayLink); - m_isActive = false; - } -} - -void QCvDisplayLink::displayLinkEvent(const CVTimeStamp *ts) -{ - // This function is called from a - // thread != gui thread. So we post the event. - // But we need to make sure that we don't post faster - // than the event loop can eat: - m_displayLinkMutex.lock(); - bool pending = m_pendingDisplayLinkEvent; - m_pendingDisplayLinkEvent = true; - m_frameTimeStamp = *ts; - m_displayLinkMutex.unlock(); - - if (!pending) - qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); -} - -bool QCvDisplayLink::event(QEvent *event) -{ - switch (event->type()){ - case QEvent::User: { - m_displayLinkMutex.lock(); - m_pendingDisplayLinkEvent = false; - CVTimeStamp ts = m_frameTimeStamp; - m_displayLinkMutex.unlock(); - - Q_EMIT tick(ts); - - return false; - } - break; - default: - break; - } - return QObject::event(event); -} - -#include "moc_qcvdisplaylink.cpp" - diff --git a/src/plugins/qt7/qt7.json b/src/plugins/qt7/qt7.json deleted file mode 100644 index b4cebad99..000000000 --- a/src/plugins/qt7/qt7.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Keys": ["qt7"], - "Services": ["org.qt-project.qt.mediaplayer"] -} diff --git a/src/plugins/qt7/qt7.pro b/src/plugins/qt7/qt7.pro deleted file mode 100644 index b23eba40a..000000000 --- a/src/plugins/qt7/qt7.pro +++ /dev/null @@ -1,67 +0,0 @@ -# Avoid clash with a variable named `slots' in a Quartz header -CONFIG += no_keywords - -TARGET = qqt7engine -QT += multimedia-private network -qtHaveModule(widgets) { - QT += multimediawidgets-private widgets -} - -PLUGIN_TYPE = mediaservice -PLUGIN_CLASS_NAME = QT7ServicePlugin -load(qt_plugin) - -!simulator { -QT += opengl -} - -#DEFINES += QT_DEBUG_QT7 - -LIBS += -framework AppKit -framework AudioUnit \ - -framework AudioToolbox -framework CoreAudio \ - -framework QuartzCore -framework QTKit - -# QUICKTIME_C_API_AVAILABLE is true only on i386 -# so make sure to link QuickTime -contains(QT_ARCH, i386) { - LIBS += -framework QuickTime -} - -HEADERS += \ - qt7backend.h \ - qt7videooutput.h \ - qt7serviceplugin.h - -OBJECTIVE_SOURCES += \ - qt7backend.mm \ - qt7serviceplugin.mm - -!simulator { - HEADERS += \ - qt7movieviewoutput.h \ - qt7movierenderer.h \ - qt7ciimagevideobuffer.h \ - qcvdisplaylink.h - - OBJECTIVE_SOURCES += \ - qt7movieviewoutput.mm \ - qt7movierenderer.mm \ - qt7videooutput.mm \ - qt7ciimagevideobuffer.mm \ - qcvdisplaylink.mm - - qtHaveModule(widgets) { - HEADERS += \ - qt7movieviewrenderer.h \ - qt7movievideowidget.h - - OBJECTIVE_SOURCES += \ - qt7movieviewrenderer.mm \ - qt7movievideowidget.mm - } -} - -include(mediaplayer/mediaplayer.pri) - -OTHER_FILES += \ - qt7.json diff --git a/src/plugins/qt7/qt7backend.h b/src/plugins/qt7/qt7backend.h deleted file mode 100644 index e2aa2c5d8..000000000 --- a/src/plugins/qt7/qt7backend.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7BACKEND_H -#define QT7BACKEND_H - -#include "qtmultimediadefs.h" - -#include <QtCore/QString> - -#ifndef Q_OS_SIMULATOR -#ifndef Q_OS_MAC64 -#define QUICKTIME_C_API_AVAILABLE -#endif -#endif // !defined(Q_WS_SIMULATOR) - -QT_BEGIN_NAMESPACE - -class AutoReleasePool -{ -private: - void *pool; -public: - AutoReleasePool(); - ~AutoReleasePool(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7ciimagevideobuffer.h b/src/plugins/qt7/qt7ciimagevideobuffer.h deleted file mode 100644 index 2ad7957c3..000000000 --- a/src/plugins/qt7/qt7ciimagevideobuffer.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7CIIMAGEVIDEOBUFFER_H -#define QT7CIIMAGEVIDEOBUFFER_H - -#include "qt7backend.h" -#import <QTKit/QTKit.h> - -#include <QtCore/qvariant.h> -#include <qabstractvideobuffer.h> - - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -class QT7CIImageVideoBuffer : public QAbstractVideoBuffer -{ -public: - QT7CIImageVideoBuffer(CIImage *image); - - virtual ~QT7CIImageVideoBuffer(); - - MapMode mapMode() const; - uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); - void unmap(); - QVariant handle() const; - -private: - CIImage *m_image; - NSBitmapImageRep *m_buffer; - MapMode m_mode; -}; - - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7ciimagevideobuffer.mm b/src/plugins/qt7/qt7ciimagevideobuffer.mm deleted file mode 100644 index adb1a689d..000000000 --- a/src/plugins/qt7/qt7ciimagevideobuffer.mm +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qt7ciimagevideobuffer.h" - -#include <QuartzCore/CIFilter.h> -#include <QuartzCore/CIVector.h> - -QT7CIImageVideoBuffer::QT7CIImageVideoBuffer(CIImage *image) - : QAbstractVideoBuffer(CoreImageHandle) - , m_image(image) - , m_buffer(0) - , m_mode(NotMapped) -{ - [m_image retain]; -} - -QT7CIImageVideoBuffer::~QT7CIImageVideoBuffer() -{ - [m_image release]; - [m_buffer release]; -} - -QAbstractVideoBuffer::MapMode QT7CIImageVideoBuffer::mapMode() const -{ - return m_mode; -} - -uchar *QT7CIImageVideoBuffer::map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int *bytesPerLine) -{ - if (mode == NotMapped || m_mode != NotMapped || !m_image) - return 0; - - if (!m_buffer) { - //swap R and B channels - CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues: - @"inputImage", m_image, - @"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0], - @"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0], - @"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0], - @"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1], - @"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0], - nil]; - CIImage *img = [colorSwapFilter valueForKey: @"outputImage"]; - - m_buffer = [[NSBitmapImageRep alloc] initWithCIImage:img]; - } - - if (numBytes) - *numBytes = [m_buffer bytesPerPlane]; - - if (bytesPerLine) - *bytesPerLine = [m_buffer bytesPerRow]; - - m_mode = mode; - - return [m_buffer bitmapData]; -} - -void QT7CIImageVideoBuffer::unmap() -{ - m_mode = NotMapped; -} - -QVariant QT7CIImageVideoBuffer::handle() const -{ - return QVariant::fromValue<void*>(m_image); -} - diff --git a/src/plugins/qt7/qt7movierenderer.h b/src/plugins/qt7/qt7movierenderer.h deleted file mode 100644 index 8abbaf847..000000000 --- a/src/plugins/qt7/qt7movierenderer.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7MOVIERENDERER_H -#define QT7MOVIERENDERER_H - -#include "qt7backend.h" - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> - -#include <qvideorenderercontrol.h> -#include <qmediaplayer.h> - -#include "qt7videooutput.h" - -#include <QuartzCore/CVOpenGLTexture.h> -#include <QuickTime/QuickTime.h> - -QT_BEGIN_NAMESPACE - -class QGLContext; - -class QCvDisplayLink; -class QT7PlayerSession; -class QT7PlayerService; - -class QT7MovieRenderer : public QT7VideoRendererControl -{ -Q_OBJECT -public: - QT7MovieRenderer(QObject *parent = 0); - virtual ~QT7MovieRenderer(); - - void setMovie(void *movie); - void updateNaturalSize(const QSize &newSize); - - QAbstractVideoSurface *surface() const; - void setSurface(QAbstractVideoSurface *surface); - - QSize nativeSize() const; - -private Q_SLOTS: - void updateVideoFrame(const CVTimeStamp &ts); - -private: - void setupVideoOutput(); - bool createPixelBufferVisualContext(); - bool createGLVisualContext(); - - void *m_movie; - - QMutex m_mutex; - - QCvDisplayLink *m_displayLink; -#ifdef QUICKTIME_C_API_AVAILABLE - QTVisualContextRef m_visualContext; - bool m_usingGLContext; - const QGLContext *m_currentGLContext; - QSize m_pixelBufferContextGeometry; -#endif - QAbstractVideoSurface *m_surface; - QSize m_nativeSize; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7movierenderer.mm b/src/plugins/qt7/qt7movierenderer.mm deleted file mode 100644 index 9110b821e..000000000 --- a/src/plugins/qt7/qt7movierenderer.mm +++ /dev/null @@ -1,481 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#import <QTKit/QTKit.h> - -#include "qt7backend.h" - -#include "qt7playercontrol.h" -#include "qt7movierenderer.h" -#include "qt7playersession.h" -#include "qt7ciimagevideobuffer.h" -#include "qcvdisplaylink.h" -#include <QtCore/qdebug.h> -#include <QtCore/qcoreapplication.h> - -#include <qabstractvideobuffer.h> -#include <qabstractvideosurface.h> -#include <qvideosurfaceformat.h> - -#include <QtOpenGL/QGLContext> - -QT_USE_NAMESPACE - -//#define USE_MAIN_MONITOR_COLOR_SPACE 1 - -class CVGLTextureVideoBuffer : public QAbstractVideoBuffer -{ -public: - CVGLTextureVideoBuffer(CVOpenGLTextureRef buffer) - : QAbstractVideoBuffer(GLTextureHandle) - , m_buffer(buffer) - , m_mode(NotMapped) - { - CVOpenGLTextureRetain(m_buffer); - } - - virtual ~CVGLTextureVideoBuffer() - { - CVOpenGLTextureRelease(m_buffer); - } - - QVariant handle() const - { - GLuint id = CVOpenGLTextureGetName(m_buffer); - return QVariant(int(id)); - } - - MapMode mapMode() const { return m_mode; } - - uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) - { - if (numBytes) - *numBytes = 0; - - if (bytesPerLine) - *bytesPerLine = 0; - - m_mode = mode; - return 0; - } - - void unmap() { m_mode = NotMapped; } - -private: - CVOpenGLTextureRef m_buffer; - MapMode m_mode; -}; - - -class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer -{ -public: - CVPixelBufferVideoBuffer(CVPixelBufferRef buffer) - : QAbstractVideoBuffer(NoHandle) - , m_buffer(buffer) - , m_mode(NotMapped) - { - CVPixelBufferRetain(m_buffer); - } - - virtual ~CVPixelBufferVideoBuffer() - { - CVPixelBufferRelease(m_buffer); - } - - MapMode mapMode() const { return m_mode; } - - uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) - { - if (mode != NotMapped && m_mode == NotMapped) { - CVPixelBufferLockBaseAddress(m_buffer, 0); - - if (numBytes) - *numBytes = CVPixelBufferGetDataSize(m_buffer); - - if (bytesPerLine) - *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer); - - m_mode = mode; - - return (uchar*)CVPixelBufferGetBaseAddress(m_buffer); - } else { - return 0; - } - } - - void unmap() - { - if (m_mode != NotMapped) { - m_mode = NotMapped; - CVPixelBufferUnlockBaseAddress(m_buffer, 0); - } - } - -private: - CVPixelBufferRef m_buffer; - MapMode m_mode; -}; - - - -QT7MovieRenderer::QT7MovieRenderer(QObject *parent) - :QT7VideoRendererControl(parent), - m_movie(0), -#ifdef QUICKTIME_C_API_AVAILABLE - m_visualContext(0), - m_usingGLContext(false), - m_currentGLContext(0), -#endif - m_surface(0) -{ -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieRenderer"; -#endif - m_displayLink = new QCvDisplayLink(this); - connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); -} - - -bool QT7MovieRenderer::createGLVisualContext() -{ -#ifdef QUICKTIME_C_API_AVAILABLE - AutoReleasePool pool; - CGLContextObj cglContext = CGLGetCurrentContext(); - NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; - CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); - - OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext, - cglPixelFormat, NULL, &m_visualContext); - if (err != noErr) - qWarning() << "Could not create visual context (OpenGL)"; - - return (err == noErr); -#endif // QUICKTIME_C_API_AVAILABLE - - return false; -} - -#ifdef QUICKTIME_C_API_AVAILABLE -static bool DictionarySetValue(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value) -{ - CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); - - if (number) { - CFDictionarySetValue( dict, key, number ); - CFRelease( number ); - return true; - } - return false; -} -#endif // QUICKTIME_C_API_AVAILABLE - -bool QT7MovieRenderer::createPixelBufferVisualContext() -{ -#ifdef QUICKTIME_C_API_AVAILABLE - if (m_visualContext) { - QTVisualContextRelease(m_visualContext); - m_visualContext = 0; - } - - m_pixelBufferContextGeometry = m_nativeSize; - - CFMutableDictionaryRef pixelBufferOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - //DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32ARGBPixelFormat ); - DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat ); - DictionarySetValue(pixelBufferOptions, kCVPixelBufferWidthKey, m_nativeSize.width() ); - DictionarySetValue(pixelBufferOptions, kCVPixelBufferHeightKey, m_nativeSize.height() ); - DictionarySetValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, 16); - //CFDictionarySetValue(pixelBufferOptions, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); - - CFMutableDictionaryRef visualContextOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions); - - CGColorSpaceRef colorSpace = NULL; - -#if USE_MAIN_MONITOR_COLOR_SPACE - CMProfileRef sysprof = NULL; - - // Get the Systems Profile for the main display - if (CMGetSystemProfile(&sysprof) == noErr) { - // Create a colorspace with the systems profile - colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); - CMCloseProfile(sysprof); - } -#endif - - if (!colorSpace) - colorSpace = CGColorSpaceCreateDeviceRGB(); - - CFDictionarySetValue(visualContextOptions, kQTVisualContextOutputColorSpaceKey, colorSpace); - - OSStatus err = QTPixelBufferContextCreate(kCFAllocatorDefault, - visualContextOptions, - &m_visualContext); - CFRelease(pixelBufferOptions); - CFRelease(visualContextOptions); - - if (err != noErr) { - qWarning() << "Could not create visual context (PixelBuffer)"; - return false; - } - - return true; -#endif // QUICKTIME_C_API_AVAILABLE - - return false; -} - - -QT7MovieRenderer::~QT7MovieRenderer() -{ - m_displayLink->stop(); -} - -void QT7MovieRenderer::setupVideoOutput() -{ - AutoReleasePool pool; - -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieRenderer::setupVideoOutput" << m_movie; -#endif - - if (m_movie == 0 || m_surface == 0) { - m_displayLink->stop(); - return; - } - - NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; - m_nativeSize = QSize(size.width, size.height); - -#ifdef QUICKTIME_C_API_AVAILABLE - bool usedGLContext = m_usingGLContext; - - if (!m_nativeSize.isEmpty()) { - - bool glSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty(); - - //Try rendering using opengl textures first: - if (glSupported) { - QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); - - if (m_surface->isActive()) - m_surface->stop(); - - if (!m_surface->start(format)) { - qWarning() << "failed to start video surface" << m_surface->error(); - qWarning() << "Surface format:" << format; - glSupported = false; - } else { - m_usingGLContext = true; - } - - } - - if (!glSupported) { - m_usingGLContext = false; - QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32); - - if (m_surface->isActive() && m_surface->surfaceFormat() != format) { -#ifdef QT_DEBUG_QT7 - qDebug() << "Surface format was changed, stop the surface."; -#endif - m_surface->stop(); - } - - if (!m_surface->isActive()) { -#ifdef QT_DEBUG_QT7 - qDebug() << "Starting the surface with format" << format; -#endif - if (!m_surface->start(format)) { - qWarning() << "failed to start video surface" << m_surface->error(); - qWarning() << "Surface format:" << format; - } - } - } - } - - - if (m_visualContext) { - //check if the visual context still can be reused - if (usedGLContext != m_usingGLContext || - (m_usingGLContext && (m_currentGLContext != QGLContext::currentContext())) || - (!m_usingGLContext && (m_pixelBufferContextGeometry != m_nativeSize))) { - QTVisualContextRelease(m_visualContext); - m_pixelBufferContextGeometry = QSize(); - m_visualContext = 0; - } - } - - if (!m_nativeSize.isEmpty()) { - if (!m_visualContext) { - if (m_usingGLContext) { -#ifdef QT_DEBUG_QT7 - qDebug() << "Building OpenGL visual context" << m_nativeSize; -#endif - m_currentGLContext = QGLContext::currentContext(); - if (!createGLVisualContext()) { - qWarning() << "QT7MovieRenderer: failed to create visual context"; - return; - } - } else { -#ifdef QT_DEBUG_QT7 - qDebug() << "Building Pixel Buffer visual context" << m_nativeSize; -#endif - if (!createPixelBufferVisualContext()) { - qWarning() << "QT7MovieRenderer: failed to create visual context"; - return; - } - } - } - - // targets a Movie to render into a visual context - SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); - - m_displayLink->start(); - } -#endif - -} - -void QT7MovieRenderer::setMovie(void *movie) -{ -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieRenderer::setMovie" << movie; -#endif - -#ifdef QUICKTIME_C_API_AVAILABLE - QMutexLocker locker(&m_mutex); - - if (m_movie != movie) { - if (m_movie) { - //ensure the old movie doesn't hold the visual context, otherwise it can't be reused - SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); - [(QTMovie*)m_movie release]; - } - - m_movie = movie; - [(QTMovie*)m_movie retain]; - - setupVideoOutput(); - } -#else - Q_UNUSED(movie); -#endif -} - -void QT7MovieRenderer::updateNaturalSize(const QSize &newSize) -{ - if (m_nativeSize != newSize) { - m_nativeSize = newSize; - setupVideoOutput(); - } -} - -QAbstractVideoSurface *QT7MovieRenderer::surface() const -{ - return m_surface; -} - -void QT7MovieRenderer::setSurface(QAbstractVideoSurface *surface) -{ -#ifdef QT_DEBUG_QT7 - qDebug() << "Set video surface" << surface; -#endif - - if (surface == m_surface) - return; - - QMutexLocker locker(&m_mutex); - - if (m_surface && m_surface->isActive()) - m_surface->stop(); - - m_surface = surface; - setupVideoOutput(); -} - - -QSize QT7MovieRenderer::nativeSize() const -{ - return m_nativeSize; -} - -void QT7MovieRenderer::updateVideoFrame(const CVTimeStamp &ts) -{ -#ifdef QUICKTIME_C_API_AVAILABLE - - QMutexLocker locker(&m_mutex); - - if (m_surface && m_surface->isActive() && - m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { - - CVImageBufferRef imageBuffer = NULL; - - OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, &imageBuffer); - - if (status == noErr && imageBuffer) { - QAbstractVideoBuffer *buffer = 0; - - if (m_usingGLContext) { - buffer = new QT7CIImageVideoBuffer([CIImage imageWithCVImageBuffer:imageBuffer]); - CVOpenGLTextureRelease((CVOpenGLTextureRef)imageBuffer); - } else { - buffer = new CVPixelBufferVideoBuffer((CVPixelBufferRef)imageBuffer); - //buffer = new QT7CIImageVideoBuffer( [CIImage imageWithCVImageBuffer:imageBuffer] ); - CVPixelBufferRelease((CVPixelBufferRef)imageBuffer); - } - - QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_RGB32); - m_surface->present(frame); - QTVisualContextTask(m_visualContext); - } - } -#else - Q_UNUSED(ts); -#endif -} - -#include "moc_qt7movierenderer.cpp" diff --git a/src/plugins/qt7/qt7movievideowidget.h b/src/plugins/qt7/qt7movievideowidget.h deleted file mode 100644 index 655037698..000000000 --- a/src/plugins/qt7/qt7movievideowidget.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7MOVIEVIDEOWIDGET_H -#define QT7MOVIEVIDEOWIDGET_H - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> - -#include <qvideowindowcontrol.h> -#include <qmediaplayer.h> - -#include "qt7videooutput.h" - -#include <QuartzCore/CVOpenGLTexture.h> -#include <QuickTime/QuickTime.h> - -class GLVideoWidget; - -QT_BEGIN_NAMESPACE - -class QCvDisplayLink; -class QT7PlayerSession; -class QT7PlayerService; - -class QT7MovieVideoWidget : public QT7VideoWidgetControl -{ -Q_OBJECT -public: - QT7MovieVideoWidget(QObject *parent = 0); - virtual ~QT7MovieVideoWidget(); - - void setMovie(void *movie); - void updateNaturalSize(const QSize &newSize); - - QWidget *videoWidget(); - - bool isFullScreen() const; - void setFullScreen(bool fullScreen); - - 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); - -private Q_SLOTS: - void updateVideoFrame(const CVTimeStamp &ts); - -private: - void setupVideoOutput(); - bool createVisualContext(); - - void updateColors(); - - void *m_movie; - GLVideoWidget *m_videoWidget; - - QCvDisplayLink *m_displayLink; - -#ifdef QUICKTIME_C_API_AVAILABLE - QTVisualContextRef m_visualContext; -#endif - - bool m_fullscreen; - QSize m_nativeSize; - Qt::AspectRatioMode m_aspectRatioMode; - int m_brightness; - int m_contrast; - int m_hue; - int m_saturation; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7movievideowidget.mm b/src/plugins/qt7/qt7movievideowidget.mm deleted file mode 100644 index ec9367bcc..000000000 --- a/src/plugins/qt7/qt7movievideowidget.mm +++ /dev/null @@ -1,437 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qt7backend.h" - -#import <QTKit/QTDataReference.h> -#import <QTKit/QTMovie.h> -#import <QTKit/QTMovieView.h> -#import <QTKit/QTMovieLayer.h> -#import <AppKit/NSImage.h> -#import <OpenGL/glu.h> - - -#include "qt7playercontrol.h" -#include "qt7movievideowidget.h" -#include "qt7playersession.h" -#include "qcvdisplaylink.h" -#include <QtCore/qdebug.h> -#include <QtCore/qcoreapplication.h> - -#include <QGLWidget> - -#include <CoreFoundation/CoreFoundation.h> - -#import <QuartzCore/QuartzCore.h> - -#include "math.h" - -QT_USE_NAMESPACE - -class GLVideoWidget : public QGLWidget -{ -public: - - GLVideoWidget(QWidget *parent, const QGLFormat &format) - : QGLWidget(format, parent), - m_texRef(0), - m_nativeSize(640,480), - m_aspectRatioMode(Qt::KeepAspectRatio) - { - setAutoFillBackground(false); - } - - void initializeGL() - { - QColor bgColor = palette().color(QPalette::Background); - glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), bgColor.alphaF()); - } - - void resizeGL(int w, int h) - { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glViewport(0, 0, GLsizei(w), GLsizei(h)); - gluOrtho2D(0, GLsizei(w), 0, GLsizei(h)); - updateGL(); - } - - void paintGL() - { - glClear(GL_COLOR_BUFFER_BIT); - if (!m_texRef) - return; - - glPushMatrix(); - glDisable(GL_CULL_FACE); - GLenum target = CVOpenGLTextureGetTarget(m_texRef); - glEnable(target); - - glBindTexture(target, CVOpenGLTextureGetName(m_texRef)); - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; - CVOpenGLTextureGetCleanTexCoords(m_texRef, lowerLeft, lowerRight, upperRight, upperLeft); - - glBegin(GL_QUADS); - QRect rect = displayRect(); - glTexCoord2f(lowerLeft[0], lowerLeft[1]); - glVertex2i(rect.topLeft().x(), rect.topLeft().y()); - glTexCoord2f(lowerRight[0], lowerRight[1]); - glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); - glTexCoord2f(upperRight[0], upperRight[1]); - glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); - glTexCoord2f(upperLeft[0], upperLeft[1]); - glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); - glEnd(); - glPopMatrix(); - } - - void setCVTexture(CVOpenGLTextureRef texRef) - { - if (m_texRef) - CVOpenGLTextureRelease(m_texRef); - - m_texRef = texRef; - - if (m_texRef) - CVOpenGLTextureRetain(m_texRef); - - if (isVisible()) { - makeCurrent(); - paintGL(); - swapBuffers(); - } - } - - QSize sizeHint() const - { - return m_nativeSize; - } - - void setNativeSize(const QSize &size) - { - m_nativeSize = size; - } - - void setAspectRatioMode(Qt::AspectRatioMode mode) - { - if (m_aspectRatioMode != mode) { - m_aspectRatioMode = mode; - update(); - } - } - -private: - QRect displayRect() const - { - QRect displayRect = rect(); - - if (m_aspectRatioMode == Qt::KeepAspectRatio) { - QSize size = m_nativeSize; - size.scale(displayRect.size(), Qt::KeepAspectRatio); - - displayRect = QRect(QPoint(0, 0), size); - displayRect.moveCenter(rect().center()); - } - return displayRect; - } - - CVOpenGLTextureRef m_texRef; - QSize m_nativeSize; - Qt::AspectRatioMode m_aspectRatioMode; -}; - -QT7MovieVideoWidget::QT7MovieVideoWidget(QObject *parent) - :QT7VideoWidgetControl(parent), - m_movie(0), - m_videoWidget(0), - m_fullscreen(false), - m_aspectRatioMode(Qt::KeepAspectRatio), - m_brightness(0), - m_contrast(0), - m_hue(0), - m_saturation(0) -{ -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieVideoWidget"; -#endif - - QGLFormat format = QGLFormat::defaultFormat(); - format.setSwapInterval(1); // Vertical sync (avoid tearing) - m_videoWidget = new GLVideoWidget(0, format); - - m_displayLink = new QCvDisplayLink(this); - - connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); - - if (!createVisualContext()) { - qWarning() << "QT7MovieVideoWidget: failed to create visual context"; - } -} - -bool QT7MovieVideoWidget::createVisualContext() -{ -#ifdef QUICKTIME_C_API_AVAILABLE - m_videoWidget->makeCurrent(); - - AutoReleasePool pool; - CGLContextObj cglContext = CGLGetCurrentContext(); - NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; - CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); - - CFTypeRef keys[] = { kQTVisualContextOutputColorSpaceKey }; - CGColorSpaceRef colorSpace = NULL; - CMProfileRef sysprof = NULL; - - // Get the Systems Profile for the main display - if (CMGetSystemProfile(&sysprof) == noErr) { - // Create a colorspace with the systems profile - colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); - CMCloseProfile(sysprof); - } - - if (!colorSpace) - colorSpace = CGColorSpaceCreateDeviceRGB(); - - CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault, - (const void **)keys, - (const void **)&colorSpace, 1, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, - cglContext, - cglPixelFormat, - textureContextAttributes, - &m_visualContext); - if (err != noErr) - qWarning() << "Could not create visual context (OpenGL)"; - - - return (err == noErr); -#endif // QUICKTIME_C_API_AVAILABLE - - return false; -} - -QT7MovieVideoWidget::~QT7MovieVideoWidget() -{ - m_displayLink->stop(); - [(QTMovie*)m_movie release]; - delete m_videoWidget; -} - -QWidget *QT7MovieVideoWidget::videoWidget() -{ - return m_videoWidget; -} - -void QT7MovieVideoWidget::setupVideoOutput() -{ - AutoReleasePool pool; - -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieVideoWidget::setupVideoOutput" << m_movie; -#endif - - if (m_movie == 0) { - m_displayLink->stop(); - return; - } - - NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; - m_nativeSize = QSize(size.width, size.height); - m_videoWidget->setNativeSize(m_nativeSize); - -#ifdef QUICKTIME_C_API_AVAILABLE - // targets a Movie to render into a visual context - SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); -#endif - - m_displayLink->start(); -} - -void QT7MovieVideoWidget::setMovie(void *movie) -{ - if (m_movie == movie) - return; - - if (m_movie) { -#ifdef QUICKTIME_C_API_AVAILABLE - SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); -#endif - [(QTMovie*)m_movie release]; - } - - m_movie = movie; - [(QTMovie*)m_movie retain]; - - setupVideoOutput(); -} - -void QT7MovieVideoWidget::updateNaturalSize(const QSize &newSize) -{ - if (m_nativeSize != newSize) { - m_nativeSize = newSize; - setupVideoOutput(); - } -} - -bool QT7MovieVideoWidget::isFullScreen() const -{ - return m_fullscreen; -} - -void QT7MovieVideoWidget::setFullScreen(bool fullScreen) -{ - m_fullscreen = fullScreen; -} - -QSize QT7MovieVideoWidget::nativeSize() const -{ - return m_nativeSize; -} - -Qt::AspectRatioMode QT7MovieVideoWidget::aspectRatioMode() const -{ - return m_aspectRatioMode; -} - -void QT7MovieVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_aspectRatioMode = mode; - m_videoWidget->setAspectRatioMode(mode); -} - -int QT7MovieVideoWidget::brightness() const -{ - return m_brightness; -} - -void QT7MovieVideoWidget::setBrightness(int brightness) -{ - m_brightness = brightness; - updateColors(); -} - -int QT7MovieVideoWidget::contrast() const -{ - return m_contrast; -} - -void QT7MovieVideoWidget::setContrast(int contrast) -{ - m_contrast = contrast; - updateColors(); -} - -int QT7MovieVideoWidget::hue() const -{ - return m_hue; -} - -void QT7MovieVideoWidget::setHue(int hue) -{ - m_hue = hue; - updateColors(); -} - -int QT7MovieVideoWidget::saturation() const -{ - return m_saturation; -} - -void QT7MovieVideoWidget::setSaturation(int saturation) -{ - m_saturation = saturation; - updateColors(); -} - -void QT7MovieVideoWidget::updateColors() -{ -#ifdef QUICKTIME_C_API_AVAILABLE - if (m_movie) { - QTMovie *movie = (QTMovie*)m_movie; - - Float32 value; - value = m_brightness/100.0; - SetMovieVisualBrightness([movie quickTimeMovie], value, 0); - value = pow(2, m_contrast/50.0); - SetMovieVisualContrast([movie quickTimeMovie], value, 0); - value = m_hue/100.0; - SetMovieVisualHue([movie quickTimeMovie], value, 0); - value = 1.0+m_saturation/100.0; - SetMovieVisualSaturation([movie quickTimeMovie], value, 0); - } -#endif -} - -void QT7MovieVideoWidget::updateVideoFrame(const CVTimeStamp &ts) -{ -#ifdef QUICKTIME_C_API_AVAILABLE - AutoReleasePool pool; - // check for new frame - if (m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { - CVOpenGLTextureRef currentFrame = NULL; - - // get a "frame" (image buffer) from the Visual Context, indexed by the provided time - OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, ¤tFrame); - - // the above call may produce a null frame so check for this first - // if we have a frame, then draw it - if (status == noErr && currentFrame) { -#ifdef QT_DEBUG_QT7 - qDebug() << "render video frame"; -#endif - m_videoWidget->setCVTexture(currentFrame); - CVOpenGLTextureRelease(currentFrame); - } - QTVisualContextTask(m_visualContext); - } -#else - Q_UNUSED(ts); -#endif -} - -#include "moc_qt7movievideowidget.cpp" diff --git a/src/plugins/qt7/qt7movieviewoutput.h b/src/plugins/qt7/qt7movieviewoutput.h deleted file mode 100644 index 874de1a09..000000000 --- a/src/plugins/qt7/qt7movieviewoutput.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7MOVIEVIEWOUTPUT_H -#define QT7MOVIEVIEWOUTPUT_H - -#include <QtCore/qobject.h> - -#include <qvideowindowcontrol.h> -#include <qmediaplayer.h> - -#include "qt7videooutput.h" - - -QT_BEGIN_NAMESPACE - -class QT7PlayerSession; -class QT7PlayerService; - -class QT7MovieViewOutput : public QT7VideoWindowControl -{ -public: - QT7MovieViewOutput(QObject *parent = 0); - ~QT7MovieViewOutput(); - - void setMovie(void *movie); - void updateNaturalSize(const QSize &newSize); - - 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); - -private: - void setupVideoOutput(); - - void *m_movie; - void *m_movieView; - bool m_layouted; - - WId m_winId; - QRect m_displayRect; - bool m_fullscreen; - QSize m_nativeSize; - Qt::AspectRatioMode m_aspectRatioMode; - int m_brightness; - int m_contrast; - int m_hue; - int m_saturation; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7movieviewoutput.mm b/src/plugins/qt7/qt7movieviewoutput.mm deleted file mode 100644 index 4fcf01d39..000000000 --- a/src/plugins/qt7/qt7movieviewoutput.mm +++ /dev/null @@ -1,339 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#import <QTKit/QTKit.h> - -#include "qt7backend.h" - -#include "qt7playercontrol.h" -#include "qt7movieviewoutput.h" -#include "qt7playersession.h" -#include <QtCore/qdebug.h> - -#include <QuartzCore/CIFilter.h> -#include <QuartzCore/CIVector.h> - -QT_USE_NAMESPACE - -#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} - -@interface TransparentQTMovieView : QTMovieView -{ -@private - QRect *m_drawRect; - qreal m_brightness, m_contrast, m_saturation, m_hue; -} - -- (TransparentQTMovieView *) init; -- (void) setDrawRect:(QRect &)rect; -- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img; -- (void) setContrast:(qreal) contrast; -@end - -@implementation TransparentQTMovieView - -- (TransparentQTMovieView *) init -{ - self = [super initWithFrame:NSZeroRect]; - if (self) { - [self setControllerVisible:NO]; - [self setContrast:1.0]; - [self setDelegate:self]; - } - return self; -} - -- (void) dealloc -{ - [super dealloc]; -} - -- (void) setContrast:(qreal) contrast -{ - m_hue = 0.0; - m_brightness = 0.0; - m_contrast = contrast; - m_saturation = 1.0; -} - - -- (void) setDrawRect:(QRect &)rect -{ - *m_drawRect = rect; - - NSRect nsrect; - nsrect.origin.x = m_drawRect->x(); - nsrect.origin.y = m_drawRect->y(); - nsrect.size.width = m_drawRect->width(); - nsrect.size.height = m_drawRect->height(); - [self setFrame:nsrect]; -} - -- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img -{ - // This method is called from QTMovieView just - // before the image will be drawn. - Q_UNUSED(view); - - if ( !qFuzzyCompare(m_brightness, 0.0) || - !qFuzzyCompare(m_contrast, 1.0) || - !qFuzzyCompare(m_saturation, 1.0)){ - CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"]; - [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"]; - [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"]; - [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"]; - [colorFilter setValue:img forKey:@"inputImage"]; - img = [colorFilter valueForKey:@"outputImage"]; - } - - /*if (m_hue){ - CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"]; - [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"]; - [colorFilter setValue:img forKey:@"inputImage"]; - img = [colorFilter valueForKey:@"outputImage"]; - }*/ - - return img; -} - - -VIDEO_TRANSPARENT(mouseDown); -VIDEO_TRANSPARENT(mouseDragged); -VIDEO_TRANSPARENT(mouseUp); -VIDEO_TRANSPARENT(mouseMoved); -VIDEO_TRANSPARENT(mouseEntered); -VIDEO_TRANSPARENT(mouseExited); -VIDEO_TRANSPARENT(rightMouseDown); -VIDEO_TRANSPARENT(rightMouseDragged); -VIDEO_TRANSPARENT(rightMouseUp); -VIDEO_TRANSPARENT(otherMouseDown); -VIDEO_TRANSPARENT(otherMouseDragged); -VIDEO_TRANSPARENT(otherMouseUp); -VIDEO_TRANSPARENT(keyDown); -VIDEO_TRANSPARENT(keyUp); -VIDEO_TRANSPARENT(scrollWheel) - -@end - - -QT7MovieViewOutput::QT7MovieViewOutput(QObject *parent) - :QT7VideoWindowControl(parent), - m_movie(0), - m_movieView(0), - m_layouted(false), - m_winId(0), - m_fullscreen(false), - m_aspectRatioMode(Qt::KeepAspectRatio), - m_brightness(0), - m_contrast(0), - m_hue(0), - m_saturation(0) -{ -} - -QT7MovieViewOutput::~QT7MovieViewOutput() -{ - [(QTMovieView*)m_movieView release]; - [(QTMovie*)m_movie release]; -} - -void QT7MovieViewOutput::setupVideoOutput() -{ - AutoReleasePool pool; - -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieViewOutput::setupVideoOutput" << m_movie << m_winId; -#endif - if (m_movie == 0 || m_winId <= 0) - return; - - NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; - m_nativeSize = QSize(size.width, size.height); - - if (!m_movieView) - m_movieView = [[TransparentQTMovieView alloc] init]; - - [(QTMovieView*)m_movieView setControllerVisible:NO]; - [(QTMovieView*)m_movieView setMovie:(QTMovie*)m_movie]; - - [(NSView *)m_winId addSubview:(QTMovieView*)m_movieView]; - m_layouted = true; - - setDisplayRect(m_displayRect); -} - -void QT7MovieViewOutput::setMovie(void *movie) -{ - if (m_movie != movie) { - if (m_movie) { - if (m_movieView) - [(QTMovieView*)m_movieView setMovie:nil]; - - [(QTMovie*)m_movie release]; - } - - m_movie = movie; - - if (m_movie) - [(QTMovie*)m_movie retain]; - - setupVideoOutput(); - } -} - -void QT7MovieViewOutput::updateNaturalSize(const QSize &newSize) -{ - if (m_nativeSize != newSize) { - m_nativeSize = newSize; - Q_EMIT nativeSizeChanged(); - } -} - -WId QT7MovieViewOutput::winId() const -{ - return m_winId; -} - -void QT7MovieViewOutput::setWinId(WId id) -{ - if (m_winId != id) { - if (m_movieView && m_layouted) { - [(QTMovieView*)m_movieView removeFromSuperview]; - m_layouted = false; - } - - m_winId = id; - setupVideoOutput(); - } -} - -QRect QT7MovieViewOutput::displayRect() const -{ - return m_displayRect; -} - -void QT7MovieViewOutput::setDisplayRect(const QRect &rect) -{ - m_displayRect = rect; - - if (m_movieView) { - AutoReleasePool pool; - [(QTMovieView*)m_movieView setPreservesAspectRatio:(m_aspectRatioMode == Qt::KeepAspectRatio ? YES : NO)]; - [(QTMovieView*)m_movieView setFrame:NSMakeRect(m_displayRect.x(), - m_displayRect.y(), - m_displayRect.width(), - m_displayRect.height())]; - } - -} - -bool QT7MovieViewOutput::isFullScreen() const -{ - return m_fullscreen; -} - -void QT7MovieViewOutput::setFullScreen(bool fullScreen) -{ - m_fullscreen = fullScreen; - setDisplayRect(m_displayRect); -} - -void QT7MovieViewOutput::repaint() -{ -} - -QSize QT7MovieViewOutput::nativeSize() const -{ - return m_nativeSize; -} - -Qt::AspectRatioMode QT7MovieViewOutput::aspectRatioMode() const -{ - return m_aspectRatioMode; -} - -void QT7MovieViewOutput::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_aspectRatioMode = mode; - setDisplayRect(m_displayRect); -} - -int QT7MovieViewOutput::brightness() const -{ - return m_brightness; -} - -void QT7MovieViewOutput::setBrightness(int brightness) -{ - m_brightness = brightness; -} - -int QT7MovieViewOutput::contrast() const -{ - return m_contrast; -} - -void QT7MovieViewOutput::setContrast(int contrast) -{ - m_contrast = contrast; - [(TransparentQTMovieView*)m_movieView setContrast:(contrast/100.0+1.0)]; -} - -int QT7MovieViewOutput::hue() const -{ - return m_hue; -} - -void QT7MovieViewOutput::setHue(int hue) -{ - m_hue = hue; -} - -int QT7MovieViewOutput::saturation() const -{ - return m_saturation; -} - -void QT7MovieViewOutput::setSaturation(int saturation) -{ - m_saturation = saturation; -} - diff --git a/src/plugins/qt7/qt7movieviewrenderer.h b/src/plugins/qt7/qt7movieviewrenderer.h deleted file mode 100644 index 2416fe7b3..000000000 --- a/src/plugins/qt7/qt7movieviewrenderer.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7MOVIEVIEWRENDERER_H -#define QT7MOVIEVIEWRENDERER_H - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> - -#include <qvideowindowcontrol.h> -#include <qmediaplayer.h> - -#include "qt7videooutput.h" -#include <qvideoframe.h> - -#include <QuartzCore/CIContext.h> - -QT_BEGIN_NAMESPACE - -class QVideoFrame; - -class QT7PlayerSession; -class QT7PlayerService; -class QGLWidget; -class QGLFramebufferObject; -class QWindow; -class QOpenGLContext; - -class QT7MovieViewRenderer : public QT7VideoRendererControl -{ -public: - QT7MovieViewRenderer(QObject *parent = 0); - ~QT7MovieViewRenderer(); - - void setMovie(void *movie); - void updateNaturalSize(const QSize &newSize); - - QAbstractVideoSurface *surface() const; - void setSurface(QAbstractVideoSurface *surface); - - void renderFrame(const QVideoFrame &); - -protected: - bool event(QEvent *event); - -private: - void setupVideoOutput(); - QVideoFrame convertCIImageToGLTexture(const QVideoFrame &frame); - - void *m_movie; - void *m_movieView; - QSize m_nativeSize; - QAbstractVideoSurface *m_surface; - QVideoFrame m_currentFrame; - QWindow *m_window; - QOpenGLContext *m_context; - QGLFramebufferObject *m_fbo; - CIContext *m_ciContext; - - bool m_pendingRenderEvent; - QMutex m_mutex; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/qt7/qt7movieviewrenderer.mm b/src/plugins/qt7/qt7movieviewrenderer.mm deleted file mode 100644 index a59031577..000000000 --- a/src/plugins/qt7/qt7movieviewrenderer.mm +++ /dev/null @@ -1,509 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#import <QTKit/QTKit.h> - -#include "qt7backend.h" - -#include "qt7playercontrol.h" -#include "qt7movieviewrenderer.h" -#include "qt7playersession.h" -#include "qt7ciimagevideobuffer.h" -#include <QtCore/qdebug.h> -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtGui/qwindow.h> -#include <QtGui/qopenglcontext.h> -#include <QtOpenGL/qgl.h> -#include <QtOpenGL/qglframebufferobject.h> - -#include <QtCore/qreadwritelock.h> - -#include <qabstractvideobuffer.h> -#include <qabstractvideosurface.h> -#include <qvideosurfaceformat.h> - -#include <QuartzCore/CIFilter.h> -#include <QuartzCore/CIVector.h> - -QT_USE_NAMESPACE - -class NSBitmapVideoBuffer : public QAbstractVideoBuffer -{ -public: - NSBitmapVideoBuffer(NSBitmapImageRep *buffer) - : QAbstractVideoBuffer(NoHandle) - , m_buffer(buffer) - , m_mode(NotMapped) - { - [m_buffer retain]; - } - - virtual ~NSBitmapVideoBuffer() - { - [m_buffer release]; - } - - MapMode mapMode() const { return m_mode; } - - uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) - { - if (mode != NotMapped && m_mode == NotMapped) { - if (numBytes) - *numBytes = [m_buffer bytesPerPlane]; - - if (bytesPerLine) - *bytesPerLine = [m_buffer bytesPerRow]; - - m_mode = mode; - - return [m_buffer bitmapData]; - } else { - return 0; - } - } - - void unmap() { m_mode = NotMapped; } - -private: - NSBitmapImageRep *m_buffer; - MapMode m_mode; -}; - -class TextureVideoBuffer : public QAbstractVideoBuffer -{ -public: - TextureVideoBuffer(GLuint textureId) - : QAbstractVideoBuffer(GLTextureHandle) - , m_textureId(textureId) - {} - - virtual ~TextureVideoBuffer() {} - - MapMode mapMode() const { return NotMapped; } - uchar *map(MapMode, int*, int*) { return 0; } - void unmap() {} - - QVariant handle() const - { - return QVariant::fromValue<unsigned int>(m_textureId); - } - -private: - GLuint m_textureId; -}; - - -#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} - -@interface HiddenQTMovieView : QTMovieView -{ -@private - QWindow *m_window; - QT7MovieViewRenderer *m_renderer; - QReadWriteLock m_rendererLock; -} - -- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer; -- (void) setRenderer:(QT7MovieViewRenderer *)renderer; -- (void) setDrawRect:(const QRect &)rect; -- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img; -@end - -@implementation HiddenQTMovieView - -- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer -{ - self = [super initWithFrame:NSZeroRect]; - if (self) { - [self setControllerVisible:NO]; - [self setDelegate:self]; - - QWriteLocker lock(&self->m_rendererLock); - self->m_renderer = renderer; - - self->m_window = new QWindow; - self->m_window->setOpacity(0.0); - self->m_window->setGeometry(0,0,1,1); - self->m_window->create(); - - [(NSView *)(self->m_window->winId()) addSubview:self]; - [self setDrawRect:QRect(0,0,1,1)]; - } - return self; -} - -- (void) dealloc -{ - self->m_window->deleteLater(); - [super dealloc]; -} - -- (void) setRenderer:(QT7MovieViewRenderer *)renderer -{ - QWriteLocker lock(&m_rendererLock); - m_renderer = renderer; -} - -- (void) setDrawRect:(const QRect &)rect -{ - NSRect nsrect; - nsrect.origin.x = rect.x(); - nsrect.origin.y = rect.y(); - nsrect.size.width = rect.width(); - nsrect.size.height = rect.height(); - [self setFrame:nsrect]; -} - -- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img -{ - // This method is called from QTMovieView just - // before the image will be drawn. - Q_UNUSED(view); - QReadLocker lock(&m_rendererLock); - AutoReleasePool pool; - - if (m_renderer) { - CGRect bounds = [img extent]; - int w = bounds.size.width; - int h = bounds.size.height; - - QVideoFrame frame; - - QAbstractVideoSurface *surface = m_renderer->surface(); - if (!surface || !surface->isActive()) - return img; - - if (surface->surfaceFormat().handleType() == QAbstractVideoBuffer::CoreImageHandle || - surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) { - //surface supports rendering of opengl based CIImage - frame = QVideoFrame(new QT7CIImageVideoBuffer(img), QSize(w,h), QVideoFrame::Format_RGB32 ); - } else { - //Swap R and B colors - CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues: - @"inputImage", img, - @"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0], - @"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0], - @"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0], - @"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1], - @"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0], - nil]; - CIImage *img = [colorSwapFilter valueForKey: @"outputImage"]; - NSBitmapImageRep *bitmap =[[NSBitmapImageRep alloc] initWithCIImage:img]; - //requesting the bitmap data is slow, - //but it's better to do it here to avoid blocking the main thread for a long. - [bitmap bitmapData]; - frame = QVideoFrame(new NSBitmapVideoBuffer(bitmap), QSize(w,h), QVideoFrame::Format_RGB32 ); - [bitmap release]; - } - - m_renderer->renderFrame(frame); - } - - return img; -} - -// Override this method so that the movie doesn't stop if -// the window becomes invisible -- (void)viewWillMoveToWindow:(NSWindow *)newWindow -{ - Q_UNUSED(newWindow); -} - - -VIDEO_TRANSPARENT(mouseDown); -VIDEO_TRANSPARENT(mouseDragged); -VIDEO_TRANSPARENT(mouseUp); -VIDEO_TRANSPARENT(mouseMoved); -VIDEO_TRANSPARENT(mouseEntered); -VIDEO_TRANSPARENT(mouseExited); -VIDEO_TRANSPARENT(rightMouseDown); -VIDEO_TRANSPARENT(rightMouseDragged); -VIDEO_TRANSPARENT(rightMouseUp); -VIDEO_TRANSPARENT(otherMouseDown); -VIDEO_TRANSPARENT(otherMouseDragged); -VIDEO_TRANSPARENT(otherMouseUp); -VIDEO_TRANSPARENT(keyDown); -VIDEO_TRANSPARENT(keyUp); -VIDEO_TRANSPARENT(scrollWheel) - -@end - - -QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent) - :QT7VideoRendererControl(parent), - m_movie(0), - m_movieView(0), - m_surface(0), - m_window(0), - m_context(0), - m_fbo(0), - m_ciContext(0), - m_pendingRenderEvent(false) -{ -} - -QT7MovieViewRenderer::~QT7MovieViewRenderer() -{ - [(HiddenQTMovieView*)m_movieView setRenderer:0]; - - QMutexLocker locker(&m_mutex); - m_currentFrame = QVideoFrame(); - [(HiddenQTMovieView*)m_movieView release]; - [m_ciContext release]; - delete m_fbo; - delete m_window; -} - -void QT7MovieViewRenderer::setupVideoOutput() -{ - AutoReleasePool pool; - -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7MovieViewRenderer::setupVideoOutput" << m_movie << m_surface; -#endif - - HiddenQTMovieView *movieView = (HiddenQTMovieView*)m_movieView; - - if (movieView && !m_movie) { - [movieView setMovie:nil]; - } - - if (m_movie) { - NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; - - m_nativeSize = QSize(size.width, size.height); - - if (!movieView) { - movieView = [[HiddenQTMovieView alloc] initWithRenderer:this]; - m_movieView = movieView; - [movieView setControllerVisible:NO]; - } - - [movieView setMovie:(QTMovie*)m_movie]; - [movieView setDrawRect:QRect(QPoint(0,0), m_nativeSize)]; - } else { - m_nativeSize = QSize(); - } - - if (m_surface && !m_nativeSize.isEmpty()) { - bool coreImageFrameSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::CoreImageHandle).isEmpty(); - bool glTextureSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty(); - - QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle; - QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_RGB32; - - if (coreImageFrameSupported) { - handleType = QAbstractVideoBuffer::CoreImageHandle; - } else if (glTextureSupported) { - handleType = QAbstractVideoBuffer::GLTextureHandle; - pixelFormat = QVideoFrame::Format_BGR32; - } - - QVideoSurfaceFormat format(m_nativeSize, pixelFormat, handleType); - - if (m_surface->isActive() && m_surface->surfaceFormat() != format) { -#ifdef QT_DEBUG_QT7 - qDebug() << "Surface format was changed, stop the surface."; -#endif - m_surface->stop(); - } - - if (!m_surface->isActive()) { -#ifdef QT_DEBUG_QT7 - qDebug() << "Starting the surface with format" << format; -#endif - if (!m_surface->start(format)) - qWarning() << "failed to start video surface" << m_surface->error(); - } - } -} - -/*! - Render the CIImage based video frame to FBO and return the video frame with resulting texture -*/ -QVideoFrame QT7MovieViewRenderer::convertCIImageToGLTexture(const QVideoFrame &frame) -{ - if (frame.handleType() != QAbstractVideoBuffer::CoreImageHandle) - return QVideoFrame(); - - if (!m_window) { - QOpenGLContext *qGlContext = 0; - - if (m_surface) - qGlContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()); - - if (qGlContext) { - m_window = new QWindow(); - - QSurfaceFormat format(qGlContext->format()); - - m_context = new QOpenGLContext(m_window); - m_context->setShareContext(qGlContext); - m_context->setFormat(format); - m_context->create(); - - m_window->setFormat(format); - m_window->setGeometry(0, 0, 1, 1); - m_window->setSurfaceType(QWindow::OpenGLSurface); - m_window->create(); - } else { - return QVideoFrame(); - } - } - - if (!m_context) - return QVideoFrame(); - - m_context->makeCurrent(m_window); - - if (!m_fbo || m_fbo->size() != frame.size()) { - delete m_fbo; - m_fbo = new QGLFramebufferObject(frame.size()); - } - - CIImage *ciImg = (CIImage*)(frame.handle().value<void*>()); - if (ciImg) { - AutoReleasePool pool; - - QPainter p(m_fbo); - p.beginNativePainting(); - CGLContextObj cglContext = CGLGetCurrentContext(); - if (cglContext) { - if (!m_ciContext) { - NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; - CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); - - m_ciContext = [CIContext contextWithCGLContext:cglContext - pixelFormat:cglPixelFormat - colorSpace:nil - options:nil]; - - [m_ciContext retain]; - } - - QRect viewport = QRect(0, 0, frame.width(), frame.height()); - CGRect sRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height()); - CGRect dRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height()); - - [m_ciContext drawImage:ciImg inRect:dRect fromRect:sRect]; - } - - p.endNativePainting(); - - QAbstractVideoBuffer *buffer = new TextureVideoBuffer(m_fbo->texture()); - return QVideoFrame(buffer, frame.size(), QVideoFrame::Format_BGR32); - } - - return QVideoFrame(); -} - -void QT7MovieViewRenderer::setMovie(void *movie) -{ - if (movie == m_movie) - return; - - QMutexLocker locker(&m_mutex); - m_movie = movie; - setupVideoOutput(); -} - -void QT7MovieViewRenderer::updateNaturalSize(const QSize &newSize) -{ - if (m_nativeSize != newSize) { - m_nativeSize = newSize; - setupVideoOutput(); - } -} - -QAbstractVideoSurface *QT7MovieViewRenderer::surface() const -{ - return m_surface; -} - -void QT7MovieViewRenderer::setSurface(QAbstractVideoSurface *surface) -{ - if (surface == m_surface) - return; - - QMutexLocker locker(&m_mutex); - - if (m_surface && m_surface->isActive()) - m_surface->stop(); - - m_surface = surface; - setupVideoOutput(); -} - -void QT7MovieViewRenderer::renderFrame(const QVideoFrame &frame) -{ - QMutexLocker locker(&m_mutex); - m_currentFrame = frame; - - if (!m_pendingRenderEvent) - qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); - - m_pendingRenderEvent = true; -} - -bool QT7MovieViewRenderer::event(QEvent *event) -{ - if (event->type() == QEvent::User) { - QMutexLocker locker(&m_mutex); - m_pendingRenderEvent = false; - - if (m_surface->isActive()) { - //For GL texture frames, render in the main thread CIImage based buffers - //to FBO shared with video surface shared context - if (m_surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) { - m_currentFrame = convertCIImageToGLTexture(m_currentFrame); - if (m_currentFrame.isValid()) - m_surface->present(m_currentFrame); - } else { - m_surface->present(m_currentFrame); - } - } - - m_currentFrame = QVideoFrame(); - } - - return QT7VideoRendererControl::event(event); -} diff --git a/src/plugins/qt7/qt7serviceplugin.h b/src/plugins/qt7/qt7serviceplugin.h deleted file mode 100644 index 2e04297ff..000000000 --- a/src/plugins/qt7/qt7serviceplugin.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QT7SERVICEPLUGIN_H -#define QT7SERVICEPLUGIN_H - -#include <qmediaserviceproviderplugin.h> - -QT_BEGIN_NAMESPACE - -class QT7ServicePlugin - : public QMediaServiceProviderPlugin - , public QMediaServiceSupportedFormatsInterface - , public QMediaServiceFeaturesInterface -{ - Q_OBJECT - Q_INTERFACES(QMediaServiceFeaturesInterface) - Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "qt7.json") - -public: - QT7ServicePlugin(); - - QMediaService* create(QString const& key); - void release(QMediaService *service); - - QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; - QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const; - QStringList supportedMimeTypes() const; - -private: - void buildSupportedTypes(); - - QStringList m_supportedMimeTypes; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/qt7/qt7videooutput.h b/src/plugins/qt7/qt7videooutput.h deleted file mode 100644 index 86fc273dd..000000000 --- a/src/plugins/qt7/qt7videooutput.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT7VIDEOOUTPUTCONTROL_H -#define QT7VIDEOOUTPUTCONTROL_H - -#include <QtCore/qobject.h> -#include <QtCore/qsize.h> - -#include <qvideowindowcontrol.h> -#ifndef QT_NO_WIDGETS -#include <qvideowidgetcontrol.h> -#endif -#include <qvideorenderercontrol.h> -#include <qmediaplayer.h> - -QT_BEGIN_NAMESPACE - -class QMediaPlaylist; -class QMediaPlaylistNavigator; -class QT7PlayerSession; -class QT7PlayerService; - - -class QT7VideoOutput { -public: - virtual ~QT7VideoOutput() {} - virtual void setMovie(void *movie) = 0; - virtual void updateNaturalSize(const QSize &newSize) = 0; -}; - -#define QT7VideoOutput_iid \ - "org.qt-project.qt.QT7VideoOutput/5.0" -Q_DECLARE_INTERFACE(QT7VideoOutput, QT7VideoOutput_iid) - -class QT7VideoWindowControl : public QVideoWindowControl, public QT7VideoOutput -{ -Q_OBJECT -Q_INTERFACES(QT7VideoOutput) -public: - virtual ~QT7VideoWindowControl() {} - -protected: - QT7VideoWindowControl(QObject *parent) - :QVideoWindowControl(parent) - {} -}; - -class QT7VideoRendererControl : public QVideoRendererControl, public QT7VideoOutput -{ -Q_OBJECT -Q_INTERFACES(QT7VideoOutput) -public: - virtual ~QT7VideoRendererControl() {} - -protected: - QT7VideoRendererControl(QObject *parent) - :QVideoRendererControl(parent) - {} -}; - -#ifndef QT_NO_WIDGETS -class QT7VideoWidgetControl : public QVideoWidgetControl, public QT7VideoOutput -{ -Q_OBJECT -Q_INTERFACES(QT7VideoOutput) -public: - virtual ~QT7VideoWidgetControl() {} - -protected: - QT7VideoWidgetControl(QObject *parent) - :QVideoWidgetControl(parent) - {} -}; -#endif - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp index 59a9f661f..4f8f03836 100644 --- a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp +++ b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp @@ -404,6 +404,7 @@ QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) Q_UNUSED(mode) QList<QByteArray> devices; +#ifndef Q_OS_WINCE //enumerate device fullnames through directshow api CoInitialize(NULL); ICreateDevEnum *pDevEnum = NULL; @@ -455,6 +456,35 @@ QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) } } CoUninitialize(); +#else // Q_OS_WINCE + if (mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for (i=0;i<iNumDevs;i++) { + if (waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + QByteArray device; + QDataStream ds(&device, QIODevice::WriteOnly); + ds << quint32(i) << QString::fromWCharArray(woc.szPname); + devices.append(device); + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for (i=0;i<iNumDevs;i++) { + if (waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + QByteArray device; + QDataStream ds(&device, QIODevice::WriteOnly); + ds << quint32(i) << QString::fromWCharArray(woc.szPname); + devices.append(device); + } + } + } +#endif // !Q_OS_WINCE return devices; } diff --git a/src/plugins/windowsaudio/qwindowsaudioinput.cpp b/src/plugins/windowsaudio/qwindowsaudioinput.cpp index 376854373..2a3d34293 100644 --- a/src/plugins/windowsaudio/qwindowsaudioinput.cpp +++ b/src/plugins/windowsaudio/qwindowsaudioinput.cpp @@ -698,7 +698,7 @@ qint64 QWindowsAudioInput::elapsedUSecs() const if (deviceState == QAudio::StoppedState) return 0; - return timeStampOpened.elapsed()*1000; + return timeStampOpened.elapsed() * qint64(1000); } void QWindowsAudioInput::reset() diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp index 659ee3a0c..a9230139c 100644 --- a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp +++ b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp @@ -674,7 +674,7 @@ qint64 QWindowsAudioOutput::elapsedUSecs() const if (deviceState == QAudio::StoppedState) return 0; - return timeStampOpened.elapsed()*1000; + return timeStampOpened.elapsed() * qint64(1000); } QAudio::Error QWindowsAudioOutput::error() const diff --git a/src/plugins/windowsaudio/windowsaudio.pro b/src/plugins/windowsaudio/windowsaudio.pro index a1a327953..ead73251b 100644 --- a/src/plugins/windowsaudio/windowsaudio.pro +++ b/src/plugins/windowsaudio/windowsaudio.pro @@ -5,7 +5,8 @@ PLUGIN_TYPE = audio PLUGIN_CLASS_NAME = QWindowsAudioPlugin load(qt_plugin) -LIBS += -lwinmm -lstrmiids -lole32 -loleaut32 +LIBS += -lstrmiids -lole32 -loleaut32 +!wince*:LIBS += -lwinmm HEADERS += \ qwindowsaudioplugin.h \ diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp new file mode 100644 index 000000000..619e97315 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -0,0 +1,783 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtcameracontrol.h" +#include "qwinrtcameravideorenderercontrol.h" +#include "qwinrtvideodeviceselectorcontrol.h" +#include "qwinrtcameraimagecapturecontrol.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QCoreApplication> +#include <QtCore/QPointer> + +#include <mfapi.h> +#include <mferror.h> +#include <mfidl.h> +#include <wrl.h> +#include <windows.devices.enumeration.h> +#include <windows.media.capture.h> +#include <windows.storage.streams.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Media; +using namespace ABI::Windows::Media::Capture; +using namespace ABI::Windows::Media::Devices; +using namespace ABI::Windows::Media::MediaProperties; +using namespace ABI::Windows::Storage::Streams; + +QT_USE_NAMESPACE + +#define RETURN_VOID_AND_EMIT_ERROR(msg) \ + if (FAILED(hr)) { \ + emit error(QCamera::CameraError, qt_error_string(hr)); \ + RETURN_VOID_IF_FAILED(msg); \ + } + +class CriticalSectionLocker +{ +public: + CriticalSectionLocker(CRITICAL_SECTION *section) + : m_section(section) + { + EnterCriticalSection(m_section); + } + ~CriticalSectionLocker() + { + LeaveCriticalSection(m_section); + } +private: + CRITICAL_SECTION *m_section; +}; + +class MediaStream : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFStreamSink, IMFMediaEventGenerator, IMFMediaTypeHandler> +{ +public: + MediaStream(IMFMediaType *type, IMFMediaSink *mediaSink, QWinRTCameraVideoRendererControl *videoRenderer) + : m_type(type), m_sink(mediaSink), m_videoRenderer(videoRenderer) + { + Q_ASSERT(m_videoRenderer); + + InitializeCriticalSectionEx(&m_mutex, 0, 0); + + HRESULT hr; + hr = MFCreateEventQueue(&m_eventQueue); + Q_ASSERT_SUCCEEDED(hr); + hr = MFAllocateSerialWorkQueue(MFASYNC_CALLBACK_QUEUE_STANDARD, &m_workQueueId); + Q_ASSERT_SUCCEEDED(hr); + } + + ~MediaStream() + { + CriticalSectionLocker locker(&m_mutex); + m_eventQueue->Shutdown(); + DeleteCriticalSection(&m_mutex); + } + + HRESULT RequestSample() + { + if (m_pendingSamples.load() < 3) { + m_pendingSamples.ref(); + return QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, Q_NULLPTR); + } + return S_OK; + } + + HRESULT __stdcall GetEvent(DWORD flags, IMFMediaEvent **event) Q_DECL_OVERRIDE + { + EnterCriticalSection(&m_mutex); + // Create an extra reference to avoid deadlock + ComPtr<IMFMediaEventQueue> eventQueue = m_eventQueue; + LeaveCriticalSection(&m_mutex); + + return eventQueue->GetEvent(flags, event); + } + + HRESULT __stdcall BeginGetEvent(IMFAsyncCallback *callback, IUnknown *state) Q_DECL_OVERRIDE + { + CriticalSectionLocker locker(&m_mutex); + HRESULT hr = m_eventQueue->BeginGetEvent(callback, state); + return hr; + } + + HRESULT __stdcall EndGetEvent(IMFAsyncResult *result, IMFMediaEvent **event) Q_DECL_OVERRIDE + { + CriticalSectionLocker locker(&m_mutex); + return m_eventQueue->EndGetEvent(result, event); + } + + HRESULT __stdcall QueueEvent(MediaEventType eventType, const GUID &extendedType, HRESULT status, const PROPVARIANT *value) Q_DECL_OVERRIDE + { + CriticalSectionLocker locker(&m_mutex); + return m_eventQueue->QueueEventParamVar(eventType, extendedType, status, value); + } + + HRESULT __stdcall GetMediaSink(IMFMediaSink **mediaSink) Q_DECL_OVERRIDE + { + *mediaSink = m_sink; + return S_OK; + } + + HRESULT __stdcall GetIdentifier(DWORD *identifier) Q_DECL_OVERRIDE + { + *identifier = 0; + return S_OK; + } + + HRESULT __stdcall GetMediaTypeHandler(IMFMediaTypeHandler **handler) Q_DECL_OVERRIDE + { + return QueryInterface(IID_PPV_ARGS(handler)); + } + + HRESULT __stdcall ProcessSample(IMFSample *sample) Q_DECL_OVERRIDE + { + ComPtr<IMFMediaBuffer> buffer; + HRESULT hr = sample->GetBufferByIndex(0, &buffer); + RETURN_HR_IF_FAILED("Failed to get buffer from camera sample"); + ComPtr<IMF2DBuffer> buffer2d; + hr = buffer.As(&buffer2d); + RETURN_HR_IF_FAILED("Failed to cast camera sample buffer to 2D buffer"); + + m_pendingSamples.deref(); + m_videoRenderer->queueBuffer(buffer2d.Get()); + + return hr; + } + + HRESULT __stdcall PlaceMarker(MFSTREAMSINK_MARKER_TYPE type, const PROPVARIANT *value, const PROPVARIANT *context) Q_DECL_OVERRIDE + { + Q_UNUSED(type); + Q_UNUSED(value); + QueueEvent(MEStreamSinkMarker, GUID_NULL, S_OK, context); + return S_OK; + } + + HRESULT __stdcall Flush() Q_DECL_OVERRIDE + { + m_videoRenderer->discardBuffers(); + m_pendingSamples.store(0); + return S_OK; + } + + HRESULT __stdcall IsMediaTypeSupported(IMFMediaType *type, IMFMediaType **) Q_DECL_OVERRIDE + { + HRESULT hr; + GUID majorType; + hr = type->GetMajorType(&majorType); + Q_ASSERT_SUCCEEDED(hr); + if (!IsEqualGUID(majorType, MFMediaType_Video)) + return MF_E_INVALIDMEDIATYPE; + return S_OK; + } + + HRESULT __stdcall GetMediaTypeCount(DWORD *typeCount) Q_DECL_OVERRIDE + { + *typeCount = 1; + return S_OK; + } + + HRESULT __stdcall GetMediaTypeByIndex(DWORD index, IMFMediaType **type) Q_DECL_OVERRIDE + { + if (index == 0) + return m_type.CopyTo(type); + return E_BOUNDS; + } + + HRESULT __stdcall SetCurrentMediaType(IMFMediaType *type) Q_DECL_OVERRIDE + { + if (FAILED(IsMediaTypeSupported(type, Q_NULLPTR))) + return MF_E_INVALIDREQUEST; + + m_type = type; + return S_OK; + } + + HRESULT __stdcall GetCurrentMediaType(IMFMediaType **type) Q_DECL_OVERRIDE + { + return m_type.CopyTo(type); + } + + HRESULT __stdcall GetMajorType(GUID *majorType) Q_DECL_OVERRIDE + { + return m_type->GetMajorType(majorType); + } + +private: + CRITICAL_SECTION m_mutex; + ComPtr<IMFMediaType> m_type; + IMFMediaSink *m_sink; + ComPtr<IMFMediaEventQueue> m_eventQueue; + DWORD m_workQueueId; + + QWinRTCameraVideoRendererControl *m_videoRenderer; + QAtomicInt m_pendingSamples; +}; + +class MediaSink : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMediaExtension, IMFMediaSink, IMFClockStateSink> +{ +public: + MediaSink(IMediaEncodingProfile *encodingProfile, QWinRTCameraVideoRendererControl *videoRenderer) + : m_videoRenderer(videoRenderer) + { + HRESULT hr; + ComPtr<IVideoEncodingProperties> videoProperties; + hr = encodingProfile->get_Video(&videoProperties); + RETURN_VOID_IF_FAILED("Failed to get video properties"); + ComPtr<IMFMediaType> videoType; + hr = MFCreateMediaTypeFromProperties(videoProperties.Get(), &videoType); + RETURN_VOID_IF_FAILED("Failed to create video type"); + m_stream = Make<MediaStream>(videoType.Get(), this, videoRenderer); + } + + ~MediaSink() + { + } + + HRESULT RequestSample() + { + return m_stream->RequestSample(); + } + + HRESULT __stdcall SetProperties(Collections::IPropertySet *configuration) Q_DECL_OVERRIDE + { + Q_UNUSED(configuration); + return E_NOTIMPL; + } + + HRESULT __stdcall GetCharacteristics(DWORD *characteristics) Q_DECL_OVERRIDE + { + *characteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS; + return S_OK; + } + + HRESULT __stdcall AddStreamSink(DWORD streamSinkIdentifier, IMFMediaType *mediaType, IMFStreamSink **streamSink) Q_DECL_OVERRIDE + { + Q_UNUSED(streamSinkIdentifier); + Q_UNUSED(mediaType); + Q_UNUSED(streamSink); + return E_NOTIMPL; + } + + HRESULT __stdcall RemoveStreamSink(DWORD streamSinkIdentifier) Q_DECL_OVERRIDE + { + Q_UNUSED(streamSinkIdentifier); + return E_NOTIMPL; + } + + HRESULT __stdcall GetStreamSinkCount(DWORD *streamSinkCount) Q_DECL_OVERRIDE + { + *streamSinkCount = 1; + return S_OK; + } + + HRESULT __stdcall GetStreamSinkByIndex(DWORD index, IMFStreamSink **streamSink) Q_DECL_OVERRIDE + { + if (index == 0) + return m_stream.CopyTo(streamSink); + return MF_E_INVALIDINDEX; + } + + HRESULT __stdcall GetStreamSinkById(DWORD streamSinkIdentifier, IMFStreamSink **streamSink) Q_DECL_OVERRIDE + { + // ID and index are always 0 + HRESULT hr = GetStreamSinkByIndex(streamSinkIdentifier, streamSink); + return hr == MF_E_INVALIDINDEX ? MF_E_INVALIDSTREAMNUMBER : hr; + } + + HRESULT __stdcall SetPresentationClock(IMFPresentationClock *presentationClock) Q_DECL_OVERRIDE + { + HRESULT hr = S_OK; + m_presentationClock = presentationClock; + if (m_presentationClock) + hr = m_presentationClock->AddClockStateSink(this); + return hr; + } + + HRESULT __stdcall GetPresentationClock(IMFPresentationClock **presentationClock) Q_DECL_OVERRIDE + { + return m_presentationClock.CopyTo(presentationClock); + } + + HRESULT __stdcall Shutdown() Q_DECL_OVERRIDE + { + m_stream->Flush(); + m_videoRenderer->setActive(false); + return m_presentationClock->Stop(); + } + + HRESULT __stdcall OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) Q_DECL_OVERRIDE + { + Q_UNUSED(systemTime); + Q_UNUSED(clockStartOffset); + + m_videoRenderer->setActive(true); + + return S_OK; + } + + HRESULT __stdcall OnClockStop(MFTIME systemTime) Q_DECL_OVERRIDE + { + Q_UNUSED(systemTime); + + m_videoRenderer->setActive(false); + + return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, Q_NULLPTR); + } + + HRESULT __stdcall OnClockPause(MFTIME systemTime) Q_DECL_OVERRIDE + { + Q_UNUSED(systemTime); + + m_videoRenderer->setActive(false); + + return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, Q_NULLPTR); + } + + HRESULT __stdcall OnClockRestart(MFTIME systemTime) Q_DECL_OVERRIDE + { + Q_UNUSED(systemTime); + + m_videoRenderer->setActive(true); + + return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, Q_NULLPTR); + } + + HRESULT __stdcall OnClockSetRate(MFTIME systemTime, float rate) Q_DECL_OVERRIDE + { + Q_UNUSED(systemTime); + Q_UNUSED(rate); + return E_NOTIMPL; + } + +private: + ComPtr<MediaStream> m_stream; + ComPtr<IMFPresentationClock> m_presentationClock; + + QWinRTCameraVideoRendererControl *m_videoRenderer; +}; + +class QWinRTCameraControlPrivate +{ +public: + QCamera::State state; + QCamera::Status status; + QCamera::CaptureModes captureMode; + + ComPtr<IMediaCapture> capture; + ComPtr<IMediaCaptureVideoPreview> capturePreview; + EventRegistrationToken captureFailedCookie; + EventRegistrationToken recordLimitationCookie; + + ComPtr<IMediaEncodingProfileStatics> encodingProfileFactory; + + ComPtr<IMediaEncodingProfile> encodingProfile; + ComPtr<MediaSink> mediaSink; + + QSize size; + QPointer<QWinRTCameraVideoRendererControl> videoRenderer; + QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector; + QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl; +}; + +QWinRTCameraControl::QWinRTCameraControl(QObject *parent) + : QCameraControl(parent), d_ptr(new QWinRTCameraControlPrivate) +{ + Q_D(QWinRTCameraControl); + + d->state = QCamera::UnloadedState; + d->status = QCamera::UnloadedStatus; + d->captureMode = QCamera::CaptureStillImage; + d->captureFailedCookie.value = 0; + d->recordLimitationCookie.value = 0; + d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this); + connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested, + this, &QWinRTCameraControl::onBufferRequested); + d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this); + d->imageCaptureControl = new QWinRTCameraImageCaptureControl(this); +} + +QWinRTCameraControl::~QWinRTCameraControl() +{ + setState(QCamera::UnloadedState); +} + +QCamera::State QWinRTCameraControl::state() const +{ + Q_D(const QWinRTCameraControl); + return d->state; +} + +void QWinRTCameraControl::setState(QCamera::State state) +{ + Q_D(QWinRTCameraControl); + + if (d->state == state) + return; + + HRESULT hr; + switch (state) { + case QCamera::ActiveState: { + // Capture has not been created or initialized + if (d->state == QCamera::UnloadedState) { + hr = initialize(); + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + } + Q_ASSERT(d->state == QCamera::LoadedState); + + d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer); + ComPtr<IAsyncAction> op; + hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op); + RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture"); + if (d->status != QCamera::StartingStatus) { + d->status = QCamera::StartingStatus; + emit statusChanged(d->status); + } + + hr = QWinRTFunctions::await(op); + if (FAILED(hr)) { + emit error(QCamera::CameraError, qt_error_string(hr)); + setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again + return; + } + + d->state = QCamera::ActiveState; + emit stateChanged(d->state); + d->status = QCamera::ActiveStatus; + emit statusChanged(d->status); + break; + } + case QCamera::LoadedState: { + // If moving from unloaded, initialize the camera + if (d->state == QCamera::UnloadedState) { + hr = initialize(); + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + } + // fall through + } + case QCamera::UnloadedState: { + // Stop the camera if it is running (transition to LoadedState) + if (d->status == QCamera::ActiveStatus) { + ComPtr<IAsyncAction> op; + hr = d->capturePreview->StopPreviewAsync(&op); + RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview"); + if (d->status != QCamera::StoppingStatus) { + d->status = QCamera::StoppingStatus; + emit statusChanged(d->status); + } + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); // Synchronize unloading + if (FAILED(hr)) + emit error(QCamera::InvalidRequestError, qt_error_string(hr)); + + d->mediaSink->Shutdown(); + d->mediaSink.Reset(); + + d->state = QCamera::LoadedState; + emit stateChanged(d->state); + + d->status = QCamera::LoadedStatus; + emit statusChanged(d->status); + } + // Completely unload if needed + if (state == QCamera::UnloadedState) { + if (!d->capture) // Already unloaded + break; + + if (d->status != QCamera::UnloadingStatus) { + d->status = QCamera::UnloadingStatus; + emit statusChanged(d->status); + } + + if (d->capture && d->captureFailedCookie.value) { + hr = d->capture->remove_Failed(d->captureFailedCookie); + Q_ASSERT_SUCCEEDED(hr); + d->captureFailedCookie.value = 0; + } + if (d->capture && d->recordLimitationCookie.value) { + d->capture->remove_RecordLimitationExceeded(d->recordLimitationCookie); + Q_ASSERT_SUCCEEDED(hr); + d->recordLimitationCookie.value = 0; + } + ComPtr<IClosable> capture; + hr = d->capture.As(&capture); + Q_ASSERT_SUCCEEDED(hr); + hr = capture->Close(); + RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger"); + d->capture.Reset(); + if (d->state != QCamera::UnloadedState) { + d->state = QCamera::UnloadedState; + emit stateChanged(d->state); + } + if (d->status != QCamera::UnloadedStatus) { + d->status = QCamera::UnloadedStatus; + emit statusChanged(d->status); + } + } + break; + } + default: + break; + } +} + +QCamera::Status QWinRTCameraControl::status() const +{ + Q_D(const QWinRTCameraControl); + return d->status; +} + +QCamera::CaptureModes QWinRTCameraControl::captureMode() const +{ + Q_D(const QWinRTCameraControl); + return d->captureMode; +} + +void QWinRTCameraControl::setCaptureMode(QCamera::CaptureModes mode) +{ + Q_D(QWinRTCameraControl); + + if (d->captureMode == mode) + return; + + if (!isCaptureModeSupported(mode)) { + qWarning("Unsupported capture mode: %d", mode); + return; + } + + d->captureMode = mode; + emit captureModeChanged(d->captureMode); +} + +bool QWinRTCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const +{ + return mode >= QCamera::CaptureViewfinder && mode <= QCamera::CaptureStillImage; +} + +bool QWinRTCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(changeType); + + return status == QCamera::UnloadedStatus; // For now, assume shutdown is required for all property changes +} + +QVideoRendererControl *QWinRTCameraControl::videoRenderer() const +{ + Q_D(const QWinRTCameraControl); + return d->videoRenderer; +} + +QVideoDeviceSelectorControl *QWinRTCameraControl::videoDeviceSelector() const +{ + Q_D(const QWinRTCameraControl); + return d->videoDeviceSelector; +} + +QCameraImageCaptureControl *QWinRTCameraControl::imageCaptureControl() const +{ + Q_D(const QWinRTCameraControl); + return d->imageCaptureControl; +} + +IMediaCapture *QWinRTCameraControl::handle() const +{ + Q_D(const QWinRTCameraControl); + return d->capture.Get(); +} + +QSize QWinRTCameraControl::imageSize() const +{ + Q_D(const QWinRTCameraControl); + return d->size; +} + +void QWinRTCameraControl::onBufferRequested() +{ + Q_D(QWinRTCameraControl); + + if (d->mediaSink) + d->mediaSink->RequestSample(); +} + +HRESULT QWinRTCameraControl::initialize() +{ + Q_D(QWinRTCameraControl); + + if (d->status != QCamera::LoadingStatus) { + d->status = QCamera::LoadingStatus; + emit statusChanged(d->status); + } + + HRESULT hr; + ComPtr<IInspectable> capture; + hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(), + &capture); + Q_ASSERT_SUCCEEDED(hr); + hr = capture.As(&d->capture); + Q_ASSERT_SUCCEEDED(hr); + hr = d->capture.As(&d->capturePreview); + Q_ASSERT_SUCCEEDED(hr); + hr = d->capture->add_Failed(Callback<IMediaCaptureFailedEventHandler>(this, &QWinRTCameraControl::onCaptureFailed).Get(), + &d->captureFailedCookie); + Q_ASSERT_SUCCEEDED(hr); + hr = d->capture->add_RecordLimitationExceeded(Callback<IRecordLimitationExceededEventHandler>(this, &QWinRTCameraControl::onRecordLimitationExceeded).Get(), + &d->recordLimitationCookie); + Q_ASSERT_SUCCEEDED(hr); + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), + IID_PPV_ARGS(&d->encodingProfileFactory)); + Q_ASSERT_SUCCEEDED(hr); + + int deviceIndex = d->videoDeviceSelector->selectedDevice(); + if (deviceIndex < 0) + deviceIndex = d->videoDeviceSelector->defaultDevice(); + + const QString deviceName = d->videoDeviceSelector->deviceName(deviceIndex); + if (deviceName.isEmpty()) { + qWarning("No video device available or selected."); + return E_FAIL; + } + + ComPtr<IMediaCaptureInitializationSettings> settings; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings).Get(), + &settings); + Q_ASSERT_SUCCEEDED(hr); + HStringReference deviceId(reinterpret_cast<LPCWSTR>(deviceName.utf16()), deviceName.length()); + hr = settings->put_VideoDeviceId(deviceId.Get()); + Q_ASSERT_SUCCEEDED(hr); + + hr = settings->put_StreamingCaptureMode(StreamingCaptureMode_Video); + Q_ASSERT_SUCCEEDED(hr); + + hr = settings->put_PhotoCaptureSource(PhotoCaptureSource_Auto); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncAction> op; + hr = d->capture->InitializeWithSettingsAsync(settings.Get(), &op); + RETURN_HR_IF_FAILED("Failed to begin initialization of media capture manager"); + hr = QWinRTFunctions::await(op, QWinRTFunctions::ProcessThreadEvents); + if (hr == E_ACCESSDENIED) { + qWarning("Access denied when initializing the media capture manager. " + "Check your manifest settings for microphone and webcam access."); + } + RETURN_HR_IF_FAILED("Failed to initialize media capture manager"); + + ComPtr<IVideoDeviceController> videoDeviceController; + hr = d->capture->get_VideoDeviceController(&videoDeviceController); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IMediaDeviceController> deviceController; + hr = videoDeviceController.As(&deviceController); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList; + hr = deviceController->GetAvailableMediaStreamProperties(MediaStreamType_Photo, &encodingPropertiesList); + Q_ASSERT_SUCCEEDED(hr); + + d->size = QSize(); + ComPtr<IVideoEncodingProperties> videoEncodingProperties; + quint32 encodingPropertiesListSize; + hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < encodingPropertiesListSize; ++i) { + ComPtr<IMediaEncodingProperties> properties; + hr = encodingPropertiesList->GetAt(i, &properties); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVideoEncodingProperties> videoProperties; + hr = properties.As(&videoEncodingProperties); + Q_ASSERT_SUCCEEDED(hr); + UINT32 width, height; + hr = videoEncodingProperties->get_Width(&width); + Q_ASSERT_SUCCEEDED(hr); + hr = videoEncodingProperties->get_Height(&height); + Q_ASSERT_SUCCEEDED(hr); + // Choose the highest-quality format + if (int(width * height) > d->size.width() * d->size.height()) { + d->size = QSize(width, height); + videoEncodingProperties = videoProperties; + } + } + + if (!videoEncodingProperties || d->size.isEmpty()) { + hr = MF_E_INVALID_FORMAT; + RETURN_HR_IF_FAILED("Failed to find a suitable video format"); + } + + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), + &d->encodingProfile); + Q_ASSERT_SUCCEEDED(hr); + hr = d->encodingProfile->put_Video(videoEncodingProperties.Get()); + Q_ASSERT_SUCCEEDED(hr); + if (d->videoRenderer) + d->videoRenderer->setSize(d->size); + + if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) { + d->state = QCamera::LoadedState; + emit stateChanged(d->state); + } + if (SUCCEEDED(hr) && d->status != QCamera::LoadedStatus) { + d->status = QCamera::LoadedStatus; + emit statusChanged(d->status); + } + return hr; +} + +HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args) +{ + HRESULT hr; + UINT32 code; + hr = args->get_Code(&code); + RETURN_HR_IF_FAILED("Failed to get error code"); + HString message; + args->get_Message(message.GetAddressOf()); + RETURN_HR_IF_FAILED("Failed to get error message"); + quint32 messageLength; + const wchar_t *messageBuffer = message.GetRawBuffer(&messageLength); + emit error(QCamera::CameraError, QString::fromWCharArray(messageBuffer, messageLength)); + setState(QCamera::LoadedState); + return S_OK; +} + +HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *) +{ + emit error(QCamera::CameraError, QStringLiteral("Recording limit exceeded.")); + setState(QCamera::LoadedState); + return S_OK; +} diff --git a/src/plugins/winrt/qwinrtcameracontrol.h b/src/plugins/winrt/qwinrtcameracontrol.h new file mode 100644 index 000000000..e75f7e476 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameracontrol.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERACONTROL_H +#define QWINRTCAMERACONTROL_H + +#include <QtMultimedia/QCameraControl> +#include <QtCore/qt_windows.h> + +namespace ABI { + namespace Windows { + namespace Media { + namespace Capture { + struct IMediaCapture; + struct IMediaCaptureFailedEventArgs; + } + } + namespace Foundation { + struct IAsyncAction; + enum class AsyncStatus; + } + } +} + +QT_BEGIN_NAMESPACE + +class QVideoRendererControl; +class QVideoDeviceSelectorControl; +class QCameraImageCaptureControl; + +class QWinRTCameraControlPrivate; +class QWinRTCameraControl : public QCameraControl +{ + Q_OBJECT +public: + explicit QWinRTCameraControl(QObject *parent = 0); + ~QWinRTCameraControl(); + + QCamera::State state() const Q_DECL_OVERRIDE; + void setState(QCamera::State state) Q_DECL_OVERRIDE; + + QCamera::Status status() const Q_DECL_OVERRIDE; + + QCamera::CaptureModes captureMode() const Q_DECL_OVERRIDE; + void setCaptureMode(QCamera::CaptureModes mode) Q_DECL_OVERRIDE; + bool isCaptureModeSupported(QCamera::CaptureModes mode) const Q_DECL_OVERRIDE; + + bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const Q_DECL_OVERRIDE; + + QVideoRendererControl *videoRenderer() const; + QVideoDeviceSelectorControl *videoDeviceSelector() const; + QCameraImageCaptureControl *imageCaptureControl() const; + + ABI::Windows::Media::Capture::IMediaCapture *handle() const; + QSize imageSize() const; + +private slots: + void onBufferRequested(); + +private: + HRESULT enumerateDevices(); + HRESULT initialize(); + HRESULT onCaptureFailed(ABI::Windows::Media::Capture::IMediaCapture *, + ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *); + HRESULT onRecordLimitationExceeded(ABI::Windows::Media::Capture::IMediaCapture *); + + QScopedPointer<QWinRTCameraControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERACONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp new file mode 100644 index 000000000..f5151c619 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtcameraimagecapturecontrol.h" +#include "qwinrtcameracontrol.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QGlobalStatic> +#include <QtCore/QPointer> +#include <QtCore/QStandardPaths> +#include <QtCore/QVector> +#include <QtCore/qfunctions_winrt.h> +#include <QtMultimedia/private/qmediastoragelocation_p.h> + +#include <wrl.h> +#include <windows.media.capture.h> +#include <windows.media.devices.h> +#include <windows.media.mediaproperties.h> +#include <windows.storage.streams.h> +#include <windows.graphics.imaging.h> +#include <robuffer.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Media::Capture; +using namespace ABI::Windows::Media::Devices; +using namespace ABI::Windows::Media::MediaProperties; +using namespace ABI::Windows::Storage::Streams; +using namespace ABI::Windows::Graphics::Imaging; + +QT_USE_NAMESPACE + +#define wchar(str) reinterpret_cast<const wchar_t *>(str.utf16()) + +struct QWinRTCameraImageCaptureControlGlobal +{ + QWinRTCameraImageCaptureControlGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_ImageEncodingProperties).Get(), + &encodingPropertiesFactory); + Q_ASSERT_SUCCEEDED(hr); + + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), + &dataReaderFactory); + } + + ComPtr<IImageEncodingPropertiesStatics2> encodingPropertiesFactory; + ComPtr<IBufferFactory> bufferFactory; + ComPtr<IDataReaderFactory> dataReaderFactory; +}; +Q_GLOBAL_STATIC(QWinRTCameraImageCaptureControlGlobal, g) + +struct CaptureRequest +{ + quint16 id; + QString fileName; + ComPtr<IImageEncodingProperties> imageFormat; + ComPtr<IRandomAccessStream> stream; + ComPtr<IAsyncAction> op; +}; + +class QWinRTCameraImageCaptureControlPrivate +{ +public: + QPointer<QWinRTCameraControl> cameraControl; + QHash<IAsyncAction *, CaptureRequest> requests; + quint16 currentCaptureId; + QMediaStorageLocation location; + + void onCameraStateChanged() + { + + } +}; + +QWinRTCameraImageCaptureControl::QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent) + : QCameraImageCaptureControl(parent), d_ptr(new QWinRTCameraImageCaptureControlPrivate) +{ + Q_D(QWinRTCameraImageCaptureControl); + + d->cameraControl = parent; + connect(d->cameraControl, &QCameraControl::stateChanged, + this, &QWinRTCameraImageCaptureControl::readyForCaptureChanged); + d->currentCaptureId = 0; +} + +bool QWinRTCameraImageCaptureControl::isReadyForCapture() const +{ + Q_D(const QWinRTCameraImageCaptureControl); + return d->cameraControl->state() == QCamera::ActiveState; +} + +QCameraImageCapture::DriveMode QWinRTCameraImageCaptureControl::driveMode() const +{ + return QCameraImageCapture::SingleImageCapture; +} + +void QWinRTCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) +{ + Q_UNUSED(mode); +} + +int QWinRTCameraImageCaptureControl::capture(const QString &fileName) +{ + Q_D(QWinRTCameraImageCaptureControl); + + ++d->currentCaptureId; + IMediaCapture *capture = d->cameraControl->handle(); + if (!capture) { + emit error(d->currentCaptureId, QCameraImageCapture::NotReadyError, tr("Camera not ready")); + return -1; + } + + CaptureRequest request = { + d->currentCaptureId, + d->location.generateFileName(fileName, QMediaStorageLocation::Pictures, QStringLiteral("IMG_"), + fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix()) + }; + + HRESULT hr; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), + &request.stream); + Q_ASSERT_SUCCEEDED(hr); + + hr = g->encodingPropertiesFactory->CreateBmp(&request.imageFormat); + Q_ASSERT_SUCCEEDED(hr); + + const QSize imageSize = d->cameraControl->imageSize(); + hr = request.imageFormat->put_Width(imageSize.width()); + Q_ASSERT_SUCCEEDED(hr); + hr = request.imageFormat->put_Height(imageSize.height()); + Q_ASSERT_SUCCEEDED(hr); + + hr = capture->CapturePhotoToStreamAsync(request.imageFormat.Get(), request.stream.Get(), &request.op); + Q_ASSERT_SUCCEEDED(hr); + d->requests.insert(request.op.Get(), request); + + hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>( + this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get()); + Q_ASSERT_SUCCEEDED(hr); + + return request.id; +} + +void QWinRTCameraImageCaptureControl::cancelCapture() +{ + Q_D(QWinRTCameraImageCaptureControl); + + QHash<IAsyncAction *, CaptureRequest>::iterator it = d->requests.begin(); + while (it != d->requests.end()) { + ComPtr<IAsyncInfo> info; + it->op.As(&info); + info->Cancel(); + it = d->requests.erase(it); + } +} + +HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncInfo, AsyncStatus status) +{ + Q_D(QWinRTCameraImageCaptureControl); + + if (status == Canceled || !d->requests.contains(asyncInfo)) + return S_OK; + + CaptureRequest request = d->requests.take(asyncInfo); + + HRESULT hr; + if (status == Error) { + hr = asyncInfo->GetResults(); + emit error(request.id, QCameraImageCapture::ResourceError, qt_error_string(hr)); + return S_OK; + } + + quint64 dataLength; + hr = request.stream->get_Size(&dataLength); + Q_ASSERT_SUCCEEDED(hr); + if (dataLength == 0 || dataLength > INT_MAX) { + emit error(request.id, QCameraImageCapture::FormatError, tr("Invalid photo data length.")); + return S_OK; + } + + ComPtr<IBitmapDecoderStatics> bitmapFactory; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapDecoder).Get(), + &bitmapFactory); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<BitmapDecoder *>> op; + hr = bitmapFactory->CreateAsync(request.stream.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IBitmapDecoder> decoder; + hr = QWinRTFunctions::await(op, decoder.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<BitmapFrame *>> op2; + hr = decoder->GetFrameAsync(0, &op2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IBitmapFrame> frame; + hr = QWinRTFunctions::await(op2, frame.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IBitmapTransform> transform; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapTransform).Get(), + &transform); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<PixelDataProvider *>> op3; + hr = frame->GetPixelDataTransformedAsync(BitmapPixelFormat_Rgba8, BitmapAlphaMode_Straight, + transform.Get(), ExifOrientationMode_IgnoreExifOrientation, + ColorManagementMode_DoNotColorManage, &op3); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IPixelDataProvider> pixelDataProvider; + hr = QWinRTFunctions::await(op3, pixelDataProvider.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 pixelDataSize; + BYTE *pixelData; + hr = pixelDataProvider->DetachPixelData(&pixelDataSize, &pixelData); + + UINT32 pixelHeight; + hr = frame->get_PixelHeight(&pixelHeight); + Q_ASSERT_SUCCEEDED(hr); + UINT32 pixelWidth; + hr = frame->get_PixelWidth(&pixelWidth); + Q_ASSERT_SUCCEEDED(hr); + const QImage image(pixelData, pixelWidth, pixelHeight, QImage::Format_RGBA8888, + reinterpret_cast<QImageCleanupFunction>(&CoTaskMemFree), pixelData); + emit imageCaptured(request.id, image); + if (image.save(request.fileName)) + emit imageSaved(request.id, request.fileName); + else + emit error(request.id, QCameraImageCapture::ResourceError, tr("Image saving failed")); + + return S_OK; +} diff --git a/src/plugins/qt7/qt7serviceplugin.mm b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h index 731f278a1..5150e4d3a 100644 --- a/src/plugins/qt7/qt7serviceplugin.mm +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Toolkit. @@ -39,80 +39,48 @@ ** ****************************************************************************/ -#import <Foundation/Foundation.h> -#import <QTKit/QTKit.h> +#ifndef QWINRTCAMERAIMAGECAPTURECONTROL_H +#define QWINRTCAMERAIMAGECAPTURECONTROL_H -#include <QtCore/qstring.h> -#include <QtCore/qdebug.h> +#include <QtMultimedia/QCameraImageCaptureControl> +#include <QtCore/qt_windows.h> -#include "qt7backend.h" -#include "qt7serviceplugin.h" -#include "qt7playerservice.h" - -#include <qmediaserviceproviderplugin.h> - -QT_BEGIN_NAMESPACE - - -QT7ServicePlugin::QT7ServicePlugin() -{ - buildSupportedTypes(); +namespace ABI { + namespace Windows { + namespace Foundation { + struct IAsyncAction; + enum class AsyncStatus; + } + } } -QMediaService* QT7ServicePlugin::create(QString const& key) -{ -#ifdef QT_DEBUG_QT7 - qDebug() << "QT7ServicePlugin::create" << key; -#endif -#ifdef QMEDIA_QT7_PLAYER - if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) - return new QT7PlayerService; -#endif - qWarning() << "unsupported key:" << key; - - return 0; -} +QT_BEGIN_NAMESPACE -void QT7ServicePlugin::release(QMediaService *service) -{ - delete service; -} +class QWinRTCameraControl; -QMediaServiceProviderHint::Features QT7ServicePlugin::supportedFeatures( - const QByteArray &service) const +class QWinRTCameraImageCaptureControlPrivate; +class QWinRTCameraImageCaptureControl : public QCameraImageCaptureControl { - if (service == Q_MEDIASERVICE_MEDIAPLAYER) - return QMediaServiceProviderHint::VideoSurface; - else - return QMediaServiceProviderHint::Features(); -} + Q_OBJECT +public: + explicit QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent); -QMultimedia::SupportEstimate QT7ServicePlugin::hasSupport(const QString &mimeType, const QStringList& codecs) const -{ - Q_UNUSED(codecs); + bool isReadyForCapture() const Q_DECL_OVERRIDE; - if (m_supportedMimeTypes.contains(mimeType)) - return QMultimedia::ProbablySupported; + QCameraImageCapture::DriveMode driveMode() const Q_DECL_OVERRIDE; + void setDriveMode(QCameraImageCapture::DriveMode mode) Q_DECL_OVERRIDE; - return QMultimedia::MaybeSupported; -} + int capture(const QString &fileName) Q_DECL_OVERRIDE; + void cancelCapture() Q_DECL_OVERRIDE; -QStringList QT7ServicePlugin::supportedMimeTypes() const -{ - return m_supportedMimeTypes; -} +private: + HRESULT onCaptureCompleted(ABI::Windows::Foundation::IAsyncAction *, + ABI::Windows::Foundation::AsyncStatus); -void QT7ServicePlugin::buildSupportedTypes() -{ - AutoReleasePool pool; - NSArray *utis = [QTMovie movieTypesWithOptions:QTIncludeCommonTypes]; - for (NSString *uti in utis) { - NSString* mimeType = (NSString*)UTTypeCopyPreferredTagWithClass((CFStringRef)uti, kUTTagClassMIMEType); - if (mimeType != 0) { - m_supportedMimeTypes.append(QString::fromUtf8([mimeType UTF8String])); - [mimeType release]; - } - } -} + QScopedPointer<QWinRTCameraImageCaptureControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraImageCaptureControl) +}; QT_END_NAMESPACE + +#endif // QWINRTCAMERAIMAGECAPTURECONTROL_H diff --git a/src/plugins/qt7/qt7videooutput.mm b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp index 53486c4e4..c16b83be8 100644 --- a/src/plugins/qt7/qt7videooutput.mm +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Toolkit. @@ -39,53 +39,22 @@ ** ****************************************************************************/ -#include "qt7playercontrol.h" -#include "qt7playersession.h" -#include <QtCore/qdebug.h> +#include "qwinrtcamerainfocontrol.h" +#include "qwinrtvideodeviceselectorcontrol.h" QT_USE_NAMESPACE -/* -QT7VideoOutputControl::QT7VideoOutputControl(QObject *parent) - :QVideoOutputControl(parent), - m_session(0), - m_output(QVideoOutputControl::NoOutput) +QWinRTCameraInfoControl::QWinRTCameraInfoControl(QObject *parent) + : QCameraInfoControl(parent) { } -QT7VideoOutputControl::~QT7VideoOutputControl() +QCamera::Position QWinRTCameraInfoControl::cameraPosition(const QString &deviceName) const { + return QWinRTVideoDeviceSelectorControl::cameraPosition(deviceName); } -void QT7VideoOutputControl::setSession(QT7PlayerSession *session) +int QWinRTCameraInfoControl::cameraOrientation(const QString &deviceName) const { - m_session = session; + return QWinRTVideoDeviceSelectorControl::cameraOrientation(deviceName); } - -QList<QVideoOutputControl::Output> QT7VideoOutputControl::availableOutputs() const -{ - return m_outputs; -} - -void QT7VideoOutputControl::enableOutput(QVideoOutputControl::Output output) -{ - if (!m_outputs.contains(output)) - m_outputs.append(output); -} - -QVideoOutputControl::Output QT7VideoOutputControl::output() const -{ - return m_output; -} - -void QT7VideoOutputControl::setOutput(Output output) -{ - if (m_output != output) { - m_output = output; - Q_EMIT videoOutputChanged(m_output); - } -} - -#include "moc_qt7videooutputcontrol.cpp" - -*/ diff --git a/src/plugins/winrt/qwinrtcamerainfocontrol.h b/src/plugins/winrt/qwinrtcamerainfocontrol.h new file mode 100644 index 000000000..bf430f038 --- /dev/null +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAINFOCONTROL_H +#define QWINRTCAMERAINFOCONTROL_H + +#include <QtMultimedia/QCameraInfoControl> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraInfoControl : public QCameraInfoControl +{ + Q_OBJECT +public: + explicit QWinRTCameraInfoControl(QObject *parent = 0); + + QCamera::Position cameraPosition(const QString &deviceName) const Q_DECL_OVERRIDE; + int cameraOrientation(const QString &deviceName) const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERAINFOCONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraservice.cpp b/src/plugins/winrt/qwinrtcameraservice.cpp new file mode 100644 index 000000000..239a1e883 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraservice.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtcameraservice.h" +#include "qwinrtcameracontrol.h" +#include "qwinrtcamerainfocontrol.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QPointer> +#include <QtMultimedia/QCameraImageCaptureControl> +#include <QtMultimedia/QVideoRendererControl> +#include <QtMultimedia/QVideoDeviceSelectorControl> + +QT_USE_NAMESPACE + +class QWinRTCameraServicePrivate +{ +public: + QPointer<QWinRTCameraControl> cameraControl; + QPointer<QWinRTCameraInfoControl> cameraInfoControl; +}; + +QWinRTCameraService::QWinRTCameraService(QObject *parent) + : QMediaService(parent), d_ptr(new QWinRTCameraServicePrivate) +{ +} + +QMediaControl *QWinRTCameraService::requestControl(const char *name) +{ + Q_D(QWinRTCameraService); + + if (qstrcmp(name, QCameraControl_iid) == 0) { + if (!d->cameraControl) + d->cameraControl = new QWinRTCameraControl(this); + return d->cameraControl; + } + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->videoRenderer(); + } + + if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->videoDeviceSelector(); + } + + if (qstrcmp(name, QCameraInfoControl_iid) == 0) { + if (!d->cameraInfoControl) + d->cameraInfoControl = new QWinRTCameraInfoControl(this); + return d->cameraInfoControl; + } + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->imageCaptureControl(); + } + + return Q_NULLPTR; +} + +void QWinRTCameraService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control); +} diff --git a/src/plugins/winrt/qwinrtcameraservice.h b/src/plugins/winrt/qwinrtcameraservice.h new file mode 100644 index 000000000..19e93a818 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraservice.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERASERVICE_H +#define QWINRTCAMERASERVICE_H + +#include <QtMultimedia/QMediaService> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraServicePrivate; +class QWinRTCameraService : public QMediaService +{ + Q_OBJECT +public: + explicit QWinRTCameraService(QObject *parent = 0); + + QMediaControl *requestControl(const char *name) Q_DECL_OVERRIDE; + void releaseControl(QMediaControl *control) Q_DECL_OVERRIDE; + +private: + QScopedPointer<QWinRTCameraServicePrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraService) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERASERVICE_H diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp new file mode 100644 index 000000000..e7e75da27 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtcameravideorenderercontrol.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QSize> +#include <QtCore/QVector> + +#include <d3d11.h> +#include <mfapi.h> +#include <wrl.h> +using namespace Microsoft::WRL; + +QT_USE_NAMESPACE + +class D3DVideoBlitter +{ +public: + D3DVideoBlitter(ID3D11Device *device, ID3D11Texture2D *target) + : m_d3dDevice(device), m_target(target) + { + HRESULT hr; + ComPtr<IDXGIResource> targetResource; + hr = target->QueryInterface(IID_PPV_ARGS(&targetResource)); + Q_ASSERT_SUCCEEDED(hr); + HANDLE sharedHandle; + hr = targetResource->GetSharedHandle(&sharedHandle); + Q_ASSERT_SUCCEEDED(hr); + hr = m_d3dDevice->OpenSharedResource(sharedHandle, IID_PPV_ARGS(&m_targetTexture)); + Q_ASSERT_SUCCEEDED(hr); + hr = m_d3dDevice.As(&m_videoDevice); + Q_ASSERT_SUCCEEDED(hr); + } + + ID3D11Device *device() const + { + return m_d3dDevice.Get(); + } + + ID3D11Texture2D *target() const + { + return m_target; + } + + void blit(ID3D11Texture2D *texture) + { + HRESULT hr; + D3D11_TEXTURE2D_DESC desc; + texture->GetDesc(&desc); + if (!m_videoEnumerator) { + D3D11_VIDEO_PROCESSOR_CONTENT_DESC videoProcessorDesc = { + D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, + { 0 }, desc.Width, desc.Height, + { 0 }, desc.Width, desc.Height, + D3D11_VIDEO_USAGE_PLAYBACK_NORMAL + }; + hr = m_videoDevice->CreateVideoProcessorEnumerator(&videoProcessorDesc, &m_videoEnumerator); + RETURN_VOID_IF_FAILED("Failed to create video enumerator"); + } + + if (!m_videoProcessor) { + hr = m_videoDevice->CreateVideoProcessor(m_videoEnumerator.Get(), 0, &m_videoProcessor); + RETURN_VOID_IF_FAILED("Failed to create video processor"); + } + + if (!m_outputView) { + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = { D3D11_VPOV_DIMENSION_TEXTURE2D }; + hr = m_videoDevice->CreateVideoProcessorOutputView( + m_targetTexture.Get(), m_videoEnumerator.Get(), &outputDesc, &m_outputView); + RETURN_VOID_IF_FAILED("Failed to create video output view"); + } + + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputViewDesc = { + 0, D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 } + }; + ComPtr<ID3D11VideoProcessorInputView> inputView; + hr = m_videoDevice->CreateVideoProcessorInputView( + texture, m_videoEnumerator.Get(), &inputViewDesc, &inputView); + RETURN_VOID_IF_FAILED("Failed to create video input view"); + + ComPtr<ID3D11DeviceContext> context; + ComPtr<ID3D11VideoContext> videoContext; + m_d3dDevice->GetImmediateContext(&context); + hr = context.As(&videoContext); + RETURN_VOID_IF_FAILED("Failed to get video context"); + + D3D11_VIDEO_PROCESSOR_STREAM stream = { TRUE }; + stream.pInputSurface = inputView.Get(); + hr = videoContext->VideoProcessorBlt( + m_videoProcessor.Get(), m_outputView.Get(), 0, 1, &stream); + RETURN_VOID_IF_FAILED("Failed to get blit video frame"); + } + +private: + ComPtr<ID3D11Device> m_d3dDevice; + ComPtr<ID3D11Texture2D> m_targetTexture; + ID3D11Texture2D *m_target; + ComPtr<ID3D11VideoDevice> m_videoDevice; + ComPtr<ID3D11VideoProcessorEnumerator> m_videoEnumerator; + ComPtr<ID3D11VideoProcessor> m_videoProcessor; + ComPtr<ID3D11VideoProcessorOutputView> m_outputView; +}; + +class QWinRTCameraVideoRendererControlPrivate +{ +public: + QScopedPointer<D3DVideoBlitter> blitter; + QVector<ComPtr<IMF2DBuffer>> buffers; +}; + +QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent) + : QWinRTAbstractVideoRendererControl(size, parent), d_ptr(new QWinRTCameraVideoRendererControlPrivate) +{ +} + +QWinRTCameraVideoRendererControl::~QWinRTCameraVideoRendererControl() +{ + shutdown(); +} + +bool QWinRTCameraVideoRendererControl::render(ID3D11Texture2D *target) +{ + Q_D(QWinRTCameraVideoRendererControl); + + if (d->buffers.isEmpty()) { + emit bufferRequested(); + return false; + } + + HRESULT hr; + ComPtr<IMF2DBuffer> buffer = d->buffers.takeFirst(); + + ComPtr<ID3D11Texture2D> sourceTexture; + ComPtr<IMFDXGIBuffer> dxgiBuffer; + hr = buffer.As(&dxgiBuffer); + Q_ASSERT_SUCCEEDED(hr); + hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&sourceTexture)); + if (FAILED(hr)) { + qErrnoWarning(hr, "The video frame does not support texture output; aborting rendering."); + return false; + } + + ComPtr<ID3D11Device> device; + sourceTexture->GetDevice(&device); + if (!d->blitter || d->blitter->device() != device.Get() || d->blitter->target() != target) + d->blitter.reset(new D3DVideoBlitter(device.Get(), target)); + + d->blitter->blit(sourceTexture.Get()); + + emit bufferRequested(); + return true; +} + +void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer) +{ + Q_D(QWinRTCameraVideoRendererControl); + Q_ASSERT(buffer); + d->buffers.append(buffer); +} + +void QWinRTCameraVideoRendererControl::discardBuffers() +{ + Q_D(QWinRTCameraVideoRendererControl); + d->buffers.clear(); +} diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h new file mode 100644 index 000000000..ed8b3388d --- /dev/null +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAVIDEORENDERERCONTROL_H +#define QWINRTCAMERAVIDEORENDERERCONTROL_H + +#include "qwinrtabstractvideorenderercontrol.h" + +struct IMF2DBuffer; + +QT_BEGIN_NAMESPACE + +class QVideoSurfaceFormat; +class QWinRTCameraVideoRendererControlPrivate; +class QWinRTCameraVideoRendererControl : public QWinRTAbstractVideoRendererControl +{ + Q_OBJECT +public: + explicit QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent); + ~QWinRTCameraVideoRendererControl(); + + bool render(ID3D11Texture2D *texture) Q_DECL_OVERRIDE; + void queueBuffer(IMF2DBuffer *buffer); + void discardBuffers(); + +signals: + void bufferRequested(); + +private: + QScopedPointer<QWinRTCameraVideoRendererControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraVideoRendererControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERAVIDEORENDERERCONTROL_H diff --git a/src/plugins/winrt/qwinrtserviceplugin.cpp b/src/plugins/winrt/qwinrtserviceplugin.cpp index 5d49b44a4..036d63e39 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.cpp +++ b/src/plugins/winrt/qwinrtserviceplugin.cpp @@ -44,6 +44,8 @@ #include "qwinrtserviceplugin.h" #include "qwinrtmediaplayerservice.h" +#include "qwinrtcameraservice.h" +#include "qwinrtvideodeviceselectorcontrol.h" QT_USE_NAMESPACE @@ -52,6 +54,9 @@ QMediaService *QWinRTServicePlugin::create(QString const &key) if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) return new QWinRTMediaPlayerService(this); + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new QWinRTCameraService(this); + return Q_NULLPTR; } @@ -68,3 +73,37 @@ QMediaServiceProviderHint::Features QWinRTServicePlugin::supportedFeatures( return QMediaServiceProviderHint::Features(); } + +QCamera::Position QWinRTServicePlugin::cameraPosition(const QByteArray &device) const +{ + return QWinRTVideoDeviceSelectorControl::cameraPosition(device); +} + +int QWinRTServicePlugin::cameraOrientation(const QByteArray &device) const +{ + return QWinRTVideoDeviceSelectorControl::cameraOrientation(device); +} + +QList<QByteArray> QWinRTServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::deviceNames(); + + return QList<QByteArray>(); +} + +QString QWinRTServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::deviceDescription(device); + + return QString(); +} + +QByteArray QWinRTServicePlugin::defaultDevice(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::defaultDeviceName(); + + return QByteArray(); +} diff --git a/src/plugins/winrt/qwinrtserviceplugin.h b/src/plugins/winrt/qwinrtserviceplugin.h index aaac79c7b..9fabadb4f 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.h +++ b/src/plugins/winrt/qwinrtserviceplugin.h @@ -48,15 +48,29 @@ QT_USE_NAMESPACE class QWinRTServicePlugin : public QMediaServiceProviderPlugin , public QMediaServiceFeaturesInterface + , public QMediaServiceCameraInfoInterface + , public QMediaServiceSupportedDevicesInterface + , public QMediaServiceDefaultDeviceInterface { Q_OBJECT Q_INTERFACES(QMediaServiceFeaturesInterface) + Q_INTERFACES(QMediaServiceCameraInfoInterface) + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + Q_INTERFACES(QMediaServiceDefaultDeviceInterface) Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "winrt.json") public: QMediaService *create(QString const &key); void release(QMediaService *service); QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + + QCamera::Position cameraPosition(const QByteArray &device) const Q_DECL_OVERRIDE; + int cameraOrientation(const QByteArray &device) const Q_DECL_OVERRIDE; + + QList<QByteArray> devices(const QByteArray &service) const Q_DECL_OVERRIDE; + QString deviceDescription(const QByteArray &service, const QByteArray &device) Q_DECL_OVERRIDE; + + QByteArray defaultDevice(const QByteArray &service) const Q_DECL_OVERRIDE; }; #endif // QWINRTSERVICEPLUGIN_H diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp new file mode 100644 index 000000000..8058c3dad --- /dev/null +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwinrtvideodeviceselectorcontrol.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QVector> +#include <QtCore/QCoreApplication> +#include <QtCore/QEventLoop> +#include <QtCore/QGlobalStatic> + +#include <wrl.h> +#include <windows.devices.enumeration.h> + +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +typedef ITypedEventHandler<DeviceWatcher *, DeviceInformation *> DeviceInformationHandler; +typedef ITypedEventHandler<DeviceWatcher *, DeviceInformationUpdate *> DeviceInformationUpdateHandler; +typedef ITypedEventHandler<DeviceWatcher *, IInspectable *> DeviceEnumerationCompletedHandler; + +QT_USE_NAMESPACE + +static QString deviceName(IDeviceInformation *device) +{ + HRESULT hr; + HString id; + hr = device->get_Id(id.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 length; + const wchar_t *buffer = id.GetRawBuffer(&length); + return QString::fromWCharArray(buffer, length); +} + +static QString deviceDescription(IDeviceInformation *device) +{ + HRESULT hr; + HString name; + hr = device->get_Name(name.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 length; + const wchar_t *buffer = name.GetRawBuffer(&length); + return QString::fromWCharArray(buffer, length); +} + +struct QWinRTVideoDeviceSelectorControlGlobal +{ + QWinRTVideoDeviceSelectorControlGlobal() + : defaultDeviceIndex(-1) + { + HRESULT hr; + ComPtr<IDeviceInformationStatics> deviceWatcherFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), + IID_PPV_ARGS(&deviceWatcherFactory)); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcherFactory->CreateWatcherDeviceClass(DeviceClass_VideoCapture, &deviceWatcher); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Added( + Callback<DeviceInformationHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceAdded).Get(), + &deviceAddedToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Removed( + Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceRemoved).Get(), + &deviceRemovedToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Updated( + Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceUpdated).Get(), + &deviceUpdatedToken); + Q_ASSERT_SUCCEEDED(hr); + + // Synchronously populate the devices on construction + ComPtr<IAsyncOperation<DeviceInformationCollection *>> op; + hr = deviceWatcherFactory->FindAllAsyncDeviceClass(DeviceClass_VideoCapture, &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<DeviceInformation *>> deviceList; + hr = QWinRTFunctions::await(op, deviceList.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 deviceCount; + hr = deviceList->get_Size(&deviceCount); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < deviceCount; ++i) { + IDeviceInformation *device; + hr = deviceList->GetAt(i, &device); + Q_ASSERT_SUCCEEDED(hr); + onDeviceAdded(Q_NULLPTR, device); + } + + // If there is no default device provided by the API, choose the first one + if (!devices.isEmpty() && defaultDeviceIndex < 0) + defaultDeviceIndex = 0; + } + + ~QWinRTVideoDeviceSelectorControlGlobal() + { + HRESULT hr; + hr = deviceWatcher->remove_Added(deviceAddedToken); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcher->remove_Removed(deviceRemovedToken); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcher->remove_Updated(deviceUpdatedToken); + Q_ASSERT_SUCCEEDED(hr); + } + +private: + HRESULT onDeviceAdded(IDeviceWatcher *, IDeviceInformation *device) + { + const QString name = deviceName(device); + if (deviceIndex.contains(name)) + return S_OK; + + devices.append(device); + const int index = devices.size() - 1; + deviceIndex.insert(name, index); + + HRESULT hr; + boolean isDefault; + hr = device->get_IsDefault(&isDefault); + Q_ASSERT_SUCCEEDED(hr); + if (isDefault) + defaultDeviceIndex = index; + + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + + HRESULT onDeviceRemoved(IDeviceWatcher *, IDeviceInformationUpdate *device) + { + HRESULT hr; + HString id; + hr = device->get_Id(id.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + HString name; + hr = device->get_Id(name.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 nameLength; + const wchar_t *nameString = name.GetRawBuffer(&nameLength); + const int index = deviceIndex.take(QString::fromWCharArray(nameString, nameLength)); + if (index >= 0) + devices.remove(index); + + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + + HRESULT onDeviceUpdated(IDeviceWatcher *, IDeviceInformationUpdate *) + { + // A name or description may have changed, so emit devicesChanged + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + +public: + void addWatcher(QWinRTVideoDeviceSelectorControl *control) + { + watchers.append(control); + + HRESULT hr; + DeviceWatcherStatus status; + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + if (status != DeviceWatcherStatus_Started) { + // We can't immediately Start() if we have just called Stop() + while (status == DeviceWatcherStatus_Stopping) { + QThread::yieldCurrentThread(); + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + } + hr = deviceWatcher->Start(); + Q_ASSERT_SUCCEEDED(hr); + } + } + + void removeWatcher(QWinRTVideoDeviceSelectorControl *control) + { + watchers.removeAll(control); + + if (!watchers.isEmpty()) + return; + + HRESULT hr; + DeviceWatcherStatus status; + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + if (status == DeviceWatcherStatus_Stopped || status == DeviceWatcherStatus_Stopping) + return; + + hr = deviceWatcher->Stop(); + Q_ASSERT_SUCCEEDED(hr); + } + + QVector<ComPtr<IDeviceInformation>> devices; + QHash<QString, int> deviceIndex; + int defaultDeviceIndex; + +private: + ComPtr<IDeviceWatcher> deviceWatcher; + QList<QWinRTVideoDeviceSelectorControl *> watchers; + EventRegistrationToken deviceAddedToken; + EventRegistrationToken deviceRemovedToken; + EventRegistrationToken deviceUpdatedToken; +}; +Q_GLOBAL_STATIC(QWinRTVideoDeviceSelectorControlGlobal, g) + +class QWinRTVideoDeviceSelectorControlPrivate +{ +public: + int selectedDevice; +}; + +QWinRTVideoDeviceSelectorControl::QWinRTVideoDeviceSelectorControl(QObject *parent) + : QVideoDeviceSelectorControl(parent), d_ptr(new QWinRTVideoDeviceSelectorControlPrivate) +{ + Q_D(QWinRTVideoDeviceSelectorControl); + d->selectedDevice = -1; + g->addWatcher(this); +} + +QWinRTVideoDeviceSelectorControl::~QWinRTVideoDeviceSelectorControl() +{ + if (g.isDestroyed()) + return; + + g->removeWatcher(this); +} + +int QWinRTVideoDeviceSelectorControl::deviceCount() const +{ + return g->devices.size(); +} + +QString QWinRTVideoDeviceSelectorControl::deviceName(int index) const +{ + if (index < 0 || index >= g->devices.size()) + return QString(); + + return ::deviceName(g->devices.at(index).Get()); +} + +QString QWinRTVideoDeviceSelectorControl::deviceDescription(int index) const +{ + if (index < 0 || index >= g->devices.size()) + return QString(); + + return ::deviceDescription(g->devices.at(index).Get()); +} + +int QWinRTVideoDeviceSelectorControl::defaultDevice() const +{ + return g->defaultDeviceIndex; +} + +int QWinRTVideoDeviceSelectorControl::selectedDevice() const +{ + Q_D(const QWinRTVideoDeviceSelectorControl); + return d->selectedDevice; +} + +QCamera::Position QWinRTVideoDeviceSelectorControl::cameraPosition(const QString &deviceName) +{ + int deviceIndex = g->deviceIndex.value(deviceName); + IDeviceInformation *deviceInfo = g->devices.value(deviceIndex).Get(); + if (!deviceInfo) + return QCamera::UnspecifiedPosition; + + ComPtr<IEnclosureLocation> enclosureLocation; + HRESULT hr; + hr = deviceInfo->get_EnclosureLocation(&enclosureLocation); + RETURN_IF_FAILED("Failed to get camera enclosure location", return QCamera::UnspecifiedPosition); + if (!enclosureLocation) + return QCamera::UnspecifiedPosition; + + Panel panel; + hr = enclosureLocation->get_Panel(&panel); + RETURN_IF_FAILED("Failed to get camera panel location", return QCamera::UnspecifiedPosition); + + switch (panel) { + case Panel_Front: + return QCamera::FrontFace; + case Panel_Back: + return QCamera::BackFace; + default: + break; + } + return QCamera::UnspecifiedPosition; +} + +int QWinRTVideoDeviceSelectorControl::cameraOrientation(const QString &deviceName) +{ + Q_UNUSED(deviceName); + return 0; +} + +QList<QByteArray> QWinRTVideoDeviceSelectorControl::deviceNames() +{ + QList<QByteArray> devices; + foreach (const QString &device, g->deviceIndex.keys()) + devices.append(device.toUtf8()); + + return devices; +} + +QByteArray QWinRTVideoDeviceSelectorControl::deviceDescription(const QByteArray &deviceName) +{ + int deviceIndex = g->deviceIndex.value(QString::fromUtf8(deviceName), -1); + if (deviceIndex < 0) + return QByteArray(); + + return ::deviceDescription(g->devices.value(deviceIndex).Get()).toUtf8(); +} + +QByteArray QWinRTVideoDeviceSelectorControl::defaultDeviceName() +{ + if (g->defaultDeviceIndex < 0) + return QByteArray(); + + return ::deviceName(g->devices.value(g->defaultDeviceIndex).Get()).toUtf8(); +} + +void QWinRTVideoDeviceSelectorControl::setSelectedDevice(int index) +{ + Q_D(QWinRTVideoDeviceSelectorControl); + + int selectedDevice = index; + if (index < 0 || index >= g->devices.size()) + selectedDevice = -1; + + if (d->selectedDevice != selectedDevice) { + d->selectedDevice = selectedDevice; + emit selectedDeviceChanged(d->selectedDevice); + emit selectedDeviceChanged(deviceName(d->selectedDevice)); + } +} diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h new file mode 100644 index 000000000..2143b3ea8 --- /dev/null +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINRTVIDEODEVICESELECTORCONTROL_H +#define QWINRTVIDEODEVICESELECTORCONTROL_H + +#include <QtMultimedia/QVideoDeviceSelectorControl> +#include <QtMultimedia/QCameraInfoControl> +#include <QtCore/qt_windows.h> + +struct IInspectable; +namespace ABI { + namespace Windows { + namespace Devices { + namespace Enumeration { + struct IDeviceInformation; + } + } + } +} + +QT_BEGIN_NAMESPACE + +class QWinRTVideoDeviceSelectorControlPrivate; +class QWinRTVideoDeviceSelectorControl : public QVideoDeviceSelectorControl +{ + Q_OBJECT +public: + explicit QWinRTVideoDeviceSelectorControl(QObject *parent = 0); + ~QWinRTVideoDeviceSelectorControl(); + + int deviceCount() const Q_DECL_OVERRIDE; + + QString deviceName(int index) const Q_DECL_OVERRIDE; + QString deviceDescription(int index) const Q_DECL_OVERRIDE; + + int defaultDevice() const Q_DECL_OVERRIDE; + int selectedDevice() const Q_DECL_OVERRIDE; + + static QCamera::Position cameraPosition(const QString &deviceName); + static int cameraOrientation(const QString &deviceName); + static QList<QByteArray> deviceNames(); + static QByteArray deviceDescription(const QByteArray &deviceName); + static QByteArray defaultDeviceName(); + +public slots: + void setSelectedDevice(int index) Q_DECL_OVERRIDE; + +private: + QScopedPointer<QWinRTVideoDeviceSelectorControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTVideoDeviceSelectorControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTVIDEODEVICESELECTORCONTROL_H diff --git a/src/plugins/winrt/winrt.json b/src/plugins/winrt/winrt.json index b85cfeb12..9af79cc37 100644 --- a/src/plugins/winrt/winrt.json +++ b/src/plugins/winrt/winrt.json @@ -1,4 +1,4 @@ { "Keys": ["winrt"], - "Services": ["org.qt-project.qt.mediaplayer"] + "Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.camera"] } diff --git a/src/plugins/winrt/winrt.pro b/src/plugins/winrt/winrt.pro index 0ea90d22e..04db71e75 100644 --- a/src/plugins/winrt/winrt.pro +++ b/src/plugins/winrt/winrt.pro @@ -5,21 +5,33 @@ PLUGIN_TYPE=mediaservice PLUGIN_CLASS_NAME = WinRTServicePlugin load(qt_plugin) -LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 +LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 -lruntimeobject HEADERS += \ qwinrtabstractvideorenderercontrol.h \ + qwinrtcameracontrol.h \ + qwinrtcamerainfocontrol.h \ + qwinrtcameraimagecapturecontrol.h \ + qwinrtcameraservice.h \ + qwinrtcameravideorenderercontrol.h \ qwinrtmediaplayercontrol.h \ qwinrtmediaplayerservice.h \ qwinrtplayerrenderercontrol.h \ - qwinrtserviceplugin.h + qwinrtserviceplugin.h \ + qwinrtvideodeviceselectorcontrol.h SOURCES += \ qwinrtabstractvideorenderercontrol.cpp \ + qwinrtcameracontrol.cpp \ + qwinrtcamerainfocontrol.cpp \ + qwinrtcameraimagecapturecontrol.cpp \ + qwinrtcameraservice.cpp \ + qwinrtcameravideorenderercontrol.cpp \ qwinrtmediaplayercontrol.cpp \ qwinrtmediaplayerservice.cpp \ qwinrtplayerrenderercontrol.cpp \ - qwinrtserviceplugin.cpp + qwinrtserviceplugin.cpp \ + qwinrtvideodeviceselectorcontrol.cpp OTHER_FILES += \ winrt.json diff --git a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp b/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp index 93c1841bc..da69f926f 100644 --- a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp +++ b/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp @@ -236,7 +236,6 @@ void MFAudioDecoderControl::handleMediaSourceReady() } if (m_sourceResolver->mediaSource()) { - IMFPresentationDescriptor *pd = 0; if (mediaType && m_resampler) { HRESULT hr = S_OK; hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0); @@ -246,9 +245,11 @@ void MFAudioDecoderControl::handleMediaSourceReady() qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler" << hr; } } + IMFPresentationDescriptor *pd; if (SUCCEEDED(m_sourceResolver->mediaSource()->CreatePresentationDescriptor(&pd))) { UINT64 duration = 0; pd->GetUINT64(MF_PD_DURATION, &duration); + pd->Release(); duration /= 10000; if (m_duration != qint64(duration)) { m_duration = qint64(duration); diff --git a/src/plugins/wmf/evrd3dpresentengine.cpp b/src/plugins/wmf/evrd3dpresentengine.cpp index a1b63321c..42d0dea4e 100644 --- a/src/plugins/wmf/evrd3dpresentengine.cpp +++ b/src/plugins/wmf/evrd3dpresentengine.cpp @@ -48,7 +48,6 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GLES2/gl2.h> #include <d3d9.h> #include <dxva2api.h> #include <WinUser.h> @@ -331,34 +330,36 @@ void D3DPresentEngine::presentSample(void *opaque, qint64) IMFMediaBuffer* buffer = NULL; IDirect3DSurface9* surface = NULL; - if (sample) { - // Get the buffer from the sample. - hr = sample->GetBufferByIndex(0, &buffer); - if (FAILED(hr)) - goto done; + if (m_surface && m_surface->isActive()) { + if (sample) { + // Get the buffer from the sample. + hr = sample->GetBufferByIndex(0, &buffer); + if (FAILED(hr)) + goto done; + + // Get the surface from the buffer. + hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface)); + if (FAILED(hr)) + goto done; + } - // Get the surface from the buffer. - hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface)); - if (FAILED(hr)) - goto done; - } + if (surface && updateTexture(surface)) { + QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture), + m_surfaceFormat.frameSize(), + m_surfaceFormat.pixelFormat()); - if (surface && updateTexture(surface)) { - QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture), - m_surfaceFormat.frameSize(), - m_surfaceFormat.pixelFormat()); + // WMF uses 100-nanosecond units, Qt uses microseconds + LONGLONG startTime = -1; + if (SUCCEEDED(sample->GetSampleTime(&startTime))) { + frame.setStartTime(startTime * 0.1); - // WMF uses 100-nanosecond units, Qt uses microseconds - LONGLONG startTime = -1; - if (SUCCEEDED(sample->GetSampleTime(&startTime))) { - frame.setStartTime(startTime * 0.1); + LONGLONG duration = -1; + if (SUCCEEDED(sample->GetSampleDuration(&duration))) + frame.setEndTime((startTime + duration) * 0.1); + } - LONGLONG duration = -1; - if (SUCCEEDED(sample->GetSampleDuration(&duration))) - frame.setEndTime((startTime + duration) * 0.1); + m_surface->present(frame); } - - m_surface->present(frame); } done: @@ -420,18 +421,19 @@ void D3DPresentEngine::createOffscreenTexture() QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); m_eglDisplay = static_cast<EGLDisplay*>( nativeInterface->nativeResourceForContext("eglDisplay", currentContext)); - m_eglConfig = static_cast<EGLDisplay*>( + m_eglConfig = static_cast<EGLConfig*>( nativeInterface->nativeResourceForContext("eglConfig", currentContext)); currentContext->functions()->glGenTextures(1, &m_glTexture); int w = m_surfaceFormat.frameWidth(); int h = m_surfaceFormat.frameHeight(); + bool hasAlpha = currentContext->format().hasAlpha(); EGLint attribs[] = { EGL_WIDTH, w, EGL_HEIGHT, h, - EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB, + EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE }; @@ -450,7 +452,7 @@ void D3DPresentEngine::createOffscreenTexture() m_device->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, - D3DFMT_X8R8G8B8, + hasAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &m_texture, &share_handle); diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp index 77b19f1b0..08baa779a 100644 --- a/src/plugins/wmf/player/mfplayersession.cpp +++ b/src/plugins/wmf/player/mfplayersession.cpp @@ -258,6 +258,7 @@ void MFPlayerSession::handleMediaSourceReady() //convert from 100 nanosecond to milisecond emit durationUpdate(qint64(m_duration / 10000)); setupPlaybackTopology(mediaSource, sourcePD); + sourcePD->Release(); } else { changeStatus(QMediaPlayer::InvalidMedia); emit error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true); @@ -415,12 +416,15 @@ IMFTopologyNode* MFPlayerSession::addOutputNode(IMFStreamDescriptor *streamDesc, if (SUCCEEDED(hr)) { hr = node->SetUINT32(MF_TOPONODE_STREAMID, sinkID); if (SUCCEEDED(hr)) { - if (SUCCEEDED(topology->AddNode(node))) + if (SUCCEEDED(topology->AddNode(node))) { + handler->Release(); return node; + } } } } } + handler->Release(); } node->Release(); return NULL; @@ -609,42 +613,39 @@ HRESULT BindOutputNode(IMFTopologyNode *pNode) // Sets the IMFStreamSink pointers on all of the output nodes in a topology. HRESULT BindOutputNodes(IMFTopology *pTopology) { - DWORD cNodes = 0; - - IMFCollection *collection = NULL; - IUnknown *element = NULL; - IMFTopologyNode *node = NULL; + IMFCollection *collection; // Get the collection of output nodes. HRESULT hr = pTopology->GetOutputNodeCollection(&collection); // Enumerate all of the nodes in the collection. - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { + DWORD cNodes; hr = collection->GetElementCount(&cNodes); - if (SUCCEEDED(hr)) { - for (DWORD i = 0; i < cNodes; i++) { - hr = collection->GetElement(i, &element); - if (FAILED(hr)) - break; + if (SUCCEEDED(hr)) { + for (DWORD i = 0; i < cNodes; i++) { + IUnknown *element; + hr = collection->GetElement(i, &element); + if (FAILED(hr)) + break; - hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node); - if (FAILED(hr)) - break; + IMFTopologyNode *node; + hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node); + element->Release(); + if (FAILED(hr)) + break; - // Bind this node. - hr = BindOutputNode(node); - if (FAILED(hr)) - break; + // Bind this node. + hr = BindOutputNode(node); + node->Release(); + if (FAILED(hr)) + break; + } } + collection->Release(); } - if (collection) - collection->Release(); - if (element) - element->Release(); - if (node) - node->Release(); return hr; } @@ -1395,14 +1396,17 @@ int MFPlayerSession::bufferStatus() if (!m_netsourceStatistics) return 0; PROPVARIANT var; + PropVariantInit(&var); PROPERTYKEY key; key.fmtid = MFNETSOURCE_STATISTICS; key.pid = MFNETSOURCE_BUFFERPROGRESS_ID; int progress = -1; - if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) { + // GetValue returns S_FALSE if the property is not available, which has + // a value > 0. We therefore can't use the SUCCEEDED macro here. + if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { progress = var.lVal; + PropVariantClear(&var); } - PropVariantClear(&var); #ifdef DEBUG_MEDIAFOUNDATION qDebug() << "bufferStatus: progress = " << progress; @@ -1413,22 +1417,30 @@ int MFPlayerSession::bufferStatus() QMediaTimeRange MFPlayerSession::availablePlaybackRanges() { - if (!m_netsourceStatistics) - return QMediaTimeRange(); + // defaults to the whole media + qint64 start = 0; + qint64 end = qint64(m_duration / 10000); - 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); + if (m_netsourceStatistics) { + PROPVARIANT var; + PropVariantInit(&var); + PROPERTYKEY key; + key.fmtid = MFNETSOURCE_STATISTICS; + key.pid = MFNETSOURCE_SEEKRANGESTART_ID; + // GetValue returns S_FALSE if the property is not available, which has + // a value > 0. We therefore can't use the SUCCEEDED macro here. + if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { + start = qint64(var.uhVal.QuadPart / 10000); + PropVariantClear(&var); + PropVariantInit(&var); + key.pid = MFNETSOURCE_SEEKRANGEEND_ID; + if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { + end = qint64(var.uhVal.QuadPart / 10000); + PropVariantClear(&var); + } } } - PropVariantClear(&var); + return QMediaTimeRange(start, end); } @@ -1491,8 +1503,11 @@ HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult) } } - if (!m_closing) + if (!m_closing) { emit sessionEvent(pEvent); + } else { + pEvent->Release(); + } return S_OK; } @@ -1615,9 +1630,6 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) } } - if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl)))) - setVolumeInternal(m_muted ? 0 : m_volume); - DWORD dwCharacteristics = 0; m_sourceResolver->mediaSource()->GetCharacteristics(&dwCharacteristics); emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics); @@ -1688,6 +1700,9 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) } } MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics)); + + if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl)))) + setVolumeInternal(m_muted ? 0 : m_volume); } } } diff --git a/src/plugins/wmf/samplegrabber.cpp b/src/plugins/wmf/samplegrabber.cpp index 016482e5d..0a11d8bb8 100644 --- a/src/plugins/wmf/samplegrabber.cpp +++ b/src/plugins/wmf/samplegrabber.cpp @@ -155,6 +155,9 @@ STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaT if (llSampleTime == _I64_MAX) { // Set default QAudioBuffer start time llSampleTime = -1; + } else { + // WMF uses 100-nanosecond units, Qt uses microseconds + llSampleTime /= 10; } foreach (MFAudioProbeControl* probe, m_audioProbes) diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp index e993609ad..72cbc9614 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp @@ -212,7 +212,7 @@ public: stride /= 4; } - m_width = qreal(m_frame.width() / stride); + m_width = qreal(m_frame.width()) / stride; textureSize.setWidth(stride); if (m_textureSize != textureSize) { diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3 b/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3 Binary files differindex 674c83405..2435f65b8 100644 --- a/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3 +++ b/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3 diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp index 5109d305b..0a1441cda 100644 --- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp @@ -39,6 +39,7 @@ #include "qaudioprobe.h" #include "qvideoprobe.h" #include <qmediaplaylist.h> +#include <qmediametadata.h> //TESTED_COMPONENT=src/multimedia @@ -71,20 +72,24 @@ private slots: void volumeAcrossFiles(); void initialVolume(); void seekPauseSeek(); + void seekInStoppedState(); void subsequentPlayback(); void probes(); void playlist(); void surfaceTest_data(); void surfaceTest(); + void metadata(); private: QMediaContent selectVideoFile(const QStringList& mediaCandidates); - QMediaContent selectSoundFile(const QStringList& mediaCandidates); + QMediaContent selectMediaFile(const QStringList& mediaCandidates); //one second local wav file QMediaContent localWavFile; + QMediaContent localWavFile2; QMediaContent localVideoFile; QMediaContent localCompressedSoundFile; + QMediaContent localFileWithMetadata; bool m_inCISystem; }; @@ -169,17 +174,17 @@ QMediaContent tst_QMediaPlayerBackend::selectVideoFile(const QStringList& mediaC return QMediaContent(); } -QMediaContent tst_QMediaPlayerBackend::selectSoundFile(const QStringList& mediaCandidates) +QMediaContent tst_QMediaPlayerBackend::selectMediaFile(const QStringList& mediaCandidates) { QMediaPlayer player; QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); foreach (QString s, mediaCandidates) { - QFileInfo soundFile(s); - if (!soundFile.exists()) + QFileInfo mediaFile(s); + if (!mediaFile.exists()) continue; - QMediaContent media = QMediaContent(QUrl::fromLocalFile(soundFile.absoluteFilePath())); + QMediaContent media = QMediaContent(QUrl::fromLocalFile(mediaFile.absoluteFilePath())); player.setMedia(media); player.play(); @@ -205,17 +210,26 @@ void tst_QMediaPlayerBackend::initTestCase() localWavFile = QMediaContent(QUrl::fromLocalFile(wavFile.absoluteFilePath())); + const QString testFileName2 = QFINDTESTDATA("testdata/_test.wav"); + QFileInfo wavFile2(testFileName2); + + QVERIFY(wavFile2.exists()); + + localWavFile2 = QMediaContent(QUrl::fromLocalFile(wavFile2.absoluteFilePath())); + qRegisterMetaType<QMediaContent>(); QStringList mediaCandidates; mediaCandidates << QFINDTESTDATA("testdata/colors.ogv"); mediaCandidates << QFINDTESTDATA("testdata/colors.mp4"); - localVideoFile = selectVideoFile(mediaCandidates); + localVideoFile = selectMediaFile(mediaCandidates); mediaCandidates.clear(); mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mkv"); mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mp3"); - localCompressedSoundFile = selectSoundFile(mediaCandidates); + localCompressedSoundFile = selectMediaFile(mediaCandidates); + + localFileWithMetadata = selectMediaFile(QStringList() << QFINDTESTDATA("testdata/nokia-tune.mp3")); qgetenv("QT_TEST_CI").toInt(&m_inCISystem,10); } @@ -239,6 +253,7 @@ void tst_QMediaPlayerBackend::loadMedia() QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); + QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); player.setMedia(localWavFile); @@ -247,11 +262,13 @@ void tst_QMediaPlayerBackend::loadMedia() QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia); QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia); QVERIFY(player.media() == localWavFile); + QVERIFY(player.currentMedia() == localWavFile); QCOMPARE(stateSpy.count(), 0); QVERIFY(statusSpy.count() > 0); QCOMPARE(mediaSpy.count(), 1); QCOMPARE(mediaSpy.last()[0].value<QMediaContent>(), localWavFile); + QCOMPARE(currentMediaSpy.last()[0].value<QMediaContent>(), localWavFile); QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); @@ -267,6 +284,7 @@ void tst_QMediaPlayerBackend::unloadMedia() QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); + QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); QSignalSpy durationSpy(&player, SIGNAL(positionChanged(qint64))); @@ -285,6 +303,7 @@ void tst_QMediaPlayerBackend::unloadMedia() stateSpy.clear(); statusSpy.clear(); mediaSpy.clear(); + currentMediaSpy.clear(); positionSpy.clear(); durationSpy.clear(); @@ -295,10 +314,12 @@ void tst_QMediaPlayerBackend::unloadMedia() QCOMPARE(player.state(), QMediaPlayer::StoppedState); QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); QCOMPARE(player.media(), QMediaContent()); + QCOMPARE(player.currentMedia(), QMediaContent()); QVERIFY(!stateSpy.isEmpty()); QVERIFY(!statusSpy.isEmpty()); QVERIFY(!mediaSpy.isEmpty()); + QVERIFY(!currentMediaSpy.isEmpty()); QVERIFY(!positionSpy.isEmpty()); } @@ -327,7 +348,7 @@ void tst_QMediaPlayerBackend::playPauseStop() QTRY_VERIFY(statusSpy.count() > 0 && statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia); - QTRY_VERIFY(player.position() > 0); + QTRY_VERIFY(player.position() > 100); QVERIFY(player.duration() > 0); QVERIFY(positionSpy.count() > 0); QVERIFY(positionSpy.last()[0].value<qint64>() > 0); @@ -361,6 +382,63 @@ void tst_QMediaPlayerBackend::playPauseStop() QCOMPARE(player.position(), qint64(0)); QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0)); QVERIFY(player.duration() > 0); + + stateSpy.clear(); + statusSpy.clear(); + positionSpy.clear(); + + player.play(); + + QCOMPARE(player.state(), QMediaPlayer::PlayingState); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); + QCOMPARE(stateSpy.count(), 1); + QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState); + QCOMPARE(statusSpy.count(), 1); // Should not go through Loading again when play -> stop -> play + QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia); + + player.stop(); + stateSpy.clear(); + statusSpy.clear(); + positionSpy.clear(); + + player.setMedia(localWavFile2); + + QTRY_VERIFY(statusSpy.count() > 0); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 0); + + player.play(); + + QTRY_VERIFY(player.position() > 100); + + player.setMedia(localWavFile); + + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); + QCOMPARE(player.position(), 0); + QCOMPARE(positionSpy.last()[0].value<qint64>(), 0); + + stateSpy.clear(); + statusSpy.clear(); + positionSpy.clear(); + + player.play(); + + QTRY_VERIFY(player.position() > 100); + + player.setMedia(QMediaContent()); + + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); + QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::NoMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); + QCOMPARE(player.position(), 0); + QCOMPARE(positionSpy.last()[0].value<qint64>(), 0); + QCOMPARE(player.duration(), 0); } @@ -383,17 +461,25 @@ void tst_QMediaPlayerBackend::processEOS() QVERIFY(statusSpy.count() > 0); QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 2); + QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); //at EOS the position stays at the end of file - QVERIFY(player.position() > 900); + QCOMPARE(player.position(), player.duration()); + QVERIFY(positionSpy.count() > 0); + QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration()); stateSpy.clear(); statusSpy.clear(); + positionSpy.clear(); player.play(); //position is reset to start QTRY_VERIFY(player.position() < 100); + QVERIFY(positionSpy.count() > 0); + QCOMPARE(positionSpy.first()[0].value<qint64>(), 0); QCOMPARE(player.state(), QMediaPlayer::PlayingState); QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); @@ -406,13 +492,16 @@ void tst_QMediaPlayerBackend::processEOS() player.setPosition(900); //wait up to 5 seconds for EOS QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); + QVERIFY(statusSpy.count() > 0); + QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 2); + QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); - //ensure the positionChanged() signal is emitted - QVERIFY(positionSpy.count() > 0); - - QCOMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); //position stays at the end of file - QVERIFY(player.position() > 900); + QCOMPARE(player.position(), player.duration()); + QVERIFY(positionSpy.count() > 0); + QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration()); //after setPosition EndOfMedia status should be reset to Loaded stateSpy.clear(); @@ -608,7 +697,7 @@ void tst_QMediaPlayerBackend::initialVolume() void tst_QMediaPlayerBackend::seekPauseSeek() { if (localVideoFile.isNull()) - QSKIP("Video format is not supported"); + QSKIP("No supported video file"); QMediaPlayer player; @@ -674,6 +763,125 @@ void tst_QMediaPlayerBackend::seekPauseSeek() } } +void tst_QMediaPlayerBackend::seekInStoppedState() +{ + if (localVideoFile.isNull()) + QSKIP("No supported video file"); + + QMediaPlayer player; + player.setNotifyInterval(500); + + QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); + QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); + + player.setMedia(localVideoFile); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(player.position(), 0); + QVERIFY(player.isSeekable()); + + stateSpy.clear(); + positionSpy.clear(); + + qint64 position = 5000; + player.setPosition(position); + + QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); + QCOMPARE(positionSpy.count(), 1); + QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); + + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 0); + + QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + + positionSpy.clear(); + + player.play(); + + QCOMPARE(player.state(), QMediaPlayer::PlayingState); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); + QVERIFY(qAbs(player.position() - position) < qint64(500)); + + QTest::qWait(2000); + // Check that it never played from the beginning + QVERIFY(player.position() > (position - 500)); + for (int i = 0; i < positionSpy.count(); ++i) + QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); + + // ------ + // Same tests but after play() --> stop() + + player.stop(); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + QCOMPARE(player.position(), 0); + + stateSpy.clear(); + positionSpy.clear(); + + player.setPosition(position); + + QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); + QCOMPARE(positionSpy.count(), 1); + QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); + + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 0); + + QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + + positionSpy.clear(); + + player.play(); + + QCOMPARE(player.state(), QMediaPlayer::PlayingState); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); + QVERIFY(qAbs(player.position() - position) < qint64(500)); + + QTest::qWait(2000); + // Check that it never played from the beginning + QVERIFY(player.position() > (position - 500)); + for (int i = 0; i < positionSpy.count(); ++i) + QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); + + // ------ + // Same tests but after reaching the end of the media + + player.setPosition(player.duration() - 500); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(player.position(), player.duration()); + + stateSpy.clear(); + positionSpy.clear(); + + player.setPosition(position); + + QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); + QCOMPARE(positionSpy.count(), 1); + QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); + + QCOMPARE(player.state(), QMediaPlayer::StoppedState); + QCOMPARE(stateSpy.count(), 0); + + QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); + + positionSpy.clear(); + + player.play(); + + QCOMPARE(player.state(), QMediaPlayer::PlayingState); + QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); + QVERIFY(qAbs(player.position() - position) < qint64(500)); + + QTest::qWait(2000); + // Check that it never played from the beginning + QVERIFY(player.position() > (position - 500)); + for (int i = 0; i < positionSpy.count(); ++i) + QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); +} + void tst_QMediaPlayerBackend::subsequentPlayback() { #ifdef Q_OS_LINUX @@ -717,7 +925,7 @@ void tst_QMediaPlayerBackend::subsequentPlayback() void tst_QMediaPlayerBackend::probes() { if (localVideoFile.isNull()) - QSKIP("Video format is not supported"); + QSKIP("No supported video file"); QMediaPlayer *player = new QMediaPlayer; @@ -733,8 +941,9 @@ void tst_QMediaPlayerBackend::probes() connect(audioProbe, SIGNAL(audioBufferProbed(QAudioBuffer)), &probeHandler, SLOT(processBuffer(QAudioBuffer))); connect(audioProbe, SIGNAL(flush()), &probeHandler, SLOT(flushAudio())); - QVERIFY(videoProbe->setSource(player)); - QVERIFY(audioProbe->setSource(player)); + if (!videoProbe->setSource(player)) + QSKIP("QVideoProbe is not supported"); + audioProbe->setSource(player); player->setMedia(localVideoFile); QTRY_COMPARE(player->mediaStatus(), QMediaPlayer::LoadedMedia); @@ -831,7 +1040,7 @@ void tst_QMediaPlayerBackend::playlist() errorSpy.clear(); // <<< Invalid2 - 1st pass >>> - fileInfo.setFile((QFINDTESTDATA("testdata/invalid_media2.m3u"))); + fileInfo.setFile(QFINDTESTDATA("/testdata/invalid_media2.m3u")); player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); player.play(); @@ -864,7 +1073,7 @@ void tst_QMediaPlayerBackend::playlist() errorSpy.clear(); // <<< Recursive - 1st pass >>> - fileInfo.setFile((QFINDTESTDATA("testdata/recursive_master.m3u"))); + fileInfo.setFile(QFINDTESTDATA("testdata/recursive_master.m3u")); player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); player.play(); @@ -929,7 +1138,7 @@ void tst_QMediaPlayerBackend::surfaceTest() { // 25 fps video file if (localVideoFile.isNull()) - QSKIP("Video format is not supported"); + QSKIP("No supported video file"); QFETCH(QList<QVideoFrame::PixelFormat>, formatsList); @@ -943,6 +1152,39 @@ void tst_QMediaPlayerBackend::surfaceTest() QVERIFY(surface.m_totalFrames >= 25); } +void tst_QMediaPlayerBackend::metadata() +{ + if (localFileWithMetadata.isNull()) + QSKIP("No supported media file"); + + QMediaPlayer player; + + QSignalSpy metadataAvailableSpy(&player, SIGNAL(metaDataAvailableChanged(bool))); + QSignalSpy metadataChangedSpy(&player, SIGNAL(metaDataChanged())); + + player.setMedia(localFileWithMetadata); + + QTRY_VERIFY(player.isMetaDataAvailable()); + QCOMPARE(metadataAvailableSpy.count(), 1); + QVERIFY(metadataAvailableSpy.last()[0].toBool()); + QVERIFY(metadataChangedSpy.count() > 0); + + QCOMPARE(player.metaData(QMediaMetaData::Title).toString(), QStringLiteral("Nokia Tune")); + QCOMPARE(player.metaData(QMediaMetaData::ContributingArtist).toString(), QStringLiteral("TestArtist")); + QCOMPARE(player.metaData(QMediaMetaData::AlbumTitle).toString(), QStringLiteral("TestAlbum")); + + metadataAvailableSpy.clear(); + metadataChangedSpy.clear(); + + player.setMedia(QMediaContent()); + + QVERIFY(!player.isMetaDataAvailable()); + QCOMPARE(metadataAvailableSpy.count(), 1); + QVERIFY(!metadataAvailableSpy.last()[0].toBool()); + QCOMPARE(metadataChangedSpy.count(), 1); + QVERIFY(player.availableMetaData().isEmpty()); +} + TestVideoSurface::TestVideoSurface(bool storeFrames): m_totalFrames(0), m_storeFrames(storeFrames) diff --git a/tests/auto/unit/qmediaplaylist/testdata/trash.pls b/tests/auto/unit/qmediaplaylist/testdata/empty.pls index 639c22b0c..639c22b0c 100644 --- a/tests/auto/unit/qmediaplaylist/testdata/trash.pls +++ b/tests/auto/unit/qmediaplaylist/testdata/empty.pls diff --git a/tests/auto/unit/qmediaplaylist/testdata/test.pls b/tests/auto/unit/qmediaplaylist/testdata/test.pls index 42a14f395..18832b101 100644 --- a/tests/auto/unit/qmediaplaylist/testdata/test.pls +++ b/tests/auto/unit/qmediaplaylist/testdata/test.pls @@ -3,7 +3,7 @@ File1=http://test.host/path Title1=First Length1=-1 -File2= http://test.host/path +File2= http://test.host/path Title2=Second Length2=-1 File3=testfile diff --git a/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp b/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp index a1250e59e..b867604e8 100644 --- a/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp +++ b/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp @@ -463,14 +463,15 @@ void tst_QMediaPlaylist::loadPLSFile() QVERIFY(!loadFailedSpy.isEmpty()); QVERIFY(playlist.error() != QMediaPlaylist::NoError); - // Try to load bogus playlist + // Try to load empty playlist loadSpy.clear(); loadFailedSpy.clear(); - testFileName = QFINDTESTDATA("testdata/trash.pls"); + testFileName = QFINDTESTDATA("testdata/empty.pls"); playlist.load(QUrl::fromLocalFile(testFileName)); - QTRY_VERIFY(loadSpy.isEmpty()); - QVERIFY(!loadFailedSpy.isEmpty()); - QVERIFY(playlist.error() == QMediaPlaylist::FormatError); + QTRY_VERIFY(!loadSpy.isEmpty()); + QVERIFY(loadFailedSpy.isEmpty()); + QCOMPARE(playlist.error(), QMediaPlaylist::NoError); + QCOMPARE(playlist.mediaCount(), 0); // Try to load regular playlist loadSpy.clear(); @@ -485,30 +486,30 @@ void tst_QMediaPlaylist::loadPLSFile() QCOMPARE(playlist.media(0).canonicalUrl(), QUrl(QLatin1String("http://test.host/path"))); QCOMPARE(playlist.media(1).canonicalUrl(), QUrl(QLatin1String("http://test.host/path"))); testFileName = QFINDTESTDATA("testdata/testfile"); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QCOMPARE(playlist.media(2).canonicalUrl(), QUrl::fromLocalFile(testFileName)); testFileName = QFINDTESTDATA("testdata"); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QCOMPARE(playlist.media(3).canonicalUrl(), QUrl::fromLocalFile(testFileName + "/testdir/testfile")); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QCOMPARE(playlist.media(4).canonicalUrl(), QUrl(QLatin1String("file:///testdir/testfile"))); QCOMPARE(playlist.media(5).canonicalUrl(), QUrl(QLatin1String("file://path/name#suffix"))); //ensure #2 suffix is not stripped from path testFileName = QFINDTESTDATA("testdata/testfile2#suffix"); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QCOMPARE(playlist.media(6).canonicalUrl(), QUrl::fromLocalFile(testFileName)); // Try to load a totem-pl generated playlist + // (Format doesn't respect the spec) loadSpy.clear(); loadFailedSpy.clear(); + playlist.clear(); testFileName = QFINDTESTDATA("testdata/totem-pl-example.pls"); playlist.load(QUrl::fromLocalFile(testFileName)); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QTRY_VERIFY(!loadSpy.isEmpty()); - QEXPECT_FAIL("", "See QTBUG-40515", Continue); QVERIFY(loadFailedSpy.isEmpty()); + QCOMPARE(playlist.error(), QMediaPlaylist::NoError); + QCOMPARE(playlist.mediaCount(), 1); + QCOMPARE(playlist.media(0).canonicalUrl(), QUrl(QLatin1String("http://test.host/path"))); + // check ability to load from QNetworkRequest loadSpy.clear(); |