diff --git a/core/api/current.txt b/core/api/current.txt index a72261bd570a9..9a34270442d0f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24470,6 +24470,7 @@ package android.media { method public java.util.List getMicrophones() throws java.io.IOException; method public int getMode(); method public String getParameters(String); + method public static int getPlaybackOffloadSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes); method public String getProperty(String); method public int getRingerMode(); method @Deprecated public int getRouting(int); @@ -24588,6 +24589,9 @@ package android.media { field public static final int MODE_NORMAL = 0; // 0x0 field public static final int MODE_RINGTONE = 1; // 0x1 field @Deprecated public static final int NUM_STREAMS = 5; // 0x5 + field public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = 2; // 0x2 + field public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED = 0; // 0x0 + field public static final int PLAYBACK_OFFLOAD_SUPPORTED = 1; // 0x1 field public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER = "android.media.property.OUTPUT_FRAMES_PER_BUFFER"; field public static final String PROPERTY_OUTPUT_SAMPLE_RATE = "android.media.property.OUTPUT_SAMPLE_RATE"; field public static final String PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED = "android.media.property.SUPPORT_AUDIO_SOURCE_UNPROCESSED"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index eef73c1f60890..5a292952bd65c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1009,6 +1009,8 @@ package android.media { field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2 field public static final int DEVICE_ROLE_NONE = 0; // 0x0 field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1 + field public static final int OFFLOAD_GAPLESS_SUPPORTED = 2; // 0x2 + field public static final int OFFLOAD_SUPPORTED = 1; // 0x1 field public static final int STREAM_DEFAULT = -1; // 0xffffffff } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index b1b0284e5295a..a95ee4f12c51e 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2083,10 +2083,9 @@ android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, (audio_devices_t)device); } -static jboolean -android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz, - jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask, jint streamType) -{ +static jint android_media_AudioSystem_getOffloadSupport(JNIEnv *env, jobject thiz, jint encoding, + jint sampleRate, jint channelMask, + jint channelIndexMask, jint streamType) { audio_offload_info_t format = AUDIO_INFO_INITIALIZER; format.format = (audio_format_t) audioFormatToNative(encoding); format.sample_rate = (uint32_t) sampleRate; @@ -2098,7 +2097,7 @@ android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz, // client side code cannot access "audio.offload.min.duration.secs" property to make a query // agnostic of duration, so using acceptable estimate of 2mn format.duration_us = 120 * 1000000; - return AudioSystem::isOffloadSupported(format); + return AudioSystem::getOffloadSupport(format); } static jint @@ -2574,8 +2573,8 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_registerRecordingCallback}, {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, - {"native_is_offload_supported", "(IIIII)Z", - (void *)android_media_AudioSystem_isOffloadSupported}, + {"native_get_offload_support", "(IIIII)I", + (void *)android_media_AudioSystem_getOffloadSupport}, {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones}, {"getSurroundFormats", "(Ljava/util/Map;Z)I", diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c67d90a5ea0fb..3bd68d893521e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2319,7 +2319,55 @@ public class AudioManager { if (attributes == null) { throw new NullPointerException("Illegal null AudioAttributes"); } - return AudioSystem.isOffloadSupported(format, attributes); + return AudioSystem.getOffloadSupport(format, attributes) != PLAYBACK_OFFLOAD_NOT_SUPPORTED; + } + + /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}: + offload playback not supported */ + public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED = AudioSystem.OFFLOAD_NOT_SUPPORTED; + /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}: + offload playback supported */ + public static final int PLAYBACK_OFFLOAD_SUPPORTED = AudioSystem.OFFLOAD_SUPPORTED; + /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}: + offload playback supported with gapless transitions */ + public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = + AudioSystem.OFFLOAD_GAPLESS_SUPPORTED; + + /** @hide */ + @IntDef(flag = false, prefix = "PLAYBACK_OFFLOAD_", value = { + PLAYBACK_OFFLOAD_NOT_SUPPORTED, + PLAYBACK_OFFLOAD_SUPPORTED, + PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED } + ) + @Retention(RetentionPolicy.SOURCE) + public @interface AudioOffloadMode {} + + /** + * Returns whether offloaded playback of an audio format is supported on the device or not and + * when supported whether gapless transitions are possible or not. + *

Offloaded playback is the feature where the decoding and playback of an audio stream + * is not competing with other software resources. In general, it is supported by dedicated + * hardware, such as audio DSPs. + *

Note that this query only provides information about the support of an audio format, + * it does not indicate whether the resources necessary for the offloaded playback are + * available at that instant. + * @param format the audio format (codec, sample rate, channels) being checked. + * @param attributes the {@link AudioAttributes} to be used for playback + * @return {@link #PLAYBACK_OFFLOAD_NOT_SUPPORTED} if offload playback if not supported, + * {@link #PLAYBACK_OFFLOAD_SUPPORTED} if offload playback is supported or + * {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} if gapless transitions are + * also supported. + */ + @AudioOffloadMode + public static int getPlaybackOffloadSupport(@NonNull AudioFormat format, + @NonNull AudioAttributes attributes) { + if (format == null) { + throw new NullPointerException("Illegal null AudioFormat"); + } + if (attributes == null) { + throw new NullPointerException("Illegal null AudioAttributes"); + } + return AudioSystem.getOffloadSupport(format, attributes); } //==================================================================== diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index bd27f6d068bca..8987a713e505a 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1639,13 +1639,22 @@ public class AudioSystem */ public static native int setAllowedCapturePolicy(int uid, int flags); - static boolean isOffloadSupported(@NonNull AudioFormat format, @NonNull AudioAttributes attr) { - return native_is_offload_supported(format.getEncoding(), format.getSampleRate(), + /** + * @hide + * Compressed audio offload decoding modes supported by audio HAL implementation. + * Keep in sync with system/media/include/media/audio.h. + */ + public static final int OFFLOAD_NOT_SUPPORTED = 0; + public static final int OFFLOAD_SUPPORTED = 1; + public static final int OFFLOAD_GAPLESS_SUPPORTED = 2; + + static int getOffloadSupport(@NonNull AudioFormat format, @NonNull AudioAttributes attr) { + return native_get_offload_support(format.getEncoding(), format.getSampleRate(), format.getChannelMask(), format.getChannelIndexMask(), attr.getVolumeControlStream()); } - private static native boolean native_is_offload_supported(int encoding, int sampleRate, + private static native int native_get_offload_support(int encoding, int sampleRate, int channelMask, int channelIndexMask, int streamType); /** @hide */ diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index e8c62067580f0..e3b6fba18eb06 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1256,7 +1256,8 @@ public class AudioTrack extends PlayerBase throw new UnsupportedOperationException( "Offload and low latency modes are incompatible"); } - if (!AudioSystem.isOffloadSupported(mFormat, mAttributes)) { + if (AudioSystem.getOffloadSupport(mFormat, mAttributes) + == AudioSystem.OFFLOAD_NOT_SUPPORTED) { throw new UnsupportedOperationException( "Cannot create AudioTrack, offload format / attributes not supported"); }