diff --git a/core/api/current.txt b/core/api/current.txt index 3b778ffb62ddb..98d3a91c99446 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20940,11 +20940,12 @@ package android.media { method @NonNull public java.util.List getAvailableCommunicationDevices(); method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice(); method public android.media.AudioDeviceInfo[] getDevices(int); + method public static int getDirectPlaybackSupport(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes); method public int getEncodedSurroundMode(); 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 @Deprecated 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); @@ -21035,6 +21036,10 @@ package android.media { field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0 field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1 field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0 + field public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED = 4; // 0x4 + field public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = 0; // 0x0 + field public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = 3; // 0x3 + field public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED = 1; // 0x1 field public static final int ENCODED_SURROUND_OUTPUT_ALWAYS = 2; // 0x2 field public static final int ENCODED_SURROUND_OUTPUT_AUTO = 0; // 0x0 field public static final int ENCODED_SURROUND_OUTPUT_MANUAL = 3; // 0x3 @@ -21425,7 +21430,7 @@ package android.media { method public int getStreamType(); method public boolean getTimestamp(android.media.AudioTimestamp); method public int getUnderrunCount(); - method public static boolean isDirectPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes); + method @Deprecated public static boolean isDirectPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes); method public boolean isOffloadedPlayback(); method public void pause() throws java.lang.IllegalStateException; method public void play() throws java.lang.IllegalStateException; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 75c81a45c860c..e61852b0027a3 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1429,6 +1429,9 @@ 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 DIRECT_BITSTREAM_SUPPORTED = 4; // 0x4 + field public static final int DIRECT_OFFLOAD_GAPLESS_SUPPORTED = 3; // 0x3 + field public static final int DIRECT_OFFLOAD_SUPPORTED = 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 8b45907b64f2c..0b0870d9700ac 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2774,6 +2774,46 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject return canBeSpatialized; } +// keep these values in sync with AudioSystem.java +#define DIRECT_NOT_SUPPORTED 0 +#define DIRECT_OFFLOAD_SUPPORTED 1 +#define DIRECT_OFFLOAD_GAPLESS_SUPPORTED 3 +#define DIRECT_BITSTREAM_SUPPORTED 4 + +static jint convertAudioDirectModeFromNative(audio_direct_mode_t directMode) { + jint result = DIRECT_NOT_SUPPORTED; + if ((directMode | AUDIO_DIRECT_OFFLOAD_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) { + result |= DIRECT_OFFLOAD_SUPPORTED; + } + if ((directMode | AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) { + result |= DIRECT_OFFLOAD_GAPLESS_SUPPORTED; + } + if ((directMode | AUDIO_DIRECT_BITSTREAM_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) { + result |= DIRECT_BITSTREAM_SUPPORTED; + } + return result; +} + +static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobject thiz, + jobject jFormat, jobject jaa) { + JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return AUDIO_DIRECT_NOT_SUPPORTED; + } + + audio_config_t nConfig; + javaAudioFormatToNativeAudioConfig(env, &nConfig, jFormat, false /*isInput*/); + + audio_direct_mode_t directMode; + status_t status = AudioSystem::getDirectPlaybackSupport(paa.get(), &nConfig, &directMode); + if (status != NO_ERROR) { + ALOGW("%s native returned error %d", __func__, status); + return AUDIO_DIRECT_NOT_SUPPORTED; + } + return convertAudioDirectModeFromNative(directMode); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = @@ -2917,7 +2957,10 @@ static const JNINativeMethod gMethods[] = {"canBeSpatialized", "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" "[Landroid/media/AudioDeviceAttributes;)Z", - (void *)android_media_AudioSystem_canBeSpatialized}}; + (void *)android_media_AudioSystem_canBeSpatialized}, + {"getDirectPlaybackSupport", + "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", + (void *)android_media_AudioSystem_getDirectPlaybackSupport}}; static const JNINativeMethod gEventHandlerMethods[] = { {"native_setup", diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index dd27cf140ca14..5c4211fdff240 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2398,6 +2398,77 @@ public class AudioManager { } } + //==================================================================== + // Direct playback query + + /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}: + direct playback not supported. */ + public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = AudioSystem.DIRECT_NOT_SUPPORTED; + /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}: + direct offload playback supported. Compressed offload is a variant of direct playback. + It is the feature that allows audio processing tasks to be done on the Android device but + not on the application processor, instead, it is handled by dedicated hardware such as audio + DSPs. That will allow the application processor to be idle as much as possible, which is + good for power saving. Compressed offload playback supports + {@link AudioTrack.StreamEventCallback} for event notifications. */ + public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED = + AudioSystem.DIRECT_OFFLOAD_SUPPORTED; + /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}: + direct offload playback supported with gapless transitions. Compressed offload is a variant + of direct playback. It is the feature that allows audio processing tasks to be done on the + Android device but not on the application processor, instead, it is handled by dedicated + hardware such as audio DSPs. That will allow the application processor to be idle as much as + possible, which is good for power saving. Compressed offload playback supports + {@link AudioTrack.StreamEventCallback} for event notifications. Gapless transitions + indicates the ability to play consecutive audio tracks without an audio silence in + between. */ + public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = + AudioSystem.DIRECT_OFFLOAD_GAPLESS_SUPPORTED; + /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}: + direct playback supported. This value covers direct playback that is bitstream pass-through + such as compressed pass-through. */ + public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED = + AudioSystem.DIRECT_BITSTREAM_SUPPORTED; + + /** @hide */ + @IntDef(flag = true, prefix = "DIRECT_PLAYBACK_", value = { + DIRECT_PLAYBACK_NOT_SUPPORTED, + DIRECT_PLAYBACK_OFFLOAD_SUPPORTED, + DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED, + DIRECT_PLAYBACK_BITSTREAM_SUPPORTED} + ) + @Retention(RetentionPolicy.SOURCE) + public @interface AudioDirectPlaybackMode {} + + /** + * Returns a bitfield representing the different forms of direct playback currently available + * for a given audio format. + *

Direct playback means that the audio stream is not altered by the framework. The audio + * stream will not be resampled, volume scaled, downmixed or mixed with other content by + * the framework. But it may be wrapped in a higher level protocol such as IEC61937 for + * passthrough. + *

Checking for direct support can help the app select the representation of audio content + * that most closely matches the capabilities of the device and peripherals (e.g. A/V receiver) + * connected to it. Note that the provided stream can still be re-encoded or mixed with other + * streams, if needed. + * @param format the audio format (codec, sample rate, channels) being checked. + * @param attributes the {@link AudioAttributes} to be used for playback + * @return the direct playback mode available with given format and attributes. The returned + * value will be {@link #DIRECT_PLAYBACK_NOT_SUPPORTED} or a combination of + * {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED}, + * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} and + * {@link #DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}. Note that if + * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} is present in the returned value, + * then {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED} will be too. + */ + @AudioDirectPlaybackMode + public static int getDirectPlaybackSupport(@NonNull AudioFormat format, + @NonNull AudioAttributes attributes) { + Objects.requireNonNull(format); + Objects.requireNonNull(attributes); + return AudioSystem.getDirectPlaybackSupport(format, attributes); + } + //==================================================================== // Offload query /** @@ -2458,7 +2529,9 @@ public class AudioManager { * {@link #PLAYBACK_OFFLOAD_SUPPORTED} if offload playback is supported or * {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} if gapless transitions are * also supported. + * @deprecated Use {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)} instead */ + @Deprecated @AudioOffloadMode public static int getPlaybackOffloadSupport(@NonNull AudioFormat format, @NonNull AudioAttributes attributes) { diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e8792b3bb3c59..d2c49e0a0a32d 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1766,15 +1766,36 @@ public class AudioSystem */ public static native int setAllowedCapturePolicy(int uid, int flags); + /** + * @hide + * Direct playback modes supported by audio HAL implementation. + */ + public static final int DIRECT_NOT_SUPPORTED = 0; + public static final int DIRECT_OFFLOAD_SUPPORTED = 1; + public static final int DIRECT_OFFLOAD_GAPLESS_SUPPORTED = 3; + public static final int DIRECT_BITSTREAM_SUPPORTED = 4; + /** * @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_NOT_SUPPORTED = DIRECT_NOT_SUPPORTED; + public static final int OFFLOAD_SUPPORTED = DIRECT_OFFLOAD_SUPPORTED; public static final int OFFLOAD_GAPLESS_SUPPORTED = 2; + /** + * @hide + * Returns how direct playback of an audio format is currently available on the device. + * @param format the audio format (codec, sample rate, channels) being checked. + * @param attributes the {@link AudioAttributes} to be used for playback + * @return the direct playback mode available with given format and attributes. Any combination + * of {@link #DIRECT_NOT_SUPPORTED}, {@link #DIRECT_OFFLOAD_SUPPORTED}, + * {@link #DIRECT_OFFLOAD_GAPLESS_SUPPORTED} and {@link #DIRECT_BITSTREAM_SUPPORTED}. + */ + public static native int getDirectPlaybackSupport( + @NonNull AudioFormat format, @NonNull AudioAttributes attributes); + static int getOffloadSupport(@NonNull AudioFormat format, @NonNull AudioAttributes attr) { return native_get_offload_support(format.getEncoding(), format.getSampleRate(), format.getChannelMask(), format.getChannelIndexMask(), diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index ea692b940c81b..55c558f36880c 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1357,8 +1357,8 @@ public class AudioTrack extends PlayerBase throw new UnsupportedOperationException( "Offload and low latency modes are incompatible"); } - if (AudioSystem.getOffloadSupport(mFormat, mAttributes) - == AudioSystem.OFFLOAD_NOT_SUPPORTED) { + if (AudioSystem.getDirectPlaybackSupport(mFormat, mAttributes) + == AudioSystem.DIRECT_NOT_SUPPORTED) { throw new UnsupportedOperationException( "Cannot create AudioTrack, offload format / attributes not supported"); } @@ -1530,7 +1530,10 @@ public class AudioTrack extends PlayerBase * the audio data. * @param attributes a non-null {@link AudioAttributes} instance. * @return true if the given audio format can be played directly. + * @deprecated Use {@link AudioManager#getDirectPlaybackSupport(AudioFormat, AudioAttributes)} + * instead. */ + @Deprecated public static boolean isDirectPlaybackSupported(@NonNull AudioFormat format, @NonNull AudioAttributes attributes) { if (format == null) {