Add getDirectProfilesForAttributes

Allows any app to query AudioManager on the available direct
AudioProfiles for the specified AudioAttributes. Only the active paths
that will be actually used to output sound are returned.
go/audio-route-t

Bug: 190810951
Test: atest android.media.audio.cts.DirectAudioProfilesForAttributesTest
Change-Id: Ia5ce587addadb52725084bd32a6244577626f44d
This commit is contained in:
Dorin Drimus
2022-01-03 12:05:37 +01:00
parent 2d0e9deee6
commit eb9bf64d06
4 changed files with 144 additions and 1 deletions

View File

@@ -21110,6 +21110,7 @@ package android.media {
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 @NonNull public java.util.List<android.media.AudioProfile> getDirectProfilesForAttributes(@NonNull android.media.AudioAttributes);
method public int getEncodedSurroundMode();
method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
method public int getMode();

View File

@@ -1236,6 +1236,65 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP
return false;
}
static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile,
const audio_profile *nAudioProfile, bool useInMask) {
size_t numPositionMasks = 0;
size_t numIndexMasks = 0;
// count up how many masks are positional and indexed
for (size_t index = 0; index < nAudioProfile->num_channel_masks; index++) {
const audio_channel_mask_t mask = nAudioProfile->channel_masks[index];
if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
numIndexMasks++;
} else {
numPositionMasks++;
}
}
ScopedLocalRef<jintArray> jSamplingRates(env,
env->NewIntArray(nAudioProfile->num_sample_rates));
ScopedLocalRef<jintArray> jChannelMasks(env, env->NewIntArray(numPositionMasks));
ScopedLocalRef<jintArray> jChannelIndexMasks(env, env->NewIntArray(numIndexMasks));
if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) {
return AUDIO_JAVA_ERROR;
}
if (nAudioProfile->num_sample_rates) {
env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, nAudioProfile->num_sample_rates,
(jint *)nAudioProfile->sample_rates);
}
// put the masks in the output arrays
for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
maskIndex < nAudioProfile->num_channel_masks; maskIndex++) {
const audio_channel_mask_t mask = nAudioProfile->channel_masks[maskIndex];
if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
jint jMask = audio_channel_mask_get_bits(mask);
env->SetIntArrayRegion(jChannelIndexMasks.get(), indexedMaskIndex++, 1, &jMask);
} else {
jint jMask = useInMask ? inChannelMaskFromNative(mask) : outChannelMaskFromNative(mask);
env->SetIntArrayRegion(jChannelMasks.get(), posMaskIndex++, 1, &jMask);
}
}
int encapsulationType;
if (audioEncapsulationTypeFromNative(nAudioProfile->encapsulation_type, &encapsulationType) !=
NO_ERROR) {
ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type);
}
*jAudioProfile =
env->NewObject(gAudioProfileClass, gAudioProfileCstor,
audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(),
jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType);
if (jAudioProfile == nullptr) {
return AUDIO_JAVA_ERROR;
}
return AUDIO_JAVA_SUCCESS;
}
static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort,
const struct audio_port_v7 *nAudioPort) {
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
@@ -2814,6 +2873,50 @@ static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobj
return convertAudioDirectModeFromNative(directMode);
}
static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env, jobject thiz,
jobject jAudioAttributes,
jobject jAudioProfilesList) {
ALOGV("getDirectProfilesForAttributes");
if (jAudioAttributes == nullptr) {
ALOGE("jAudioAttributes is NULL");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (jAudioProfilesList == nullptr) {
ALOGE("jAudioProfilesList is NULL");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (!env->IsInstanceOf(jAudioProfilesList, gArrayListClass)) {
ALOGE("jAudioProfilesList not an ArrayList");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get());
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
return jStatus;
}
std::vector<audio_profile> audioProfiles;
status_t status = AudioSystem::getDirectProfilesForAttributes(paa.get(), &audioProfiles);
if (status != NO_ERROR) {
ALOGE("AudioSystem::getDirectProfilesForAttributes error %d", status);
jStatus = nativeToJavaStatus(status);
return jStatus;
}
for (const auto &audioProfile : audioProfiles) {
jobject jAudioProfile;
jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false);
if (jStatus != AUDIO_JAVA_SUCCESS) {
return jStatus;
}
env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile);
env->DeleteLocalRef(jAudioProfile);
}
return jStatus;
}
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] =
@@ -2960,7 +3063,10 @@ static const JNINativeMethod gMethods[] =
(void *)android_media_AudioSystem_canBeSpatialized},
{"getDirectPlaybackSupport",
"(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
(void *)android_media_AudioSystem_getDirectPlaybackSupport}};
(void *)android_media_AudioSystem_getDirectPlaybackSupport},
{"getDirectProfilesForAttributes",
"(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
(void *)android_media_AudioSystem_getDirectProfilesForAttributes}};
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",

View File

@@ -7670,6 +7670,33 @@ public class AudioManager {
}
}
/**
* Returns a list of direct {@link AudioProfile} that are supported for the specified
* {@link AudioAttributes}. This can be empty in case of an error or if no direct playback
* is possible.
*
* <p>Direct playback means that the audio stream is not resampled or downmixed
* by the framework. 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.
* <p>When using this information to inform your application which audio format to play,
* query again whenever audio output devices change (see {@link AudioDeviceCallback}).
* @param attributes a non-null {@link AudioAttributes} instance.
* @return a list of {@link AudioProfile}
*/
@NonNull
public List<AudioProfile> getDirectProfilesForAttributes(@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
ArrayList<AudioProfile> audioProfilesList = new ArrayList<>();
int status = AudioSystem.getDirectProfilesForAttributes(attributes, audioProfilesList);
if (status != SUCCESS) {
Log.w(TAG, "getDirectProfilesForAttributes failed.");
return new ArrayList<>();
}
return audioProfilesList;
}
/**
* @hide
* Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.

View File

@@ -2108,6 +2108,15 @@ public class AudioSystem
AudioFormat format,
AudioDeviceAttributes[] devices);
/**
* @hide
* @param attributes audio attributes describing the playback use case
* @param audioProfilesList the list of AudioProfiles that can be played as direct output
* @return {@link #SUCCESS} if the list of AudioProfiles was successfully created (can be empty)
*/
public static native int getDirectProfilesForAttributes(@NonNull AudioAttributes attributes,
@NonNull ArrayList<AudioProfile> audioProfilesList);
// Items shared with audio service
/**