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>
This commit is contained in:
committed by
The Qt Project
parent
ddd22fab2b
commit
dcf11bba3c
@@ -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;
|
||||
private SurfaceHolder mSurfaceHolder = 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 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;
|
||||
}
|
||||
|
||||
private void setState(int state)
|
||||
{
|
||||
if (mState == state)
|
||||
return;
|
||||
|
||||
mState = state;
|
||||
|
||||
onStateChangedNative(mState, mID);
|
||||
}
|
||||
|
||||
|
||||
private void init()
|
||||
{
|
||||
if (mMediaPlayer == null) {
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
setState(State.Idle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
if (!mInitialized) {
|
||||
onMediaPlayerInfoNative(MEDIA_PLAYER_INVALID_STATE, 0, mID);
|
||||
if ((mState & (State.Prepared
|
||||
| State.Started
|
||||
| State.Paused
|
||||
| State.PlaybackCompleted)) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mApplicationContext == null)
|
||||
return;
|
||||
|
||||
if (mPreparing)
|
||||
return;
|
||||
|
||||
if (isPlaying())
|
||||
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 (mInitialized)
|
||||
reset();
|
||||
if ((mState & (State.Initialized | State.Stopped)) == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
mPreparing = true;
|
||||
onMediaPlayerInfoNative(MEDIA_PLAYER_PREPARING, 0, mID);
|
||||
mMediaPlayer.prepareAsync();
|
||||
setState(State.Preparing);
|
||||
} catch (final IllegalStateException e) {
|
||||
Log.d(TAG, "" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void setDataSource(final String path)
|
||||
{
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
virtual void setVideoSize(const QSize &) { }
|
||||
virtual void stop() { }
|
||||
virtual void reset() { }
|
||||
|
||||
// signals:
|
||||
// void readyChanged(bool);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
mPendingSetMedia(false),
|
||||
mPendingVolume(-1),
|
||||
mPendingMute(-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)));
|
||||
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();
|
||||
|
||||
return mMediaPlayer->getCurrentPosition();
|
||||
if ((mState & (JMediaPlayer::Idle
|
||||
| JMediaPlayer::Initialized
|
||||
| JMediaPlayer::Prepared
|
||||
| JMediaPlayer::Started
|
||||
| JMediaPlayer::Paused
|
||||
| JMediaPlayer::Stopped
|
||||
| JMediaPlayer::PlaybackCompleted)) == 0) {
|
||||
return (mPendingPosition == -1) ? 0 : mPendingPosition;
|
||||
}
|
||||
|
||||
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 (mVideoOutput && !mMediaPlayer->display()) {
|
||||
if (!reloading) {
|
||||
mMediaContent = mediaContent;
|
||||
mMediaStream = stream;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Q_EMIT mediaChanged(mMediaContent);
|
||||
if (!mMediaPlayer->display() && mVideoOutput)
|
||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
|
||||
mMediaPlayer->setDataSource(mediaPath);
|
||||
mMediaPlayer->prepareAsync();
|
||||
|
||||
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,12 +507,89 @@ void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
|
||||
mVideoOutput->setVideoSize(mVideoSize);
|
||||
}
|
||||
|
||||
void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
|
||||
{
|
||||
// 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) {
|
||||
if (!mMediaPlayer->display() && mVideoOutput && ready)
|
||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
|
||||
flushPendingStates();
|
||||
}
|
||||
|
||||
flushPendingStates();
|
||||
}
|
||||
|
||||
void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
|
||||
@@ -442,14 +597,8 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
mp->onBufferingUpdate(percent);
|
||||
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;
|
||||
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user