From 700b4cdf42335ad02ff308cddbfc37b8d49a1e71 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 1 Oct 2012 10:20:19 +0200 Subject: [PATCH] Add MediaPlayer support to AVFoundation plugin This plugin would be used on Mac 10.7+ where without the QuickTime C API our QT7 media player performance was crippled. Change-Id: Iaadb1990a8f63393c4cd02d096624e0fed42b40f Reviewed-by: Lars Knoll Reviewed-by: Jason Barron Reviewed-by: Andy Nichols --- src/plugins/avfoundation/avfoundation.pro | 3 +- .../avfoundation/mediaplayer/avfdisplaylink.h | 84 +++ .../avfoundation/mediaplayer/avfdisplaylink.mm | 159 ++++ .../avfoundation/mediaplayer/avfmediaplayer.json | 3 + .../mediaplayer/avfmediaplayercontrol.h | 101 +++ .../mediaplayer/avfmediaplayercontrol.mm | 186 +++++ .../mediaplayer/avfmediaplayermetadatacontrol.h | 76 ++ .../mediaplayer/avfmediaplayermetadatacontrol.mm | 157 ++++ .../mediaplayer/avfmediaplayerservice.h | 72 ++ .../mediaplayer/avfmediaplayerservice.mm | 117 +++ .../mediaplayer/avfmediaplayerserviceplugin.h | 79 ++ .../mediaplayer/avfmediaplayerserviceplugin.mm | 109 +++ .../mediaplayer/avfmediaplayersession.h | 181 +++++ .../mediaplayer/avfmediaplayersession.mm | 832 +++++++++++++++++++++ .../mediaplayer/avfvideoframerenderer.h | 94 +++ .../mediaplayer/avfvideoframerenderer.mm | 258 +++++++ .../avfoundation/mediaplayer/avfvideooutput.h | 62 ++ .../avfoundation/mediaplayer/avfvideooutput.mm | 44 ++ .../mediaplayer/avfvideorenderercontrol.h | 92 +++ .../mediaplayer/avfvideorenderercontrol.mm | 213 ++++++ .../avfoundation/mediaplayer/avfvideowidget.h | 86 +++ .../avfoundation/mediaplayer/avfvideowidget.mm | 201 +++++ .../mediaplayer/avfvideowidgetcontrol.h | 108 +++ .../mediaplayer/avfvideowidgetcontrol.mm | 219 ++++++ .../avfoundation/mediaplayer/mediaplayer.pro | 56 ++ 25 files changed, 3591 insertions(+), 1 deletion(-) create mode 100644 src/plugins/avfoundation/mediaplayer/avfdisplaylink.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayer.json create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideooutput.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideooutput.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideowidget.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideowidget.mm create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h create mode 100644 src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm create mode 100644 src/plugins/avfoundation/mediaplayer/mediaplayer.pro diff --git a/src/plugins/avfoundation/avfoundation.pro b/src/plugins/avfoundation/avfoundation.pro index 9a3dbf6..7f2ddb2 100644 --- a/src/plugins/avfoundation/avfoundation.pro +++ b/src/plugins/avfoundation/avfoundation.pro @@ -1,3 +1,4 @@ TEMPLATE = subdirs -SUBDIRS += camera +SUBDIRS += camera \ + mediaplayer diff --git a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h new file mode 100644 index 0000000..d83f344 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFDISPLAYLINK_H +#define AVFDISPLAYLINK_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class AVFDisplayLink : public QObject +{ + Q_OBJECT +public: + explicit AVFDisplayLink(QObject *parent = 0); + virtual ~AVFDisplayLink(); + bool isValid() const; + 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 // AVFDISPLAYLINK_H diff --git a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm new file mode 100644 index 0000000..59fb335 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfdisplaylink.h" +#include + +#ifdef QT_DEBUG_AVF +#include +#endif + +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); + + AVFDisplayLink *link = (AVFDisplayLink *)displayLinkContext; + + link->displayLinkEvent(inOutputTime); + return kCVReturnSuccess; +} + +AVFDisplayLink::AVFDisplayLink(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); + } +} + +AVFDisplayLink::~AVFDisplayLink() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + + if (m_displayLink) { + CVDisplayLinkStop(m_displayLink); + CVDisplayLinkRelease(m_displayLink); + m_displayLink = NULL; + } +} + +bool AVFDisplayLink::isValid() const +{ + return m_displayLink != 0; +} + +bool AVFDisplayLink::isActive() const +{ + return m_isActive; +} + +void AVFDisplayLink::start() +{ + if (m_displayLink && !m_isActive) { + CVDisplayLinkStart(m_displayLink); + m_isActive = true; + } +} + +void AVFDisplayLink::stop() +{ + if (m_displayLink && m_isActive) { + CVDisplayLinkStop(m_displayLink); + m_isActive = false; + } +} + + +void AVFDisplayLink::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 AVFDisplayLink::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); +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json b/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json new file mode 100644 index 0000000..c4a27ea --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json @@ -0,0 +1,3 @@ +{ + "Keys": ["org.qt-project.qt.mediaplayer"] +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h new file mode 100644 index 0000000..641e200 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFMEDIAPLAYERCONTROL_H +#define AVFMEDIAPLAYERCONTROL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class AVFMediaPlayerSession; + +class AVFMediaPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + explicit AVFMediaPlayerControl(QObject *parent = 0); + ~AVFMediaPlayerControl(); + + void setSession(AVFMediaPlayerSession *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: + AVFMediaPlayerSession *m_session; +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIAPLAYERCONTROL_H diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm new file mode 100644 index 0000000..c6c114f --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfmediaplayercontrol.h" +#include "avfmediaplayersession.h" + +QT_USE_NAMESPACE + +AVFMediaPlayerControl::AVFMediaPlayerControl(QObject *parent) : + QMediaPlayerControl(parent) +{ +} + +AVFMediaPlayerControl::~AVFMediaPlayerControl() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif +} + +void AVFMediaPlayerControl::setSession(AVFMediaPlayerSession *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))); +} + +QMediaPlayer::State AVFMediaPlayerControl::state() const +{ + return m_session->state(); +} + +QMediaPlayer::MediaStatus AVFMediaPlayerControl::mediaStatus() const +{ + return m_session->mediaStatus(); +} + +QMediaContent AVFMediaPlayerControl::media() const +{ + return m_session->media(); +} + +const QIODevice *AVFMediaPlayerControl::mediaStream() const +{ + return m_session->mediaStream(); +} + +void AVFMediaPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + m_session->setMedia(content, stream); + + Q_EMIT mediaChanged(content); +} + +qint64 AVFMediaPlayerControl::position() const +{ + return m_session->position(); +} + +qint64 AVFMediaPlayerControl::duration() const +{ + return m_session->duration(); +} + +int AVFMediaPlayerControl::bufferStatus() const +{ + return m_session->bufferStatus(); +} + +int AVFMediaPlayerControl::volume() const +{ + return m_session->volume(); +} + +bool AVFMediaPlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool AVFMediaPlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool AVFMediaPlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +bool AVFMediaPlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange AVFMediaPlayerControl::availablePlaybackRanges() const +{ + return m_session->availablePlaybackRanges(); +} + +qreal AVFMediaPlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void AVFMediaPlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void AVFMediaPlayerControl::setPosition(qint64 pos) +{ + m_session->setPosition(pos); +} + +void AVFMediaPlayerControl::play() +{ + m_session->play(); +} + +void AVFMediaPlayerControl::pause() +{ + m_session->pause(); +} + +void AVFMediaPlayerControl::stop() +{ + m_session->stop(); +} + +void AVFMediaPlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void AVFMediaPlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h new file mode 100644 index 0000000..2a717ed --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFMEDIAPLAYERMETADATACONTROL_H +#define AVFMEDIAPLAYERMETADATACONTROL_H + +#include + +QT_BEGIN_NAMESPACE + +class AVFMediaPlayerSession; + +class AVFMediaPlayerMetaDataControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + explicit AVFMediaPlayerMetaDataControl(AVFMediaPlayerSession *session, QObject *parent = 0); + virtual ~AVFMediaPlayerMetaDataControl(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(const QString &key) const; + QStringList availableMetaData() const; + +private Q_SLOTS: + void updateTags(); + +private: + AVFMediaPlayerSession *m_session; + QMap m_tags; + void *m_asset; + +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIAPLAYERMETADATACONTROL_H diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm new file mode 100644 index 0000000..84a6bef --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfmediaplayermetadatacontrol.h" +#include "avfmediaplayersession.h" + +#include + +#import + +QT_USE_NAMESPACE + +AVFMediaPlayerMetaDataControl::AVFMediaPlayerMetaDataControl(AVFMediaPlayerSession *session, QObject *parent) + : QMetaDataReaderControl(parent) + , m_session(session) + , m_asset(0) +{ + QObject::connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(updateTags())); +} + +AVFMediaPlayerMetaDataControl::~AVFMediaPlayerMetaDataControl() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif +} + +bool AVFMediaPlayerMetaDataControl::isMetaDataAvailable() const +{ + return !m_tags.isEmpty(); +} + +bool AVFMediaPlayerMetaDataControl::isWritable() const +{ + return false; +} + +QVariant AVFMediaPlayerMetaDataControl::metaData(const QString &key) const +{ + return m_tags.value(key); +} + +QStringList AVFMediaPlayerMetaDataControl::availableMetaData() const +{ + return m_tags.keys(); +} + +void AVFMediaPlayerMetaDataControl::updateTags() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + AVAsset *currentAsset = (AVAsset*)m_session->currentAssetHandle(); + + //Don't read the tags from the same asset more than once + if (currentAsset == m_asset) { + return; + } + + m_asset = currentAsset; + + //Since we've changed assets, clear old tags + m_tags.clear(); + + NSArray *metadataFormats = [currentAsset availableMetadataFormats]; + for ( NSString *format in metadataFormats) { +#ifdef QT_DEBUG_AVF + qDebug() << "format: " << [format UTF8String]; +#endif + NSArray *metadataItems = [currentAsset metadataForFormat:format]; + for (AVMetadataItem* item in metadataItems) { + NSString *keyString = [item commonKey]; + NSString *value = [item stringValue]; + + if (keyString.length != 0) { + //Process "commonMetadata" tags here: + if ([keyString isEqualToString:AVMetadataCommonKeyTitle]) { + m_tags.insert(QtMultimedia::MetaData::Title, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyCreator]) { + m_tags.insert(QtMultimedia::MetaData::Author, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeySubject]) { + m_tags.insert(QtMultimedia::MetaData::SubTitle, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyDescription]) { + m_tags.insert(QtMultimedia::MetaData::Description, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyPublisher]) { + m_tags.insert(QtMultimedia::MetaData::Publisher, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyContributor]) { + m_tags.insert(QtMultimedia::MetaData::ContributingArtist, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyCreationDate]) { + m_tags.insert(QtMultimedia::MetaData::Date, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyType]) { + m_tags.insert(QtMultimedia::MetaData::MediaType, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyLanguage]) { + m_tags.insert(QtMultimedia::MetaData::Language, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyCopyrights]) { + m_tags.insert(QtMultimedia::MetaData::Copyright, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyAlbumName]) { + m_tags.insert(QtMultimedia::MetaData::AlbumTitle, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyAuthor]) { + m_tags.insert(QtMultimedia::MetaData::Author, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyArtist]) { + m_tags.insert(QtMultimedia::MetaData::AlbumArtist, QString([value UTF8String])); + } else if ([keyString isEqualToString: AVMetadataCommonKeyArtwork]) { + m_tags.insert(QtMultimedia::MetaData::PosterUrl, QString([value UTF8String])); + } + } + + if ([format isEqualToString:AVMetadataFormatID3Metadata]) { + //TODO: Process ID3 metadata + } else if ([format isEqualToString:AVMetadataFormatiTunesMetadata]) { + //TODO: Process iTunes metadata + } else if ([format isEqualToString:AVMetadataFormatQuickTimeUserData]) { + //TODO: Process QuickTime metadata + } + } + } + + Q_EMIT metaDataChanged(); +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h new file mode 100644 index 0000000..ec96669 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFMEDIAPLAYERSERVICE_H +#define AVFMEDIAPLAYERSERVICE_H + +#include + +QT_BEGIN_NAMESPACE + +class AVFMediaPlayerSession; +class AVFMediaPlayerControl; +class AVFMediaPlayerMetaDataControl; +class AVFVideoOutput; + +class AVFMediaPlayerService : public QMediaService +{ +public: + explicit AVFMediaPlayerService(QObject *parent = 0); + ~AVFMediaPlayerService(); + + QMediaControl* requestControl(const char *name); + void releaseControl(QMediaControl *control); + +private: + AVFMediaPlayerSession *m_session; + AVFMediaPlayerControl *m_control; + QMediaControl *m_videoOutput; + AVFMediaPlayerMetaDataControl *m_playerMetaDataControl; +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIAPLAYERSERVICE_H diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm new file mode 100644 index 0000000..2ea8475 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfmediaplayerservice.h" +#include "avfmediaplayersession.h" +#include "avfmediaplayercontrol.h" +#include "avfmediaplayermetadatacontrol.h" +#include "avfvideooutput.h" +#include "avfvideorenderercontrol.h" + +#ifndef QT_NO_WIDGETS +#include "avfvideowidgetcontrol.h" +#endif + +QT_USE_NAMESPACE + +AVFMediaPlayerService::AVFMediaPlayerService(QObject *parent) + : QMediaService(parent) + , m_videoOutput(0) +{ + m_session = new AVFMediaPlayerSession(this); + m_control = new AVFMediaPlayerControl(this); + m_control->setSession(m_session); + m_playerMetaDataControl = new AVFMediaPlayerMetaDataControl(m_session, this); + + connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags())); +} + +AVFMediaPlayerService::~AVFMediaPlayerService() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + delete m_session; +} + +QMediaControl *AVFMediaPlayerService::requestControl(const char *name) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << name; +#endif + + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) + return m_playerMetaDataControl; + + if (!m_videoOutput) { + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + m_videoOutput = new AVFVideoRendererControl(this); +#ifndef QT_NO_WIDGETS + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) + m_videoOutput = new AVFVideoWidgetControl(this); +#endif + } + if (m_videoOutput) { + m_session->setVideoOutput(qobject_cast(m_videoOutput)); + return m_videoOutput; + } + + return 0; +} + +void AVFMediaPlayerService::releaseControl(QMediaControl *control) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << control; +#endif + + if (m_videoOutput == control) { + AVFVideoRendererControl *renderControl = qobject_cast(m_videoOutput); + if (renderControl) + renderControl->setSurface(0); + m_videoOutput = 0; + m_session->setVideoOutput(0); + delete control; + } +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h new file mode 100644 index 0000000..4646906 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFMEDIAPLAYERSERVICEPLUGIN_H +#define AVFMEDIAPLAYERSERVICEPLUGIN_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class AVFMediaPlayerServicePlugin + : public QMediaServiceProviderPlugin + , public QMediaServiceSupportedFormatsInterface + , public QMediaServiceFeaturesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedFormatsInterface) + Q_INTERFACES(QMediaServiceFeaturesInterface) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "avfmediaplayer.json") + +public: + explicit AVFMediaPlayerServicePlugin(); + + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + QtMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const; + QStringList supportedMimeTypes() const; + +private: + void buildSupportedTypes(); + + QStringList m_supportedMimeTypes; +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIAPLAYERSERVICEPLUGIN_H diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm new file mode 100644 index 0000000..cb75197 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfmediaplayerserviceplugin.h" +#include + +#include "avfmediaplayerservice.h" + +#import + +QT_USE_NAMESPACE + +AVFMediaPlayerServicePlugin::AVFMediaPlayerServicePlugin() +{ + buildSupportedTypes(); +} + +QMediaService *AVFMediaPlayerServicePlugin::create(const QString &key) +{ +#ifdef QT_DEBUG_AVF + qDebug() << "AVFMediaPlayerServicePlugin::create" << key; +#endif + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new AVFMediaPlayerService; + + qWarning() << "unsupported key: " << key; + return 0; +} + +void AVFMediaPlayerServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QMediaServiceProviderHint::Features AVFMediaPlayerServicePlugin::supportedFeatures(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_MEDIAPLAYER) + return QMediaServiceProviderHint::VideoSurface; + else + return QMediaServiceProviderHint::Features(); +} + +QtMultimedia::SupportEstimate AVFMediaPlayerServicePlugin::hasSupport(const QString &mimeType, const QStringList &codecs) const +{ + Q_UNUSED(codecs); + + if (m_supportedMimeTypes.contains(mimeType)) + return QtMultimedia::ProbablySupported; + + return QtMultimedia::MaybeSupported; +} + +QStringList AVFMediaPlayerServicePlugin::supportedMimeTypes() const +{ + return m_supportedMimeTypes; +} + +void AVFMediaPlayerServicePlugin::buildSupportedTypes() +{ + //Populate m_supportedMimeTypes with mimetypes AVAsset supports + NSArray *mimeTypes = [AVURLAsset audiovisualMIMETypes]; + for (NSString *mimeType in mimeTypes) + { + m_supportedMimeTypes.append(QString::fromUtf8([mimeType UTF8String])); + } +#ifdef QT_DEBUG_AVF + qDebug() << "AVFMediaPlayerServicePlugin::buildSupportedTypes"; + qDebug() << "Supported Types: " << m_supportedMimeTypes; +#endif + +} diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h new file mode 100644 index 0000000..3b70395 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFMEDIAPLAYERSESSION_H +#define AVFMEDIAPLAYERSESSION_H + +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class AVFMediaPlayerService; +class AVFVideoOutput; + +class AVFMediaPlayerSession : public QObject +{ + Q_OBJECT +public: + AVFMediaPlayerSession(AVFMediaPlayerService *service, QObject *parent = 0); + virtual ~AVFMediaPlayerSession(); + + void setVideoOutput(AVFVideoOutput *output); + void *currentAssetHandle(); + + 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 processPositionChange(); + + void processCurrentItemChanged(); + +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; + }; + + AVFMediaPlayerService *m_service; + AVFVideoOutput *m_videoOutput; + + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QIODevice *m_mediaStream; + QMediaContent m_resources; + ResourceHandler m_resourceHandler; + + bool m_muted; + bool m_tryingAsync; + int m_volume; + qreal m_rate; + + qint64 m_duration; + bool m_videoAvailable; + bool m_audioAvailable; + + void *m_observer; +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIAPLAYERSESSION_H diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm new file mode 100644 index 0000000..3fb9354 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -0,0 +1,832 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfmediaplayersession.h" +#include "avfmediaplayerservice.h" +#include "avfvideooutput.h" + +#import + +QT_USE_NAMESPACE + +//AVAsset Keys +static NSString* const AVF_TRACKS_KEY = @"tracks"; +static NSString* const AVF_PLAYABLE_KEY = @"playable"; + +//AVPlayerItem keys +static NSString* const AVF_STATUS_KEY = @"status"; + +//AVPlayer keys +static NSString* const AVF_RATE_KEY = @"rate"; +static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem"; + +static void *AVFMediaPlayerSessionObserverRateObservationContext = &AVFMediaPlayerSessionObserverRateObservationContext; +static void *AVFMediaPlayerSessionObserverStatusObservationContext = &AVFMediaPlayerSessionObserverStatusObservationContext; +static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMediaPlayerSessionObserverCurrentItemObservationContext; + +@interface AVFMediaPlayerSessionObserver : NSObject +{ +@private + AVFMediaPlayerSession *m_session; + AVPlayer *m_player; + 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; +- (void) setURL:(NSURL *)url; +- (void) unloadMedia; +- (void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys; +- (void) assetFailedToPrepareForPlayback:(NSError *)error; +- (void) playerItemDidReachEnd:(NSNotification *)notification; +- (void) playerItemTimeJumped:(NSNotification *)notification; +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context; +- (void) detatchSession; +- (void) dealloc; +@end + +@implementation AVFMediaPlayerSessionObserver + +@synthesize m_player, m_playerItem, m_playerLayer, m_audioAvailable, m_videoAvailable, m_session; + +- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session +{ + if (!(self = [super init])) + return nil; + + self->m_session = session; + return self; +} + +- (void) setURL:(NSURL *)url +{ + if (m_URL != url) + { + [m_URL release]; + m_URL = [url copy]; + + //Create an asset for inspection of a resource referenced by a given URL. + //Load the values for the asset keys "tracks", "playable". + + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:m_URL options:nil]; + NSArray *requestedKeys = [NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil]; + + // Tells the asset to load the values of any of the specified keys that are not already loaded. + [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler: + ^{ + dispatch_async( dispatch_get_main_queue(), + ^{ + [self prepareToPlayAsset:asset withKeys:requestedKeys]; + }); + }]; + } +} + +- (void) unloadMedia +{ + [m_player setRate:0.0]; + [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY]; + + [[NSNotificationCenter defaultCenter] removeObserver:self + name:AVPlayerItemDidPlayToEndTimeNotification + object:m_playerItem]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:AVPlayerItemTimeJumpedNotification + object:m_playerItem]; + m_playerItem = 0; +} + +- (void) prepareToPlayAsset:(AVURLAsset *)asset + withKeys:(NSArray *)requestedKeys +{ + //Make sure that the value of each key has loaded successfully. + for (NSString *thisKey in requestedKeys) + { + NSError *error = nil; + AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error]; +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus; +#endif + if (keyStatus == AVKeyValueStatusFailed) + { + [self assetFailedToPrepareForPlayback:error]; + return; + } + } + + //Use the AVAsset playable property to detect whether the asset can be played. +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable]; +#endif + if (!asset.playable) + { + //Generate an error describing the failure. + NSString *localizedDescription = NSLocalizedString(@"Item cannot be played", @"Item cannot be played description"); + NSString *localizedFailureReason = NSLocalizedString(@"The assets tracks were loaded, but could not be made playable.", @"Item cannot be played failure reason"); + NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys: + localizedDescription, NSLocalizedDescriptionKey, + localizedFailureReason, NSLocalizedFailureReasonErrorKey, + nil]; + NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict]; + + [self assetFailedToPrepareForPlayback:assetCannotBePlayedError]; + + 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) + { + //Remove existing player item key value observers and notifications. + [self unloadMedia]; + } + + //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset. + m_playerItem = [AVPlayerItem playerItemWithAsset:asset]; + + //Observe the player item "status" key to determine when it is ready to play. + [m_playerItem addObserver:self + forKeyPath:AVF_STATUS_KEY + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:AVFMediaPlayerSessionObserverStatusObservationContext]; + + //When the player item has played to its end time we'll toggle + //the movie controller Pause button to be the Play button + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(playerItemDidReachEnd:) + name:AVPlayerItemDidPlayToEndTimeNotification + object:m_playerItem]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(playerItemTimeJumped:) + name:AVPlayerItemTimeJumpedNotification + object:m_playerItem]; + + + //Clean up old player if we have one + if (m_player) { + [m_player setRate:0.0]; + [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY]; + [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; + [m_player release]; + m_player = 0; + [m_playerLayer release]; + m_playerLayer = 0; //Will have been released + } + + //Get a new AVPlayer initialized to play the specified player item. + m_player = [AVPlayer playerWithPlayerItem:m_playerItem]; + [m_player retain]; + + //Set the initial volume on new player object + if (self.session) + m_player.volume = m_session->volume() / 100.0f; + + //Create a new player layer if we don't have one already + if (!m_playerLayer) + { + 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 = NSMakePoint(0.0f, 0.0f); + m_playerLayer.bounds = NSMakeRect(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height); + } + } + + } + + //Observe the AVPlayer "currentItem" property to find out when any + //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did + //occur. + [m_player addObserver:self + forKeyPath:AVF_CURRENT_ITEM_KEY + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:AVFMediaPlayerSessionObserverCurrentItemObservationContext]; + + //Observe the AVPlayer "rate" property to update the scrubber control. + [m_player addObserver:self + forKeyPath:AVF_RATE_KEY + options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew + context:AVFMediaPlayerSessionObserverRateObservationContext]; + +} + +-(void) assetFailedToPrepareForPlayback:(NSError *)error +{ + Q_UNUSED(error) + //TODO: Let the session know that the assest failed to prepare for playback +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; + qDebug() << [[error localizedDescription] UTF8String]; + qDebug() << [[error localizedFailureReason] UTF8String]; + qDebug() << [[error localizedRecoverySuggestion] UTF8String]; +#endif +} + +- (void) playerItemDidReachEnd:(NSNotification *)notification +{ + Q_UNUSED(notification) + if (self.session) + QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection); +} + +- (void) playerItemTimeJumped:(NSNotification *)notification +{ + Q_UNUSED(notification) + if (self.session) + QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection); +} + +- (void) observeValueForKeyPath:(NSString*) path + ofObject:(id)object + change:(NSDictionary*)change + context:(void*)context +{ + //AVPlayerItem "status" property value observer. + if (context == AVFMediaPlayerSessionObserverStatusObservationContext) + { + AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; + switch (status) + { + //Indicates that the status of the player is not yet known because + //it has not tried to load new media resources for playback + case AVPlayerStatusUnknown: + { + //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); + } + break; + + case AVPlayerStatusReadyToPlay: + { + //Once the AVPlayerItem becomes ready to play, i.e. + //[playerItem status] == AVPlayerItemStatusReadyToPlay, + //its duration can be fetched from the item. + if (self.session) + QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); + } + break; + + case AVPlayerStatusFailed: + { + AVPlayerItem *playerItem = (AVPlayerItem *)object; + [self assetFailedToPrepareForPlayback:playerItem.error]; + + if (self.session) + QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); + } + break; + } + } + //AVPlayer "rate" property value observer. + else if (context == AVFMediaPlayerSessionObserverRateObservationContext) + { + //QMetaObject::invokeMethod(m_session, "setPlaybackRate", Qt::AutoConnection, Q_ARG(qreal, [m_player rate])); + } + //AVPlayer "currentItem" property observer. + //Called when the AVPlayer replaceCurrentItemWithPlayerItem: + //replacement will/did occur. + else if (context == AVFMediaPlayerSessionObserverCurrentItemObservationContext) + { + 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 = NSMakePoint(0.0f, 0.0f); + m_playerLayer.bounds = NSMakeRect(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height); + } + } + + } + if (self.session) + QMetaObject::invokeMethod(m_session, "processCurrentItemChanged", Qt::AutoConnection); + } + else + { + [super observeValueForKeyPath:path ofObject:object change:change context:context]; + } +} + +- (void) detatchSession +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + m_session = 0; +} + +- (void) dealloc +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY]; + [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; + [m_player release]; + + [m_playerLayer release]; + + [self unloadMedia]; + [m_URL release]; + + [super dealloc]; +} + +@end + +AVFMediaPlayerSession::AVFMediaPlayerSession(AVFMediaPlayerService *service, QObject *parent) + : QObject(parent) + , m_service(service) + , m_videoOutput(0) + , m_state(QMediaPlayer::StoppedState) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_mediaStream(0) + , m_muted(false) + , m_tryingAsync(false) + , m_volume(100) + , m_rate(1.0) + , m_duration(0) + , m_videoAvailable(false) + , m_audioAvailable(false) +{ + m_observer = [[AVFMediaPlayerSessionObserver alloc] initWithMediaPlayerSession:this]; +} + +AVFMediaPlayerSession::~AVFMediaPlayerSession() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session). + [(AVFMediaPlayerSessionObserver*)m_observer detatchSession]; + [(AVFMediaPlayerSessionObserver*)m_observer release]; +} + +void AVFMediaPlayerSession::setVideoOutput(AVFVideoOutput *output) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << output; +#endif + + if (m_videoOutput == output) + return; + + //Set the current ouput layer to null to stop rendering + if (m_videoOutput) { + m_videoOutput->setLayer(0); + } + + m_videoOutput = output; + + if (m_videoOutput && m_state != QMediaPlayer::StoppedState) + m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]); +} + +void *AVFMediaPlayerSession::currentAssetHandle() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + AVAsset *currentAsset = [[(AVFMediaPlayerSessionObserver*)m_observer playerItem] asset]; + return currentAsset; +} + +QMediaPlayer::State AVFMediaPlayerSession::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus AVFMediaPlayerSession::mediaStatus() const +{ + return m_mediaStatus; +} + +QMediaContent AVFMediaPlayerSession::media() const +{ + return m_resources; +} + +const QIODevice *AVFMediaPlayerSession::mediaStream() const +{ + return m_mediaStream; +} + +void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *stream) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << content.canonicalUrl(); +#endif + + m_resources = content; + m_mediaStream = stream; + + QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus; + + if (content.isNull() || content.canonicalUrl().isEmpty()) { + [(AVFMediaPlayerSessionObserver*)m_observer unloadMedia]; + 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; + } else { + + m_mediaStatus = QMediaPlayer::LoadingMedia; + if (m_mediaStatus != oldMediaStatus) + Q_EMIT mediaStatusChanged(m_mediaStatus); + } + //Load AVURLAsset + //initialize asset using content's URL + NSString *urlString = [NSString stringWithUTF8String:content.canonicalUrl().toEncoded().constData()]; + NSURL *url = [NSURL URLWithString:urlString]; + [(AVFMediaPlayerSessionObserver*)m_observer setURL:url]; +} + +qint64 AVFMediaPlayerSession::position() const +{ + AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem]; + + if (!playerItem) + return 0; + + CMTime time = [playerItem currentTime]; + return static_cast(float(time.value) / float(time.timescale) * 1000.0f); +} + +qint64 AVFMediaPlayerSession::duration() const +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem]; + + if (!playerItem) + return 0; + + CMTime time = [playerItem duration]; + return static_cast(float(time.value) / float(time.timescale) * 1000.0f); +} + +int AVFMediaPlayerSession::bufferStatus() const +{ + //BUG: bufferStatus may be relevant? +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + return 100; +} + +int AVFMediaPlayerSession::volume() const +{ + return m_volume; +} + +bool AVFMediaPlayerSession::isMuted() const +{ + return m_muted; +} + +bool AVFMediaPlayerSession::isAudioAvailable() const +{ + return [(AVFMediaPlayerSessionObserver*)m_observer audioAvailable]; +} + +bool AVFMediaPlayerSession::isVideoAvailable() const +{ + return [(AVFMediaPlayerSessionObserver*)m_observer videoAvailable]; +} + +bool AVFMediaPlayerSession::isSeekable() const +{ + return true; +} + +QMediaTimeRange AVFMediaPlayerSession::availablePlaybackRanges() const +{ + AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem]; + + if (playerItem) { + QMediaTimeRange timeRanges; + + NSArray *ranges = [playerItem loadedTimeRanges]; + for (NSValue *timeRange in ranges) { + CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue]; + qint64 startTime = qint64(float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0); + timeRanges.addInterval(startTime, startTime + qint64(float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0)); + } + if (!timeRanges.isEmpty()) + return timeRanges; + } + return QMediaTimeRange(0, duration()); +} + +qreal AVFMediaPlayerSession::playbackRate() const +{ + return m_rate; +} + +void AVFMediaPlayerSession::setPlaybackRate(qreal rate) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << rate; +#endif + + if (qFuzzyCompare(m_rate, rate)) + return; + + m_rate = rate; + + AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player]; + + if (player != 0 && m_state == QMediaPlayer::PlayingState) { + [player setRate:m_rate]; + } +} + +void AVFMediaPlayerSession::setPosition(qint64 pos) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << pos; +#endif + + if ( !isSeekable() || pos == position()) + return; + + AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem]; + + if (!playerItem) + return; + + if (duration() > 0) + pos = qMin(pos, duration()); + + CMTime newTime = [playerItem currentTime]; + newTime.value = (pos / 1000.0f) * newTime.timescale; + [playerItem seekToTime:newTime]; + + //reset the EndOfMedia status position is changed after playback is finished + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + processLoadStateChange(); +} + +void AVFMediaPlayerSession::play() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << "currently: " << m_state; +#endif + + if (m_state == QMediaPlayer::PlayingState) + return; + + m_state = QMediaPlayer::PlayingState; + + if (m_videoOutput) { + m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]); + } + + //reset the EndOfMedia status if the same file is played again + if (m_mediaStatus == QMediaPlayer::EndOfMedia) { + setPosition(0); + processLoadStateChange(); + } + + if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) + [[(AVFMediaPlayerSessionObserver*)m_observer player] play]; + + //processLoadStateChange(); + Q_EMIT stateChanged(m_state); +} + +void AVFMediaPlayerSession::pause() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << "currently: " << m_state; +#endif + + if (m_state == QMediaPlayer::PausedState) + return; + + m_state = QMediaPlayer::PausedState; + + if (m_videoOutput) { + m_videoOutput->setLayer([(AVFMediaPlayerSessionObserver*)m_observer playerLayer]); + } + + //reset the EndOfMedia status if the same file is played again + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + processLoadStateChange(); + + [[(AVFMediaPlayerSessionObserver*)m_observer player] pause]; + + //processLoadStateChange(); + Q_EMIT stateChanged(m_state); +} + +void AVFMediaPlayerSession::stop() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << "currently: " << m_state; +#endif + + if (m_state == QMediaPlayer::StoppedState) + return; + + m_state = QMediaPlayer::StoppedState; + m_rate = 0.0f; + [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate]; + setPosition(0); + + if (m_videoOutput) { + m_videoOutput->setLayer(0); + } + + processLoadStateChange(); + Q_EMIT stateChanged(m_state); + Q_EMIT positionChanged(position()); +} + +void AVFMediaPlayerSession::setVolume(int volume) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << volume; +#endif + + if (m_volume == volume) + return; + + m_volume = volume; + + AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player]; + if (player) { + [[(AVFMediaPlayerSessionObserver*)m_observer player] setVolume:m_volume / 100.0f]; + } + + Q_EMIT volumeChanged(m_volume); +} + +void AVFMediaPlayerSession::setMuted(bool muted) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << muted; +#endif + if (m_muted == muted) + return; + + m_muted = muted; + + [[(AVFMediaPlayerSessionObserver*)m_observer player] setMuted:m_muted]; + + Q_EMIT mutedChanged(muted); +} + +void AVFMediaPlayerSession::processEOS() +{ + //AVPlayerItem has reached end of track/stream +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + Q_EMIT positionChanged(position()); + m_mediaStatus = QMediaPlayer::EndOfMedia; + + Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState); + Q_EMIT mediaStatusChanged(m_mediaStatus); +} + +void AVFMediaPlayerSession::processLoadStateChange() +{ + AVPlayerStatus currentStatus = [[(AVFMediaPlayerSessionObserver*)m_observer player] status]; + +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << currentStatus; +#endif + + QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia; + bool isPlaying = (m_state != QMediaPlayer::StoppedState); + + if (currentStatus == AVPlayerStatusReadyToPlay) { + 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]) { + [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate]; + [[(AVFMediaPlayerSessionObserver*)m_observer player] play]; + } + + } else { + 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; + } + + if (newStatus != m_mediaStatus) + Q_EMIT mediaStatusChanged(m_mediaStatus = newStatus); +} + +void AVFMediaPlayerSession::processPositionChange() +{ + Q_EMIT positionChanged(position()); +} + +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/avfvideoframerenderer.h b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h new file mode 100644 index 0000000..01c39c0 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFVIDEOFRAMERENDERER_H +#define AVFVIDEOFRAMERENDERER_H + +#include +#include +#include +#include + +@class CARenderer; +@class AVPlayerLayer; + +QT_BEGIN_NAMESPACE + +class QOpenGLFramebufferObject; +class QWindow; +class QOpenGLContext; +class QAbstractVideoSurface; +class QGLWidget; + +class AVFVideoFrameRenderer : public QObject +{ +public: + AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent = 0); +#ifndef QT_NO_WIDGETS + AVFVideoFrameRenderer(QGLWidget *glWidget, const QSize &size, QObject *parent = 0); +#endif + + virtual ~AVFVideoFrameRenderer(); + + GLuint renderLayerToTexture(AVPlayerLayer *layer); + QImage renderLayerToImage(AVPlayerLayer *layer); + +private: + QOpenGLFramebufferObject* initRenderer(AVPlayerLayer *layer); + void renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo); + + CARenderer *m_videoLayerRenderer; +#ifndef QT_NO_WIDGETS + QGLWidget *m_glWidget; +#endif + QAbstractVideoSurface *m_surface; + QOpenGLFramebufferObject *m_fbo[2]; + QWindow *m_offscreenSurface; + QOpenGLContext *m_glContext; + QSize m_targetSize; + + uint m_currentBuffer; + bool m_isContextShared; +}; + +QT_END_NAMESPACE + +#endif // AVFVIDEOFRAMERENDERER_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm new file mode 100644 index 0000000..ea787dc --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfvideoframerenderer.h" + +#include +#include +#include + +#ifndef QT_NO_WIDGETS +#include +#endif + +#ifdef QT_DEBUG_AVF +#include +#endif + +#import +#import + +QT_USE_NAMESPACE + +AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent) + : QObject(parent) + , m_videoLayerRenderer(0) + , m_surface(surface) + , m_glContext(0) + , m_currentBuffer(1) + , m_isContextShared(true) +{ + m_fbo[0] = 0; + m_fbo[1] = 0; + + //Create Hidden QWindow surface to create context in this thread + m_offscreenSurface = new QWindow(); + m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface); + //Needs geometry to be a valid surface, but size is not important + m_offscreenSurface->setGeometry(0, 0, 1, 1); + m_offscreenSurface->create(); +} +#ifndef QT_NO_WIDGETS +AVFVideoFrameRenderer::AVFVideoFrameRenderer(QGLWidget *glWidget, const QSize &size, QObject *parent) + : QObject(parent) + , m_videoLayerRenderer(0) + , m_glWidget(glWidget) + , m_surface(0) + , m_offscreenSurface(0) + , m_glContext(0) + , m_targetSize(size) + , m_currentBuffer(1) + , m_isContextShared(true) +{ + m_fbo[0] = 0; + m_fbo[1] = 0; + + //Create Hidden QWindow surface to create context in this thread + m_offscreenSurface = new QWindow(); + m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface); + //Needs geometry to be a valid surface, but size is not important + m_offscreenSurface->setGeometry(0, 0, 1, 1); + m_offscreenSurface->create(); + + +} +#endif + +AVFVideoFrameRenderer::~AVFVideoFrameRenderer() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + + [m_videoLayerRenderer release]; + delete m_fbo[0]; + delete m_fbo[1]; + delete m_offscreenSurface; + delete m_glContext; +} + +GLuint AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer) +{ + //Is layer valid + if (!layer) + return 0; + + //If the glContext isn't shared, it doesn't make sense to return a texture for us + if (m_offscreenSurface && !m_isContextShared) + return 0; + + QOpenGLFramebufferObject *fbo = initRenderer(layer); + + if (!fbo) + return 0; + + renderLayerToFBO(layer, fbo); + + return fbo->texture(); +} + +QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer) +{ + //Is layer valid + if (!layer) { + return QImage(); + } + + QOpenGLFramebufferObject *fbo = initRenderer(layer); + + if (!fbo) + return QImage(); + + renderLayerToFBO(layer, fbo); + + return fbo->toImage(); +} + +QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *layer) +{ + + //Get size from AVPlayerLayer + m_targetSize = QSize(layer.bounds.size.width, layer.bounds.size.height); + + //Make sure we have an OpenGL context to make current + if (!m_glContext) { + //Create OpenGL context and set share context from surface + QOpenGLContext *shareContext = 0; + if (m_surface) { + //QOpenGLContext *renderThreadContext = 0; + shareContext = qobject_cast(m_surface->property("GLContext").value()); +#ifndef QT_NO_WIDGETS + } else { + shareContext = m_glWidget->context()->contextHandle(); +#endif + } + m_glContext = new QOpenGLContext(); + m_glContext->setFormat(m_offscreenSurface->requestedFormat()); + + if (shareContext) { + m_glContext->setShareContext(shareContext); + m_isContextShared = true; + } else { +#ifdef QT_DEBUG_AVF + qWarning("failed to get Render Thread context"); + m_isContextShared = false; +#endif + } + if (!m_glContext->create()) { + qWarning("failed to create QOpenGLContext"); + return 0; + } + } + + //Need current context + m_glContext->makeCurrent(m_offscreenSurface); + + //Create the CARenderer if needed + if (!m_videoLayerRenderer) { + m_videoLayerRenderer = [CARenderer rendererWithCGLContext: CGLGetCurrentContext() options: nil]; + [m_videoLayerRenderer retain]; + } + + //Set/Change render source if needed + if (m_videoLayerRenderer.layer != layer) { + m_videoLayerRenderer.layer = layer; + m_videoLayerRenderer.bounds = layer.bounds; + } + + //Do we have FBO's already? + if ((!m_fbo[0] && !m_fbo[0]) || (m_fbo[0]->size() != m_targetSize)) { + delete m_fbo[0]; + delete m_fbo[1]; + m_fbo[0] = new QOpenGLFramebufferObject(m_targetSize); + m_fbo[1] = new QOpenGLFramebufferObject(m_targetSize); + } + + //Switch buffer target + m_currentBuffer = !m_currentBuffer; + return m_fbo[m_currentBuffer]; +} + +void AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo) +{ + //Start Rendering + //NOTE: This rendering method will NOT work on iOS as there is no CARenderer in iOS + if (!fbo->bind()) { + qWarning("AVFVideoRender FBO failed to bind"); + return; + } + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, m_targetSize.width(), m_targetSize.height()); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glOrtho(0.0f, m_targetSize.width(), m_targetSize.height(), 0.0f, 0.0f, 1.0f); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + [m_videoLayerRenderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL]; + [m_videoLayerRenderer addUpdateRect:layer.bounds]; + [m_videoLayerRenderer render]; + [m_videoLayerRenderer endFrame]; + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glFinish(); //Rendering needs to be done before passing texture to video frame + + fbo->release(); + + m_glContext->doneCurrent(); +} diff --git a/src/plugins/avfoundation/mediaplayer/avfvideooutput.h b/src/plugins/avfoundation/mediaplayer/avfvideooutput.h new file mode 100644 index 0000000..1d9eb21 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideooutput.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFVIDEOOUTPUT_H +#define AVFVIDEOOUTPUT_H + +#include + +QT_BEGIN_NAMESPACE + +class AVFVideoOutput +{ +public: + virtual ~AVFVideoOutput() {} + virtual void setLayer(void *playerLayer) = 0; +}; + +#define AVFVideoOutput_iid \ + "org.qt-project.qt.AVFVideoOuput/5.0" +Q_DECLARE_INTERFACE(AVFVideoOutput, AVFVideoOutput_iid) + +QT_END_NAMESPACE + +#endif // AVFVIDEOOUTPUT_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm b/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm new file mode 100644 index 0000000..1055169 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfvideooutput.h" + +QT_USE_NAMESPACE diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h new file mode 100644 index 0000000..19916bd --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFVIDEORENDERERCONTROL_H +#define AVFVIDEORENDERERCONTROL_H + +#include +#include +#include + +#include "avfvideooutput.h" + +#import + +QT_BEGIN_NAMESPACE + +class AVFDisplayLink; +class AVFVideoFrameRenderer; + +class AVFVideoRendererControl : public QVideoRendererControl, public AVFVideoOutput +{ + Q_OBJECT + Q_INTERFACES(AVFVideoOutput) +public: + explicit AVFVideoRendererControl(QObject *parent = 0); + virtual ~AVFVideoRendererControl(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void setLayer(void *playerLayer); + +private Q_SLOTS: + void updateVideoFrame(const CVTimeStamp &ts); + +Q_SIGNALS: + void surfaceChanged(QAbstractVideoSurface *surface); + +private: + void setupVideoOutput(); + + QMutex m_mutex; + QAbstractVideoSurface *m_surface; + + void *m_playerLayer; + + AVFVideoFrameRenderer *m_frameRenderer; + AVFDisplayLink *m_displayLink; + QSize m_nativeSize; +}; + +QT_END_NAMESPACE + +#endif // AVFVIDEORENDERERCONTROL_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm new file mode 100644 index 0000000..e7d99cd --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfvideorenderercontrol.h" +#include "avfdisplaylink.h" +#include "avfvideoframerenderer.h" + +#include +#include +#include +#include + +#import + +QT_USE_NAMESPACE + +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(m_textureId); + } + +private: + GLuint m_textureId; +}; + +AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent) + : QVideoRendererControl(parent) + , m_surface(0) + , m_playerLayer(0) + , m_frameRenderer(0) + +{ + m_displayLink = new AVFDisplayLink(this); + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); +} + +AVFVideoRendererControl::~AVFVideoRendererControl() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + m_displayLink->stop(); + if (m_playerLayer) + [(AVPlayerLayer*)m_playerLayer release]; +} + +QAbstractVideoSurface *AVFVideoRendererControl::surface() const +{ + return m_surface; +} + +void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ +#ifdef QT_DEBUG_AVF + qDebug() << "Set video surface" << surface; +#endif + + //When we have a valid surface, we can setup a frame renderer + //and schedule surface updates with the display link. + if (surface == m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + + //If the surface changed, then the current frame renderer is no longer valid + if (m_frameRenderer) + delete m_frameRenderer; + + //If there is now no surface to render too + if (m_surface == 0) { + m_displayLink->stop(); + return; + } + + //Surface changed, so we need a new frame renderer + m_frameRenderer = new AVFVideoFrameRenderer(m_surface, this); + + //If we already have a layer, but changed surfaces start rendering again + if (m_playerLayer && !m_displayLink->isActive()) { + m_displayLink->start(); + } + +} + +void AVFVideoRendererControl::setLayer(void *playerLayer) +{ + if (m_playerLayer == playerLayer) + return; + + [(AVPlayerLayer*)playerLayer retain]; + [(AVPlayerLayer*)m_playerLayer release]; + + m_playerLayer = playerLayer; + + //If there is no layer to render, stop scheduling updates + if (m_playerLayer == 0) { + m_displayLink->stop(); + return; + } + + setupVideoOutput(); + + //If we now have both a valid surface and layer, start scheduling updates + if (m_surface && !m_displayLink->isActive()) { + m_displayLink->start(); + } +} + +void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts) +{ + Q_UNUSED(ts) + + AVPlayerLayer *playerLayer = (AVPlayerLayer*)m_playerLayer; + + if (!playerLayer) { + qWarning("updateVideoFrame called without AVPlayerLayer (which shouldn't happen"); + return; + } + + if (!playerLayer.readyForDisplay) + return; + + GLuint textureId = m_frameRenderer->renderLayerToTexture(playerLayer); + + //Make sure we got a valid texture + if (textureId == 0) { + qWarning("renderLayerToTexture failed"); + return; + } + + QAbstractVideoBuffer *buffer = new TextureVideoBuffer(textureId); + QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32); + + if (m_surface && frame.isValid()) { + if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()) + m_surface->stop(); + + if (!m_surface->isActive()) { + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::GLTextureHandle); + + if (!m_surface->start(format)) { + qWarning("Failed to activate video surface"); + } + } + + if (m_surface->isActive()) + m_surface->present(frame); + } +} + +void AVFVideoRendererControl::setupVideoOutput() +{ + AVPlayerLayer *playerLayer = (AVPlayerLayer*)m_playerLayer; + if (playerLayer) + m_nativeSize = QSize(playerLayer.bounds.size.width, playerLayer.bounds.size.height); +} diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.h b/src/plugins/avfoundation/mediaplayer/avfvideowidget.h new file mode 100644 index 0000000..57b1fe9 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidget.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFVIDEOWIDGET_H +#define AVFVIDEOWIDGET_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; + +class AVFVideoWidget : public QGLWidget +{ +public: + AVFVideoWidget(QWidget *parent, const QGLFormat &format); + virtual ~AVFVideoWidget(); + + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + + void setTexture(GLuint texture); + + QSize sizeHint() const; + void setNativeSize(const QSize &size); + + void setAspectRatioMode(Qt::AspectRatioMode mode); + +private: + QRect displayRect() const; + + GLuint m_textureId; + QSize m_nativeSize; + Qt::AspectRatioMode m_aspectRatioMode; + + QOpenGLShaderProgram *m_shaderProgram; + QMatrix4x4 m_transformMatrix; + + int m_matrixLocation; + int m_vertexCoordEntry; + int m_textureCoordEntry; +}; + +QT_END_NAMESPACE + +#endif // AVFVIDEOWIDGET_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm new file mode 100644 index 0000000..3075398 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfvideowidget.h" +#include +#include + +QT_USE_NAMESPACE + +AVFVideoWidget::AVFVideoWidget(QWidget *parent, const QGLFormat &format) + : QGLWidget(format, parent) + , m_textureId(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_shaderProgram(0) +{ + setAutoFillBackground(false); +} + +AVFVideoWidget::~AVFVideoWidget() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + delete m_shaderProgram; +} + +void AVFVideoWidget::initializeGL() +{ + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + m_shaderProgram = new QOpenGLShaderProgram; + + static const char *textureVertexProgram = + "uniform highp mat4 matrix;\n" + "attribute highp vec3 vertexCoordEntry;\n" + "attribute highp vec2 textureCoordEntry;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " textureCoord = textureCoordEntry;\n" + " gl_Position = matrix * vec4(vertexCoordEntry, 1);\n" + "}\n"; + + static const char *textureFragmentProgram = + "uniform sampler2D texture;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(texture, textureCoord);\n" + "}\n"; + + m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, textureVertexProgram); + m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, textureFragmentProgram); + m_shaderProgram->link(); +} + +void AVFVideoWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, GLsizei(w), GLsizei(h)); + updateGL(); +} + +void AVFVideoWidget::paintGL() +{ + glClear(GL_COLOR_BUFFER_BIT); + if (!m_textureId) + return; + + QRect targetRect = displayRect(); + int x1 = targetRect.left(); + int x2 = targetRect.right(); + int y1 = targetRect.bottom(); + int y2 = targetRect.top(); + int zValue = 0; + + const GLfloat textureCoordinates[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1 + }; + + const GLfloat vertexCoordinates[] = { + x1, y1, zValue, + x2, y1, zValue, + x2, y2, zValue, + x1, y2, zValue + }; + + //Set matrix to transfrom geometry values into gl coordinate space. + m_transformMatrix.setToIdentity(); + m_transformMatrix.scale( 2.0f / size().width(), 2.0f / size().height() ); + m_transformMatrix.translate(-size().width() / 2.0f, -size().height() / 2.0f); + + m_shaderProgram->bind(); + + m_vertexCoordEntry = m_shaderProgram->attributeLocation("vertexCoordEntry"); + m_textureCoordEntry = m_shaderProgram->attributeLocation("textureCoordEntry"); + m_matrixLocation = m_shaderProgram->uniformLocation("matrix"); + + //attach the data! + glEnableVertexAttribArray(m_vertexCoordEntry); + glEnableVertexAttribArray(m_textureCoordEntry); + + glVertexAttribPointer(m_vertexCoordEntry, 3, GL_FLOAT, GL_FALSE, 0, vertexCoordinates); + glVertexAttribPointer(m_textureCoordEntry, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates); + m_shaderProgram->setUniformValue(m_matrixLocation, m_transformMatrix); + + glBindTexture(GL_TEXTURE_2D, m_textureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindTexture(GL_TEXTURE_2D, 0); + + glDisableVertexAttribArray(m_vertexCoordEntry); + glDisableVertexAttribArray(m_textureCoordEntry); + + m_shaderProgram->release(); +} + +void AVFVideoWidget::setTexture(GLuint texture) +{ + m_textureId = texture; + + if (isVisible()) { + makeCurrent(); + updateGL(); + } +} + +QSize AVFVideoWidget::sizeHint() const +{ + return m_nativeSize; +} + +void AVFVideoWidget::setNativeSize(const QSize &size) +{ + m_nativeSize = size; +} + +void AVFVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + if (m_aspectRatioMode != mode) { + m_aspectRatioMode = mode; + update(); + } +} + +QRect AVFVideoWidget::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; +} diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h new file mode 100644 index 0000000..d27da77 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 AVFVIDEOWIDGETCONTROL_H +#define AVFVIDEOWIDGETCONTROL_H + +#include +#include "avfvideooutput.h" + +#import + +QT_BEGIN_NAMESPACE + +class AVFDisplayLink; +class AVFVideoWidget; +class AVFVideoFrameRenderer; + +class AVFVideoWidgetControl : public QVideoWidgetControl, public AVFVideoOutput +{ + Q_OBJECT + Q_INTERFACES(AVFVideoOutput) +public: + AVFVideoWidgetControl(QObject *parent = 0); + virtual ~AVFVideoWidgetControl(); + + void setLayer(void *playerLayer); + + QWidget *videoWidget(); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + 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(); + + AVFDisplayLink *m_displayLink; + AVFVideoWidget *m_videoWidget; + AVFVideoFrameRenderer *m_frameRenderer; + QSize m_nativeSize; + Qt::AspectRatioMode m_aspectRatioMode; + bool m_fullscreen; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; + + void *m_playerLayer; +}; + +QT_END_NAMESPACE + +#endif // AVFVIDEOWIDGETCONTROL_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm new file mode 100644 index 0000000..b8cd782 --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "avfvideowidgetcontrol.h" + +#include "avfvideowidget.h" +#include "avfvideoframerenderer.h" +#include "avfdisplaylink.h" + +#ifdef QT_DEBUG_AVF +#include +#endif + +#import + +QT_USE_NAMESPACE + +AVFVideoWidgetControl::AVFVideoWidgetControl(QObject *parent) + : QVideoWidgetControl(parent) + , m_frameRenderer(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_fullscreen(false) + , m_brightness(0) + , m_contrast(0) + , m_hue(0) + , m_saturation(0) + , m_playerLayer(0) +{ + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Vertical sync (avoid tearing) + format.setDoubleBuffer(true); + m_videoWidget = new AVFVideoWidget(0, format); + + m_displayLink = new AVFDisplayLink(this); + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), this, SLOT(updateVideoFrame(CVTimeStamp))); +} + +AVFVideoWidgetControl::~AVFVideoWidgetControl() +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO; +#endif + m_displayLink->stop(); + if (m_playerLayer) + [(AVPlayerLayer*)m_playerLayer release]; + + delete m_videoWidget; +} + +void AVFVideoWidgetControl::setLayer(void *playerLayer) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << playerLayer; +#endif + + if (m_playerLayer == playerLayer) + return; + + [(AVPlayerLayer*)playerLayer retain]; + [(AVPlayerLayer*)m_playerLayer release]; + + m_playerLayer = playerLayer; + + //If there is no layer to render, stop scheduling updates + if (m_playerLayer == 0) { + m_displayLink->stop(); + return; + } + + setupVideoOutput(); + + //make sure we schedule updates + if (!m_displayLink->isActive()) { + m_displayLink->start(); + } +} + +QWidget *AVFVideoWidgetControl::videoWidget() +{ + return m_videoWidget; +} + +bool AVFVideoWidgetControl::isFullScreen() const +{ + return m_fullscreen; +} + +void AVFVideoWidgetControl::setFullScreen(bool fullScreen) +{ + m_fullscreen = fullScreen; +} + +Qt::AspectRatioMode AVFVideoWidgetControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void AVFVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + m_videoWidget->setAspectRatioMode(mode); +} + +int AVFVideoWidgetControl::brightness() const +{ + return m_brightness; +} + +void AVFVideoWidgetControl::setBrightness(int brightness) +{ + m_brightness = brightness; +} + +int AVFVideoWidgetControl::contrast() const +{ + return m_contrast; +} + +void AVFVideoWidgetControl::setContrast(int contrast) +{ + m_contrast = contrast; +} + +int AVFVideoWidgetControl::hue() const +{ + return m_hue; +} + +void AVFVideoWidgetControl::setHue(int hue) +{ + m_hue = hue; +} + +int AVFVideoWidgetControl::saturation() const +{ + return m_saturation; +} + +void AVFVideoWidgetControl::setSaturation(int saturation) +{ + m_saturation = saturation; +} + +void AVFVideoWidgetControl::updateVideoFrame(const CVTimeStamp &ts) +{ + Q_UNUSED(ts) + + AVPlayerLayer *playerLayer = (AVPlayerLayer*)m_playerLayer; + + if (!playerLayer) { + qWarning("updateVideoFrame called without AVPlayerLayer (which shouldn't happen)"); + return; + } + + //Don't try to render a layer that is not ready + if (!playerLayer.readyForDisplay) + return; + + GLuint textureId = m_frameRenderer->renderLayerToTexture(playerLayer); + + //Make sure we have a valid texture + if (textureId == 0) { + qWarning("renderLayerToTexture failed"); + return; + } + + m_videoWidget->setTexture(textureId); +} + +void AVFVideoWidgetControl::setupVideoOutput() +{ + NSRect layerBounds = [(AVPlayerLayer*)m_playerLayer bounds]; + m_nativeSize = QSize(layerBounds.size.width, layerBounds.size.height); + m_videoWidget->setNativeSize(m_nativeSize); + + if (m_frameRenderer) + delete m_frameRenderer; + + m_frameRenderer = new AVFVideoFrameRenderer(m_videoWidget, m_nativeSize, this); +} + diff --git a/src/plugins/avfoundation/mediaplayer/mediaplayer.pro b/src/plugins/avfoundation/mediaplayer/mediaplayer.pro new file mode 100644 index 0000000..ae320ac --- /dev/null +++ b/src/plugins/avfoundation/mediaplayer/mediaplayer.pro @@ -0,0 +1,56 @@ +load(qt_build_config) + +#DEFINES += QT_DEBUG_AVF +# Avoid clash with a variable named `slots' in a Quartz header +CONFIG += no_keywords + +TARGET = qavfmediaplayer +QT += multimedia-private network + +PLUGIN_TYPE = mediaservice + +load(qt_plugin) +DESTDIR = $$QT.multimedia.plugins/$${PLUGIN_TYPE} + +LIBS += -framework AVFoundation -framework CoreMedia + +target.path += $$[QT_INSTALL_PLUGINS]/$${PLUGIN_TYPE} +INSTALLS += target + +DEFINES += QMEDIA_AVF_MEDIAPLAYER + +HEADERS += \ + avfmediaplayercontrol.h \ + avfmediaplayermetadatacontrol.h \ + avfmediaplayerservice.h \ + avfmediaplayersession.h \ + avfmediaplayerserviceplugin.h \ + avfvideorenderercontrol.h \ + avfdisplaylink.h \ + avfvideoframerenderer.h \ + avfvideooutput.h + +OBJECTIVE_SOURCES += \ + avfmediaplayercontrol.mm \ + avfmediaplayermetadatacontrol.mm \ + avfmediaplayerservice.mm \ + avfmediaplayerserviceplugin.mm \ + avfmediaplayersession.mm \ + avfvideorenderercontrol.mm \ + avfdisplaylink.mm \ + avfvideoframerenderer.mm \ + avfvideooutput.mm + +!isEmpty(QT.widgets.name) { + QT += multimediawidgets-private opengl + HEADERS += \ + avfvideowidgetcontrol.h \ + avfvideowidget.h + + OBJECTIVE_SOURCES += \ + avfvideowidgetcontrol.mm \ + avfvideowidget.mm +} + +OTHER_FILES += \ + avfmediaplayer.json -- 2.7.4