diff --git a/api/current.txt b/api/current.txt index e509a00fd2741..8a4d0b620ea42 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15988,6 +15988,7 @@ package android.media { method public int getAudioSessionId(); method public int getCurrentPosition(); method public int getDuration(); + method public android.media.PlaybackSettings getPlaybackSettings(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; method public android.media.MediaPlayer.TrackInfo[] getTrackInfo() throws java.lang.IllegalStateException; method public int getVideoHeight(); @@ -16024,6 +16025,7 @@ package android.media { 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 setPlaybackSettings(android.media.PlaybackSettings); method public void setScreenOnWhilePlaying(boolean); method public void setSurface(android.view.Surface); method public void setVideoScalingMode(int); @@ -16049,7 +16051,9 @@ package android.media { 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 PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0 + field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2 + field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1 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 } @@ -16342,12 +16346,16 @@ package android.media { method public void configureAudioTrack(android.media.AudioTrack); method public void configureSurface(android.view.Surface); method public final android.view.Surface createInputSurface(); + method public android.media.PlaybackSettings getPlaybackSettings(); method public boolean getTimestamp(android.media.MediaTimestamp); method public void queueAudio(java.nio.ByteBuffer, int, int, long); method public final void release(); method public void setCallback(android.media.MediaSync.Callback, android.os.Handler); method public void setPlaybackRate(float, int); - field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0 + method public void setPlaybackSettings(android.media.PlaybackSettings); + field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0 + field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2 + field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1 } public static abstract class MediaSync.Callback { diff --git a/api/system-current.txt b/api/system-current.txt index 8b4052b4936b7..14e95637629a3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -17201,6 +17201,7 @@ package android.media { method public int getAudioSessionId(); method public int getCurrentPosition(); method public int getDuration(); + method public android.media.PlaybackSettings getPlaybackSettings(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; method public android.media.MediaPlayer.TrackInfo[] getTrackInfo() throws java.lang.IllegalStateException; method public int getVideoHeight(); @@ -17237,6 +17238,7 @@ package android.media { 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 setPlaybackSettings(android.media.PlaybackSettings); method public void setScreenOnWhilePlaying(boolean); method public void setSurface(android.view.Surface); method public void setVideoScalingMode(int); @@ -17262,7 +17264,9 @@ package android.media { 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 PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0 + field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2 + field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1 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 } @@ -17557,12 +17561,16 @@ package android.media { method public void configureAudioTrack(android.media.AudioTrack); method public void configureSurface(android.view.Surface); method public final android.view.Surface createInputSurface(); + method public android.media.PlaybackSettings getPlaybackSettings(); method public boolean getTimestamp(android.media.MediaTimestamp); method public void queueAudio(java.nio.ByteBuffer, int, int, long); method public final void release(); method public void setCallback(android.media.MediaSync.Callback, android.os.Handler); method public void setPlaybackRate(float, int); - field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0 + method public void setPlaybackSettings(android.media.PlaybackSettings); + field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0 + field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2 + field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1 } public static abstract class MediaSync.Callback { diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 210d08f7c210d..85c6c67b0c614 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.NonNull; import android.app.ActivityThread; import android.app.AppOpsManager; import android.content.ContentResolver; @@ -44,6 +45,7 @@ import android.graphics.SurfaceTexture; import android.media.AudioManager; import android.media.MediaFormat; import android.media.MediaTimeProvider; +import android.media.PlaybackSettings; import android.media.SubtitleController; import android.media.SubtitleController.Anchor; import android.media.SubtitleData; @@ -471,16 +473,21 @@ import java.lang.ref.WeakReference; * {}

* This method can be called in any state and calling it does not change * the object state.

- * setScreenOnWhilePlaying - * any

- * {}

- * This method can be called in any state and calling it does not change - * the object state.

* setPlaybackRate

* any

* {}

* This method can be called in any state and calling it does not change * the object state.

+ * setPlaybackSettings

+ * any

+ * {}

+ * This method can be called in any state and calling it does not change + * the object state.

+ * setScreenOnWhilePlaying + * any

+ * {}

+ * This method can be called in any state and calling it does not change + * the object state.

* setVolume

* {Idle, Initialized, Stopped, Prepared, Started, Paused, * PlaybackCompleted}

@@ -1342,6 +1349,8 @@ public class MediaPlayer implements SubtitleController.Listener public native boolean isPlaying(); /** + * Change playback speed of audio by resampling the audio. + *

* 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 @@ -1349,33 +1358,44 @@ public class MediaPlayer implements SubtitleController.Listener * 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; + public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; /** + * Change playback speed of audio without changing its pitch. + *

* 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 + *

+ * This mode is only supported for a limited range of playback speed factors, + * e.g. between 1/2x and 2x. */ public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; + /** + * Change playback speed of audio without changing its pitch, and + * possibly mute audio if time stretching is not supported for the playback + * speed. + *

+ * Try to keep audio pitch when changing the playback rate, but allow the + * system to determine how to change audio playback if the rate is out + * of range. + */ + public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; + /** @hide */ @IntDef( value = { + PLAYBACK_RATE_AUDIO_MODE_DEFAULT, + PLAYBACK_RATE_AUDIO_MODE_STRETCH, PLAYBACK_RATE_AUDIO_MODE_RESAMPLE, - PLAYBACK_RATE_AUDIO_MODE_STRETCH }) + }) @Retention(RetentionPolicy.SOURCE) public @interface PlaybackRateAudioMode {} /** * Sets playback rate and audio mode. * - *

The supported audio modes are: - *

- * * @param rate the ratio between desired playback rate and normal one. * @param audioMode audio playback mode. Must be one of the supported * audio modes. @@ -1385,14 +1405,46 @@ public class MediaPlayer implements SubtitleController.Listener * @throws IllegalArgumentException if audioMode is not supported. */ public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) { - if (!isAudioPlaybackModeSupported(audioMode)) { + PlaybackSettings settings = new PlaybackSettings(); + settings.allowDefaults(); + switch (audioMode) { + case PLAYBACK_RATE_AUDIO_MODE_DEFAULT: + settings.setSpeed(rate).setPitch(1.0f); + break; + case PLAYBACK_RATE_AUDIO_MODE_STRETCH: + settings.setSpeed(rate).setPitch(1.0f) + .setAudioFallbackMode(settings.AUDIO_FALLBACK_MODE_FAIL); + break; + case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE: + settings.setSpeed(rate).setPitch(rate); + break; + default: final String msg = "Audio playback mode " + audioMode + " is not supported"; throw new IllegalArgumentException(msg); } - _setPlaybackRate(rate); + setPlaybackSettings(settings); } - private native void _setPlaybackRate(float rate) throws IllegalStateException; + /** + * Sets playback rate using {@link PlaybackSettings}. + * + * @param settings the playback settings. + * + * @throws IllegalStateException if the internal player engine has not been + * initialized. + * @throws IllegalArgumentException if settings is not supported. + */ + public native void setPlaybackSettings(@NonNull PlaybackSettings settings); + + /** + * Gets the playback settings, containing the current playback rate. + * + * @return the playback settings. + * @throws IllegalStateException if the internal player engine has not been + * initialized. + */ + @NonNull + public native PlaybackSettings getPlaybackSettings(); /** * Seeks to specified time position. @@ -3219,14 +3271,6 @@ public class MediaPlayer implements SubtitleController.Listener 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/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java index 74a2fb29c0194..cc894cbae412a 100644 --- a/media/java/android/media/MediaSync.java +++ b/media/java/android/media/MediaSync.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioTrack; +import android.media.PlaybackSettings; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -282,60 +283,58 @@ final public class MediaSync { public native final Surface createInputSurface(); /** - * Specifies resampling as audio mode for variable rate playback, i.e., - * resample the waveform based on the requested playback rate to get + * Resample audio data when changing playback speed. + *

+ * 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; + public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; /** - * Specifies time stretching as audio mode for variable rate playback. + * Time stretch audio when changing playback speed. + *

* Time stretching changes the duration of the audio samples without - * affecting its pitch. - * FIXME: implement time strectching. - * @hide + * affecting their pitch. This is only supported for a limited range + * of playback speeds, e.g. from 1/2x to 2x. If the rate is adjusted + * beyond this limit, the rate change will fail. */ public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; + /** + * Time stretch audio when changing playback speed, and may mute if + * stretching is no longer supported. + *

+ * Time stretching changes the duration of the audio samples without + * affecting their pitch. This is only supported for a limited range + * of playback speeds, e.g. from 1/2x to 2x. When it is no longer + * supported, the audio may be muted. Using this mode will not fail + * for non-negative playback rates. + */ + public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; + /** @hide */ @IntDef( value = { + PLAYBACK_RATE_AUDIO_MODE_DEFAULT, + PLAYBACK_RATE_AUDIO_MODE_STRETCH, PLAYBACK_RATE_AUDIO_MODE_RESAMPLE, - PLAYBACK_RATE_AUDIO_MODE_STRETCH }) + }) @Retention(RetentionPolicy.SOURCE) public @interface PlaybackRateAudioMode {} - /** - * Sets playback rate. It does same as {@link #setPlaybackRate(float, int)}, - * except that it always uses {@link #PLAYBACK_RATE_AUDIO_MODE_STRETCH} for audioMode. - * - * @param rate the ratio between desired playback rate and normal one. 1.0 means normal - * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback, - * while value between 0.0 and 1.0 for slower playback. - * - * @throws IllegalStateException if the internal sync engine or the audio track has not - * been initialized. - * TODO: unhide when PLAYBACK_RATE_AUDIO_MODE_STRETCH is supported. - * @hide - */ - public void setPlaybackRate(float rate) { - setPlaybackRate(rate, PLAYBACK_RATE_AUDIO_MODE_STRETCH); - } - /** * Sets playback rate and audio mode. * - *

The supported audio modes are: - *

- * * @param rate the ratio between desired playback rate and normal one. 1.0 means normal - * playback speed. 0.0 means stop or pause. Value larger than 1.0 means faster playback, - * while value between 0.0 and 1.0 for slower playback. + * playback speed. 0.0 means pause. Value larger than 1.0 means faster playback, + * while value between 0.0 and 1.0 for slower playback. Note: the normal rate + * does not change as a result of this call. To restore the original rate at any time, + * use 1.0. * @param audioMode audio playback mode. Must be one of the supported * audio modes. * @@ -344,52 +343,104 @@ final public class MediaSync { * @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); - } - - int status = AudioTrack.SUCCESS; - if (mAudioTrack != null) { - int nativeSampleRateInHz = mAudioTrack.getSampleRate(); - int playbackSampleRate = (int)(rate * nativeSampleRateInHz + 0.5); - rate = playbackSampleRate / (float)nativeSampleRateInHz; - - try { - if (rate == 0.0) { - mAudioTrack.pause(); - } else { - status = mAudioTrack.setPlaybackRate(playbackSampleRate); - mAudioTrack.play(); - } - } catch (IllegalStateException e) { - throw e; + PlaybackSettings rateSettings = new PlaybackSettings(); + rateSettings.allowDefaults(); + switch (audioMode) { + case PLAYBACK_RATE_AUDIO_MODE_DEFAULT: + rateSettings.setSpeed(rate).setPitch(1.0f); + break; + case PLAYBACK_RATE_AUDIO_MODE_STRETCH: + rateSettings.setSpeed(rate).setPitch(1.0f) + .setAudioFallbackMode(rateSettings.AUDIO_FALLBACK_MODE_FAIL); + break; + case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE: + rateSettings.setSpeed(rate).setPitch(rate); + break; + default: + { + final String msg = "Audio playback mode " + audioMode + " is not supported"; + throw new IllegalArgumentException(msg); } } + setPlaybackSettings(rateSettings); + } - if (status != AudioTrack.SUCCESS) { - throw new IllegalArgumentException("Fail to set playback rate in audio track"); - } + /** + * Sets playback rate using {@link PlaybackSettings}. + *

+ * When using MediaSync with {@link AudioTrack}, set playback settings using this + * call instead of calling it directly on the track, so that the sync is aware of + * the settings change. + *

+ * This call also works if there is no audio track. + * + * @param settings the playback settings to use. {@link PlaybackSettings#getSpeed + * Speed} is the ratio between desired playback rate and normal one. 1.0 means + * normal playback speed. 0.0 means pause. Value larger than 1.0 means faster playback, + * while value between 0.0 and 1.0 for slower playback. Note: the normal rate + * does not change as a result of this call. To restore the original rate at any time, + * use speed of 1.0. + * + * @throws IllegalStateException if the internal sync engine or the audio track has not + * been initialized. + * @throws IllegalArgumentException if the settings are not supported. + */ + public void setPlaybackSettings(@NonNull PlaybackSettings settings) { + float rate; + try { + rate = settings.getSpeed(); - synchronized(mAudioLock) { - mPlaybackRate = rate; + // rate is specified + if (mAudioTrack != null) { + try { + if (rate == 0.0) { + mAudioTrack.pause(); + } else { + mAudioTrack.setPlaybackSettings(settings); + mAudioTrack.play(); + } + } catch (IllegalStateException e) { + throw e; + } + } + + synchronized(mAudioLock) { + mPlaybackRate = rate; + } + if (mPlaybackRate != 0.0 && mAudioThread != null) { + postRenderAudio(0); + } + native_setPlaybackRate(mPlaybackRate); + } catch (IllegalStateException e) { + // rate is not specified; still, propagate settings to audio track + if (mAudioTrack != null) { + mAudioTrack.setPlaybackSettings(settings); + } } - if (mPlaybackRate != 0.0 && mAudioThread != null) { - postRenderAudio(0); + } + + /** + * Gets the playback rate using {@link PlaybackSettings}. + * + * @return the playback rate being used. + * + * @throws IllegalStateException if the internal sync engine or the audio track has not + * been initialized. + */ + @NonNull + public PlaybackSettings getPlaybackSettings() { + if (mAudioTrack != null) { + return mAudioTrack.getPlaybackSettings(); + } else { + PlaybackSettings settings = new PlaybackSettings(); + settings.allowDefaults(); + settings.setSpeed(mPlaybackRate); + return settings; } - native_setPlaybackRate(mPlaybackRate); } private native final void native_setPlaybackRate(float rate); - /* - * Test whether a given audio playback mode is supported. - * TODO query supported AudioPlaybackMode from audio track. - */ - private boolean isAudioPlaybackModeSupported(int mode) { - return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE); - } - /** * Get current playback position. *

diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index c2472201c7512..b79a6bb61826b 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -20,6 +20,7 @@ #include "utils/Log.h" #include +#include #include #include #include @@ -37,6 +38,7 @@ #include "utils/KeyedVector.h" #include "utils/String8.h" #include "android_media_MediaDataSource.h" +#include "android_media_PlaybackSettings.h" #include "android_media_Utils.h" #include "android_os_Parcel.h" @@ -66,6 +68,8 @@ struct fields_t { }; static fields_t fields; +static PlaybackSettings::fields_t gPlaybackSettingsFields; + static Mutex sLock; // ---------------------------------------------------------------------------- @@ -420,15 +424,55 @@ android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz) } static void -android_media_MediaPlayer_setPlaybackRate(JNIEnv *env, jobject thiz, jfloat rate) +android_media_MediaPlayer_setPlaybackSettings(JNIEnv *env, jobject thiz, jobject settings) { sp 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); + + PlaybackSettings pbs; + pbs.fillFromJobject(env, gPlaybackSettingsFields, settings); + ALOGV("setPlaybackSettings: %d:%f %d:%f %d:%u %d:%u", + pbs.speedSet, pbs.audioRate.mSpeed, + pbs.pitchSet, pbs.audioRate.mPitch, + pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode, + pbs.audioStretchModeSet, pbs.audioRate.mStretchMode); + + // TODO: pass playback settings to mediaplayer when audiotrack supports it + process_media_player_call(env, thiz, mp->setPlaybackRate(pbs.audioRate.mSpeed), NULL, NULL); +} + +static jobject +android_media_MediaPlayer_getPlaybackSettings(JNIEnv *env, jobject thiz) +{ + sp mp = getMediaPlayer(env, thiz); + if (mp == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + PlaybackSettings pbs; + AudioPlaybackRate &audioRate = pbs.audioRate; + + audioRate.mSpeed = 1.0f; + audioRate.mPitch = 1.0f; + audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_DEFAULT; + audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_DEFAULT; + + // TODO: get this from mediaplayer when audiotrack supports it + // process_media_player_call( + // env, thiz, mp->getPlaybackSettings(&audioRate), NULL, NULL); + ALOGV("getPlaybackSettings: %f %f %d %d", + audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode); + + pbs.speedSet = true; + pbs.pitchSet = true; + pbs.audioFallbackModeSet = true; + pbs.audioStretchModeSet = true; + + return pbs.asJobject(env, gPlaybackSettingsFields); } static void @@ -697,6 +741,8 @@ android_media_MediaPlayer_native_init(JNIEnv *env) return; } + env->DeleteLocalRef(clazz); + clazz = env->FindClass("android/net/ProxyInfo"); if (clazz == NULL) { return; @@ -710,6 +756,10 @@ android_media_MediaPlayer_native_init(JNIEnv *env) fields.proxyConfigGetExclusionList = env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;"); + + env->DeleteLocalRef(clazz); + + gPlaybackSettingsFields.init(env); } static void @@ -898,7 +948,8 @@ static JNINativeMethod gMethods[] = { {"_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}, + {"setPlaybackSettings", "(Landroid/media/PlaybackSettings;)V", (void *)android_media_MediaPlayer_setPlaybackSettings}, + {"getPlaybackSettings", "()Landroid/media/PlaybackSettings;", (void *)android_media_MediaPlayer_getPlaybackSettings}, {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, {"_pause", "()V", (void *)android_media_MediaPlayer_pause}, {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, diff --git a/media/jni/android_media_PlaybackSettings.h b/media/jni/android_media_PlaybackSettings.h new file mode 100644 index 0000000000000..1f4f256103966 --- /dev/null +++ b/media/jni/android_media_PlaybackSettings.h @@ -0,0 +1,120 @@ +/* + * Copyright 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_ +#define _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_ + +#include + +namespace android { + +// This entire class is inline as it is used from both core and media +struct PlaybackSettings { + AudioPlaybackRate audioRate; + bool speedSet; + bool pitchSet; + bool audioFallbackModeSet; + bool audioStretchModeSet; + + struct fields_t { + jclass clazz; + jmethodID constructID; + + jfieldID speed; + jfieldID pitch; + jfieldID audio_fallback_mode; + jfieldID audio_stretch_mode; + jfieldID set; + jint set_speed; + jint set_pitch; + jint set_audio_fallback_mode; + jint set_audio_stretch_mode; + + void init(JNIEnv *env) { + jclass lclazz = env->FindClass("android/media/PlaybackSettings"); + if (lclazz == NULL) { + return; + } + + clazz = (jclass)env->NewGlobalRef(lclazz); + if (clazz == NULL) { + return; + } + + constructID = env->GetMethodID(clazz, "", "()V"); + + speed = env->GetFieldID(clazz, "mSpeed", "F"); + pitch = env->GetFieldID(clazz, "mPitch", "F"); + audio_fallback_mode = env->GetFieldID(clazz, "mAudioFallbackMode", "I"); + audio_stretch_mode = env->GetFieldID(clazz, "mAudioStretchMode", "I"); + set = env->GetFieldID(clazz, "mSet", "I"); + + set_speed = + env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_SPEED", "I")); + set_pitch = + env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "SET_PITCH", "I")); + set_audio_fallback_mode = env->GetStaticIntField( + clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_FALLBACK_MODE", "I")); + set_audio_stretch_mode = env->GetStaticIntField( + clazz, env->GetStaticFieldID(clazz, "SET_AUDIO_STRETCH_MODE", "I")); + + env->DeleteLocalRef(lclazz); + } + + void exit(JNIEnv *env) { + env->DeleteGlobalRef(clazz); + clazz = NULL; + } + }; + + void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) { + audioRate.mSpeed = env->GetFloatField(settings, fields.speed); + audioRate.mPitch = env->GetFloatField(settings, fields.pitch); + audioRate.mFallbackMode = + (AudioTimestretchFallbackMode)env->GetIntField(settings, fields.audio_fallback_mode); + audioRate.mStretchMode = + (AudioTimestretchStretchMode)env->GetIntField(settings, fields.audio_stretch_mode); + int set = env->GetIntField(settings, fields.set); + + speedSet = set & fields.set_speed; + pitchSet = set & fields.set_pitch; + audioFallbackModeSet = set & fields.set_audio_fallback_mode; + audioStretchModeSet = set & fields.set_audio_stretch_mode; + } + + jobject asJobject(JNIEnv *env, const fields_t& fields) { + jobject settings = env->NewObject(fields.clazz, fields.constructID); + if (settings == NULL) { + return NULL; + } + env->SetFloatField(settings, fields.speed, (jfloat)audioRate.mSpeed); + env->SetFloatField(settings, fields.pitch, (jfloat)audioRate.mPitch); + env->SetIntField(settings, fields.audio_fallback_mode, (jint)audioRate.mFallbackMode); + env->SetIntField(settings, fields.audio_stretch_mode, (jint)audioRate.mStretchMode); + env->SetIntField( + settings, fields.set, + (speedSet ? fields.set_speed : 0) + | (pitchSet ? fields.set_pitch : 0) + | (audioFallbackModeSet ? fields.set_audio_fallback_mode : 0) + | (audioStretchModeSet ? fields.set_audio_stretch_mode : 0)); + + return settings; + } +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_PLAYBACK_SETTINGS_H_