Add MediaPlayer support to AVFoundation plugin
This plugin would be used on Mac 10.7+ where without the QuickTime C API our QT7 media player performance was crippled. Change-Id: Iaadb1990a8f63393c4cd02d096624e0fed42b40f Reviewed-by: Lars Knoll <lars.knoll@digia.com> Reviewed-by: Jason Barron <jason.barron@digia.com> Reviewed-by: Andy Nichols <andy.nichols@digia.com>
This commit is contained in:
committed by
The Qt Project
parent
7f25187774
commit
700b4cdf42
@@ -1,3 +1,4 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS += camera
|
SUBDIRS += camera \
|
||||||
|
mediaplayer
|
||||||
|
|||||||
84
src/plugins/avfoundation/mediaplayer/avfdisplaylink.h
Normal file
84
src/plugins/avfoundation/mediaplayer/avfdisplaylink.h
Normal file
@@ -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
|
||||||
159
src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm
Normal file
159
src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm
Normal file
@@ -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);
|
||||||
|
}
|
||||||
3
src/plugins/avfoundation/mediaplayer/avfmediaplayer.json
Normal file
3
src/plugins/avfoundation/mediaplayer/avfmediaplayer.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"Keys": ["org.qt-project.qt.mediaplayer"]
|
||||||
|
}
|
||||||
101
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h
Normal file
101
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h
Normal file
@@ -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
|
||||||
186
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
Normal file
186
src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
72
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h
Normal file
72
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h
Normal file
@@ -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
|
||||||
117
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm
Normal file
117
src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
181
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
Normal file
181
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
Normal file
@@ -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
|
||||||
832
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
Normal file
832
src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h
Normal file
94
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h
Normal file
@@ -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
|
||||||
258
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
Normal file
258
src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
Normal file
@@ -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();
|
||||||
|
}
|
||||||
62
src/plugins/avfoundation/mediaplayer/avfvideooutput.h
Normal file
62
src/plugins/avfoundation/mediaplayer/avfvideooutput.h
Normal file
@@ -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
|
||||||
44
src/plugins/avfoundation/mediaplayer/avfvideooutput.mm
Normal file
44
src/plugins/avfoundation/mediaplayer/avfvideooutput.mm
Normal file
@@ -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
|
||||||
@@ -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
|
||||||
213
src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm
Normal file
213
src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm
Normal file
@@ -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);
|
||||||
|
}
|
||||||
86
src/plugins/avfoundation/mediaplayer/avfvideowidget.h
Normal file
86
src/plugins/avfoundation/mediaplayer/avfvideowidget.h
Normal file
@@ -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
|
||||||
201
src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
Normal file
201
src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
Normal file
@@ -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;
|
||||||
|
}
|
||||||
108
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h
Normal file
108
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h
Normal file
@@ -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
|
||||||
219
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm
Normal file
219
src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
56
src/plugins/avfoundation/mediaplayer/mediaplayer.pro
Normal file
56
src/plugins/avfoundation/mediaplayer/mediaplayer.pro
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user