Playback rate on OpenSL ES AudioPlayer playing from URI/FD

Allow AudioPlayer to use the platform's 500-2000 range (in
  permille) of playback rate for audio when the data source
  is a URI or FD.
Since play events for those players are simulated through
  monitoring the playback position, scale the time
  between updates so play events are delivered in content
  time, not presentation time (as defined in OpenSL ES
  specification, "positions do not scale with changes in
  playback rate").

Bug 5898620

Change-Id: Ib512caebd7f3983c9e845be62e48dfee8499e59c
diff --git a/src/android/AudioPlayer_to_android.cpp b/src/android/AudioPlayer_to_android.cpp
index 7002829..73ee427 100644
--- a/src/android/AudioPlayer_to_android.cpp
+++ b/src/android/AudioPlayer_to_android.cpp
@@ -33,6 +33,9 @@
 #define AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE  500
 #define AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE 2000
 
+#define MEDIAPLAYER_MIN_PLAYBACKRATE_PERMILLE AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE
+#define MEDIAPLAYER_MAX_PLAYBACKRATE_PERMILLE AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE
+
 //-----------------------------------------------------------------------------
 // FIXME this method will be absorbed into android_audioPlayer_setPlayState() once
 //       bufferqueue and uri/fd playback are moved under the GenericPlayer C++ object
@@ -1276,12 +1279,15 @@
     // can be set or used regardless of whether the interface is
     // exposed on the AudioPlayer or not
 
-    // Only AudioTrack supports a non-trivial playback rate
     switch (pAudioPlayer->mAndroidObjType) {
     case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
         pAudioPlayer->mPlaybackRate.mMinRate = AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE;
         pAudioPlayer->mPlaybackRate.mMaxRate = AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE;
         break;
+    case AUDIOPLAYER_FROM_URIFD:
+        pAudioPlayer->mPlaybackRate.mMinRate = MEDIAPLAYER_MIN_PLAYBACKRATE_PERMILLE;
+        pAudioPlayer->mPlaybackRate.mMaxRate = MEDIAPLAYER_MAX_PLAYBACKRATE_PERMILLE;
+        break;
     default:
         // use the default range
         break;
@@ -1740,10 +1746,15 @@
         }
         }
         break;
-    case AUDIOPLAYER_FROM_URIFD:
-        assert(rate == 1000);
+    case AUDIOPLAYER_FROM_URIFD: {
+        assert((MEDIAPLAYER_MIN_PLAYBACKRATE_PERMILLE <= rate) &&
+                        (rate <= MEDIAPLAYER_MAX_PLAYBACKRATE_PERMILLE));
         assert(constraints & SL_RATEPROP_NOPITCHCORAUDIO);
-        // that was easy
+        // apply the SL ES playback rate on the GenericPlayer
+        if (ap->mAPlayer != 0) {
+            ap->mAPlayer->setPlaybackRate((int16_t)rate);
+        }
+        }
         break;
 
     default:
diff --git a/src/android/android_GenericMediaPlayer.cpp b/src/android/android_GenericMediaPlayer.cpp
index 04f4f8e..629c54e 100644
--- a/src/android/android_GenericMediaPlayer.cpp
+++ b/src/android/android_GenericMediaPlayer.cpp
@@ -308,6 +308,20 @@
     mVideoSurfaceTexture = surfaceTexture;
 }
 
+//--------------------------------------------------
+void GenericMediaPlayer::setPlaybackRate(int32_t ratePermille) {
+    SL_LOGV("GenericMediaPlayer::setPlaybackRate(%d)", ratePermille);
+    GenericPlayer::setPlaybackRate(ratePermille);
+    sp<IMediaPlayer> player;
+    getPreparedPlayer(player);
+    if (player != 0) {
+        Parcel rateParcel;
+        if (rateParcel.writeInt32(ratePermille) == OK) {
+            player->setParameter(KEY_PARAMETER_PLAYBACK_RATE_PERMILLE, rateParcel);
+        }
+    }
+}
+
 
 //--------------------------------------------------
 // Event handlers
@@ -575,6 +589,11 @@
     } else {
         SL_LOGD("media player prepared on non-local source");
     }
+    // when the MediaPlayer mPlayer is prepared, apply the playback rate
+    Parcel rateParcel;
+    if (rateParcel.writeInt32((int32_t)mPlaybackRatePermille) == OK) {
+        mPlayer->setParameter(KEY_PARAMETER_PLAYBACK_RATE_PERMILLE, rateParcel);
+    }
 }
 
 
diff --git a/src/android/android_GenericMediaPlayer.h b/src/android/android_GenericMediaPlayer.h
index c0497ba..e4e556b 100644
--- a/src/android/android_GenericMediaPlayer.h
+++ b/src/android/android_GenericMediaPlayer.h
@@ -91,6 +91,8 @@
 
     virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
 
+    virtual void setPlaybackRate(int32_t ratePermille);
+
 protected:
     friend class MediaPlayerNotificationClient;
 
diff --git a/src/android/android_GenericPlayer.cpp b/src/android/android_GenericPlayer.cpp
index 0fd975c..e87f9a8 100644
--- a/src/android/android_GenericPlayer.cpp
+++ b/src/android/android_GenericPlayer.cpp
@@ -31,6 +31,7 @@
         mStateFlags(0),
         mPlaybackParams(*params),
         mDurationMsec(ANDROID_UNKNOWN_TIME),
+        mPlaybackRatePermille(1000),
         mCacheStatus(kStatusEmpty),
         mCacheFill(0),
         mLastNotifiedCacheFill(0),
@@ -231,6 +232,15 @@
 
 
 //--------------------------------------------------
+void GenericPlayer::setPlaybackRate(int32_t ratePermille) {
+    SL_LOGV("GenericPlayer::setPlaybackRate(ratePermille=%d)", ratePermille);
+    {
+        Mutex::Autolock _l(mSettingsLock);
+        mPlaybackRatePermille = (int16_t)ratePermille;
+    }
+}
+
+//--------------------------------------------------
 // Call after changing any of the IPlay settings related to SL_PLAYEVENT_*
 void GenericPlayer::setPlayEvents(int32_t eventFlags, int32_t markerPositionMs,
         int32_t positionUpdatePeriodMs)
@@ -671,8 +681,20 @@
     // we have a new observed position
     mObservedPositionMs = positionMs;
 
+    if (mPlaybackRatePermille == 0) {
+        // playback is frozen, no update expected (and no division by zero below)
+        return;
+    }
+
     // post the new one-shot message if needed
     if (advancesPositionInRealTime() && delayUs >= 0) {
+        // scale delay according to playback rate (reported positions won't change, but reported
+        // time will advance slower or faster depending on rate)
+        {
+            Mutex::Autolock _l(mSettingsLock);
+            delayUs =  delayUs * 1000 / mPlaybackRatePermille;
+        }
+
         // 20 ms min delay to avoid near busy waiting
         if (delayUs < 20000LL) {
             delayUs = 20000LL;
diff --git a/src/android/android_GenericPlayer.h b/src/android/android_GenericPlayer.h
index bc52846..f73eb3e 100644
--- a/src/android/android_GenericPlayer.h
+++ b/src/android/android_GenericPlayer.h
@@ -84,6 +84,8 @@
     void attachAuxEffect(int32_t effectId);
     void setAuxEffectSendLevel(float level);
 
+    virtual void setPlaybackRate(int32_t ratePermille);
+
     // Call after changing any of the IPlay settings related to SL_PLAYEVENT_*
     void setPlayEvents(int32_t eventFlags, int32_t markerPosition, int32_t positionUpdatePeriod);
 
@@ -181,6 +183,7 @@
 
     // protected by mSettingsLock
     int32_t mDurationMsec;
+    int16_t mPlaybackRatePermille;
 
     CacheStatus_t mCacheStatus;
     int16_t mCacheFill; // cache fill level + played back level in permille
@@ -191,6 +194,9 @@
     // supply the latest known position or ANDROID_UNKNOWN_TIME if position is unknown to caller.
     void updateOneShot(int positionMs = ANDROID_UNKNOWN_TIME);
 
+    // players that "render" data to present it to the user (a music player, a video player),
+    // should return true, while players that only decode (hopefully faster than "real time")
+    // should return false.
     virtual bool advancesPositionInRealTime() const { return true; }
 
 private: