From e1f9659e5de244f9e0d9607f184732f15dcfdc97 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 29 Oct 2021 22:34:26 +0000 Subject: [PATCH] Enhance channel masks conversion between AIDL and SDK Provide conversion for layouts not listed in AudioChannelLayout::LAYOUT_* constants. In this case the conversion is performed on bit-by-bit correspondence between AudioChannelLayout::CHANNEL_* constants and SDK channel bits from AudioFormat. Added missing input channel bits to AudioFormat to match native. Currently hidden, since we would need to update docs if we reveal these bits and masks to apps. Bug: 198812639 Bug: 204362366 Test: atest AidlConversionUnitTests Change-Id: I7cb80fef2bfe9513f16dc632b22ef81b1e0f0526 --- media/java/android/media/AudioFormat.java | 36 +++++ .../media/audio/common/AidlConversion.java | 137 +++++++++++++++++- .../media/AidlConversionUnitTests.java | 27 ++++ 3 files changed, 196 insertions(+), 4 deletions(-) diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 5cdb89838159d..d06bbc61f6ebd 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -675,8 +675,44 @@ public final class AudioFormat implements Parcelable { public static final int CHANNEL_IN_Z_AXIS = 0x2000; public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000; public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000; + // CHANNEL_IN_BACK_LEFT to TOP_RIGHT are not microphone positions + // but surround channels which are used when dealing with multi-channel inputs, + // e.g. via HDMI input on TV. + /** @hide */ + public static final int CHANNEL_IN_BACK_LEFT = 0x10000; + /** @hide */ + public static final int CHANNEL_IN_BACK_RIGHT = 0x20000; + /** @hide */ + public static final int CHANNEL_IN_CENTER = 0x40000; + /** @hide */ + public static final int CHANNEL_IN_LOW_FREQUENCY = 0x100000; + /** @hide */ + public static final int CHANNEL_IN_TOP_LEFT = 0x200000; + /** @hide */ + public static final int CHANNEL_IN_TOP_RIGHT = 0x400000; public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT; public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT); + // Surround channel masks corresponding to output masks, used for + // surround sound inputs. + /** @hide */ + public static final int CHANNEL_IN_2POINT0POINT2 = ( + CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT | CHANNEL_IN_TOP_RIGHT); + /** @hide */ + public static final int CHANNEL_IN_2POINT1POINT2 = ( + CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT | CHANNEL_IN_TOP_RIGHT + | CHANNEL_IN_LOW_FREQUENCY); + /** @hide */ + public static final int CHANNEL_IN_3POINT0POINT2 = ( + CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT + | CHANNEL_IN_TOP_RIGHT); + /** @hide */ + public static final int CHANNEL_IN_3POINT1POINT2 = ( + CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT + | CHANNEL_IN_TOP_RIGHT | CHANNEL_IN_LOW_FREQUENCY); + /** @hide */ + public static final int CHANNEL_IN_5POINT1 = ( + CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_BACK_LEFT + | CHANNEL_IN_BACK_RIGHT | CHANNEL_IN_LOW_FREQUENCY); /** @hide */ public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK; // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java index f96ba8cd7cb58..1053fb717fda9 100644 --- a/media/java/android/media/audio/common/AidlConversion.java +++ b/media/java/android/media/audio/common/AidlConversion.java @@ -131,6 +131,124 @@ public class AidlConversion { /** Convert from legacy audio_usage_t to AIDL AudioUsage. */ public static native int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy); + /** Convert from a channel bit of AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_* bit. */ + private static int aidl2api_AudioChannelLayoutBit_AudioFormatChannel( + int aidlBit, boolean isInput) { + if (isInput) { + switch (aidlBit) { + case AudioChannelLayout.CHANNEL_FRONT_LEFT: + return AudioFormat.CHANNEL_IN_LEFT; + case AudioChannelLayout.CHANNEL_FRONT_RIGHT: + return AudioFormat.CHANNEL_IN_RIGHT; + case AudioChannelLayout.CHANNEL_FRONT_CENTER: + return AudioFormat.CHANNEL_IN_CENTER; + case AudioChannelLayout.CHANNEL_BACK_CENTER: + return AudioFormat.CHANNEL_IN_BACK; + // CHANNEL_IN_*_PROCESSED not supported + // CHANNEL_IN_PRESSURE not supported + // CHANNEL_IN_*_AXIS not supported + // CHANNEL_IN_VOICE_* not supported + case AudioChannelLayout.CHANNEL_BACK_LEFT: + return AudioFormat.CHANNEL_IN_BACK_LEFT; + case AudioChannelLayout.CHANNEL_BACK_RIGHT: + return AudioFormat.CHANNEL_IN_BACK_RIGHT; + case AudioChannelLayout.CHANNEL_LOW_FREQUENCY: + return AudioFormat.CHANNEL_IN_LOW_FREQUENCY; + case AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT: + return AudioFormat.CHANNEL_IN_TOP_LEFT; + case AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT: + return AudioFormat.CHANNEL_IN_TOP_RIGHT; + default: + return AudioFormat.CHANNEL_INVALID; + } + } else { + switch (aidlBit) { + case AudioChannelLayout.CHANNEL_FRONT_LEFT: + return AudioFormat.CHANNEL_OUT_FRONT_LEFT; + case AudioChannelLayout.CHANNEL_FRONT_RIGHT: + return AudioFormat.CHANNEL_OUT_FRONT_RIGHT; + case AudioChannelLayout.CHANNEL_FRONT_CENTER: + return AudioFormat.CHANNEL_OUT_FRONT_CENTER; + case AudioChannelLayout.CHANNEL_LOW_FREQUENCY: + return AudioFormat.CHANNEL_OUT_LOW_FREQUENCY; + case AudioChannelLayout.CHANNEL_BACK_LEFT: + return AudioFormat.CHANNEL_OUT_BACK_LEFT; + case AudioChannelLayout.CHANNEL_BACK_RIGHT: + return AudioFormat.CHANNEL_OUT_BACK_RIGHT; + case AudioChannelLayout.CHANNEL_FRONT_LEFT_OF_CENTER: + return AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER; + case AudioChannelLayout.CHANNEL_FRONT_RIGHT_OF_CENTER: + return AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; + case AudioChannelLayout.CHANNEL_BACK_CENTER: + return AudioFormat.CHANNEL_OUT_BACK_CENTER; + case AudioChannelLayout.CHANNEL_SIDE_LEFT: + return AudioFormat.CHANNEL_OUT_SIDE_LEFT; + case AudioChannelLayout.CHANNEL_SIDE_RIGHT: + return AudioFormat.CHANNEL_OUT_SIDE_RIGHT; + case AudioChannelLayout.CHANNEL_TOP_CENTER: + return AudioFormat.CHANNEL_OUT_TOP_CENTER; + case AudioChannelLayout.CHANNEL_TOP_FRONT_LEFT: + return AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT; + case AudioChannelLayout.CHANNEL_TOP_FRONT_CENTER: + return AudioFormat.CHANNEL_OUT_TOP_FRONT_CENTER; + case AudioChannelLayout.CHANNEL_TOP_FRONT_RIGHT: + return AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT; + case AudioChannelLayout.CHANNEL_TOP_BACK_LEFT: + return AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT; + case AudioChannelLayout.CHANNEL_TOP_BACK_CENTER: + return AudioFormat.CHANNEL_OUT_TOP_BACK_CENTER; + case AudioChannelLayout.CHANNEL_TOP_BACK_RIGHT: + return AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT; + case AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT: + return AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT; + case AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT: + return AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT; + case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_LEFT: + return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT; + case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_CENTER: + return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER; + case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_RIGHT: + return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT; + case AudioChannelLayout.CHANNEL_LOW_FREQUENCY_2: + return AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2; + case AudioChannelLayout.CHANNEL_FRONT_WIDE_LEFT: + return AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT; + case AudioChannelLayout.CHANNEL_FRONT_WIDE_RIGHT: + return AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT; + case AudioChannelLayout.CHANNEL_HAPTIC_A: + return AudioFormat.CHANNEL_OUT_HAPTIC_A; + case AudioChannelLayout.CHANNEL_HAPTIC_B: + return AudioFormat.CHANNEL_OUT_HAPTIC_B; + default: + return AudioFormat.CHANNEL_INVALID; + } + } + } + + /** + * Convert from a channel bitmask of AIDL AudioChannelLayout to + * SDK AudioFormat.CHANNEL_* bitmask. + */ + private static int aidl2api_AudioChannelLayoutBitMask_AudioFormatChannelMask( + int aidlBitMask, boolean isInput) { + int apiMask = 0; + for (int bit = 1 << 31; bit != 0; bit >>>= 1) { + if ((aidlBitMask & bit) == bit) { + int apiBit = aidl2api_AudioChannelLayoutBit_AudioFormatChannel(bit, isInput); + if (apiBit != AudioFormat.CHANNEL_INVALID) { + apiMask |= apiBit; + aidlBitMask &= ~bit; + if (aidlBitMask == 0) { + return apiMask; + } + } else { + break; + } + } + } + return AudioFormat.CHANNEL_INVALID; + } + /** Convert from AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_*. */ public static int aidl2api_AudioChannelLayout_AudioFormatChannelMask( @NonNull AudioChannelLayout aidlMask, boolean isInput) { @@ -155,8 +273,17 @@ public class AidlConversion { return AudioFormat.CHANNEL_IN_STEREO; case AudioChannelLayout.LAYOUT_FRONT_BACK: return AudioFormat.CHANNEL_IN_FRONT_BACK; - default: - return AudioFormat.CHANNEL_INVALID; + case AudioChannelLayout.LAYOUT_2POINT0POINT2: + return AudioFormat.CHANNEL_IN_2POINT0POINT2; + case AudioChannelLayout.LAYOUT_2POINT1POINT2: + return AudioFormat.CHANNEL_IN_2POINT1POINT2; + case AudioChannelLayout.LAYOUT_3POINT0POINT2: + return AudioFormat.CHANNEL_IN_3POINT0POINT2; + case AudioChannelLayout.LAYOUT_3POINT1POINT2: + return AudioFormat.CHANNEL_IN_3POINT1POINT2; + case AudioChannelLayout.LAYOUT_5POINT1: + return AudioFormat.CHANNEL_IN_5POINT1; + default: // fall through } } else { switch (aidlMask.getLayoutMask()) { @@ -264,10 +391,12 @@ public class AidlConversion { case AudioChannelLayout.LAYOUT_FRONT_BACK: return AudioFormat.CHANNEL_OUT_FRONT_CENTER | AudioFormat.CHANNEL_OUT_BACK_CENTER; - default: - return AudioFormat.CHANNEL_INVALID; + default: // fall through } } + // If a match for a predefined layout wasn't found, make a custom one from bits. + return aidl2api_AudioChannelLayoutBitMask_AudioFormatChannelMask( + aidlMask.getLayoutMask(), isInput); case AudioChannelLayout.voiceMask: if (isInput) { switch (aidlMask.getVoiceMask()) { diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java index 5f64d2096ee70..414de89f82185 100644 --- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java +++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java @@ -66,6 +66,19 @@ public final class AidlConversionUnitTests { assertEquals(AudioFormat.CHANNEL_OUT_MONO, api); } + @Test + public void testAudioChannelConversionApiOutputMask() { + final AudioChannelLayout aidl = AudioChannelLayout.layoutMask( + AudioChannelLayout.CHANNEL_FRONT_LEFT | AudioChannelLayout.CHANNEL_FRONT_RIGHT + | AudioChannelLayout.CHANNEL_FRONT_WIDE_LEFT + | AudioChannelLayout.CHANNEL_FRONT_WIDE_RIGHT); + final int api = AidlConversion.aidl2api_AudioChannelLayout_AudioFormatChannelMask( + aidl, false /*isInput*/); + assertEquals(AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT + | AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT + | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT, api); + } + @Test public void testAudioChannelConversionApiInput() { final AudioChannelLayout aidl = AudioChannelLayout.layoutMask( @@ -75,6 +88,20 @@ public final class AidlConversionUnitTests { assertEquals(AudioFormat.CHANNEL_IN_MONO, api); } + @Test + public void testAudioChannelConversionApiInputMask() { + final AudioChannelLayout aidl = AudioChannelLayout.layoutMask( + AudioChannelLayout.CHANNEL_FRONT_LEFT | AudioChannelLayout.CHANNEL_FRONT_RIGHT + | AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT + | AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT); + final int api = AidlConversion.aidl2api_AudioChannelLayout_AudioFormatChannelMask( + aidl, true /*isInput*/); + assertEquals(AudioFormat.CHANNEL_IN_LEFT | AudioFormat.CHANNEL_IN_RIGHT + // | AudioFormat.CHANNEL_IN_TOP_LEFT | AudioFormat.CHANNEL_IN_TOP_RIGHT, + | 0x200000 | 0x400000, // TODO: Replace with names when revealed. + api); + } + @Test public void testAudioChannelConversionApiIndex() { final AudioChannelLayout aidl = AudioChannelLayout.indexMask(