Add MediaPlayer support to AVFoundation plugin
authorAndy Nichols <andy.nichols@digia.com>
Mon, 1 Oct 2012 08:20:19 +0000 (10:20 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 19 Oct 2012 12:54:27 +0000 (14:54 +0200)
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 <lars.knoll@digia.com>
Reviewed-by: Jason Barron <jason.barron@digia.com>
Reviewed-by: Andy Nichols <andy.nichols@digia.com>
25 files changed:
src/plugins/avfoundation/avfoundation.pro
src/plugins/avfoundation/mediaplayer/avfdisplaylink.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayer.json [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideooutput.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideooutput.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideowidget.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideowidget.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm [new file with mode: 0644]
src/plugins/avfoundation/mediaplayer/mediaplayer.pro [new file with mode: 0644]

index 9a3dbf6..7f2ddb2 100644 (file)
@@ -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 (file)
index 0000000..d83f344
--- /dev/null
@@ -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 <QtCore/qobject.h>
+#include <QtCore/qmutex.h>
+
+#include <QuartzCore/CVDisplayLink.h>
+
+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 (file)
index 0000000..59fb335
--- /dev/null
@@ -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 <QtCore/qcoreapplication.h>
+
+#ifdef QT_DEBUG_AVF
+#include <QtCore/qdebug.h>
+#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 (file)
index 0000000..c4a27ea
--- /dev/null
@@ -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 (file)
index 0000000..641e200
--- /dev/null
@@ -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 <QtMultimedia/QMediaPlayerControl>
+#include <QtCore/QObject>
+
+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 (file)
index 0000000..c6c114f
--- /dev/null
@@ -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 (file)
index 0000000..2a717ed
--- /dev/null
@@ -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 <QtMultimedia/QMetaDataReaderControl>
+
+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<QString, QVariant> 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 (file)
index 0000000..84a6bef
--- /dev/null
@@ -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 <QtMultimedia/qtmedianamespace.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+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 (file)
index 0000000..ec96669
--- /dev/null
@@ -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 <QtMultimedia/QMediaService>
+
+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 (file)
index 0000000..2ea8475
--- /dev/null
@@ -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<AVFVideoOutput*>(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<AVFVideoRendererControl*>(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 (file)
index 0000000..4646906
--- /dev/null
@@ -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 <QtCore/qglobal.h>
+#include <QtMultimedia/qmediaserviceproviderplugin.h>
+
+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 (file)
index 0000000..cb75197
--- /dev/null
@@ -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 <QtCore/QDebug>
+
+#include "avfmediaplayerservice.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+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 (file)
index 0000000..3b70395
--- /dev/null
@@ -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 <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QSet>
+#include <QtCore/QResource>
+
+#include <QtMultimedia/QMediaPlayerControl>
+#include <QtMultimedia/QMediaPlayer>
+
+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 (file)
index 0000000..3fb9354
--- /dev/null
@@ -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 <AVFoundation/AVFoundation.h>
+
+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<quint64>(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<quint64>(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 (file)
index 0000000..01c39c0
--- /dev/null
@@ -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 <QtCore/QObject>
+#include <QtGui/QImage>
+#include <QtGui/QOpenGLContext>
+#include <QtCore/QSize>
+
+@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 (file)
index 0000000..ea787dc
--- /dev/null
@@ -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 <QtMultimedia/qabstractvideosurface.h>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QWindow>
+
+#ifndef QT_NO_WIDGETS
+#include <QtOpenGL/QGLWidget>
+#endif
+
+#ifdef QT_DEBUG_AVF
+#include <QtCore/qdebug.h>
+#endif
+
+#import <CoreVideo/CVBase.h>
+#import <AVFoundation/AVFoundation.h>
+
+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<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
+#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 (file)
index 0000000..1d9eb21
--- /dev/null
@@ -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 <QtCore/qobject.h>
+
+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 (file)
index 0000000..1055169
--- /dev/null
@@ -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 (file)
index 0000000..19916bd
--- /dev/null
@@ -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 <QtMultimedia/QVideoRendererControl>
+#include <QtCore/QMutex>
+#include <QtCore/QSize>
+
+#include "avfvideooutput.h"
+
+#import <CoreVideo/CVBase.h>
+
+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 (file)
index 0000000..e7d99cd
--- /dev/null
@@ -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 <QtMultimedia/qabstractvideobuffer.h>
+#include <QtMultimedia/qabstractvideosurface.h>
+#include <QtMultimedia/qvideosurfaceformat.h>
+#include <QtCore/qdebug.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+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<unsigned int>(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 (file)
index 0000000..57b1fe9
--- /dev/null
@@ -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 <QtOpenGL/QGLWidget>
+#include <QtGui/QMatrix4x4>
+
+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 (file)
index 0000000..3075398
--- /dev/null
@@ -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 <QtCore/QDebug>
+#include <QtGui/QOpenGLShaderProgram>
+
+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 (file)
index 0000000..d27da77
--- /dev/null
@@ -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 <qvideowidgetcontrol.h>
+#include "avfvideooutput.h"
+
+#import <CoreVideo/CVBase.h>
+
+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 (file)
index 0000000..b8cd782
--- /dev/null
@@ -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 <QtCore/QDebug>
+#endif
+
+#import <AVFoundation/AVFoundation.h>
+
+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 (file)
index 0000000..ae320ac
--- /dev/null
@@ -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