From ebc2c14b0777042f2325d437078b0e7815129b75 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Thu, 12 Jan 2017 19:20:29 -0800 Subject: [PATCH] AudioTrack: Enable deep buffer mode from Java Test: AudioTrack CTS test, Play Movies Bug: 30687201 Change-Id: Id3a8d675966671ed13c488936228bd76dfc6c1fe --- api/current.txt | 7 +- api/system-current.txt | 7 +- api/test-current.txt | 7 +- core/jni/android_media_AudioTrack.cpp | 13 ++ media/java/android/media/AudioAttributes.java | 29 +++- media/java/android/media/AudioTrack.java | 129 ++++++++++++++++++ 6 files changed, 188 insertions(+), 4 deletions(-) diff --git a/api/current.txt b/api/current.txt index 25dd3d820db1e..7fd7ff6e22239 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20127,7 +20127,7 @@ package android.media { field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 field public static final int FLAG_HW_AV_SYNC = 16; // 0x10 - field public static final int FLAG_LOW_LATENCY = 256; // 0x100 + field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100 field public static final int USAGE_ALARM = 4; // 0x4 field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc @@ -20568,6 +20568,7 @@ package android.media { method protected deprecated int getNativeFrameCount(); method public static int getNativeOutputSampleRate(int); method public int getNotificationMarkerPosition(); + method public int getPerformanceMode(); method public int getPlayState(); method public int getPlaybackHeadPosition(); method public android.media.PlaybackParams getPlaybackParams(); @@ -20614,6 +20615,9 @@ package android.media { field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd field public static final int MODE_STATIC = 0; // 0x0 field public static final int MODE_STREAM = 1; // 0x1 + field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1 + field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0 + field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2 field public static final int PLAYSTATE_PAUSED = 2; // 0x2 field public static final int PLAYSTATE_PLAYING = 3; // 0x3 field public static final int PLAYSTATE_STOPPED = 1; // 0x1 @@ -20631,6 +20635,7 @@ package android.media { method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioTrack.Builder setPerformanceMode(int); method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; } diff --git a/api/system-current.txt b/api/system-current.txt index 6862762e385b1..4e681e8ed175c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -21646,7 +21646,7 @@ package android.media { field public static final int FLAG_BYPASS_MUTE = 128; // 0x80 field public static final int FLAG_HW_AV_SYNC = 16; // 0x10 field public static final int FLAG_HW_HOTWORD = 32; // 0x20 - field public static final int FLAG_LOW_LATENCY = 256; // 0x100 + field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100 field public static final int USAGE_ALARM = 4; // 0x4 field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc @@ -22132,6 +22132,7 @@ package android.media { method protected deprecated int getNativeFrameCount(); method public static int getNativeOutputSampleRate(int); method public int getNotificationMarkerPosition(); + method public int getPerformanceMode(); method public int getPlayState(); method public int getPlaybackHeadPosition(); method public android.media.PlaybackParams getPlaybackParams(); @@ -22178,6 +22179,9 @@ package android.media { field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd field public static final int MODE_STATIC = 0; // 0x0 field public static final int MODE_STREAM = 1; // 0x1 + field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1 + field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0 + field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2 field public static final int PLAYSTATE_PAUSED = 2; // 0x2 field public static final int PLAYSTATE_PLAYING = 3; // 0x3 field public static final int PLAYSTATE_STOPPED = 1; // 0x1 @@ -22195,6 +22199,7 @@ package android.media { method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioTrack.Builder setPerformanceMode(int); method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; } diff --git a/api/test-current.txt b/api/test-current.txt index 0839043c2ab4c..0cbb952f12036 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -20217,7 +20217,7 @@ package android.media { field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 field public static final int FLAG_HW_AV_SYNC = 16; // 0x10 - field public static final int FLAG_LOW_LATENCY = 256; // 0x100 + field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100 field public static final int USAGE_ALARM = 4; // 0x4 field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc @@ -20658,6 +20658,7 @@ package android.media { method protected deprecated int getNativeFrameCount(); method public static int getNativeOutputSampleRate(int); method public int getNotificationMarkerPosition(); + method public int getPerformanceMode(); method public int getPlayState(); method public int getPlaybackHeadPosition(); method public android.media.PlaybackParams getPlaybackParams(); @@ -20704,6 +20705,9 @@ package android.media { field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd field public static final int MODE_STATIC = 0; // 0x0 field public static final int MODE_STREAM = 1; // 0x1 + field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1 + field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0 + field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2 field public static final int PLAYSTATE_PAUSED = 2; // 0x2 field public static final int PLAYSTATE_PLAYING = 3; // 0x3 field public static final int PLAYSTATE_STOPPED = 1; // 0x1 @@ -20721,6 +20725,7 @@ package android.media { method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioTrack.Builder setPerformanceMode(int); method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 86c4df7588112..b2c8168b367a4 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -971,6 +971,18 @@ static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject th return (jint)lpTrack->getUnderrunCount(); } +// ---------------------------------------------------------------------------- +static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) { + sp lpTrack = getAudioTrack(env, thiz); + + if (lpTrack == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for getFlags()"); + return (jint)AUDIO_JAVA_ERROR; + } + return (jint)lpTrack->getFlags(); +} + // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) { sp lpTrack = getAudioTrack(env, thiz); @@ -1212,6 +1224,7 @@ static const JNINativeMethod gMethods[] = { {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, + {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index dc8264a81427c..391a905d0419d 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -306,12 +306,26 @@ public final class AudioAttributes implements Parcelable { * until there are no glitches. * This tuning step should be done while playing silence. * This technique provides a compromise between latency and glitch rate. + * + * @deprecated Use {@link AudioTrack.Builder#setPerformanceMode(int)} with + * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY} to control performance. */ public final static int FLAG_LOW_LATENCY = 0x1 << 8; + /** + * @hide + * Flag requesting a deep buffer path when creating an {@code AudioTrack}. + * + * A deep buffer path, if available, may consume less power and is + * suitable for media playback where latency is not a concern. + * Use {@link AudioTrack.Builder#setPerformanceMode(int)} with + * {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING} to enable. + */ + public final static int FLAG_DEEP_BUFFER = 0x1 << 9; + private final static int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY | - FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY; + FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER; private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY; @@ -541,6 +555,8 @@ public final class AudioAttributes implements Parcelable { /** * Sets the combination of flags. + * + * This is a bitwise OR with the existing flags. * @param flags a combination of {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED}, * {@link AudioAttributes#FLAG_HW_AV_SYNC}. * @return the same Builder instance. @@ -551,6 +567,17 @@ public final class AudioAttributes implements Parcelable { return this; } + /** + * @hide + * Replaces flags. + * @param flags any combination of {@link AudioAttributes#FLAG_ALL}. + * @return the same Builder instance. + */ + public Builder replaceFlags(int flags) { + mFlags = flags & AudioAttributes.FLAG_ALL; + return this; + } + /** * @hide * Adds a Bundle of data diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 031ac0667c64d..b23f5fd683407 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -214,6 +214,66 @@ public class AudioTrack extends PlayerBase */ public final static int WRITE_NON_BLOCKING = 1; + /** @hide */ + @IntDef({ + PERFORMANCE_MODE_NONE, + PERFORMANCE_MODE_LOW_LATENCY, + PERFORMANCE_MODE_POWER_SAVING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PerformanceMode {} + + /** + * Default performance mode for an {@link AudioTrack}. + */ + public static final int PERFORMANCE_MODE_NONE = 0; + + /** + * Low latency performance mode for an {@link AudioTrack}. + * If the device supports it, this mode + * enables a lower latency path through to the audio output sink. + * Effects may no longer work with such an {@code AudioTrack} and + * the sample rate must match that of the output sink. + *

+ * Applications should be aware that low latency requires careful + * buffer management, with smaller chunks of audio data written by each + * {@code write()} call. + *

+ * If this flag is used without specifying a {@code bufferSizeInBytes} then the + * {@code AudioTrack}'s actual buffer size may be too small. + * It is recommended that a fairly + * large buffer should be specified when the {@code AudioTrack} is created. + * Then the actual size can be reduced by calling + * {@link #setBufferSizeInFrames(int)}. The buffer size can be optimized + * by lowering it after each {@code write()} call until the audio glitches, + * which is detected by calling + * {@link #getUnderrunCount()}. Then the buffer size can be increased + * until there are no glitches. + * This tuning step should be done while playing silence. + * This technique provides a compromise between latency and glitch rate. + */ + public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; + + /** + * Power saving performance mode for an {@link AudioTrack}. + * If the device supports it, this + * mode will enable a lower power path to the audio output sink. + * In addition, this lower power path typically will have + * deeper internal buffers and better underrun resistance, + * with a tradeoff of higher latency. + *

+ * In this mode, applications should attempt to use a larger buffer size + * and deliver larger chunks of audio data per {@code write()} call. + * Use {@link #getBufferSizeInFrames()} to determine + * the actual buffer size of the {@code AudioTrack} as it may have increased + * to accommodate a deeper buffer. + */ + public static final int PERFORMANCE_MODE_POWER_SAVING = 2; + + // keep in sync with system/media/audio/include/system/audio-base.h + private static final int AUDIO_OUTPUT_FLAG_FAST = 0x4; + private static final int AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8; + //-------------------------------------------------------------------------- // Member variables //-------------------- @@ -648,6 +708,7 @@ public class AudioTrack extends PlayerBase private int mBufferSizeInBytes; private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE; private int mMode = MODE_STREAM; + private int mPerformanceMode = PERFORMANCE_MODE_NONE; /** * Constructs a new Builder with the default values as described above. @@ -751,6 +812,32 @@ public class AudioTrack extends PlayerBase return this; } + /** + * Sets the {@link AudioTrack} performance mode. This is an advisory request which + * may not be supported by the particular device, and the framework is free + * to ignore such request if it is incompatible with other requests or hardware. + * + * @param performanceMode one of + * {@link AudioTrack#PERFORMANCE_MODE_NONE}, + * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY}, + * or {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING}. + * @return the same Builder instance. + * @throws IllegalArgumentException if {@code performanceMode} is not valid. + */ + public @NonNull Builder setPerformanceMode(@PerformanceMode int performanceMode) { + switch (performanceMode) { + case PERFORMANCE_MODE_NONE: + case PERFORMANCE_MODE_LOW_LATENCY: + case PERFORMANCE_MODE_POWER_SAVING: + mPerformanceMode = performanceMode; + break; + default: + throw new IllegalArgumentException( + "Invalid performance mode " + performanceMode); + } + return this; + } + /** * Builds an {@link AudioTrack} instance initialized with all the parameters set * on this Builder. @@ -765,6 +852,25 @@ public class AudioTrack extends PlayerBase .setUsage(AudioAttributes.USAGE_MEDIA) .build(); } + switch (mPerformanceMode) { + case PERFORMANCE_MODE_LOW_LATENCY: + mAttributes = new AudioAttributes.Builder(mAttributes) + .replaceFlags((mAttributes.getAllFlags() + | AudioAttributes.FLAG_LOW_LATENCY) + & ~AudioAttributes.FLAG_DEEP_BUFFER) + .build(); + break; + case PERFORMANCE_MODE_NONE: + break; + case PERFORMANCE_MODE_POWER_SAVING: + mAttributes = new AudioAttributes.Builder(mAttributes) + .replaceFlags((mAttributes.getAllFlags() + | AudioAttributes.FLAG_DEEP_BUFFER) + & ~AudioAttributes.FLAG_LOW_LATENCY) + .build(); + break; + } + if (mFormat == null) { mFormat = new AudioFormat.Builder() .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO) @@ -1277,6 +1383,27 @@ public class AudioTrack extends PlayerBase return native_get_underrun_count(); } + /** + * Returns the current performance mode of the {@link AudioTrack}. + * + * @return one of {@link AudioTrack#PERFORMANCE_MODE_NONE}, + * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY}, + * or {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING}. + * Use {@link AudioTrack.Builder#setPerformanceMode} + * in the {@link AudioTrack.Builder} to enable a performance mode. + * @throws IllegalStateException if track is not initialized. + */ + public @PerformanceMode int getPerformanceMode() { + final int flags = native_get_flags(); + if ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) { + return PERFORMANCE_MODE_LOW_LATENCY; + } else if ((flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) { + return PERFORMANCE_MODE_POWER_SAVING; + } else { + return PERFORMANCE_MODE_NONE; + } + } + /** * Returns the output sample rate in Hz for the specified stream type. */ @@ -2855,6 +2982,8 @@ public class AudioTrack extends PlayerBase private native final int native_get_underrun_count(); + private native final int native_get_flags(); + // longArray must be a non-null array of length >= 2 // [0] is assigned the frame position // [1] is assigned the time in CLOCK_MONOTONIC nanoseconds