From 909a09069fbdec8d99cf91f505ca8c925a0e2ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Mon, 14 Jan 2013 14:23:38 +0100 Subject: Android multimedia plug-in. Adds MediaPlayer support for Android. Change-Id: I4c7b1e19927b2e50b227f3a3b3f7ca2e99397618 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/multimedia/multimedia.pro | 5 + src/plugins/android/android.pro | 5 + src/plugins/android/jar/AndroidManifest.xml | 6 + src/plugins/android/jar/jar.pro | 11 + .../android/multimedia/QtAndroidMediaPlayer.java | 445 ++++++++++++++++++ .../qt5/android/multimedia/QtSurfaceTexture.java | 74 +++ .../android/multimedia/QtSurfaceTextureHolder.java | 114 +++++ src/plugins/android/mediaplayer/mediaplayer.json | 3 + src/plugins/android/mediaplayer/mediaplayer.pro | 24 + .../mediaplayer/qandroidmediaplayercontrol.cpp | 496 +++++++++++++++++++++ .../mediaplayer/qandroidmediaplayercontrol.h | 127 ++++++ .../android/mediaplayer/qandroidmediaservice.cpp | 97 ++++ .../android/mediaplayer/qandroidmediaservice.h | 71 +++ .../mediaplayer/qandroidmediaserviceplugin.cpp | 107 +++++ .../mediaplayer/qandroidmediaserviceplugin.h | 72 +++ .../mediaplayer/qandroidmetadatareadercontrol.cpp | 221 +++++++++ .../mediaplayer/qandroidmetadatareadercontrol.h | 80 ++++ .../android/mediaplayer/qandroidvideooutput.h | 64 +++ .../mediaplayer/qandroidvideorendercontrol.cpp | 375 ++++++++++++++++ .../mediaplayer/qandroidvideorendercontrol.h | 95 ++++ .../android/wrappers/jmediametadataretriever.cpp | 126 ++++++ .../android/wrappers/jmediametadataretriever.h | 91 ++++ src/plugins/android/wrappers/jmediaplayer.cpp | 265 +++++++++++ src/plugins/android/wrappers/jmediaplayer.h | 133 ++++++ src/plugins/android/wrappers/jsurfacetexture.cpp | 118 +++++ src/plugins/android/wrappers/jsurfacetexture.h | 75 ++++ .../android/wrappers/jsurfacetextureholder.cpp | 65 +++ .../android/wrappers/jsurfacetextureholder.h | 59 +++ src/plugins/android/wrappers/wrappers.pri | 15 + src/plugins/plugins.pro | 4 + 30 files changed, 3443 insertions(+) create mode 100644 src/plugins/android/android.pro create mode 100644 src/plugins/android/jar/AndroidManifest.xml create mode 100644 src/plugins/android/jar/jar.pro create mode 100644 src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java create mode 100644 src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTexture.java create mode 100644 src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java create mode 100644 src/plugins/android/mediaplayer/mediaplayer.json create mode 100644 src/plugins/android/mediaplayer/mediaplayer.pro create mode 100644 src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp create mode 100644 src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h create mode 100644 src/plugins/android/mediaplayer/qandroidmediaservice.cpp create mode 100644 src/plugins/android/mediaplayer/qandroidmediaservice.h create mode 100644 src/plugins/android/mediaplayer/qandroidmediaserviceplugin.cpp create mode 100644 src/plugins/android/mediaplayer/qandroidmediaserviceplugin.h create mode 100644 src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.cpp create mode 100644 src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.h create mode 100644 src/plugins/android/mediaplayer/qandroidvideooutput.h create mode 100644 src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp create mode 100644 src/plugins/android/mediaplayer/qandroidvideorendercontrol.h create mode 100644 src/plugins/android/wrappers/jmediametadataretriever.cpp create mode 100644 src/plugins/android/wrappers/jmediametadataretriever.h create mode 100644 src/plugins/android/wrappers/jmediaplayer.cpp create mode 100644 src/plugins/android/wrappers/jmediaplayer.h create mode 100644 src/plugins/android/wrappers/jsurfacetexture.cpp create mode 100644 src/plugins/android/wrappers/jsurfacetexture.h create mode 100644 src/plugins/android/wrappers/jsurfacetextureholder.cpp create mode 100644 src/plugins/android/wrappers/jsurfacetextureholder.h create mode 100644 src/plugins/android/wrappers/wrappers.pri diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro index 9dbea399b..2a1c6afb5 100644 --- a/src/multimedia/multimedia.pro +++ b/src/multimedia/multimedia.pro @@ -51,6 +51,11 @@ include(radio/radio.pri) include(recording/recording.pri) include(video/video.pri) +ANDROID_JAR_DEPENDENCIES = \ + jar/QtMultimedia.jar:org.qtproject.qt5.android.multimedia.QtAndroidMediaPlayer +ANDROID_LIB_DEPENDENCIES = \ + plugins/mediaservice/libandroidmediaplayer.so + mac { LIBS += -framework AppKit -framework QuartzCore -framework QTKit } diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro new file mode 100644 index 000000000..2cfc83f0e --- /dev/null +++ b/src/plugins/android/android.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += mediaplayer \ + jar + diff --git a/src/plugins/android/jar/AndroidManifest.xml b/src/plugins/android/jar/AndroidManifest.xml new file mode 100644 index 000000000..7eae1854d --- /dev/null +++ b/src/plugins/android/jar/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + diff --git a/src/plugins/android/jar/jar.pro b/src/plugins/android/jar/jar.pro new file mode 100644 index 000000000..b256412fc --- /dev/null +++ b/src/plugins/android/jar/jar.pro @@ -0,0 +1,11 @@ +load(qt_build_paths) +CONFIG += java +TARGET = QtMultimedia +DESTDIR = $$MODULE_BASE_OUTDIR/jar +API_VERSION = android-11 + +JAVACLASSPATH += $$PWD/src + +JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java \ + $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTexture.java \ + $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java new file mode 100644 index 000000000..b2115b7d9 --- /dev/null +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java @@ -0,0 +1,445 @@ +/**************************************************************************** + ** + ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/legal + ** + ** This file is part of the QtMultimedia module 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$ + ** + ****************************************************************************/ + +package org.qtproject.qt5.android.multimedia; + +import java.io.IOException; +import java.lang.String; + +// API is level is < 9 unless marked otherwise. +import android.app.Activity; +import android.content.Context; +import android.media.MediaPlayer; +import android.net.Uri; +import android.util.Log; + +public class QtAndroidMediaPlayer extends MediaPlayer +{ + // Native callback functions for MediaPlayer + native public void onErrorNative(int what, int extra, long id); + native public void onBufferingUpdateNative(int percent, long id); + native public void onInfoNative(int what, int extra, long id); + native public void onMediaPlayerInfoNative(int what, int extra, long id); + native public void onVideoSizeChangedNative(int width, int height, long id); + + private Uri mUri = null; + private final long mID; + private boolean mMuted = false; + private boolean mPreparing = false; + private boolean mInitialized = false; + private int mVolume = 100; + private static final String TAG = "Qt MediaPlayer"; + private static Context mApplicationContext = null; + + final int MEDIA_PLAYER_INVALID_STATE = 1; + final int MEDIA_PLAYER_PREPARING = 2; + final int MEDIA_PLAYER_READY = 3; + final int MEDIA_PLAYER_DURATION = 4; + final int MEDIA_PLAYER_PROGRESS = 5; + final int MEDIA_PLAYER_FINISHED = 6; + + // Activity set by Qt on load. + static public void setActivity(final Activity activity) + { + try { + mApplicationContext = activity.getApplicationContext(); + } catch(final Exception e) { + Log.d(TAG, e.getMessage()); + } + } + + private class ProgressWatcher implements Runnable + { + @Override + public void run() + { + final int duratation = getDuration(); + int currentPosition = getCurrentPosition(); + + try { + while (duratation >= currentPosition && isPlaying()) { + onMediaPlayerInfoNative(MEDIA_PLAYER_PROGRESS, currentPosition, mID); + Thread.sleep(1000); + currentPosition = getCurrentPosition(); + } + } catch (final InterruptedException e) { + Log.d(TAG, e.getMessage()); + return; + } + } + } + + /** + * MediaPlayer OnErrorListener + */ + private class MediaPlayerErrorListener + implements MediaPlayer.OnErrorListener + { + @Override + public boolean onError(final MediaPlayer mp, + final int what, + final int extra) + { + reset(); + onErrorNative(what, extra, mID); + return true; + } + + } + + /** + * MediaPlayer OnBufferingListener + */ + private class MediaPlayerBufferingListener + implements MediaPlayer.OnBufferingUpdateListener + { + private int mBufferPercent = -1; + @Override + public void onBufferingUpdate(final android.media.MediaPlayer mp, + final int percent) + { + // Avoid updates when percent is unchanged. + // E.g., we keep getting updates when percent == 100 + if (mBufferPercent == percent) + return; + + onBufferingUpdateNative((mBufferPercent = percent), mID); + } + + } + + /** + * MediaPlayer OnCompletionListener + */ + private class MediaPlayerCompletionListener + implements MediaPlayer.OnCompletionListener + { + @Override + public void onCompletion(final MediaPlayer mp) + { + onMediaPlayerInfoNative(MEDIA_PLAYER_FINISHED, 0, mID); + reset(); + } + + } + + /** + * MediaPlayer OnInfoListener + */ + private class MediaPlayerInfoListener + implements MediaPlayer.OnInfoListener + { + @Override + public boolean onInfo(final MediaPlayer mp, + final int what, + final int extra) + { + onInfoNative(what, extra, mID); + return true; + } + + } + + /** + * MediaPlayer OnPreparedListener + */ + private class MediaPlayerPreparedListener + implements MediaPlayer.OnPreparedListener + { + + @Override + public void onPrepared(final MediaPlayer mp) + { + onMediaPlayerInfoNative(MEDIA_PLAYER_DURATION, getDuration(), mID); + onMediaPlayerInfoNative(MEDIA_PLAYER_READY, 0, mID); + mPreparing = false; + } + + } + + /** + * MediaPlayer OnSeekCompleteListener + */ + private class MediaPlayerSeekCompleteListener + implements MediaPlayer.OnSeekCompleteListener + { + + @Override + public void onSeekComplete(final MediaPlayer mp) + { + onMediaPlayerInfoNative(MEDIA_PLAYER_PROGRESS, getCurrentPosition(), mID); + } + + } + + /** + * MediaPlayer OnVideoSizeChangedListener + */ + private class MediaPlayerVideoSizeChangedListener + implements MediaPlayer.OnVideoSizeChangedListener + { + + @Override + public void onVideoSizeChanged(final MediaPlayer mp, + final int width, + final int height) + { + onVideoSizeChangedNative(width, height, mID); + } + + } + + public QtAndroidMediaPlayer(final long id) + { + super(); + mID = id; + setOnBufferingUpdateListener(new MediaPlayerBufferingListener()); + setOnCompletionListener(new MediaPlayerCompletionListener()); + setOnInfoListener(new MediaPlayerInfoListener()); + setOnSeekCompleteListener(new MediaPlayerSeekCompleteListener()); + setOnVideoSizeChangedListener(new MediaPlayerVideoSizeChangedListener()); + setOnErrorListener(new MediaPlayerErrorListener()); + } + + @Override + public void start() + { + if (!mInitialized) { + onMediaPlayerInfoNative(MEDIA_PLAYER_INVALID_STATE, 0, mID); + return; + } + + if (mApplicationContext == null) + return; + + if (mPreparing) + return; + + if (isPlaying()) + return; + + try { + super.start(); + Thread progressThread = new Thread(new ProgressWatcher()); + progressThread.start(); + } catch (final IllegalStateException e) { + reset(); + Log.d(TAG, e.getMessage()); + } + } + + @Override + public void pause() + { + if (!isPlaying()) + return; + + try { + super.pause(); + } catch (final IllegalStateException e) { + reset(); + Log.d(TAG, e.getMessage()); + } + } + + @Override + public void stop() + { + if (!mInitialized) + return; + + try { + super.stop(); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } finally { + reset(); + } + } + + @Override + public void seekTo(final int msec) + { + if (!mInitialized) + return; + + try { + super.seekTo(msec); + onMediaPlayerInfoNative(MEDIA_PLAYER_PROGRESS, msec, mID); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } + } + + @Override + public boolean isPlaying() + { + boolean playing = false; + + if (!mInitialized) + return playing; + + try { + playing = super.isPlaying(); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } + + return playing; + } + + public void setMediaPath(final String path) + { + if (mInitialized) + reset(); + + try { + mPreparing = true; + onMediaPlayerInfoNative(MEDIA_PLAYER_PREPARING, 0, mID); + mUri = Uri.parse(path); + setDataSource(mApplicationContext, mUri); + mInitialized = true; + setOnPreparedListener(new MediaPlayerPreparedListener()); + prepareAsync(); + } catch (final IOException e) { + mPreparing = false; + onErrorNative(MEDIA_ERROR_UNKNOWN, + /* MEDIA_ERROR_UNSUPPORTED= */ -1010, + mID); + } catch (final IllegalArgumentException e) { + Log.d(TAG, e.getMessage()); + } catch (final SecurityException e) { + Log.d(TAG, e.getMessage()); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } catch (final NullPointerException e) { + Log.d(TAG, e.getMessage()); + } + } + + @Override + public int getCurrentPosition() + { + int currentPosition = 0; + + if (!mInitialized) + return currentPosition; + + try { + currentPosition = super.getCurrentPosition(); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } + + return currentPosition; + } + + @Override + public int getDuration() + { + int duration = 0; + + if (!mInitialized) + return duration; + + try { + duration = super.getDuration(); + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } + + return duration; + } + + private float adjustVolume(final int volume) + { + if (volume < 1) + return 0.0f; + + if (volume > 98) + return 1.0f; + + return (float) (1-(Math.log(100-volume)/Math.log(100))); + } + + public void setVolume(int volume) + { + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + float newVolume = adjustVolume(volume); + + try { + super.setVolume(newVolume, newVolume); + if (!mMuted) + mVolume = volume; + } catch (final IllegalStateException e) { + Log.d(TAG, e.getMessage()); + } + } + + public int getVolume() + { + return mVolume; + } + + public void mute(final boolean mute) + { + mMuted = mute; + setVolume(mute ? 0 : mVolume); + } + + public boolean isMuted() + { + return mMuted; + } + + @Override + public void reset() + { + mInitialized = false; + super.reset(); + } + +} diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTexture.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTexture.java new file mode 100644 index 000000000..b8837d557 --- /dev/null +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTexture.java @@ -0,0 +1,74 @@ +/**************************************************************************** + ** + ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/legal + ** + ** This file is part of the QtMultimedia module 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$ + ** + ****************************************************************************/ + +package org.qtproject.qt5.android.multimedia; + +import android.graphics.SurfaceTexture; + +public class QtSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener +{ + public SurfaceTexture surfaceTexture; + private int texID; + + public QtSurfaceTexture(int texName) + { + texID = texName; + surfaceTexture = new SurfaceTexture(texName); + surfaceTexture.setOnFrameAvailableListener(this); + } + + public void getTransformMatrix(float[] mtx) + { + surfaceTexture.getTransformMatrix(mtx); + } + + public void updateTexImage() + { + surfaceTexture.updateTexImage(); + } + + public void onFrameAvailable(SurfaceTexture surfaceTexture) + { + notifyFrameAvailable(texID); + } + + private static native void notifyFrameAvailable(int id); +} diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java new file mode 100644 index 000000000..15c5f231f --- /dev/null +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java @@ -0,0 +1,114 @@ +/**************************************************************************** + ** + ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/legal + ** + ** This file is part of the QtMultimedia module 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$ + ** + ****************************************************************************/ + +package org.qtproject.qt5.android.multimedia; + +import android.view.SurfaceHolder; +import android.view.Surface; +import android.graphics.Rect; +import android.graphics.Canvas; + +public class QtSurfaceTextureHolder implements SurfaceHolder +{ + private Surface surfaceTexture; + + public QtSurfaceTextureHolder(Surface surface) + { + surfaceTexture = surface; + } + + public void addCallback(SurfaceHolder.Callback callback) + { + } + + public Surface getSurface() + { + return surfaceTexture; + } + + public Rect getSurfaceFrame() + { + return new Rect(); + } + + public boolean isCreating() + { + return false; + } + + public Canvas lockCanvas(Rect dirty) + { + return new Canvas(); + } + + public Canvas lockCanvas() + { + return new Canvas(); + } + + public void removeCallback(SurfaceHolder.Callback callback) + { + } + + public void setFixedSize(int width, int height) + { + } + + public void setFormat(int format) + { + } + + public void setKeepScreenOn(boolean screenOn) + { + } + + public void setSizeFromLayout() + { + } + + public void setType(int type) + { + } + + public void unlockCanvasAndPost(Canvas canvas) + { + } +} diff --git a/src/plugins/android/mediaplayer/mediaplayer.json b/src/plugins/android/mediaplayer/mediaplayer.json new file mode 100644 index 000000000..c4a27ea01 --- /dev/null +++ b/src/plugins/android/mediaplayer/mediaplayer.json @@ -0,0 +1,3 @@ +{ + "Keys": ["org.qt-project.qt.mediaplayer"] +} diff --git a/src/plugins/android/mediaplayer/mediaplayer.pro b/src/plugins/android/mediaplayer/mediaplayer.pro new file mode 100644 index 000000000..cabe4c666 --- /dev/null +++ b/src/plugins/android/mediaplayer/mediaplayer.pro @@ -0,0 +1,24 @@ +TARGET = androidmediaplayer +QT += multimedia-private gui-private platformsupport-private network + +PLUGIN_TYPE = mediaservice +PLUGIN_CLASS_NAME = QAndroidMediaPlayerServicePlugin +load(qt_plugin) + +HEADERS += \ + qandroidmediaplayercontrol.h \ + qandroidmediaservice.h \ + qandroidmetadatareadercontrol.h \ + qandroidmediaserviceplugin.h \ + qandroidvideorendercontrol.h \ + qandroidvideooutput.h +SOURCES += \ + qandroidmediaplayercontrol.cpp \ + qandroidmediaservice.cpp \ + qandroidmetadatareadercontrol.cpp \ + qandroidmediaserviceplugin.cpp \ + qandroidvideorendercontrol.cpp + +OTHER_FILES += mediaplayer.json + +include (../wrappers/wrappers.pri) diff --git a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp new file mode 100644 index 000000000..5a9c8b84f --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** 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 + +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) +{ + 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() +{ + 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) ? 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; + + 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; +} + +void QAndroidMediaPlayerControl::play() +{ + if (!mMediaPlayerReady) { + mPendingState = QMediaPlayer::PlayingState; + if (mCurrentState == QMediaPlayer::StoppedState + && !mMediaContent.isNull() + && mCurrentMediaStatus != QMediaPlayer::LoadingMedia) { + 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: + if (mBuffering) { + setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia + : QMediaPlayer::BufferingMedia); + } else { + setMediaStatus(QMediaPlayer::LoadedMedia); + mBufferPercent = 100; + Q_EMIT bufferStatusChanged(mBufferPercent); + updateAvailablePlaybackRanges(); + } + 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 = true; + mBufferPercent = percent; + Q_EMIT bufferStatusChanged(mBufferPercent); + + updateAvailablePlaybackRanges(); + + if (mBufferPercent == 100) + setMediaStatus(QMediaPlayer::BufferedMedia); +} + +void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height) +{ + if (width == 0 || height == 0) + return; + + setVideoAvailable(true); + + if (mVideoOutput) { + if (!mMediaPlayer->display()) + mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder()); + if (mMediaPlayer->display()) + mVideoOutput->setVideoSize(QSize(width, height)); + } +} + +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; + + mVideoAvailable = available; + Q_EMIT videoAvailableChanged(mVideoAvailable); +} + +void QAndroidMediaPlayerControl::resetBufferingProgress() +{ + mBuffering = false; + mBufferPercent = 0; + mAvailablePlaybackRange = QMediaTimeRange(); + Q_EMIT bufferStatusChanged(mBufferPercent); +} + +void QAndroidMediaPlayerControl::flushPendingStates() +{ + 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/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h new file mode 100644 index 000000000..445e8de7a --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** 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 + +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); + +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; + bool mBuffering; + QMediaTimeRange mAvailablePlaybackRange; + bool mMediaPlayerReady; + QMediaPlayer::State mPendingState; + qint64 mPendingPosition; + + 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/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/mediaplayer/qandroidmediaservice.cpp new file mode 100644 index 000000000..175958676 --- /dev/null +++ b/src/plugins/android/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/mediaplayer/qandroidmediaservice.h b/src/plugins/android/mediaplayer/qandroidmediaservice.h new file mode 100644 index 000000000..4d310e8e0 --- /dev/null +++ b/src/plugins/android/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/mediaplayer/qandroidmediaserviceplugin.cpp b/src/plugins/android/mediaplayer/qandroidmediaserviceplugin.cpp new file mode 100644 index 000000000..3bf703413 --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidmediaserviceplugin.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 "qandroidmediaserviceplugin.h" + +#include "qandroidmediaservice.h" +#include "jmediaplayer.h" +#include "jsurfacetexture.h" +#include "jsurfacetextureholder.h" +#include + +QT_BEGIN_NAMESPACE + +QAndroidMediaServicePlugin::QAndroidMediaServicePlugin() +{ +} + +QAndroidMediaServicePlugin::~QAndroidMediaServicePlugin() +{ +} + +QMediaService *QAndroidMediaServicePlugin::create(const QString &key) +{ + if (key == QStringLiteral(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QAndroidMediaService; + + qWarning() << "Android service plugin: unsupported key:" << key; + return 0; +} + +void QAndroidMediaServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QMediaServiceProviderHint::Features QAndroidMediaServicePlugin::supportedFeatures(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_MEDIAPLAYER) + return QMediaServiceProviderHint::VideoSurface; + + return QMediaServiceProviderHint::Features(); +} + + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) +{ + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) + return JNI_ERR; + + JNIEnv *jniEnv = uenv.nativeEnvironment; + + if (!JMediaPlayer::initJNI(jniEnv) || + !JSurfaceTexture::initJNI(jniEnv) || + !JSurfaceTextureHolder::initJNI(jniEnv)) { + return JNI_ERR; + } + + return JNI_VERSION_1_4; +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/mediaplayer/qandroidmediaserviceplugin.h b/src/plugins/android/mediaplayer/qandroidmediaserviceplugin.h new file mode 100644 index 000000000..d004635f2 --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidmediaserviceplugin.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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 QANDROIDMEDIASERVICEPLUGIN_H +#define QANDROIDMEDIASERVICEPLUGIN_H + +#include + +QT_BEGIN_NAMESPACE + +class QMediaService; +class QAndroidMediaService; + +class QAndroidMediaServicePlugin + : public QMediaServiceProviderPlugin + , public QMediaServiceFeaturesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceFeaturesInterface) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" + FILE "mediaplayer.json") + +public: + QAndroidMediaServicePlugin(); + ~QAndroidMediaServicePlugin(); + + QMediaService* create(QString const& key) Q_DECL_OVERRIDE; + void release(QMediaService *service) Q_DECL_OVERRIDE; + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIASERVICEPLUGIN_H diff --git a/src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.cpp new file mode 100644 index 000000000..9b2f4e17b --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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 + +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/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/mediaplayer/qandroidmetadatareadercontrol.h new file mode 100644 index 000000000..7ea736ffd --- /dev/null +++ b/src/plugins/android/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 diff --git a/src/plugins/android/mediaplayer/qandroidvideooutput.h b/src/plugins/android/mediaplayer/qandroidvideooutput.h new file mode 100644 index 000000000..99db7c3e7 --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidvideooutput.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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 QANDROIDVIDEOOUTPUT_H +#define QANDROIDVIDEOOUTPUT_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QAndroidVideoOutput +{ +public: + QAndroidVideoOutput() { } + virtual ~QAndroidVideoOutput() { } + + virtual jobject surfaceHolder() = 0; + virtual void setVideoSize(const QSize &size) = 0; + virtual void stop() = 0; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDVIDEOOUTPUT_H diff --git a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp new file mode 100644 index 000000000..7b810fa41 --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** 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 "qandroidvideorendercontrol.h" + +#include +#include "jsurfacetextureholder.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const GLfloat g_vertex_data[] = { + -1.f, 1.f, + 1.f, 1.f, + 1.f, -1.f, + -1.f, -1.f +}; + +static const GLfloat g_texture_data[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f +}; + +class TextureVideoBuffer : public QAbstractVideoBuffer +{ +public: + TextureVideoBuffer(GLuint textureId) + : QAbstractVideoBuffer(GLTextureHandle) + , m_textureId(textureId) + {} + + virtual ~TextureVideoBuffer() {} + + MapMode mapMode() const { return NotMapped; } + uchar *map(MapMode, int*, int*) { return 0; } + void unmap() {} + + QVariant handle() const + { + return QVariant::fromValue(m_textureId); + } + +private: + GLuint m_textureId; +}; + +class ImageVideoBuffer : public QAbstractVideoBuffer +{ +public: + ImageVideoBuffer(const QImage &image) + : QAbstractVideoBuffer(NoHandle) + , m_image(image) + , m_mode(NotMapped) + { + + } + + MapMode mapMode() const { return m_mode; } + uchar *map(MapMode mode, int *, int *) + { + if (mode != NotMapped && m_mode == NotMapped) { + m_mode = mode; + return m_image.bits(); + } + + return 0; + } + + void unmap() + { + m_mode = NotMapped; + } + +private: + QImage m_image; + MapMode m_mode; +}; + +QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) + : QVideoRendererControl(parent) + , m_surface(0) + , m_offscreenSurface(0) + , m_glContext(0) + , m_fbo(0) + , m_program(0) + , m_useImage(false) + , m_androidSurface(0) + , m_surfaceTexture(0) + , m_surfaceHolder(0) + , m_externalTex(0) +{ +} + +QAndroidVideoRendererControl::~QAndroidVideoRendererControl() +{ + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + if (m_surfaceTexture) { + QJNILocalRef surfaceTex = m_surfaceTexture->surfaceTexture(); + QJNIObject obj(surfaceTex.object()); + obj.callMethod("release"); + delete m_surfaceTexture; + m_surfaceTexture = 0; + } + if (m_androidSurface) { + m_androidSurface->callMethod("release"); + delete m_androidSurface; + m_androidSurface = 0; + } + if (m_surfaceHolder) { + delete m_surfaceHolder; + m_surfaceHolder = 0; + } + if (m_externalTex) + glDeleteTextures(1, &m_externalTex); + + delete m_fbo; + delete m_program; + delete m_glContext; + delete m_offscreenSurface; +} + +QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == m_surface) + return; + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + + m_useImage = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32); +} + +jobject QAndroidVideoRendererControl::surfaceHolder() +{ + if (m_surfaceHolder) + return m_surfaceHolder->object(); + + QOpenGLContext *currContext = QOpenGLContext::currentContext(); + + // If we don't have a GL context in the current thread, create one and share it + // with the render thread GL context + if (!currContext && !m_glContext) { + m_offscreenSurface = new QOffscreenSurface; + QSurfaceFormat format; + format.setSwapBehavior(QSurfaceFormat::SingleBuffer); + m_offscreenSurface->setFormat(format); + m_offscreenSurface->create(); + + QOpenGLContext *shareContext = 0; + if (m_surface) + shareContext = qobject_cast(m_surface->property("GLContext").value()); + m_glContext = new QOpenGLContext; + m_glContext->setFormat(m_offscreenSurface->requestedFormat()); + + if (shareContext) + m_glContext->setShareContext(shareContext); + + if (!m_glContext->create()) + return 0; + + // if sharing contexts is not supported, fallback to image rendering and send the bits + // to the video surface + if (!m_glContext->shareContext()) + m_useImage = true; + } + + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + glGenTextures(1, &m_externalTex); + m_surfaceTexture = new JSurfaceTexture(m_externalTex); + + if (m_surfaceTexture->isValid()) { + connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable())); + + QJNILocalRef surfaceTex = m_surfaceTexture->surfaceTexture(); + + m_androidSurface = new QJNIObject("android/view/Surface", + "(Landroid/graphics/SurfaceTexture;)V", + surfaceTex.object()); + + m_surfaceHolder = new JSurfaceTextureHolder(m_androidSurface->object()); + } else { + delete m_surfaceTexture; + m_surfaceTexture = 0; + glDeleteTextures(1, &m_externalTex); + } + + if (m_surfaceHolder) + return m_surfaceHolder->object(); + + return 0; +} + +void QAndroidVideoRendererControl::setVideoSize(const QSize &size) +{ + if (m_nativeSize == size) + return; + + m_nativeSize = size; + + delete m_fbo; + m_fbo = 0; +} + +void QAndroidVideoRendererControl::stop() +{ + if (m_surface && m_surface->isActive()) + m_surface->stop(); + m_nativeSize = QSize(); +} + +void QAndroidVideoRendererControl::onFrameAvailable() +{ + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + m_surfaceTexture->updateTexImage(); + + if (!m_nativeSize.isValid()) + return; + + renderFrameToFbo(); + + QAbstractVideoBuffer *buffer = 0; + QVideoFrame frame; + + if (m_useImage) { + buffer = new ImageVideoBuffer(m_fbo->toImage().mirrored()); + frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_RGB32); + } else { + buffer = new TextureVideoBuffer(m_fbo->texture()); + 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->nativeResolution() != frame.size())) { + m_surface->stop(); + } + + if (!m_surface->isActive()) { + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), + m_useImage ? QAbstractVideoBuffer::NoHandle + : QAbstractVideoBuffer::GLTextureHandle); + + m_surface->start(format); + } + + if (m_surface->isActive()) + m_surface->present(frame); + } +} + +void QAndroidVideoRendererControl::renderFrameToFbo() +{ + createGLResources(); + + m_fbo->bind(); + + glViewport(0, 0, m_nativeSize.width(), m_nativeSize.height()); + + m_program->bind(); + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_program->setUniformValue("frameTexture", GLuint(0)); + m_program->setUniformValue("texMatrix", m_surfaceTexture->getTransformMatrix()); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, g_vertex_data); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, g_texture_data); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + m_program->disableAttributeArray(0); + m_program->disableAttributeArray(1); + m_program->release(); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + m_fbo->release(); + + glFinish(); +} + +void QAndroidVideoRendererControl::createGLResources() +{ + if (!m_fbo) + m_fbo = new QOpenGLFramebufferObject(m_nativeSize); + + if (!m_program) { + m_program = new QOpenGLShaderProgram; + + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_program); + vertexShader->compileSourceCode("attribute highp vec4 vertexCoordsArray; \n" \ + "attribute highp vec2 textureCoordArray; \n" \ + "uniform highp mat4 texMatrix; \n" \ + "varying highp vec2 textureCoords; \n" \ + "void main(void) \n" \ + "{ \n" \ + " gl_Position = vertexCoordsArray; \n" \ + " textureCoords = (texMatrix * vec4(textureCoordArray, 0.0, 1.0)).xy; \n" \ + "}\n"); + m_program->addShader(vertexShader); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_program); + fragmentShader->compileSourceCode("#extension GL_OES_EGL_image_external : require \n" \ + "varying highp vec2 textureCoords; \n" \ + "uniform samplerExternalOES frameTexture; \n" \ + "void main() \n" \ + "{ \n" \ + " gl_FragColor = texture2D(frameTexture, textureCoords); \n" \ + "}\n"); + m_program->addShader(fragmentShader); + + m_program->bindAttributeLocation("vertexCoordsArray", 0); + m_program->bindAttributeLocation("textureCoordArray", 1); + m_program->link(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h new file mode 100644 index 000000000..525291e1f --- /dev/null +++ b/src/plugins/android/mediaplayer/qandroidvideorendercontrol.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 QANDROIDVIDEORENDERCONTROL_H +#define QANDROIDVIDEORENDERCONTROL_H + +#include +#include "qandroidvideooutput.h" +#include "jsurfacetexture.h" + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QOffscreenSurface; +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; +class JSurfaceTextureHolder; + +class QAndroidVideoRendererControl : public QVideoRendererControl, public QAndroidVideoOutput +{ + Q_OBJECT +public: + explicit QAndroidVideoRendererControl(QObject *parent = 0); + ~QAndroidVideoRendererControl() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; + void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; + + jobject surfaceHolder() Q_DECL_OVERRIDE; + void setVideoSize(const QSize &size) Q_DECL_OVERRIDE; + void stop() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onFrameAvailable(); + +private: + void setupSurface(); + void renderFrameToFbo(); + void createGLResources(); + + QAbstractVideoSurface *m_surface; + QOffscreenSurface *m_offscreenSurface; + QOpenGLContext *m_glContext; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + bool m_useImage; + QSize m_nativeSize; + + QJNIObject *m_androidSurface; + JSurfaceTexture *m_surfaceTexture; + JSurfaceTextureHolder *m_surfaceHolder; + uint m_externalTex; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDVIDEORENDERCONTROL_H diff --git a/src/plugins/android/wrappers/jmediametadataretriever.cpp b/src/plugins/android/wrappers/jmediametadataretriever.cpp new file mode 100644 index 000000000..ae5abcf43 --- /dev/null +++ b/src/plugins/android/wrappers/jmediametadataretriever.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** 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 "jmediametadataretriever.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static jobject g_activity = 0; + +JMediaMetadataRetriever::JMediaMetadataRetriever() + : QJNIObject("android/media/MediaMetadataRetriever") +{ + if (!g_activity) { + QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + g_activity = static_cast(nativeInterface->nativeResourceForIntegration("QtActivity")); + } +} + +JMediaMetadataRetriever::~JMediaMetadataRetriever() +{ +} + +QString JMediaMetadataRetriever::extractMetadata(MetadataKey key) +{ + QString value; + + QJNILocalRef metadata = callObjectMethod("extractMetadata", + "(I)Ljava/lang/String;", + jint(key)); + if (!metadata.isNull()) + value = qt_convertJString(metadata.object()); + + return value; +} + +void JMediaMetadataRetriever::release() +{ + callMethod("release"); +} + +bool JMediaMetadataRetriever::setDataSource(const QUrl &url) +{ + QAttachedJNIEnv env; + + bool loaded = false; + + QJNILocalRef string = qt_toJString(url.toString()); + + QJNILocalRef uri = callStaticObjectMethod("android/net/Uri", + "parse", + "(Ljava/lang/String;)Landroid/net/Uri;", + string.object()); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } else { + callMethod("setDataSource", + "(Landroid/content/Context;Landroid/net/Uri;)V", + g_activity, + uri.object()); + if (env->ExceptionCheck()) + env->ExceptionClear(); + else + loaded = true; + } + + return loaded; +} + +bool JMediaMetadataRetriever::setDataSource(const QString &path) +{ + QAttachedJNIEnv env; + + bool loaded = false; + + callMethod("setDataSource", "(Ljava/lang/String;)V", qt_toJString(path).object()); + if (env->ExceptionCheck()) + env->ExceptionClear(); + else + loaded = true; + + return loaded; +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/wrappers/jmediametadataretriever.h b/src/plugins/android/wrappers/jmediametadataretriever.h new file mode 100644 index 000000000..dd63e0d87 --- /dev/null +++ b/src/plugins/android/wrappers/jmediametadataretriever.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 JMEDIAMETADATARETRIEVER_H +#define JMEDIAMETADATARETRIEVER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class JMediaMetadataRetriever : public QJNIObject +{ +public: + enum MetadataKey { + Album = 1, + AlbumArtist = 13, + Artist = 2, + Author = 3, + Bitrate = 20, + CDTrackNumber = 0, + Compilation = 15, + Composer = 4, + Date = 5, + DiscNumber = 14, + Duration = 9, + Genre = 6, + HasAudio = 16, + HasVideo = 17, + Location = 23, + MimeType = 12, + NumTracks = 10, + Title = 7, + VideoHeight = 19, + VideoWidth = 18, + VideoRotation = 24, + Writer = 11, + Year = 8 + }; + + JMediaMetadataRetriever(); + ~JMediaMetadataRetriever(); + + QString extractMetadata(MetadataKey key); + void release(); + bool setDataSource(const QUrl &url); + bool setDataSource(const QString &path); + +}; + +QT_END_NAMESPACE + +#endif // JMEDIAMETADATARETRIEVER_H diff --git a/src/plugins/android/wrappers/jmediaplayer.cpp b/src/plugins/android/wrappers/jmediaplayer.cpp new file mode 100644 index 000000000..48e743b26 --- /dev/null +++ b/src/plugins/android/wrappers/jmediaplayer.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** 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 "jmediaplayer.h" + +#include +#include +#include +#include + +namespace { + +jclass mediaPlayerClass = 0; + +QMap mplayers; + +} + +QT_BEGIN_NAMESPACE + +bool JMediaPlayer::mActivitySet = false; + +JMediaPlayer::JMediaPlayer() + : QObject() + , QJNIObject(mediaPlayerClass, "(J)V", reinterpret_cast(this)) + , mId(reinterpret_cast(this)) + , mDisplay(0) +{ + mplayers.insert(mId, this); + + if (!mActivitySet) { + QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + jobject activity = static_cast(nativeInterface->nativeResourceForIntegration("QtActivity")); + QJNIObject::callStaticMethod(mediaPlayerClass, + "setActivity", + "(Landroid/app/Activity;)V", + activity); + mActivitySet = true; + } +} + +JMediaPlayer::~JMediaPlayer() +{ + mplayers.remove(mId); +} + +void JMediaPlayer::onError(qint32 what, qint32 extra) +{ + Q_EMIT error(what, extra); +} + +void JMediaPlayer::onBufferingUpdate(qint32 percent) +{ + Q_EMIT bufferingUpdate(percent); +} + +void JMediaPlayer::onInfo(qint32 what, qint32 extra) +{ + Q_EMIT info(what, extra); +} + +void JMediaPlayer::onMediaPlayerInfo(qint32 what, qint32 extra) +{ + Q_EMIT mediaPlayerInfo(what, extra); +} + +void JMediaPlayer::onVideoSizeChanged(qint32 width, qint32 height) +{ + Q_EMIT videoSizeChanged(width, height); +} + +int JMediaPlayer::getCurrentPosition() +{ + return callMethod("getCurrentPosition"); +} + +int JMediaPlayer::getDuration() +{ + return callMethod("getDuration"); +} + +bool JMediaPlayer::isPlaying() +{ + return callMethod("isPlaying"); +} + +int JMediaPlayer::volume() +{ + return callMethod("getVolume"); +} + +bool JMediaPlayer::isMuted() +{ + return callMethod("isMuted"); +} + +void JMediaPlayer::play() +{ + callMethod("start"); +} + +void JMediaPlayer::pause() +{ + callMethod("pause"); +} + +void JMediaPlayer::stop() +{ + callMethod("stop"); +} + +void JMediaPlayer::seekTo(qint32 msec) +{ + callMethod("seekTo", "(I)V", jint(msec)); +} + +void JMediaPlayer::setMuted(bool mute) +{ + callMethod("mute", "(Z)V", jboolean(mute)); +} + +void JMediaPlayer::setDataSource(const QString &path) +{ + QJNILocalRef string = qt_toJString(path); + callMethod("setMediaPath", "(Ljava/lang/String;)V", string.object()); +} + +void JMediaPlayer::setVolume(int volume) +{ + callMethod("setVolume", "(I)V", jint(volume)); +} + +void JMediaPlayer::setDisplay(jobject surfaceHolder) +{ + mDisplay = surfaceHolder; + callMethod("setDisplay", "(Landroid/view/SurfaceHolder;)V", mDisplay); +} + +QT_END_NAMESPACE + +static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + JMediaPlayer *const mp = mplayers[id]; + if (!mp) + return; + + mp->onError(what, extra); +} + +static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + JMediaPlayer *const mp = mplayers[id]; + if (!mp) + return; + + mp->onBufferingUpdate(percent); +} + +static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + JMediaPlayer *const mp = mplayers[id]; + if (!mp) + return; + + mp->onInfo(what, extra); +} + +static void onMediaPlayerInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + JMediaPlayer *const mp = mplayers[id]; + if (!mp) + return; + + mp->onMediaPlayerInfo(what, extra); +} + +static void onVideoSizeChangedNative(JNIEnv *env, + jobject thiz, + jint width, + jint height, + jlong id) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + JMediaPlayer *const mp = mplayers[id]; + if (!mp) + return; + + mp->onVideoSizeChanged(width, height); +} + +QT_BEGIN_NAMESPACE + +bool JMediaPlayer::initJNI(JNIEnv *env) +{ + jclass jClass = env->FindClass("org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer"); + + if (jClass) { + mediaPlayerClass = static_cast(env->NewGlobalRef(jClass)); + + JNINativeMethod methods[] = { + {"onErrorNative", "(IIJ)V", reinterpret_cast(onErrorNative)}, + {"onBufferingUpdateNative", "(IJ)V", reinterpret_cast(onBufferingUpdateNative)}, + {"onInfoNative", "(IIJ)V", reinterpret_cast(onInfoNative)}, + {"onMediaPlayerInfoNative", "(IIJ)V", reinterpret_cast(onMediaPlayerInfoNative)}, + {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast(onVideoSizeChangedNative)} + }; + + if (env->RegisterNatives(mediaPlayerClass, + methods, + sizeof(methods) / sizeof(methods[0])) < 0) { + return false; + } + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/wrappers/jmediaplayer.h b/src/plugins/android/wrappers/jmediaplayer.h new file mode 100644 index 000000000..f5cb11773 --- /dev/null +++ b/src/plugins/android/wrappers/jmediaplayer.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** 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 QANDROIDMEDIAPLAYER_H +#define QANDROIDMEDIAPLAYER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class JMediaPlayer : public QObject, public QJNIObject +{ + Q_OBJECT +public: + JMediaPlayer(); + ~JMediaPlayer(); + + enum MediaError + { + // What + MEDIA_ERROR_UNKNOWN = 1, + MEDIA_ERROR_SERVER_DIED = 100, + // Extra + MEDIA_ERROR_IO = -1004, + MEDIA_ERROR_MALFORMED = -1007, + MEDIA_ERROR_UNSUPPORTED = -1010, + MEDIA_ERROR_TIMED_OUT = -110, + MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200 + }; + + enum MediaInfo + { + MEDIA_INFO_UNKNOWN = 1, + MEDIA_INFO_VIDEO_TRACK_LAGGING = 700, + MEDIA_INFO_VIDEO_RENDERING_START = 3, + MEDIA_INFO_BUFFERING_START = 701, + MEDIA_INFO_BUFFERING_END = 702, + MEDIA_INFO_BAD_INTERLEAVING = 800, + MEDIA_INFO_NOT_SEEKABLE = 801, + MEDIA_INFO_METADATA_UPDATE = 802 + }; + + enum MediaPlayerInfo + { + MEDIA_PLAYER_INVALID_STATE = 1, + MEDIA_PLAYER_PREPARING = 2, + MEDIA_PLAYER_READY = 3, + MEDIA_PLAYER_DURATION = 4, + MEDIA_PLAYER_PROGRESS = 5, + MEDIA_PLAYER_FINISHED = 6 + }; + + int getCurrentPosition(); + int getDuration(); + bool isPlaying(); + int volume(); + bool isMuted(); + jobject display() { return mDisplay; } + + void play(); + void pause(); + void stop(); + void seekTo(qint32 msec); + void setMuted(bool mute); + void setDataSource(const QString &path); + void setVolume(int volume); + void setDisplay(jobject surfaceHolder); + + void onError(qint32 what, qint32 extra); + void onBufferingUpdate(qint32 percent); + void onInfo(qint32 what, qint32 extra); + void onMediaPlayerInfo(qint32 what, qint32 extra); + void onVideoSizeChanged(qint32 width, qint32 height); + + static bool initJNI(JNIEnv *env); + +Q_SIGNALS: + void error(qint32 what, qint32 extra); + void bufferingUpdate(qint32 percent); + void completion(); + void info(qint32 what, qint32 extra); + void mediaPlayerInfo(qint32 what, qint32 extra); + void videoSizeChanged(qint32 width, qint32 height); + +private: + jlong mId; + jobject mDisplay; + + static bool mActivitySet; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIAPLAYER_H diff --git a/src/plugins/android/wrappers/jsurfacetexture.cpp b/src/plugins/android/wrappers/jsurfacetexture.cpp new file mode 100644 index 000000000..107f7be39 --- /dev/null +++ b/src/plugins/android/wrappers/jsurfacetexture.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** 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 "jsurfacetexture.h" +#include + +QT_BEGIN_NAMESPACE + +static jclass g_qtSurfaceTextureClass = 0; +static QHash g_objectMap; + +// native method for QtSurfaceTexture.java +static void notifyFrameAvailable(JNIEnv* , jobject, int id) +{ + JSurfaceTexture *obj = g_objectMap.value(id, 0); + if (obj) + Q_EMIT obj->frameAvailable(); +} + +JSurfaceTexture::JSurfaceTexture(unsigned int texName) + : QObject() + , QJNIObject(g_qtSurfaceTextureClass, "(I)V", jint(texName)) + , m_texID(int(texName)) +{ + if (m_jobject) + g_objectMap.insert(int(texName), this); +} + +JSurfaceTexture::~JSurfaceTexture() +{ + if (m_jobject) + g_objectMap.remove(m_texID); +} + +QMatrix4x4 JSurfaceTexture::getTransformMatrix() +{ + QAttachedJNIEnv env; + + QMatrix4x4 matrix; + jfloatArray array = env->NewFloatArray(16); + callMethod("getTransformMatrix", "([F)V", array); + env->GetFloatArrayRegion(array, 0, 16, matrix.data()); + env->DeleteLocalRef(array); + + return matrix; +} + +void JSurfaceTexture::updateTexImage() +{ + callMethod("updateTexImage"); +} + +QJNILocalRef JSurfaceTexture::surfaceTexture() +{ + return getObjectField("surfaceTexture", "Landroid/graphics/SurfaceTexture;"); +} + +static JNINativeMethod methods[] = { + {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable} +}; + +bool JSurfaceTexture::initJNI(JNIEnv *env) +{ + jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtSurfaceTexture"); + if (env->ExceptionCheck()) + env->ExceptionClear(); + + if (clazz) { + g_qtSurfaceTextureClass = static_cast(env->NewGlobalRef(clazz)); + if (env->RegisterNatives(g_qtSurfaceTextureClass, + methods, + sizeof(methods) / sizeof(methods[0])) < 0) { + return false; + } + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/wrappers/jsurfacetexture.h b/src/plugins/android/wrappers/jsurfacetexture.h new file mode 100644 index 000000000..49e007666 --- /dev/null +++ b/src/plugins/android/wrappers/jsurfacetexture.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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 JSURFACETEXTURE_H +#define JSURFACETEXTURE_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class JSurfaceTexture : public QObject, public QJNIObject +{ + Q_OBJECT +public: + explicit JSurfaceTexture(unsigned int texName); + ~JSurfaceTexture(); + + QMatrix4x4 getTransformMatrix(); + void updateTexImage(); + + QJNILocalRef surfaceTexture(); + + static bool initJNI(JNIEnv *env); + +Q_SIGNALS: + void frameAvailable(); + +private: + int m_texID; +}; + +QT_END_NAMESPACE + +#endif // JSURFACETEXTURE_H diff --git a/src/plugins/android/wrappers/jsurfacetextureholder.cpp b/src/plugins/android/wrappers/jsurfacetextureholder.cpp new file mode 100644 index 000000000..4ec8935c2 --- /dev/null +++ b/src/plugins/android/wrappers/jsurfacetextureholder.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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 "jsurfacetextureholder.h" + +QT_BEGIN_NAMESPACE + +static jclass g_qtSurfaceTextureHolderClass = 0; + +JSurfaceTextureHolder::JSurfaceTextureHolder(jobject surface) + : QJNIObject(g_qtSurfaceTextureHolderClass, "(Landroid/view/Surface;)V", surface) +{ +} + +bool JSurfaceTextureHolder::initJNI(JNIEnv *env) +{ + jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder"); + if (env->ExceptionCheck()) + env->ExceptionClear(); + + if (clazz) + g_qtSurfaceTextureHolderClass = static_cast(env->NewGlobalRef(clazz)); + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/wrappers/jsurfacetextureholder.h b/src/plugins/android/wrappers/jsurfacetextureholder.h new file mode 100644 index 000000000..5b567de3a --- /dev/null +++ b/src/plugins/android/wrappers/jsurfacetextureholder.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 JSURFACETEXTUREHOLDER_H +#define JSURFACETEXTUREHOLDER_H + +#include + +QT_BEGIN_NAMESPACE + +class JSurfaceTextureHolder : public QJNIObject +{ +public: + JSurfaceTextureHolder(jobject surface); + + static bool initJNI(JNIEnv *env); +}; + +QT_END_NAMESPACE + +#endif // JSURFACETEXTUREHOLDER_H diff --git a/src/plugins/android/wrappers/wrappers.pri b/src/plugins/android/wrappers/wrappers.pri new file mode 100644 index 000000000..5b47ef531 --- /dev/null +++ b/src/plugins/android/wrappers/wrappers.pri @@ -0,0 +1,15 @@ +QT += platformsupport-private + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/jmediaplayer.h \ + $$PWD/jsurfacetexture.h \ + $$PWD/jsurfacetextureholder.h \ + $$PWD/jmediametadataretriever.h + +SOURCES += \ + $$PWD/jmediaplayer.cpp \ + $$PWD/jsurfacetexture.cpp \ + $$PWD/jsurfacetextureholder.cpp \ + $$PWD/jmediametadataretriever.cpp diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index c8826a814..998713f2c 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -8,6 +8,10 @@ TEMPLATE = subdirs SUBDIRS += m3u +android { + SUBDIRS += android +} + blackberry { SUBDIRS += blackberry } -- cgit v1.2.3