From dcf11bba3ccfd2a2a44556b1947406947aaa2be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Thu, 20 Feb 2014 14:25:35 +0100 Subject: 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 --- .../android/multimedia/QtAndroidMediaPlayer.java | 309 ++++++++++------ .../android/src/common/qandroidvideooutput.h | 1 + .../src/common/qandroidvideorendercontrol.cpp | 39 +- .../src/common/qandroidvideorendercontrol.h | 2 + .../src/mediaplayer/qandroidmediaplayercontrol.cpp | 397 ++++++++++++++------- .../src/mediaplayer/qandroidmediaplayercontrol.h | 11 +- src/plugins/android/src/wrappers/jmediaplayer.cpp | 146 ++++---- src/plugins/android/src/wrappers/jmediaplayer.h | 46 +-- 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("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; - } + 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("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; + } +} + 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(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 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 #include -namespace { - -jclass mediaPlayerClass = 0; - -QMap mplayers; - -} +static jclass mediaPlayerClass = Q_NULLPTR; +typedef QMap MediaPlayerMap; +Q_GLOBAL_STATIC(MediaPlayerMap, mediaPlayers) QT_BEGIN_NAMESPACE -bool JMediaPlayer::mActivitySet = false; - JMediaPlayer::JMediaPlayer() : QObject() - , QJNIObjectPrivate(mediaPlayerClass, "(J)V", reinterpret_cast(this)) - , mId(reinterpret_cast(this)) - , mDisplay(0) { - mplayers.insert(mId, this); - - if (!mActivitySet) { - QJNIObjectPrivate::callStaticMethod(mediaPlayerClass, - "setActivity", - "(Landroid/app/Activity;)V", - QtAndroidPrivate::activity()); - mActivitySet = true; - } + + const jlong id = reinterpret_cast(this); + mMediaPlayer = QJNIObjectPrivate(mediaPlayerClass, + "(Landroid/app/Activity;J)V", + QtAndroidPrivate::activity(), + id); + (*mediaPlayers)[id] = this; } JMediaPlayer::~JMediaPlayer() { - mplayers.remove(mId); + mediaPlayers->remove(reinterpret_cast(this)); } void JMediaPlayer::release() { - callMethod("release"); + mMediaPlayer.callMethod("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("reset"); } int JMediaPlayer::getCurrentPosition() { - return callMethod("getCurrentPosition"); + return mMediaPlayer.callMethod("getCurrentPosition"); } int JMediaPlayer::getDuration() { - return callMethod("getDuration"); + return mMediaPlayer.callMethod("getDuration"); } bool JMediaPlayer::isPlaying() { - return callMethod("isPlaying"); + return mMediaPlayer.callMethod("isPlaying"); } int JMediaPlayer::volume() { - return callMethod("getVolume"); + return mMediaPlayer.callMethod("getVolume"); } bool JMediaPlayer::isMuted() { - return callMethod("isMuted"); + return mMediaPlayer.callMethod("isMuted"); +} + +jobject JMediaPlayer::display() +{ + return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object(); } void JMediaPlayer::play() { - callMethod("start"); + mMediaPlayer.callMethod("start"); } void JMediaPlayer::pause() { - callMethod("pause"); + mMediaPlayer.callMethod("pause"); } void JMediaPlayer::stop() { - callMethod("stop"); + mMediaPlayer.callMethod("stop"); } void JMediaPlayer::seekTo(qint32 msec) { - callMethod("seekTo", "(I)V", jint(msec)); + mMediaPlayer.callMethod("seekTo", "(I)V", jint(msec)); } void JMediaPlayer::setMuted(bool mute) { - callMethod("mute", "(Z)V", jboolean(mute)); + mMediaPlayer.callMethod("mute", "(Z)V", jboolean(mute)); } void JMediaPlayer::setDataSource(const QString &path) { QJNIObjectPrivate string = QJNIObjectPrivate::fromString(path); - callMethod("setMediaPath", "(Ljava/lang/String;)V", string.object()); + mMediaPlayer.callMethod("setDataSource", "(Ljava/lang/String;)V", string.object()); +} + +void JMediaPlayer::prepareAsync() +{ + mMediaPlayer.callMethod("prepareAsync"); } void JMediaPlayer::setVolume(int volume) { - callMethod("setVolume", "(I)V", jint(volume)); + mMediaPlayer.callMethod("setVolume", "(I)V", jint(volume)); } void JMediaPlayer::setDisplay(jobject surfaceHolder) { - mDisplay = surfaceHolder; - callMethod("setDisplay", "(Landroid/view/SurfaceHolder;)V", mDisplay); + mMediaPlayer.callMethod("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(onErrorNative)}, {"onBufferingUpdateNative", "(IJ)V", reinterpret_cast(onBufferingUpdateNative)}, + {"onProgressUpdateNative", "(IJ)V", reinterpret_cast(onProgressUpdateNative)}, + {"onDurationChangedNative", "(IJ)V", reinterpret_cast(onDurationChangedNative)}, {"onInfoNative", "(IIJ)V", reinterpret_cast(onInfoNative)}, - {"onMediaPlayerInfoNative", "(IIJ)V", reinterpret_cast(onMediaPlayerInfoNative)}, - {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast(onVideoSizeChangedNative)} + {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast(onVideoSizeChangedNative)}, + {"onStateChangedNative", "(IJ)V", reinterpret_cast(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 -- cgit v1.2.1