From 0a7882f6b308296ff9d44d891d3bdfed91679ce2 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Mon, 25 Mar 2013 18:11:27 +0100 Subject: Android: added camera support. This patch includes all camera features: viewport, settings, image capture, and video recording. It also adds support for QAudioRecorder. Change-Id: Ib962177cc8de4bac03f42a2bc0f534e03464bbfc Reviewed-by: Christian Stromme --- .../android/src/mediaplayer/mediaplayer.pri | 11 + .../src/mediaplayer/qandroidmediaplayercontrol.cpp | 535 +++++++++++++++++++++ .../src/mediaplayer/qandroidmediaplayercontrol.h | 131 +++++ .../src/mediaplayer/qandroidmediaservice.cpp | 97 ++++ .../android/src/mediaplayer/qandroidmediaservice.h | 71 +++ .../mediaplayer/qandroidmetadatareadercontrol.cpp | 222 +++++++++ .../mediaplayer/qandroidmetadatareadercontrol.h | 80 +++ 7 files changed, 1147 insertions(+) create mode 100644 src/plugins/android/src/mediaplayer/mediaplayer.pri create mode 100644 src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp create mode 100644 src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h create mode 100644 src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp create mode 100644 src/plugins/android/src/mediaplayer/qandroidmediaservice.h create mode 100644 src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp create mode 100644 src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h (limited to 'src/plugins/android/src/mediaplayer') diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri new file mode 100644 index 000000000..c386d996b --- /dev/null +++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qandroidmediaplayercontrol.h \ + $$PWD/qandroidmediaservice.h \ + $$PWD/qandroidmetadatareadercontrol.h + +SOURCES += \ + $$PWD/qandroidmediaplayercontrol.cpp \ + $$PWD/qandroidmediaservice.cpp \ + $$PWD/qandroidmetadatareadercontrol.cpp diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp new file mode 100644 index 000000000..753c60662 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidmediaplayercontrol.h" +#include "jmediaplayer.h" +#include "qandroidvideooutput.h" + +QT_BEGIN_NAMESPACE + +static void textureReadyCallback(void *context) +{ + if (context) + reinterpret_cast(context)->onSurfaceTextureReady(); +} + +QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent) + : QMediaPlayerControl(parent), + mMediaPlayer(new JMediaPlayer), + mCurrentState(QMediaPlayer::StoppedState), + mCurrentMediaStatus(QMediaPlayer::NoMedia), + mMediaStream(0), + mVideoOutput(0), + mSeekable(true), + mBufferPercent(-1), + mAudioAvailable(false), + mVideoAvailable(false), + mBuffering(false), + mMediaPlayerReady(false), + mPendingPosition(-1), + mPendingSetMedia(false) +{ + connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)), + this, SLOT(onBufferChanged(qint32))); + connect(mMediaPlayer, SIGNAL(info(qint32, qint32)), + this, SLOT(onInfo(qint32, qint32))); + connect(mMediaPlayer, SIGNAL(error(qint32, qint32)), + this, SLOT(onError(qint32, qint32))); + connect(mMediaPlayer, SIGNAL(mediaPlayerInfo(qint32, qint32)), + this, SLOT(onMediaPlayerInfo(qint32, qint32))); + connect(mMediaPlayer, SIGNAL(videoSizeChanged(qint32,qint32)), + this, SLOT(onVideoSizeChanged(qint32,qint32))); +} + +QAndroidMediaPlayerControl::~QAndroidMediaPlayerControl() +{ + mMediaPlayer->stop(); + mMediaPlayer->release(); + delete mMediaPlayer; +} + +QMediaPlayer::State QAndroidMediaPlayerControl::state() const +{ + return mCurrentState; +} + +QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const +{ + return mCurrentMediaStatus; +} + +qint64 QAndroidMediaPlayerControl::duration() const +{ + return (mCurrentMediaStatus == QMediaPlayer::InvalidMedia + || mCurrentMediaStatus == QMediaPlayer::NoMedia + || !mMediaPlayerReady) ? 0 + : mMediaPlayer->getDuration(); +} + +qint64 QAndroidMediaPlayerControl::position() const +{ + if (!mMediaPlayerReady) + return mPendingPosition < 0 ? 0 : mPendingPosition; + + return mMediaPlayer->getCurrentPosition(); +} + +void QAndroidMediaPlayerControl::setPosition(qint64 position) +{ + if (!mSeekable) + return; + + const int seekPosition = (position > INT_MAX) ? INT_MAX : position; + + if (!mMediaPlayerReady) { + mPendingPosition = seekPosition; + Q_EMIT positionChanged(seekPosition); + return; + } + + mMediaPlayer->seekTo(seekPosition); + mPendingPosition = -1; +} + +int QAndroidMediaPlayerControl::volume() const +{ + return mMediaPlayer->volume(); +} + +void QAndroidMediaPlayerControl::setVolume(int volume) +{ + mMediaPlayer->setVolume(volume); + Q_EMIT volumeChanged(volume); +} + +bool QAndroidMediaPlayerControl::isMuted() const +{ + return mMediaPlayer->isMuted(); +} + +void QAndroidMediaPlayerControl::setMuted(bool muted) +{ + mMediaPlayer->setMuted(muted); + Q_EMIT mutedChanged(muted); +} + +int QAndroidMediaPlayerControl::bufferStatus() const +{ + return mBufferPercent; +} + +bool QAndroidMediaPlayerControl::isAudioAvailable() const +{ + return mAudioAvailable; +} + +bool QAndroidMediaPlayerControl::isVideoAvailable() const +{ + return mVideoAvailable; +} + +bool QAndroidMediaPlayerControl::isSeekable() const +{ + return mSeekable; +} + +QMediaTimeRange QAndroidMediaPlayerControl::availablePlaybackRanges() const +{ + return mAvailablePlaybackRange; +} + +void QAndroidMediaPlayerControl::updateAvailablePlaybackRanges() +{ + if (mBuffering) { + const qint64 pos = position(); + const qint64 end = (duration() / 100) * mBufferPercent; + mAvailablePlaybackRange.addInterval(pos, end); + } else if (mSeekable) { + mAvailablePlaybackRange = QMediaTimeRange(0, duration()); + } else { + mAvailablePlaybackRange = QMediaTimeRange(); + } + + Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange); +} + +qreal QAndroidMediaPlayerControl::playbackRate() const +{ + return 1.0f; +} + +void QAndroidMediaPlayerControl::setPlaybackRate(qreal rate) +{ + Q_UNUSED(rate); +} + +QMediaContent QAndroidMediaPlayerControl::media() const +{ + return mMediaContent; +} + +const QIODevice *QAndroidMediaPlayerControl::mediaStream() const +{ + return mMediaStream; +} + +void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, + QIODevice *stream) +{ + mMediaContent = mediaContent; + mMediaStream = stream; + + if (mVideoOutput && !mMediaPlayer->display()) { + // if a video output is set but the video texture is not ready, delay loading the media + // since it can cause problems on some hardware + mPendingSetMedia = true; + return; + } + + const QString uri = mediaContent.canonicalUrl().toString(); + + if (!uri.isEmpty()) + mMediaPlayer->setDataSource(uri); + else + setMediaStatus(QMediaPlayer::NoMedia); + + Q_EMIT mediaChanged(mMediaContent); + + resetBufferingProgress(); + + // reset some properties + setAudioAvailable(false); + setVideoAvailable(false); + setSeekable(true); +} + +void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput) +{ + if (mVideoOutput) + mVideoOutput->stop(); + + mVideoOutput = videoOutput; + + if (mVideoOutput && !mMediaPlayer->display()) { + if (mVideoOutput->isTextureReady()) + mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder()); + else + mVideoOutput->setTextureReadyCallback(textureReadyCallback, this); + } +} + +void QAndroidMediaPlayerControl::play() +{ + if (!mMediaPlayerReady) { + mPendingState = QMediaPlayer::PlayingState; + if (mCurrentState == QMediaPlayer::StoppedState + && !mMediaContent.isNull() + && mCurrentMediaStatus != QMediaPlayer::LoadingMedia + && !mPendingSetMedia) { + setMedia(mMediaContent, 0); + } + return; + } + + mMediaPlayer->play(); + setState(QMediaPlayer::PlayingState); +} + +void QAndroidMediaPlayerControl::pause() +{ + if (!mMediaPlayerReady) { + mPendingState = QMediaPlayer::PausedState; + return; + } + + mMediaPlayer->pause(); + setState(QMediaPlayer::PausedState); +} + +void QAndroidMediaPlayerControl::stop() +{ + mMediaPlayer->stop(); + setState(QMediaPlayer::StoppedState); +} + +void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra) +{ + Q_UNUSED(extra); + switch (what) { + case JMediaPlayer::MEDIA_INFO_UNKNOWN: + break; + case JMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING: + // IGNORE + break; + case JMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START: + break; + case JMediaPlayer::MEDIA_INFO_BUFFERING_START: + mPendingState = mCurrentState; + setState(QMediaPlayer::PausedState); + setMediaStatus(QMediaPlayer::StalledMedia); + break; + case JMediaPlayer::MEDIA_INFO_BUFFERING_END: + setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia); + flushPendingStates(); + break; + case JMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING: + break; + case JMediaPlayer::MEDIA_INFO_NOT_SEEKABLE: + setSeekable(false); + break; + case JMediaPlayer::MEDIA_INFO_METADATA_UPDATE: + Q_EMIT metaDataUpdated(); + break; + } +} + +void QAndroidMediaPlayerControl::onMediaPlayerInfo(qint32 what, qint32 extra) +{ + switch (what) { + case JMediaPlayer::MEDIA_PLAYER_INVALID_STATE: + setError(what, QStringLiteral("Error: Invalid state")); + break; + case JMediaPlayer::MEDIA_PLAYER_PREPARING: + setMediaStatus(QMediaPlayer::LoadingMedia); + setState(QMediaPlayer::StoppedState); + break; + case JMediaPlayer::MEDIA_PLAYER_READY: + setMediaStatus(QMediaPlayer::LoadedMedia); + if (mBuffering) { + setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia + : QMediaPlayer::BufferingMedia); + } else { + onBufferChanged(100); + } + setAudioAvailable(true); + mMediaPlayerReady = true; + flushPendingStates(); + break; + case JMediaPlayer::MEDIA_PLAYER_DURATION: + Q_EMIT durationChanged(extra); + break; + case JMediaPlayer::MEDIA_PLAYER_PROGRESS: + Q_EMIT positionChanged(extra); + break; + case JMediaPlayer::MEDIA_PLAYER_FINISHED: + setState(QMediaPlayer::StoppedState); + setMediaStatus(QMediaPlayer::EndOfMedia); + break; + } +} + +void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra) +{ + QString errorString; + QMediaPlayer::Error error = QMediaPlayer::ResourceError; + + switch (what) { + case JMediaPlayer::MEDIA_ERROR_UNKNOWN: + errorString = QLatin1String("Error:"); + break; + case JMediaPlayer::MEDIA_ERROR_SERVER_DIED: + errorString = QLatin1String("Error: Server died"); + error = QMediaPlayer::ServiceMissingError; + break; + } + + switch (extra) { + case JMediaPlayer::MEDIA_ERROR_IO: // Network OR file error + errorString += QLatin1String(" (I/O operation failed)"); + error = QMediaPlayer::NetworkError; + setMediaStatus(QMediaPlayer::InvalidMedia); + break; + case JMediaPlayer::MEDIA_ERROR_MALFORMED: + errorString += QLatin1String(" (Malformed bitstream)"); + error = QMediaPlayer::FormatError; + setMediaStatus(QMediaPlayer::InvalidMedia); + break; + case JMediaPlayer::MEDIA_ERROR_UNSUPPORTED: + errorString += QLatin1String(" (Unsupported media)"); + error = QMediaPlayer::FormatError; + setMediaStatus(QMediaPlayer::InvalidMedia); + break; + case JMediaPlayer::MEDIA_ERROR_TIMED_OUT: + errorString += QLatin1String(" (Timed out)"); + break; + case JMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: + errorString += QLatin1String(" (Unable to start progressive playback')"); + error = QMediaPlayer::FormatError; + setMediaStatus(QMediaPlayer::InvalidMedia); + break; + } + + setError(error, errorString); +} + +void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent) +{ + mBuffering = percent != 100; + mBufferPercent = percent; + Q_EMIT bufferStatusChanged(mBufferPercent); + + updateAvailablePlaybackRanges(); + + if (mBufferPercent == 100) + setMediaStatus(QMediaPlayer::BufferedMedia); +} + +void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height) +{ + QSize newSize(width, height); + + if (width == 0 || height == 0 || newSize == mVideoSize) + return; + + setVideoAvailable(true); + mVideoSize = newSize; + + if (mVideoOutput) + mVideoOutput->setVideoSize(mVideoSize); +} + +void QAndroidMediaPlayerControl::onSurfaceTextureReady() +{ + if (!mMediaPlayer->display() && mVideoOutput) { + mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder()); + flushPendingStates(); + } +} + +void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state) +{ + if (mCurrentState == state) + return; + + if (state == QMediaPlayer::StoppedState) { + if (mVideoOutput) + mVideoOutput->stop(); + resetBufferingProgress(); + mMediaPlayerReady = false; + mPendingPosition = -1; + Q_EMIT positionChanged(0); + } + + mCurrentState = state; + Q_EMIT stateChanged(mCurrentState); +} + +void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status) +{ + if (mCurrentMediaStatus == status) + return; + + if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) + Q_EMIT durationChanged(0); + + mCurrentMediaStatus = status; + Q_EMIT mediaStatusChanged(mCurrentMediaStatus); +} + +void QAndroidMediaPlayerControl::setError(int error, + const QString &errorString) +{ + setState(QMediaPlayer::StoppedState); + Q_EMIT QMediaPlayerControl::error(error, errorString); +} + +void QAndroidMediaPlayerControl::setSeekable(bool seekable) +{ + if (mSeekable == seekable) + return; + + mSeekable = seekable; + Q_EMIT seekableChanged(mSeekable); +} + +void QAndroidMediaPlayerControl::setAudioAvailable(bool available) +{ + if (mAudioAvailable == available) + return; + + mAudioAvailable = available; + Q_EMIT audioAvailableChanged(mAudioAvailable); +} + +void QAndroidMediaPlayerControl::setVideoAvailable(bool available) +{ + if (mVideoAvailable == available) + return; + + if (!available) + mVideoSize = QSize(); + + mVideoAvailable = available; + Q_EMIT videoAvailableChanged(mVideoAvailable); +} + +void QAndroidMediaPlayerControl::resetBufferingProgress() +{ + mBuffering = false; + mBufferPercent = 0; + mAvailablePlaybackRange = QMediaTimeRange(); + Q_EMIT bufferStatusChanged(mBufferPercent); +} + +void QAndroidMediaPlayerControl::flushPendingStates() +{ + if (mPendingSetMedia) { + setMedia(mMediaContent, 0); + mPendingSetMedia = false; + return; + } + + switch (mPendingState) { + case QMediaPlayer::PlayingState: + if (mPendingPosition > -1) + setPosition(mPendingPosition); + play(); + break; + case QMediaPlayer::PausedState: + pause(); + break; + case QMediaPlayer::StoppedState: + stop(); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h new file mode 100644 index 000000000..93eced853 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDMEDIAPLAYERCONTROL_H +#define QANDROIDMEDIAPLAYERCONTROL_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class JMediaPlayer; +class QAndroidVideoOutput; + +class QAndroidMediaPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + explicit QAndroidMediaPlayerControl(QObject *parent = 0); + ~QAndroidMediaPlayerControl() Q_DECL_OVERRIDE; + + QMediaPlayer::State state() const Q_DECL_OVERRIDE; + QMediaPlayer::MediaStatus mediaStatus() const Q_DECL_OVERRIDE; + qint64 duration() const Q_DECL_OVERRIDE; + qint64 position() const Q_DECL_OVERRIDE; + int volume() const Q_DECL_OVERRIDE; + bool isMuted() const Q_DECL_OVERRIDE; + int bufferStatus() const Q_DECL_OVERRIDE; + bool isAudioAvailable() const Q_DECL_OVERRIDE; + bool isVideoAvailable() const Q_DECL_OVERRIDE; + bool isSeekable() const Q_DECL_OVERRIDE; + QMediaTimeRange availablePlaybackRanges() const Q_DECL_OVERRIDE; + qreal playbackRate() const Q_DECL_OVERRIDE; + void setPlaybackRate(qreal rate) Q_DECL_OVERRIDE; + QMediaContent media() const Q_DECL_OVERRIDE; + const QIODevice *mediaStream() const Q_DECL_OVERRIDE; + void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE; + + void setVideoOutput(QAndroidVideoOutput *videoOutput); + void onSurfaceTextureReady(); + +Q_SIGNALS: + void metaDataUpdated(); + +public Q_SLOTS: + void setPosition(qint64 position) Q_DECL_OVERRIDE; + void play() Q_DECL_OVERRIDE; + void pause() Q_DECL_OVERRIDE; + void stop() Q_DECL_OVERRIDE; + void setVolume(int volume) Q_DECL_OVERRIDE; + void setMuted(bool muted) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onError(qint32 what, qint32 extra); + void onInfo(qint32 what, qint32 extra); + void onMediaPlayerInfo(qint32 what, qint32 extra); + void onBufferChanged(qint32 percent); + void onVideoSizeChanged(qint32 width, qint32 height); + +private: + JMediaPlayer *mMediaPlayer; + QMediaPlayer::State mCurrentState; + QMediaPlayer::MediaStatus mCurrentMediaStatus; + QMediaContent mMediaContent; + QIODevice *mMediaStream; + QAndroidVideoOutput *mVideoOutput; + bool mSeekable; + int mBufferPercent; + bool mAudioAvailable; + bool mVideoAvailable; + QSize mVideoSize; + bool mBuffering; + QMediaTimeRange mAvailablePlaybackRange; + bool mMediaPlayerReady; + QMediaPlayer::State mPendingState; + qint64 mPendingPosition; + bool mPendingSetMedia; + + void setState(QMediaPlayer::State state); + void setMediaStatus(QMediaPlayer::MediaStatus status); + void setError(int error, const QString &errorString); + void setSeekable(bool seekable); + void setAudioAvailable(bool available); + void setVideoAvailable(bool available); + void updateAvailablePlaybackRanges(); + void resetBufferingProgress(); + void flushPendingStates(); +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIAPLAYERCONTROL_H diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp new file mode 100644 index 000000000..175958676 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidmediaservice.h" + +#include "qandroidmediaplayercontrol.h" +#include "qandroidmetadatareadercontrol.h" +#include "qandroidvideorendercontrol.h" + +QT_BEGIN_NAMESPACE + +QAndroidMediaService::QAndroidMediaService(QObject *parent) + : QMediaService(parent) + , mVideoRendererControl(0) +{ + mMediaControl = new QAndroidMediaPlayerControl; + mMetadataControl = new QAndroidMetaDataReaderControl; + connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)), + mMetadataControl, SLOT(onMediaChanged(QMediaContent))); + connect(mMediaControl, SIGNAL(metaDataUpdated()), + mMetadataControl, SLOT(onUpdateMetaData())); +} + +QAndroidMediaService::~QAndroidMediaService() +{ + delete mMediaControl; + delete mMetadataControl; + delete mVideoRendererControl; +} + +QMediaControl *QAndroidMediaService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return mMediaControl; + + if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) + return mMetadataControl; + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + if (!mVideoRendererControl) { + mVideoRendererControl = new QAndroidVideoRendererControl; + mMediaControl->setVideoOutput(mVideoRendererControl); + return mVideoRendererControl; + } + } + + return 0; +} + +void QAndroidMediaService::releaseControl(QMediaControl *control) +{ + if (control == mVideoRendererControl) { + mMediaControl->setVideoOutput(0); + delete mVideoRendererControl; + mVideoRendererControl = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h new file mode 100644 index 000000000..4d310e8e0 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDMEDIASERVICE_H +#define QANDROIDMEDIASERVICE_H + +#include + +QT_BEGIN_NAMESPACE + +class QAndroidMediaPlayerControl; +class QAndroidMetaDataReaderControl; +class QAndroidVideoRendererControl; + +class QAndroidMediaService : public QMediaService +{ + Q_OBJECT +public: + explicit QAndroidMediaService(QObject *parent = 0); + ~QAndroidMediaService() Q_DECL_OVERRIDE; + + QMediaControl* requestControl(const char *name) Q_DECL_OVERRIDE; + void releaseControl(QMediaControl *control) Q_DECL_OVERRIDE; + +private: + QAndroidMediaPlayerControl *mMediaControl; + QAndroidMetaDataReaderControl *mMetadataControl; + QAndroidVideoRendererControl *mVideoRendererControl; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIASERVICE_H diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp new file mode 100644 index 000000000..e52c46387 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidmetadatareadercontrol.h" + +#include "jmediametadataretriever.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Genre name ordered by ID +// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1 +static const char* qt_ID3GenreNames[] = +{ + "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", + "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", + "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", + "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", + "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", + "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", + "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", + "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", + "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", + "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", + "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", + "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", + "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", + "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", + "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", + "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", + "Euro-House", "Dance Hall" +}; + +QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent) + : QMetaDataReaderControl(parent) + , m_available(false) + , m_retriever(0) +{ + m_retriever = new JMediaMetadataRetriever; + if (!m_retriever->isValid()) { + delete m_retriever; + m_retriever = 0; + } +} + +QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl() +{ + if (m_retriever) { + m_retriever->release(); + delete m_retriever; + } +} + +bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const +{ + return m_available; +} + +QVariant QAndroidMetaDataReaderControl::metaData(const QString &key) const +{ + return m_metadata.value(key); +} + +QStringList QAndroidMetaDataReaderControl::availableMetaData() const +{ + return m_metadata.keys(); +} + +void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media) +{ + if (!m_retriever) + return; + + m_mediaContent = media; + updateData(); +} + +void QAndroidMetaDataReaderControl::onUpdateMetaData() +{ + if (!m_retriever || m_mediaContent.isNull()) + return; + + updateData(); +} + +void QAndroidMetaDataReaderControl::updateData() +{ + m_metadata.clear(); + + if (!m_mediaContent.isNull()) { + if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) { + QString mimeType = m_retriever->extractMetadata(JMediaMetadataRetriever::MimeType); + if (!mimeType.isNull()) + m_metadata.insert(QMediaMetaData::MediaType, mimeType); + + bool isVideo = !m_retriever->extractMetadata(JMediaMetadataRetriever::HasVideo).isNull() + || mimeType.startsWith(QStringLiteral("video")); + + QString string = m_retriever->extractMetadata(JMediaMetadataRetriever::Album); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::AlbumTitle, string); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::AlbumArtist); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::AlbumArtist, string); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Artist); + if (!string.isNull()) { + m_metadata.insert(isVideo ? QMediaMetaData::LeadPerformer + : QMediaMetaData::ContributingArtist, + string.split('/', QString::SkipEmptyParts)); + } + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Author); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts)); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Bitrate); + if (!string.isNull()) { + m_metadata.insert(isVideo ? QMediaMetaData::VideoBitRate + : QMediaMetaData::AudioBitRate, + string.toInt()); + } + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::CDTrackNumber); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::TrackNumber, string.toInt()); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Composer); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts)); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Date); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date()); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Duration); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Duration, string.toLongLong()); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Genre); + if (!string.isNull()) { + // The genre can be returned as an ID3v2 id, get the name for it in that case + if (string.startsWith('(') && string.endsWith(')')) { + bool ok = false; + int genreId = string.mid(1, string.length() - 2).toInt(&ok); + if (ok && genreId >= 0 && genreId <= 125) + string = QLatin1String(qt_ID3GenreNames[genreId]); + } + m_metadata.insert(QMediaMetaData::Genre, string); + } + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Title); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Title, string); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::VideoHeight); + if (!string.isNull()) { + int height = string.toInt(); + int width = m_retriever->extractMetadata(JMediaMetadataRetriever::VideoWidth).toInt(); + m_metadata.insert(QMediaMetaData::Resolution, QSize(width, height)); + } + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Writer); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts)); + + string = m_retriever->extractMetadata(JMediaMetadataRetriever::Year); + if (!string.isNull()) + m_metadata.insert(QMediaMetaData::Year, string.toInt()); + } + } + + bool oldAvailable = m_available; + m_available = !m_metadata.isEmpty(); + if (m_available != oldAvailable) + Q_EMIT metaDataAvailableChanged(m_available); + + Q_EMIT metaDataChanged(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h new file mode 100644 index 000000000..7ea736ffd --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDMETADATAREADERCONTROL_H +#define QANDROIDMETADATAREADERCONTROL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class JMediaMetadataRetriever; + +class QAndroidMetaDataReaderControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + explicit QAndroidMetaDataReaderControl(QObject *parent = 0); + ~QAndroidMetaDataReaderControl() Q_DECL_OVERRIDE; + + bool isMetaDataAvailable() const Q_DECL_OVERRIDE; + + QVariant metaData(const QString &key) const Q_DECL_OVERRIDE; + QStringList availableMetaData() const Q_DECL_OVERRIDE; + +public Q_SLOTS: + void onMediaChanged(const QMediaContent &media); + void onUpdateMetaData(); + +private: + void updateData(); + + QMediaContent m_mediaContent; + bool m_available; + QVariantMap m_metadata; + + JMediaMetadataRetriever *m_retriever; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMETADATAREADERCONTROL_H -- cgit v1.2.3