MediaPlayer: add setPlaybackRate JAVA API.

Bug: 19196501
Change-Id: I43daced7d9b53bcaca4e6a8d81ca729b32efc79f
diff --git a/api/current.txt b/api/current.txt
index 0046938..707999b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15404,6 +15404,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
+    method public void setPlaybackRate(float, int);
     method public void setScreenOnWhilePlaying(boolean);
     method public void setSurface(android.view.Surface);
     method public void setVideoScalingMode(int);
@@ -15429,6 +15430,7 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index d48fd0d..2ffc4e1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16400,6 +16400,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
+    method public void setPlaybackRate(float, int);
     method public void setScreenOnWhilePlaying(boolean);
     method public void setSurface(android.view.Surface);
     method public void setVideoScalingMode(int);
@@ -16425,6 +16426,7 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
   }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d61610d..d77fcd8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
@@ -61,6 +62,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.Runnable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.InetSocketAddress;
 import java.util.Map;
 import java.util.Scanner;
@@ -472,6 +475,11 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state.  </p></td></tr>
+ * <tr><td>setPlaybackRate</p></td>
+ *     <td>any </p></td>
+ *     <td>{} </p></td>
+ *     <td>This method can be called in any state and calling it does not change
+ *         the object state. </p></td></tr>
  * <tr><td>setVolume </p></td>
  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
  *          PlaybackCompleted}</p></td>
@@ -1319,6 +1327,59 @@
     public native boolean isPlaying();
 
     /**
+     * Specifies resampling as audio mode for variable rate playback, i.e.,
+     * resample the waveform based on the requested playback rate to get
+     * a new waveform, and play back the new waveform at the original sampling
+     * frequency.
+     * When rate is larger than 1.0, pitch becomes higher.
+     * When rate is smaller than 1.0, pitch becomes lower.
+     */
+    public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0;
+
+    /**
+     * Specifies time stretching as audio mode for variable rate playback.
+     * Time stretching changes the duration of the audio samples without
+     * affecting its pitch.
+     * FIXME: implement time strectching.
+     * @hide
+     */
+    public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
+
+    /** @hide */
+    @IntDef(
+        value = {
+            PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
+            PLAYBACK_RATE_AUDIO_MODE_STRETCH })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PlaybackRateAudioMode {}
+
+    /**
+     * Sets playback rate and audio mode.
+     *
+     * <p> The supported audio modes are:
+     * <ul>
+     * <li> {@link #PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
+     * </ul>
+     *
+     * @param rate the ratio between desired playback rate and normal one.
+     * @param audioMode audio playback mode. Must be one of the supported
+     * audio modes.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     * @throws IllegalArgumentException if audioMode is not supported.
+     */
+    public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
+        if (!isAudioPlaybackModeSupported(audioMode)) {
+            final String msg = "Audio playback mode " + audioMode + " is not supported";
+            throw new IllegalArgumentException(msg);
+        }
+        _setPlaybackRate(rate);
+    }
+
+    private native void _setPlaybackRate(float rate) throws IllegalStateException;
+
+    /**
      * Seeks to specified time position.
      *
      * @param msec the offset in milliseconds from the start to seek to
@@ -3078,6 +3139,14 @@
                 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
     }
 
+    /*
+     * Test whether a given audio playback mode is supported.
+     * TODO query supported AudioPlaybackMode from player.
+     */
+    private boolean isAudioPlaybackModeSupported(int mode) {
+        return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+    }
+
     /** @hide */
     static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
             MediaTimeProvider {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 820de5b..55643f7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -402,6 +402,18 @@
 }
 
 static void
+android_media_MediaPlayer_setPlaybackRate(JNIEnv *env, jobject thiz, jfloat rate)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+    ALOGV("setPlaybackRate: %f", rate);
+    process_media_player_call(env, thiz, mp->setPlaybackRate(rate), NULL, NULL);
+}
+
+static void
 android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, jint msec)
 {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -867,6 +879,7 @@
     {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
+    {"_setPlaybackRate",    "(F)V",                             (void *)android_media_MediaPlayer_setPlaybackRate},
     {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
     {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
     {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},