diff options
author | Bartlomiej Moskal <bartlomiej.moskal@qt.io> | 2022-09-22 20:59:40 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-10-13 23:07:08 +0000 |
commit | c4c8f90ece84f36cd6a0252391e70065e66d0e1f (patch) | |
tree | 91a3cd6212e0b885c13de8c4e8b39925e8f7c513 | |
parent | 274ade6e542b6948f8f99e2fe1a60cb6ad74573a (diff) |
Android: Fix for setting video playback rate
According to doc[0], setting playback params may change the state of the
player. Calling it on a prepared MediaPlayer with non-zero speed is
equivalent to calling start(). This behavior is not expected in the Qt
implementation. Therefore, changing the input speed should only be done
in the Playing state or instead of calling play().
In such case, QtAndroidMediaPlayer need to handle state change,
notification and start progress watcher. Therefore, setting playback
params has been moved to QtAndroidMediaPlayer.
Also playbackRate() should not rely on value from Android MediaPlayer.
The value can be wrong for example when Video is in Pause state.
[0]https://developer.android.com/reference/android/media/MediaPlayer#setPlaybackParams(android.media.PlaybackParams)
Fixes: QTBUG-104670
Change-Id: I5ce7696cf986a1e1e2aaa0035df806e172a5b7b7
Reviewed-by: Samuel Mira <samuel.mira@qt.io>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit 98a2c27bf443e42c46afc5f9e9f26f0bb5f9ad09)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
4 files changed, 47 insertions, 58 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java index cac26d357..6ddc64dc2 100644 --- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java +++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java @@ -12,6 +12,7 @@ import java.io.FileInputStream; import android.content.Context; import android.media.MediaPlayer; import android.media.MediaFormat; +import android.media.PlaybackParams; import android.media.AudioAttributes; import android.media.TimedText; import android.net.Uri; @@ -723,4 +724,28 @@ public class QtAndroidMediaPlayer Log.w(TAG, exception); } } + + public boolean setPlaybackRate(float rate) + { + PlaybackParams playbackParams = mMediaPlayer.getPlaybackParams(); + playbackParams.setSpeed(rate); + // According to discussion under the patch from QTBUG-61115: At least with DirectShow + // and GStreamer, it changes both speed and pitch. (...) need to be consistent + if (rate != 0.0) + playbackParams.setPitch(Math.abs(rate)); + + try { + mMediaPlayer.setPlaybackParams(playbackParams); + } catch (IllegalStateException | IllegalArgumentException e) { + Log.e(TAG, "Cannot set playback rate " + rate + " :" + e.toString()); + return false; + } + + if ((mState & State.Started) == 0 && mMediaPlayer.isPlaying()) { + setState(State.Started); + startProgressWatcher(); + } + + return true; + } } diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp index 1243b1e6f..1eea0a09f 100644 --- a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp +++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp @@ -213,43 +213,24 @@ void QAndroidMediaPlayer::updateAvailablePlaybackRanges() qreal QAndroidMediaPlayer::playbackRate() const { - if (mHasPendingPlaybackRate || - (mState & (AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted - | AndroidMediaPlayer::Error)) == 0) { - return mPendingPlaybackRate; - } - - return mMediaPlayer->playbackRate(); + return mCurrentPlaybackRate; } void QAndroidMediaPlayer::setPlaybackRate(qreal rate) { - if ((mState & (AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted - | AndroidMediaPlayer::Error)) == 0) { - if (mPendingPlaybackRate != rate) { - mPendingPlaybackRate = rate; + if (mState != AndroidMediaPlayer::Started) { + // If video isn't playing, changing speed rate may start it automatically + // It need to be postponed + if (mCurrentPlaybackRate != rate) { + mCurrentPlaybackRate = rate; mHasPendingPlaybackRate = true; Q_EMIT playbackRateChanged(rate); } return; } - bool succeeded = mMediaPlayer->setPlaybackRate(rate); - - if (mHasPendingPlaybackRate) { - mHasPendingPlaybackRate = false; - mPendingPlaybackRate = qreal(1.0); - if (!succeeded) - Q_EMIT playbackRateChanged(playbackRate()); - } else if (succeeded) { + if (mMediaPlayer->setPlaybackRate(rate)) { + mCurrentPlaybackRate = rate; Q_EMIT playbackRateChanged(rate); } } @@ -378,6 +359,14 @@ void QAndroidMediaPlayer::play() updateAudioDevice(); + if (mHasPendingPlaybackRate) { + mHasPendingPlaybackRate = false; + if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate)) + return; + mCurrentPlaybackRate = mMediaPlayer->playbackRate(); + Q_EMIT playbackRateChanged(mCurrentPlaybackRate); + } + mMediaPlayer->play(); } @@ -422,6 +411,10 @@ void QAndroidMediaPlayer::stop() return; } + if (mCurrentPlaybackRate != 1.) + // Playback rate need to by reapplied + mHasPendingPlaybackRate = true; + if (mVideoOutput) mVideoOutput->stop(); @@ -941,8 +934,6 @@ void QAndroidMediaPlayer::flushPendingStates() setVolume(mPendingVolume); if (mPendingMute != -1) setMuted((mPendingMute == 1)); - if (mHasPendingPlaybackRate) - setPlaybackRate(mPendingPlaybackRate); switch (newState) { case QMediaPlayer::PlayingState: diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h index a9ce78992..08d1a33fc 100644 --- a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h +++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h @@ -102,7 +102,7 @@ private: int mPendingMute = -1; bool mReloadingMedia = false; int mActiveStateChangeNotifiers = 0; - qreal mPendingPlaybackRate = 1.; + qreal mCurrentPlaybackRate = 1.; bool mHasPendingPlaybackRate = false; // we need this because the rate can theoretically be negative QMap<TrackType, QList<QAndroidMetaData>> mTracksMetadata; diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp index 3476d2676..58a546710 100644 --- a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp +++ b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp @@ -260,34 +260,7 @@ bool AndroidMediaPlayer::setPlaybackRate(qreal rate) return false; } - QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", - "()Landroid/media/MediaPlayer;"); - if (player.isValid()) { - QJniObject playbackParams = player.callObjectMethod("getPlaybackParams", - "()Landroid/media/PlaybackParams;"); - if (playbackParams.isValid()) { - playbackParams.callObjectMethod("setSpeed", "(F)Landroid/media/PlaybackParams;", - jfloat(rate)); - // pitch can only be > 0 - if (!qFuzzyIsNull(rate)) - playbackParams.callObjectMethod("setPitch", "(F)Landroid/media/PlaybackParams;", - jfloat(qAbs(rate))); - - QJniEnvironment env; - auto methodId = env->GetMethodID(player.objectClass(), "setPlaybackParams", - "(Landroid/media/PlaybackParams;)V"); - env->CallVoidMethod(player.object(), methodId, playbackParams.object()); - - if (env.checkAndClearExceptions()) { - qWarning() << "Invalid playback rate" << rate; - return false; - } else { - return true; - } - } - } - - return false; + return mMediaPlayer.callMethod<jboolean>("setPlaybackRate", jfloat(rate)); } void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture) |