summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Strømme <christian.stromme@digia.com>2014-02-20 14:25:35 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-03-21 00:31:17 +0100
commitdcf11bba3ccfd2a2a44556b1947406947aaa2be1 (patch)
treea138cb2ba0c668b56df82e9e93e7a99d61771bf2
parentddd22fab2bb26cd452edc1b8fc0c0750a7c52ae5 (diff)
Android: Make the Mediaplayer more robust
In some cases the the Android media player would get into a unexpected state and we where then not able to recover. With this patch we monitor the state changes more closely and recover when possible. Task-number: QTBUG-35651 Change-Id: I142c63fbbf716d3f94ebdcf016a7cadad7b13207 Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java309
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.h1
-rw-r--r--src/plugins/android/src/common/qandroidvideorendercontrol.cpp39
-rw-r--r--src/plugins/android/src/common/qandroidvideorendercontrol.h2
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp397
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h11
-rw-r--r--src/plugins/android/src/wrappers/jmediaplayer.cpp146
-rw-r--r--src/plugins/android/src/wrappers/jmediaplayer.h46
8 files changed, 614 insertions, 337 deletions
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
index cd79c979c..86ec30a5f 100644
--- 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
@@ -53,59 +53,56 @@ import android.util.Log;
import java.io.FileDescriptor;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
+import android.view.SurfaceHolder;
-public class QtAndroidMediaPlayer extends MediaPlayer
+public class QtAndroidMediaPlayer
{
// 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 onProgressUpdateNative(int progress, long id);
+ native public void onDurationChangedNative(int duration, 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);
+ native public void onStateChangedNative(int state, long id);
+ private MediaPlayer mMediaPlayer = null;
private Uri mUri = null;
private final long mID;
+ private final Activity mActivity;
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 SurfaceHolder mSurfaceHolder = null;
+
+ private class State {
+ final static int Uninitialized = 0x1 /* End */;
+ final static int Idle = 0x2;
+ final static int Preparing = 0x4;
+ final static int Prepared = 0x8;
+ final static int Initialized = 0x10;
+ final static int Started = 0x20;
+ final static int Stopped = 0x40;
+ final static int Paused = 0x80;
+ final static int PlaybackCompleted = 0x100;
+ final static int Error = 0x200;
}
- private class ProgressWatcher implements Runnable
+ private volatile int mState = State.Uninitialized;
+
+ 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);
+ while ((mState & (State.Started)) != 0) {
+ onProgressUpdateNative(getCurrentPosition(), mID);
Thread.sleep(1000);
- currentPosition = getCurrentPosition();
}
} catch (final InterruptedException e) {
- Log.d(TAG, "" + e.getMessage());
- return;
+ // Ignore
}
}
}
@@ -121,7 +118,7 @@ public class QtAndroidMediaPlayer extends MediaPlayer
final int what,
final int extra)
{
- reset();
+ setState(State.Error);
onErrorNative(what, extra, mID);
return true;
}
@@ -158,7 +155,7 @@ public class QtAndroidMediaPlayer extends MediaPlayer
@Override
public void onCompletion(final MediaPlayer mp)
{
- onMediaPlayerInfoNative(MEDIA_PLAYER_FINISHED, 0, mID);
+ setState(State.PlaybackCompleted);
}
}
@@ -190,9 +187,8 @@ public class QtAndroidMediaPlayer extends MediaPlayer
@Override
public void onPrepared(final MediaPlayer mp)
{
- mPreparing = false;
- onMediaPlayerInfoNative(MEDIA_PLAYER_READY, 0, mID);
- onMediaPlayerInfoNative(MEDIA_PLAYER_DURATION, getDuration(), mID);
+ setState(State.Prepared);
+ onDurationChangedNative(getDuration(), mID);
}
}
@@ -207,7 +203,7 @@ public class QtAndroidMediaPlayer extends MediaPlayer
@Override
public void onSeekComplete(final MediaPlayer mp)
{
- onMediaPlayerInfoNative(MEDIA_PLAYER_PROGRESS, getCurrentPosition(), mID);
+ onProgressUpdateNative(getCurrentPosition(), mID);
}
}
@@ -229,98 +225,117 @@ public class QtAndroidMediaPlayer extends MediaPlayer
}
- public QtAndroidMediaPlayer(final long id)
+ public QtAndroidMediaPlayer(final Activity activity, final long id)
{
- super();
mID = id;
- setOnBufferingUpdateListener(new MediaPlayerBufferingListener());
- setOnCompletionListener(new MediaPlayerCompletionListener());
- setOnInfoListener(new MediaPlayerInfoListener());
- setOnSeekCompleteListener(new MediaPlayerSeekCompleteListener());
- setOnVideoSizeChangedListener(new MediaPlayerVideoSizeChangedListener());
- setOnErrorListener(new MediaPlayerErrorListener());
+ mActivity = activity;
}
- @Override
- public void start()
+ private void setState(int state)
{
- if (!mInitialized) {
- onMediaPlayerInfoNative(MEDIA_PLAYER_INVALID_STATE, 0, mID);
+ if (mState == state)
return;
- }
- if (mApplicationContext == null)
- return;
+ mState = state;
- if (mPreparing)
- return;
+ onStateChangedNative(mState, mID);
+ }
+
+
+ private void init()
+ {
+ if (mMediaPlayer == null) {
+ mMediaPlayer = new MediaPlayer();
+ setState(State.Idle);
+ }
+ }
- if (isPlaying())
+ public void start()
+ {
+ if ((mState & (State.Prepared
+ | State.Started
+ | State.Paused
+ | State.PlaybackCompleted)) == 0) {
return;
+ }
try {
- super.start();
+ mMediaPlayer.start();
+ setState(State.Started);
Thread progressThread = new Thread(new ProgressWatcher());
progressThread.start();
} catch (final IllegalStateException e) {
- reset();
Log.d(TAG, "" + e.getMessage());
}
}
- @Override
+
public void pause()
{
- if (!isPlaying())
+ if ((mState & (State.Started | State.Paused | State.PlaybackCompleted)) == 0)
return;
try {
- super.pause();
+ mMediaPlayer.pause();
+ setState(State.Paused);
} catch (final IllegalStateException e) {
- reset();
Log.d(TAG, "" + e.getMessage());
}
}
- @Override
+
public void stop()
{
- if (!mInitialized)
+ if ((mState & (State.Prepared
+ | State.Started
+ | State.Stopped
+ | State.Paused
+ | State.PlaybackCompleted)) == 0) {
return;
+ }
try {
- super.stop();
+ mMediaPlayer.stop();
+ setState(State.Stopped);
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
- } finally {
- reset();
}
}
- @Override
+
public void seekTo(final int msec)
{
- if (!mInitialized)
+ if ((mState & (State.Prepared
+ | State.Started
+ | State.Paused
+ | State.PlaybackCompleted)) == 0) {
return;
+ }
try {
- super.seekTo(msec);
- onMediaPlayerInfoNative(MEDIA_PLAYER_PROGRESS, msec, mID);
+ mMediaPlayer.seekTo(msec);
+ onProgressUpdateNative(msec, mID);
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}
}
- @Override
+
public boolean isPlaying()
{
boolean playing = false;
-
- if (!mInitialized)
+ if ((mState & (State.Idle
+ | State.Initialized
+ | State.Prepared
+ | State.Started
+ | State.Paused
+ | State.Stopped
+ | State.PlaybackCompleted)) == 0) {
return playing;
+ }
try {
- playing = super.isPlaying();
+ playing = mMediaPlayer.isPlaying();
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}
@@ -328,34 +343,56 @@ public class QtAndroidMediaPlayer extends MediaPlayer
return playing;
}
- public void setMediaPath(final String path)
+ public void prepareAsync()
+ {
+ if ((mState & (State.Initialized | State.Stopped)) == 0)
+ return;
+
+ try {
+ mMediaPlayer.prepareAsync();
+ setState(State.Preparing);
+ } catch (final IllegalStateException e) {
+ Log.d(TAG, "" + e.getMessage());
+ }
+ }
+
+ public void setDataSource(final String path)
{
- if (mInitialized)
- reset();
+ if ((mState & State.Uninitialized) != 0)
+ init();
+
+ if ((mState & State.Idle) == 0)
+ return;
+
+ mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayerBufferingListener());
+ mMediaPlayer.setOnCompletionListener(new MediaPlayerCompletionListener());
+ mMediaPlayer.setOnInfoListener(new MediaPlayerInfoListener());
+ mMediaPlayer.setOnSeekCompleteListener(new MediaPlayerSeekCompleteListener());
+ mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayerVideoSizeChangedListener());
+ mMediaPlayer.setOnErrorListener(new MediaPlayerErrorListener());
+ mMediaPlayer.setOnPreparedListener(new MediaPlayerPreparedListener());
+ if (mSurfaceHolder != null)
+ mMediaPlayer.setDisplay(mSurfaceHolder);
+
+ AssetFileDescriptor afd = null;
try {
- mPreparing = true;
- onMediaPlayerInfoNative(MEDIA_PLAYER_PREPARING, 0, mID);
mUri = Uri.parse(path);
- if (mUri.getScheme().compareTo("assets") == 0) {
+ final boolean inAssets = (mUri.getScheme().compareTo("assets") == 0);
+ if (inAssets) {
final String asset = mUri.getPath().substring(1 /* Remove first '/' */);
- final AssetManager am = mApplicationContext.getAssets();
- final AssetFileDescriptor afd = am.openFd(asset);
+ final AssetManager am = mActivity.getAssets();
+ afd = am.openFd(asset);
final long offset = afd.getStartOffset();
final long length = afd.getLength();
FileDescriptor fd = afd.getFileDescriptor();
- setDataSource(fd, offset, length);
+ mMediaPlayer.setDataSource(fd, offset, length);
} else {
- setDataSource(mApplicationContext, mUri);
+ mMediaPlayer.setDataSource(mActivity, mUri);
}
- mInitialized = true;
- setOnPreparedListener(new MediaPlayerPreparedListener());
- prepareAsync();
+ setState(State.Initialized);
} catch (final IOException e) {
- mPreparing = false;
- onErrorNative(MEDIA_ERROR_UNKNOWN,
- /* MEDIA_ERROR_UNSUPPORTED= */ -1010,
- mID);
+ Log.d(TAG, "" + e.getMessage());
} catch (final IllegalArgumentException e) {
Log.d(TAG, "" + e.getMessage());
} catch (final SecurityException e) {
@@ -364,19 +401,36 @@ public class QtAndroidMediaPlayer extends MediaPlayer
Log.d(TAG, "" + e.getMessage());
} catch (final NullPointerException e) {
Log.d(TAG, "" + e.getMessage());
+ } finally {
+ if (afd !=null) {
+ try { afd.close(); } catch (final IOException ioe) { /* Ignore... */ }
+ }
+ if ((mState & State.Initialized) == 0) {
+ setState(State.Error);
+ onErrorNative(MediaPlayer.MEDIA_ERROR_UNKNOWN,
+ -1004 /*MEDIA_ERROR_IO*/,
+ mID);
+ return;
+ }
}
}
- @Override
+
public int getCurrentPosition()
{
int currentPosition = 0;
-
- if (!mInitialized)
+ if ((mState & (State.Idle
+ | State.Initialized
+ | State.Prepared
+ | State.Started
+ | State.Paused
+ | State.Stopped
+ | State.PlaybackCompleted)) == 0) {
return currentPosition;
+ }
try {
- currentPosition = super.getCurrentPosition();
+ currentPosition = mMediaPlayer.getCurrentPosition();
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}
@@ -384,16 +438,20 @@ public class QtAndroidMediaPlayer extends MediaPlayer
return currentPosition;
}
- @Override
+
public int getDuration()
{
int duration = 0;
-
- if (!mInitialized)
+ if ((mState & (State.Prepared
+ | State.Started
+ | State.Paused
+ | State.Stopped
+ | State.PlaybackCompleted)) == 0) {
return duration;
+ }
try {
- duration = super.getDuration();
+ duration = mMediaPlayer.getDuration();
} catch (final IllegalStateException e) {
Log.d(TAG, "" + e.getMessage());
}
@@ -414,6 +472,16 @@ public class QtAndroidMediaPlayer extends MediaPlayer
public void setVolume(int volume)
{
+ if ((mState & (State.Idle
+ | State.Initialized
+ | State.Stopped
+ | State.Prepared
+ | State.Started
+ | State.Paused
+ | State.PlaybackCompleted)) == 0) {
+ return;
+ }
+
if (volume < 0)
volume = 0;
@@ -423,7 +491,7 @@ public class QtAndroidMediaPlayer extends MediaPlayer
float newVolume = adjustVolume(volume);
try {
- super.setVolume(newVolume, newVolume);
+ mMediaPlayer.setVolume(newVolume, newVolume);
if (!mMuted)
mVolume = volume;
} catch (final IllegalStateException e) {
@@ -431,6 +499,22 @@ public class QtAndroidMediaPlayer extends MediaPlayer
}
}
+ public SurfaceHolder display()
+ {
+ return mSurfaceHolder;
+ }
+
+ public void setDisplay(SurfaceHolder sh)
+ {
+ mSurfaceHolder = sh;
+
+ if ((mState & State.Uninitialized) != 0)
+ return;
+
+ mMediaPlayer.setDisplay(mSurfaceHolder);
+ }
+
+
public int getVolume()
{
return mVolume;
@@ -447,11 +531,32 @@ public class QtAndroidMediaPlayer extends MediaPlayer
return mMuted;
}
- @Override
+
public void reset()
{
- mInitialized = false;
- super.reset();
+ if ((mState & (State.Idle
+ | State.Initialized
+ | State.Prepared
+ | State.Started
+ | State.Paused
+ | State.Stopped
+ | State.PlaybackCompleted
+ | State.Error)) == 0) {
+ return;
+ }
+
+ mMediaPlayer.reset();
+ setState(State.Idle);
}
+ public void release()
+ {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+
+ setState(State.Uninitialized);
+ }
}
diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h
index 6e4a32e3f..8bf6be6cb 100644
--- a/src/plugins/android/src/common/qandroidvideooutput.h
+++ b/src/plugins/android/src/common/qandroidvideooutput.h
@@ -60,6 +60,7 @@ public:
virtual void setVideoSize(const QSize &) { }
virtual void stop() { }
+ virtual void reset() { }
// signals:
// void readyChanged(bool);
diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
index 55f71d735..b737e8a42 100644
--- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
+++ b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
@@ -122,20 +122,8 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
QAndroidVideoRendererControl::~QAndroidVideoRendererControl()
{
- if (m_surfaceTexture) {
- m_surfaceTexture->callMethod<void>("release");
- delete m_surfaceTexture;
- m_surfaceTexture = 0;
- }
- if (m_androidSurface) {
- m_androidSurface->callMethod<void>("release");
- delete m_androidSurface;
- m_androidSurface = 0;
- }
- if (m_surfaceHolder) {
- delete m_surfaceHolder;
- m_surfaceHolder = 0;
- }
+ clearSurfaceTexture();
+
if (m_glDeleter)
m_glDeleter->deleteLater();
}
@@ -202,6 +190,24 @@ bool QAndroidVideoRendererControl::initSurfaceTexture()
return m_surfaceTexture != 0;
}
+void QAndroidVideoRendererControl::clearSurfaceTexture()
+{
+ if (m_surfaceTexture) {
+ m_surfaceTexture->callMethod<void>("release");
+ delete m_surfaceTexture;
+ m_surfaceTexture = 0;
+ }
+ if (m_androidSurface) {
+ m_androidSurface->callMethod<void>("release");
+ delete m_androidSurface;
+ m_androidSurface = 0;
+ }
+ if (m_surfaceHolder) {
+ delete m_surfaceHolder;
+ m_surfaceHolder = 0;
+ }
+}
+
jobject QAndroidVideoRendererControl::surfaceHolder()
{
if (!initSurfaceTexture())
@@ -245,6 +251,11 @@ void QAndroidVideoRendererControl::stop()
m_nativeSize = QSize();
}
+void QAndroidVideoRendererControl::reset()
+{
+ clearSurfaceTexture();
+}
+
void QAndroidVideoRendererControl::onFrameAvailable()
{
if (!m_nativeSize.isValid() || !m_surface)
diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.h b/src/plugins/android/src/common/qandroidvideorendercontrol.h
index 6ce1e2dd4..56407d5de 100644
--- a/src/plugins/android/src/common/qandroidvideorendercontrol.h
+++ b/src/plugins/android/src/common/qandroidvideorendercontrol.h
@@ -92,6 +92,7 @@ public:
bool isReady() Q_DECL_OVERRIDE;
void setVideoSize(const QSize &size) Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
+ void reset() Q_DECL_OVERRIDE;
void customEvent(QEvent *) Q_DECL_OVERRIDE;
@@ -107,6 +108,7 @@ private:
void createGLResources();
QMutex m_mutex;
+ void clearSurfaceTexture();
QAbstractVideoSurface *m_surface;
QSize m_nativeSize;
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
index 7b0c58277..4b836ddf4 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -57,25 +57,31 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
mAudioAvailable(false),
mVideoAvailable(false),
mBuffering(false),
- mMediaPlayerReady(false),
+ mState(JMediaPlayer::Uninitialized),
+ mPendingState(-1),
mPendingPosition(-1),
- mPendingSetMedia(false)
-{
- connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
- this, SLOT(onBufferChanged(qint32)));
- connect(mMediaPlayer, SIGNAL(info(qint32,qint32)),
- this, SLOT(onInfo(qint32,qint32)));
- connect(mMediaPlayer, SIGNAL(error(qint32,qint32)),
- this, SLOT(onError(qint32,qint32)));
- connect(mMediaPlayer, SIGNAL(mediaPlayerInfo(qint32,qint32)),
- this, SLOT(onMediaPlayerInfo(qint32,qint32)));
- connect(mMediaPlayer, SIGNAL(videoSizeChanged(qint32,qint32)),
- this, SLOT(onVideoSizeChanged(qint32,qint32)));
+ mPendingSetMedia(false),
+ mPendingVolume(-1),
+ mPendingMute(-1)
+{
+ connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
+ this,SLOT(onBufferingChanged(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(stateChanged(qint32)),
+ this,SLOT(onStateChanged(qint32)));
+ connect(mMediaPlayer,SIGNAL(videoSizeChanged(qint32,qint32)),
+ this,SLOT(onVideoSizeChanged(qint32,qint32)));
+ connect(mMediaPlayer,SIGNAL(progressChanged(qint64)),
+ this,SIGNAL(positionChanged(qint64)));
+ connect(mMediaPlayer,SIGNAL(durationChanged(qint64)),
+ this,SIGNAL(durationChanged(qint64)));
}
QAndroidMediaPlayerControl::~QAndroidMediaPlayerControl()
{
- mMediaPlayer->stop();
mMediaPlayer->release();
delete mMediaPlayer;
}
@@ -92,18 +98,33 @@ QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const
qint64 QAndroidMediaPlayerControl::duration() const
{
- return (mCurrentMediaStatus == QMediaPlayer::InvalidMedia
- || mCurrentMediaStatus == QMediaPlayer::NoMedia
- || !mMediaPlayerReady) ? 0
- : mMediaPlayer->getDuration();
+ if ((mState & (JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::Stopped
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ return 0;
+ }
+
+ return mMediaPlayer->getDuration();
}
qint64 QAndroidMediaPlayerControl::position() const
{
- if (!mMediaPlayerReady)
- return mPendingPosition < 0 ? 0 : mPendingPosition;
+ if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
+ return duration();
+
+ if ((mState & (JMediaPlayer::Idle
+ | JMediaPlayer::Initialized
+ | JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::Stopped
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ return (mPendingPosition == -1) ? 0 : mPendingPosition;
+ }
- return mMediaPlayer->getCurrentPosition();
+ return (mCurrentState == QMediaPlayer::StoppedState) ? 0 : mMediaPlayer->getCurrentPosition();
}
void QAndroidMediaPlayerControl::setPosition(qint64 position)
@@ -113,35 +134,88 @@ void QAndroidMediaPlayerControl::setPosition(qint64 position)
const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
- if (!mMediaPlayerReady) {
- mPendingPosition = seekPosition;
- Q_EMIT positionChanged(seekPosition);
+ if ((mState & (JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ if (mPendingPosition != seekPosition) {
+ mPendingPosition = seekPosition;
+ Q_EMIT positionChanged(seekPosition);
+ }
return;
}
+ if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+
mMediaPlayer->seekTo(seekPosition);
- mPendingPosition = -1;
+
+ if (mPendingPosition != -1) {
+ mPendingPosition = -1;
+ }
+
+ Q_EMIT positionChanged(seekPosition);
}
int QAndroidMediaPlayerControl::volume() const
{
- return mMediaPlayer->volume();
+ return (mPendingVolume == -1) ? mMediaPlayer->volume() : mPendingVolume;
}
void QAndroidMediaPlayerControl::setVolume(int volume)
{
+ if ((mState & (JMediaPlayer::Idle
+ | JMediaPlayer::Initialized
+ | JMediaPlayer::Stopped
+ | JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ if (mPendingVolume != volume) {
+ mPendingVolume = volume;
+ Q_EMIT volumeChanged(volume);
+ }
+ return;
+ }
+
mMediaPlayer->setVolume(volume);
+
+ if (mPendingVolume != -1) {
+ mPendingVolume = -1;
+ return;
+ }
+
Q_EMIT volumeChanged(volume);
}
bool QAndroidMediaPlayerControl::isMuted() const
{
- return mMediaPlayer->isMuted();
+ return (mPendingMute == -1) ? mMediaPlayer->isMuted() : (mPendingMute == 1);
}
void QAndroidMediaPlayerControl::setMuted(bool muted)
{
+ if ((mState & (JMediaPlayer::Idle
+ | JMediaPlayer::Initialized
+ | JMediaPlayer::Stopped
+ | JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ if (mPendingMute != muted) {
+ mPendingMute = muted;
+ Q_EMIT mutedChanged(muted);
+ }
+ return;
+ }
+
mMediaPlayer->setMuted(muted);
+
+ if (mPendingMute != -1) {
+ mPendingMute = -1;
+ return;
+ }
+
Q_EMIT mutedChanged(muted);
}
@@ -208,10 +282,21 @@ const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
QIODevice *stream)
{
- mMediaContent = mediaContent;
- mMediaStream = stream;
+ const bool reloading = (mMediaContent == mediaContent);
+
+ if (!reloading) {
+ mMediaContent = mediaContent;
+ mMediaStream = stream;
+ }
- if (mVideoOutput && !mMediaPlayer->display()) {
+ mMediaPlayer->release();
+
+ if (mediaContent.isNull()) {
+ setMediaStatus(QMediaPlayer::NoMedia);
+ return;
+ }
+
+ if (mVideoOutput && !mVideoOutput->isReady()) {
// if a video output is set but the video texture is not ready, delay loading the media
// since it can cause problems on some hardware
mPendingSetMedia = true;
@@ -229,68 +314,88 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
mediaPath = url.toString();
}
- if (!mediaPath.isEmpty())
- mMediaPlayer->setDataSource(mediaPath);
- else
- setMediaStatus(QMediaPlayer::NoMedia);
+ if (mVideoSize.isValid() && mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+
+ if (!mMediaPlayer->display() && mVideoOutput)
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ mMediaPlayer->setDataSource(mediaPath);
+ mMediaPlayer->prepareAsync();
- Q_EMIT mediaChanged(mMediaContent);
+ if (!reloading)
+ Q_EMIT mediaChanged(mMediaContent);
resetBufferingProgress();
-
- // reset some properties
- setAudioAvailable(false);
- setVideoAvailable(false);
- setSeekable(true);
}
void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
{
- if (mVideoOutput)
+ if (mVideoOutput) {
+ mMediaPlayer->setDisplay(0);
mVideoOutput->stop();
+ mVideoOutput->reset();
+ }
mVideoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput);
- if (mVideoOutput && !mMediaPlayer->display()) {
- if (mVideoOutput->isReady())
- mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
- else
- connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
- }
+ if (!mVideoOutput)
+ return;
+
+ if (mVideoOutput->isReady())
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+
+ connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
}
void QAndroidMediaPlayerControl::play()
{
- if (!mMediaPlayerReady) {
+ // We need to prepare the mediaplayer again.
+ if ((mState & JMediaPlayer::Stopped) && !mMediaContent.isNull()) {
+ setMedia(mMediaContent, mMediaStream);
+ }
+
+ setState(QMediaPlayer::PlayingState);
+
+ if ((mState & (JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
mPendingState = QMediaPlayer::PlayingState;
- if (mCurrentState == QMediaPlayer::StoppedState
- && !mMediaContent.isNull()
- && mCurrentMediaStatus != QMediaPlayer::LoadingMedia
- && !mPendingSetMedia) {
- setMedia(mMediaContent, 0);
- }
return;
}
mMediaPlayer->play();
- setState(QMediaPlayer::PlayingState);
}
void QAndroidMediaPlayerControl::pause()
{
- if (!mMediaPlayerReady) {
+ setState(QMediaPlayer::PausedState);
+
+ if ((mState & (JMediaPlayer::Started
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
mPendingState = QMediaPlayer::PausedState;
return;
}
mMediaPlayer->pause();
- setState(QMediaPlayer::PausedState);
}
void QAndroidMediaPlayerControl::stop()
{
- mMediaPlayer->stop();
setState(QMediaPlayer::StoppedState);
+
+ if ((mState & (JMediaPlayer::Prepared
+ | JMediaPlayer::Started
+ | JMediaPlayer::Stopped
+ | JMediaPlayer::Paused
+ | JMediaPlayer::PlaybackCompleted)) == 0) {
+ if ((mState & (JMediaPlayer::Idle | JMediaPlayer::Uninitialized | JMediaPlayer::Error)) == 0)
+ mPendingState = QMediaPlayer::StoppedState;
+ return;
+ }
+
+ mMediaPlayer->stop();
}
void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
@@ -310,8 +415,8 @@ void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
setMediaStatus(QMediaPlayer::StalledMedia);
break;
case JMediaPlayer::MEDIA_INFO_BUFFERING_END:
- setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia);
- flushPendingStates();
+ if (mCurrentState != QMediaPlayer::StoppedState)
+ flushPendingStates();
break;
case JMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING:
break;
@@ -324,41 +429,6 @@ void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
}
}
-void QAndroidMediaPlayerControl::onMediaPlayerInfo(qint32 what, qint32 extra)
-{
- switch (what) {
- case JMediaPlayer::MEDIA_PLAYER_INVALID_STATE:
- setError(what, QStringLiteral("Error: Invalid state"));
- break;
- case JMediaPlayer::MEDIA_PLAYER_PREPARING:
- setMediaStatus(QMediaPlayer::LoadingMedia);
- setState(QMediaPlayer::StoppedState);
- break;
- case JMediaPlayer::MEDIA_PLAYER_READY:
- setMediaStatus(QMediaPlayer::LoadedMedia);
- if (mBuffering) {
- setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
- : QMediaPlayer::BufferingMedia);
- } else {
- onBufferChanged(100);
- }
- setAudioAvailable(true);
- mMediaPlayerReady = true;
- flushPendingStates();
- break;
- case JMediaPlayer::MEDIA_PLAYER_DURATION:
- Q_EMIT durationChanged(extra);
- break;
- case JMediaPlayer::MEDIA_PLAYER_PROGRESS:
- Q_EMIT positionChanged(extra);
- break;
- case JMediaPlayer::MEDIA_PLAYER_FINISHED:
- stop();
- setMediaStatus(QMediaPlayer::EndOfMedia);
- break;
- }
-}
-
void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
{
QString errorString;
@@ -372,6 +442,10 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
errorString = QLatin1String("Error: Server died");
error = QMediaPlayer::ServiceMissingError;
break;
+ case JMediaPlayer::MEDIA_ERROR_INVALID_STATE:
+ errorString = QLatin1String("Error: Invalid state");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
}
switch (extra) {
@@ -398,12 +472,16 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
error = QMediaPlayer::FormatError;
setMediaStatus(QMediaPlayer::InvalidMedia);
break;
+ case JMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
+ errorString += QLatin1String(" (Unknown error/Insufficient resources)");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
}
- setError(error, errorString);
+ Q_EMIT QMediaPlayerControl::error(error, errorString);
}
-void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent)
+void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent)
{
mBuffering = percent != 100;
mBufferPercent = percent;
@@ -411,8 +489,8 @@ void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent)
updateAvailablePlaybackRanges();
- if (mBufferPercent == 100)
- setMediaStatus(QMediaPlayer::BufferedMedia);
+ if (mCurrentState != QMediaPlayer::StoppedState)
+ setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
}
void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
@@ -429,27 +507,98 @@ void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
mVideoOutput->setVideoSize(mVideoSize);
}
-void QAndroidMediaPlayerControl::onVideoOutputReady(bool ready)
+void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
{
- if (!mMediaPlayer->display() && mVideoOutput && ready) {
- mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ // If reloading, don't report state changes unless the new state is Prepared or Error.
+ if ((mState & JMediaPlayer::Stopped) && !(state & (JMediaPlayer::Prepared | JMediaPlayer::Error)))
+ return;
+
+ mState = state;
+ switch (mState) {
+ case JMediaPlayer::Idle:
+ break;
+ case JMediaPlayer::Initialized:
+ break;
+ case JMediaPlayer::Preparing:
+ setMediaStatus(QMediaPlayer::LoadingMedia);
+ break;
+ case JMediaPlayer::Prepared:
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ onBufferingChanged(100);
+ }
+ setAudioAvailable(true);
flushPendingStates();
+ break;
+ case JMediaPlayer::Started:
+ setState(QMediaPlayer::PlayingState);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ setMediaStatus(QMediaPlayer::BufferedMedia);
+ }
+ break;
+ case JMediaPlayer::Paused:
+ setState(QMediaPlayer::PausedState);
+ break;
+ case JMediaPlayer::Error:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::UnknownMediaStatus);
+ mMediaPlayer->release();
+ break;
+ case JMediaPlayer::Stopped:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ setPosition(0);
+ break;
+ case JMediaPlayer::PlaybackCompleted:
+ setState(QMediaPlayer::StoppedState);
+ setPosition(0);
+ setMediaStatus(QMediaPlayer::EndOfMedia);
+ break;
+ case JMediaPlayer::Uninitialized:
+ // reset some properties
+ resetBufferingProgress();
+ mPendingPosition = -1;
+ mPendingSetMedia = false;
+ mPendingState = -1;
+
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ setSeekable(true);
+ break;
+ default:
+ break;
+ }
+
+ if ((mState & (JMediaPlayer::Stopped | JMediaPlayer::Uninitialized)) != 0) {
+ mMediaPlayer->setDisplay(0);
+ if (mVideoOutput) {
+ mVideoOutput->stop();
+ mVideoOutput->reset();
+ }
}
}
+void QAndroidMediaPlayerControl::onVideoOutputReady(bool ready)
+{
+ if (!mMediaPlayer->display() && mVideoOutput && ready)
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+
+ flushPendingStates();
+}
+
void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
{
if (mCurrentState == state)
return;
- if (state == QMediaPlayer::StoppedState) {
- if (mVideoOutput)
- mVideoOutput->stop();
- resetBufferingProgress();
- mMediaPlayerReady = false;
- mPendingPosition = -1;
- Q_EMIT positionChanged(0);
- }
+ if (mCurrentState == QMediaPlayer::StoppedState && state == QMediaPlayer::PausedState)
+ return;
mCurrentState = state;
Q_EMIT stateChanged(mCurrentState);
@@ -463,17 +612,13 @@ void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status
if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
Q_EMIT durationChanged(0);
+ if (status == QMediaPlayer::EndOfMedia)
+ Q_EMIT durationChanged(duration());
+
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)
@@ -515,15 +660,23 @@ void QAndroidMediaPlayerControl::resetBufferingProgress()
void QAndroidMediaPlayerControl::flushPendingStates()
{
if (mPendingSetMedia) {
- setMedia(mMediaContent, 0);
mPendingSetMedia = false;
+ setMedia(mMediaContent, 0);
return;
}
- switch (mPendingState) {
+ const int newState = mPendingState;
+ mPendingState = -1;
+
+ if (mPendingPosition != -1)
+ setPosition(mPendingPosition);
+ if (mPendingVolume != -1)
+ setVolume(mPendingVolume);
+ if (mPendingMute != -1)
+ setMuted((mPendingMute == 1));
+
+ switch (newState) {
case QMediaPlayer::PlayingState:
- if (mPendingPosition > -1)
- setPosition(mPendingPosition);
play();
break;
case QMediaPlayer::PausedState:
@@ -532,6 +685,8 @@ void QAndroidMediaPlayerControl::flushPendingStates()
case QMediaPlayer::StoppedState:
stop();
break;
+ default:
+ break;
}
}
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
index fadac3c19..1be3b4428 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
@@ -93,9 +93,9 @@ private Q_SLOTS:
void onVideoOutputReady(bool ready);
void onError(qint32 what, qint32 extra);
void onInfo(qint32 what, qint32 extra);
- void onMediaPlayerInfo(qint32 what, qint32 extra);
- void onBufferChanged(qint32 percent);
+ void onBufferingChanged(qint32 percent);
void onVideoSizeChanged(qint32 width, qint32 height);
+ void onStateChanged(qint32 state);
private:
JMediaPlayer *mMediaPlayer;
@@ -111,15 +111,16 @@ private:
QSize mVideoSize;
bool mBuffering;
QMediaTimeRange mAvailablePlaybackRange;
- bool mMediaPlayerReady;
- QMediaPlayer::State mPendingState;
+ int mState;
+ int mPendingState;
qint64 mPendingPosition;
bool mPendingSetMedia;
+ int mPendingVolume;
+ int mPendingMute;
QScopedPointer<QTemporaryFile> mTempFile;
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);
diff --git a/src/plugins/android/src/wrappers/jmediaplayer.cpp b/src/plugins/android/src/wrappers/jmediaplayer.cpp
index 3d7f7f9d1..de86cd041 100644
--- a/src/plugins/android/src/wrappers/jmediaplayer.cpp
+++ b/src/plugins/android/src/wrappers/jmediaplayer.cpp
@@ -46,135 +46,113 @@
#include <QtCore/private/qjnihelpers_p.h>
#include <QMap>
-namespace {
-
-jclass mediaPlayerClass = 0;
-
-QMap<jlong, JMediaPlayer *> mplayers;
-
-}
+static jclass mediaPlayerClass = Q_NULLPTR;
+typedef QMap<jlong, JMediaPlayer *> MediaPlayerMap;
+Q_GLOBAL_STATIC(MediaPlayerMap, mediaPlayers)
QT_BEGIN_NAMESPACE
-bool JMediaPlayer::mActivitySet = false;
-
JMediaPlayer::JMediaPlayer()
: QObject()
- , QJNIObjectPrivate(mediaPlayerClass, "(J)V", reinterpret_cast<jlong>(this))
- , mId(reinterpret_cast<jlong>(this))
- , mDisplay(0)
{
- mplayers.insert(mId, this);
-
- if (!mActivitySet) {
- QJNIObjectPrivate::callStaticMethod<void>(mediaPlayerClass,
- "setActivity",
- "(Landroid/app/Activity;)V",
- QtAndroidPrivate::activity());
- mActivitySet = true;
- }
+
+ const jlong id = reinterpret_cast<jlong>(this);
+ mMediaPlayer = QJNIObjectPrivate(mediaPlayerClass,
+ "(Landroid/app/Activity;J)V",
+ QtAndroidPrivate::activity(),
+ id);
+ (*mediaPlayers)[id] = this;
}
JMediaPlayer::~JMediaPlayer()
{
- mplayers.remove(mId);
+ mediaPlayers->remove(reinterpret_cast<jlong>(this));
}
void JMediaPlayer::release()
{
- callMethod<void>("release");
+ mMediaPlayer.callMethod<void>("release");
}
-void JMediaPlayer::onError(qint32 what, qint32 extra)
+void JMediaPlayer::reset()
{
- 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);
+ mMediaPlayer.callMethod<void>("reset");
}
int JMediaPlayer::getCurrentPosition()
{
- return callMethod<jint>("getCurrentPosition");
+ return mMediaPlayer.callMethod<jint>("getCurrentPosition");
}
int JMediaPlayer::getDuration()
{
- return callMethod<jint>("getDuration");
+ return mMediaPlayer.callMethod<jint>("getDuration");
}
bool JMediaPlayer::isPlaying()
{
- return callMethod<jboolean>("isPlaying");
+ return mMediaPlayer.callMethod<jboolean>("isPlaying");
}
int JMediaPlayer::volume()
{
- return callMethod<jint>("getVolume");
+ return mMediaPlayer.callMethod<jint>("getVolume");
}
bool JMediaPlayer::isMuted()
{
- return callMethod<jboolean>("isMuted");
+ return mMediaPlayer.callMethod<jboolean>("isMuted");
+}
+
+jobject JMediaPlayer::display()
+{
+ return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object();
}
void JMediaPlayer::play()
{
- callMethod<void>("start");
+ mMediaPlayer.callMethod<void>("start");
}
void JMediaPlayer::pause()
{
- callMethod<void>("pause");
+ mMediaPlayer.callMethod<void>("pause");
}
void JMediaPlayer::stop()
{
- callMethod<void>("stop");
+ mMediaPlayer.callMethod<void>("stop");
}
void JMediaPlayer::seekTo(qint32 msec)
{
- callMethod<void>("seekTo", "(I)V", jint(msec));
+ mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec));
}
void JMediaPlayer::setMuted(bool mute)
{
- callMethod<void>("mute", "(Z)V", jboolean(mute));
+ mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute));
}
void JMediaPlayer::setDataSource(const QString &path)
{
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(path);
- callMethod<void>("setMediaPath", "(Ljava/lang/String;)V", string.object());
+ mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object());
+}
+
+void JMediaPlayer::prepareAsync()
+{
+ mMediaPlayer.callMethod<void>("prepareAsync");
}
void JMediaPlayer::setVolume(int volume)
{
- callMethod<void>("setVolume", "(I)V", jint(volume));
+ mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume));
}
void JMediaPlayer::setDisplay(jobject surfaceHolder)
{
- mDisplay = surfaceHolder;
- callMethod<void>("setDisplay", "(Landroid/view/SurfaceHolder;)V", mDisplay);
+ mMediaPlayer.callMethod<void>("setDisplay", "(Landroid/view/SurfaceHolder;)V", surfaceHolder);
}
QT_END_NAMESPACE
@@ -183,44 +161,66 @@ static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlon
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- JMediaPlayer *const mp = mplayers[id];
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
if (!mp)
return;
- mp->onError(what, extra);
+ Q_EMIT mp->error(what, extra);
}
static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- JMediaPlayer *const mp = mplayers[id];
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
+ if (!mp)
+ return;
+
+ Q_EMIT mp->bufferingChanged(percent);
+}
+
+static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
+ if (!mp)
+ return;
+
+ Q_EMIT mp->progressChanged(progress);
+}
+
+static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
if (!mp)
return;
- mp->onBufferingUpdate(percent);
+ Q_EMIT mp->durationChanged(duration);
}
static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- JMediaPlayer *const mp = mplayers[id];
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
if (!mp)
return;
- mp->onInfo(what, extra);
+ Q_EMIT mp->info(what, extra);
}
-static void onMediaPlayerInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
+static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- JMediaPlayer *const mp = mplayers[id];
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
if (!mp)
return;
- mp->onMediaPlayerInfo(what, extra);
+ Q_EMIT mp->stateChanged(state);
}
static void onVideoSizeChangedNative(JNIEnv *env,
@@ -231,11 +231,11 @@ static void onVideoSizeChangedNative(JNIEnv *env,
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- JMediaPlayer *const mp = mplayers[id];
+ JMediaPlayer *const mp = (*mediaPlayers)[id];
if (!mp)
return;
- mp->onVideoSizeChanged(width, height);
+ Q_EMIT mp->videoSizeChanged(width, height);
}
QT_BEGIN_NAMESPACE
@@ -250,9 +250,11 @@ bool JMediaPlayer::initJNI(JNIEnv *env)
JNINativeMethod methods[] = {
{"onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative)},
{"onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative)},
+ {"onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative)},
+ {"onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative)},
{"onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative)},
- {"onMediaPlayerInfoNative", "(IIJ)V", reinterpret_cast<void *>(onMediaPlayerInfoNative)},
- {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast<void *>(onVideoSizeChangedNative)}
+ {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast<void *>(onVideoSizeChangedNative)},
+ {"onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative)}
};
if (env->RegisterNatives(mediaPlayerClass,
diff --git a/src/plugins/android/src/wrappers/jmediaplayer.h b/src/plugins/android/src/wrappers/jmediaplayer.h
index c737cfa26..cd469e677 100644
--- a/src/plugins/android/src/wrappers/jmediaplayer.h
+++ b/src/plugins/android/src/wrappers/jmediaplayer.h
@@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
-class JMediaPlayer : public QObject, public QJNIObjectPrivate
+class JMediaPlayer : public QObject
{
Q_OBJECT
public:
@@ -59,12 +59,14 @@ public:
// What
MEDIA_ERROR_UNKNOWN = 1,
MEDIA_ERROR_SERVER_DIED = 100,
+ MEDIA_ERROR_INVALID_STATE = -38, // Undocumented
// 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
+ MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN = -2147483648 // Undocumented
};
enum MediaInfo
@@ -79,24 +81,29 @@ public:
MEDIA_INFO_METADATA_UPDATE = 802
};
- enum MediaPlayerInfo
+ enum MediaPlayerState
{
- 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
+ Uninitialized = 0x1, /* End */
+ Idle = 0x2,
+ Preparing = 0x4,
+ Prepared = 0x8,
+ Initialized = 0x10,
+ Started = 0x20,
+ Stopped = 0x40,
+ Paused = 0x80,
+ PlaybackCompleted = 0x100,
+ Error = 0x200
};
void release();
+ void reset();
int getCurrentPosition();
int getDuration();
bool isPlaying();
int volume();
bool isMuted();
- jobject display() { return mDisplay; }
+ jobject display();
void play();
void pause();
@@ -104,30 +111,23 @@ public:
void seekTo(qint32 msec);
void setMuted(bool mute);
void setDataSource(const QString &path);
+ void prepareAsync();
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 bufferingChanged(qint32 percent);
+ void durationChanged(qint64 duration);
+ void progressChanged(qint64 progress);
+ void stateChanged(qint32 state);
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;
+ QJNIObjectPrivate mMediaPlayer;
};
QT_END_NAMESPACE