Merge "Introduce AudioProfile to describe the capabilities of audio ports."
This commit is contained in:
committed by
Android (Google) Code Review
commit
7349aa999b
@@ -24309,6 +24309,7 @@ package android.media {
|
||||
|
||||
public final class AudioDeviceInfo {
|
||||
method @NonNull public String getAddress();
|
||||
method @NonNull public java.util.List<android.media.AudioProfile> getAudioProfiles();
|
||||
method @NonNull public int[] getChannelCounts();
|
||||
method @NonNull public int[] getChannelIndexMasks();
|
||||
method @NonNull public int[] getChannelMasks();
|
||||
@@ -24726,6 +24727,13 @@ package android.media {
|
||||
method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
|
||||
}
|
||||
|
||||
public class AudioProfile {
|
||||
method @NonNull public int[] getChannelIndexMasks();
|
||||
method @NonNull public int[] getChannelMasks();
|
||||
method public int getFormat();
|
||||
method @NonNull public int[] getSampleRates();
|
||||
}
|
||||
|
||||
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
|
||||
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
|
||||
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
|
||||
|
||||
@@ -48,8 +48,9 @@ static const char* const kClassPathName = "android/media/AudioSystem";
|
||||
|
||||
static jclass gArrayListClass;
|
||||
static struct {
|
||||
jmethodID add;
|
||||
jmethodID toArray;
|
||||
jmethodID cstor;
|
||||
jmethodID add;
|
||||
jmethodID toArray;
|
||||
} gArrayListMethods;
|
||||
|
||||
static jclass gBooleanClass;
|
||||
@@ -185,6 +186,9 @@ jmethodID gMidAudioRecordRoutingProxy_release;
|
||||
jclass gClsAudioTrackRoutingProxy;
|
||||
jclass gClsAudioRecordRoutingProxy;
|
||||
|
||||
jclass gAudioProfileClass;
|
||||
jmethodID gAudioProfileCstor;
|
||||
|
||||
static Mutex gLock;
|
||||
|
||||
enum AudioError {
|
||||
@@ -807,13 +811,6 @@ android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz)
|
||||
return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger());
|
||||
}
|
||||
|
||||
|
||||
static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role)
|
||||
{
|
||||
return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
|
||||
((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
|
||||
}
|
||||
|
||||
static void convertAudioGainConfigToNative(JNIEnv *env,
|
||||
struct audio_gain_config *nAudioGainConfig,
|
||||
const jobject jAudioGainConfig,
|
||||
@@ -873,7 +870,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env,
|
||||
configMask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
|
||||
bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig);
|
||||
audio_channel_mask_t nMask;
|
||||
jint jMask = env->GetIntField(jAudioPortConfig,
|
||||
gAudioPortConfigFields.mChannelMask);
|
||||
@@ -1000,7 +997,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env,
|
||||
audioportCreated = true;
|
||||
}
|
||||
|
||||
bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
|
||||
bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig);
|
||||
|
||||
audio_channel_mask_t nMask;
|
||||
jint jMask;
|
||||
@@ -1137,15 +1134,6 @@ exit:
|
||||
return jStatus;
|
||||
}
|
||||
|
||||
static bool hasFormat(int* formats, size_t size, int format) {
|
||||
for (size_t index = 0; index < size; index++) {
|
||||
if (formats[index] == format) {
|
||||
return true; // found
|
||||
}
|
||||
}
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// TODO: pull out to separate file
|
||||
template <typename T, size_t N>
|
||||
static constexpr size_t array_size(const T (&)[N]) {
|
||||
@@ -1167,43 +1155,46 @@ static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encaps
|
||||
return result;
|
||||
}
|
||||
|
||||
static jint convertAudioPortFromNative(JNIEnv *env,
|
||||
jobject *jAudioPort, const struct audio_port *nAudioPort)
|
||||
{
|
||||
static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioPort,
|
||||
std::stringstream &ss) {
|
||||
ss << " num_audio_profiles " << nAudioPort->num_audio_profiles << " num_gains "
|
||||
<< nAudioPort->num_gains;
|
||||
if (nAudioPort->num_audio_profiles > array_size(nAudioPort->audio_profiles) ||
|
||||
nAudioPort->num_gains > array_size(nAudioPort->gains)) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) {
|
||||
ss << " (" << i << ") audio profile,"
|
||||
<< " num_sample_rates " << nAudioPort->audio_profiles[i].num_sample_rates
|
||||
<< " num_channel_masks " << nAudioPort->audio_profiles[i].num_channel_masks;
|
||||
if (nAudioPort->audio_profiles[i].num_sample_rates >
|
||||
array_size(nAudioPort->audio_profiles[i].sample_rates) ||
|
||||
nAudioPort->audio_profiles[i].num_channel_masks >
|
||||
array_size(nAudioPort->audio_profiles[i].channel_masks)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort,
|
||||
const struct audio_port_v7 *nAudioPort) {
|
||||
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
|
||||
jintArray jSamplingRates = NULL;
|
||||
jintArray jChannelMasks = NULL;
|
||||
jintArray jChannelIndexMasks = NULL;
|
||||
jintArray jEncapsulationModes = NULL;
|
||||
jintArray jEncapsulationMetadataTypes = NULL;
|
||||
int* cFormats = NULL;
|
||||
jintArray jFormats = NULL;
|
||||
jobjectArray jGains = NULL;
|
||||
jobject jHandle = NULL;
|
||||
jobject jAudioPortConfig = NULL;
|
||||
jstring jDeviceName = NULL;
|
||||
jobject jAudioProfiles = NULL;
|
||||
bool useInMask;
|
||||
size_t numPositionMasks = 0;
|
||||
size_t numIndexMasks = 0;
|
||||
size_t numUniqueFormats = 0;
|
||||
|
||||
ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
|
||||
nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name);
|
||||
|
||||
// Verify audio port array count info.
|
||||
if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates)
|
||||
|| nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks)
|
||||
|| nAudioPort->num_formats > array_size(nAudioPort->formats)
|
||||
|| nAudioPort->num_gains > array_size(nAudioPort->gains)) {
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "convertAudioPortFromNative array count out of bounds:"
|
||||
<< " num_sample_rates " << nAudioPort->num_sample_rates
|
||||
<< " num_channel_masks " << nAudioPort->num_channel_masks
|
||||
<< " num_formats " << nAudioPort->num_formats
|
||||
<< " num_gains " << nAudioPort->num_gains
|
||||
;
|
||||
std::string s = ss.str();
|
||||
if (std::stringstream ss; isAudioPortArrayCountOutOfBounds(nAudioPort, ss)) {
|
||||
std::string s = "convertAudioPortFromNative array count out of bounds:" + ss.str();
|
||||
|
||||
// Prefer to log through Java wtf instead of native ALOGE.
|
||||
ScopedLocalRef<jclass> jLogClass(env, env->FindClass("android/util/Log"));
|
||||
@@ -1222,69 +1213,68 @@ static jint convertAudioPortFromNative(JNIEnv *env,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
|
||||
if (jSamplingRates == NULL) {
|
||||
useInMask = audio_has_input_direction(nAudioPort->type, nAudioPort->role);
|
||||
jAudioProfiles = env->NewObject(gArrayListClass, gArrayListMethods.cstor);
|
||||
if (jAudioProfiles == nullptr) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (nAudioPort->num_sample_rates) {
|
||||
env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates,
|
||||
(jint *)nAudioPort->sample_rates);
|
||||
}
|
||||
|
||||
// count up how many masks are positional and indexed
|
||||
for(size_t index = 0; index < nAudioPort->num_channel_masks; index++) {
|
||||
const audio_channel_mask_t mask = nAudioPort->channel_masks[index];
|
||||
if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
|
||||
numIndexMasks++;
|
||||
} else {
|
||||
numPositionMasks++;
|
||||
}
|
||||
}
|
||||
|
||||
jChannelMasks = env->NewIntArray(numPositionMasks);
|
||||
if (jChannelMasks == NULL) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
jChannelIndexMasks = env->NewIntArray(numIndexMasks);
|
||||
if (jChannelIndexMasks == NULL) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
|
||||
|
||||
// put the masks in the output arrays
|
||||
for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
|
||||
maskIndex < nAudioPort->num_channel_masks; maskIndex++) {
|
||||
const audio_channel_mask_t mask = nAudioPort->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, indexedMaskIndex++, 1, &jMask);
|
||||
} else {
|
||||
jint jMask = useInMask ? inChannelMaskFromNative(mask)
|
||||
: outChannelMaskFromNative(mask);
|
||||
env->SetIntArrayRegion(jChannelMasks, posMaskIndex++, 1, &jMask);
|
||||
}
|
||||
}
|
||||
|
||||
// formats
|
||||
if (nAudioPort->num_formats != 0) {
|
||||
cFormats = new int[nAudioPort->num_formats];
|
||||
for (size_t index = 0; index < nAudioPort->num_formats; index++) {
|
||||
int format = audioFormatFromNative(nAudioPort->formats[index]);
|
||||
if (!hasFormat(cFormats, numUniqueFormats, format)) {
|
||||
cFormats[numUniqueFormats++] = format;
|
||||
for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) {
|
||||
size_t numPositionMasks = 0;
|
||||
size_t numIndexMasks = 0;
|
||||
// count up how many masks are positional and indexed
|
||||
for (size_t index = 0; index < nAudioPort->audio_profiles[i].num_channel_masks; index++) {
|
||||
const audio_channel_mask_t mask = nAudioPort->audio_profiles[i].channel_masks[index];
|
||||
if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
|
||||
numIndexMasks++;
|
||||
} else {
|
||||
numPositionMasks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
jFormats = env->NewIntArray(numUniqueFormats);
|
||||
if (jFormats == NULL) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (numUniqueFormats != 0) {
|
||||
env->SetIntArrayRegion(jFormats, 0, numUniqueFormats, cFormats);
|
||||
|
||||
ScopedLocalRef<jintArray> jSamplingRates(env,
|
||||
env->NewIntArray(nAudioPort->audio_profiles[i]
|
||||
.num_sample_rates));
|
||||
ScopedLocalRef<jintArray> jChannelMasks(env, env->NewIntArray(numPositionMasks));
|
||||
ScopedLocalRef<jintArray> jChannelIndexMasks(env, env->NewIntArray(numIndexMasks));
|
||||
if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nAudioPort->audio_profiles[i].num_sample_rates) {
|
||||
env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/,
|
||||
nAudioPort->audio_profiles[i].num_sample_rates,
|
||||
(jint *)nAudioPort->audio_profiles[i].sample_rates);
|
||||
}
|
||||
|
||||
// put the masks in the output arrays
|
||||
for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0;
|
||||
maskIndex < nAudioPort->audio_profiles[i].num_channel_masks; maskIndex++) {
|
||||
const audio_channel_mask_t mask =
|
||||
nAudioPort->audio_profiles[i].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);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedLocalRef<jobject>
|
||||
jAudioProfile(env,
|
||||
env->NewObject(gAudioProfileClass, gAudioProfileCstor,
|
||||
audioFormatFromNative(
|
||||
nAudioPort->audio_profiles[i].format),
|
||||
jSamplingRates.get(), jChannelMasks.get(),
|
||||
jChannelIndexMasks.get()));
|
||||
if (jAudioProfile == nullptr) {
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile.get());
|
||||
}
|
||||
|
||||
// gains
|
||||
@@ -1342,18 +1332,16 @@ static jint convertAudioPortFromNative(JNIEnv *env,
|
||||
convertEncapsulationInfoFromNative(env,
|
||||
nAudioPort->ext.device
|
||||
.encapsulation_metadata_types);
|
||||
*jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle,
|
||||
jDeviceName, jSamplingRates, jChannelMasks, jChannelIndexMasks,
|
||||
jFormats, jGains, nAudioPort->ext.device.type, jAddress,
|
||||
jEncapsulationModes, jEncapsulationMetadataTypes);
|
||||
*jAudioPort =
|
||||
env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName,
|
||||
jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress,
|
||||
jEncapsulationModes, jEncapsulationMetadataTypes);
|
||||
env->DeleteLocalRef(jAddress);
|
||||
} else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
|
||||
ALOGV("convertAudioPortFromNative is a mix");
|
||||
*jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
|
||||
jHandle, nAudioPort->ext.mix.handle,
|
||||
nAudioPort->role, jDeviceName,
|
||||
jSamplingRates, jChannelMasks, jChannelIndexMasks,
|
||||
jFormats, jGains);
|
||||
*jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle,
|
||||
nAudioPort->ext.mix.handle, nAudioPort->role, jDeviceName,
|
||||
jAudioProfiles, jGains);
|
||||
} else {
|
||||
ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
|
||||
jStatus = (jint)AUDIO_JAVA_ERROR;
|
||||
@@ -1378,26 +1366,14 @@ exit:
|
||||
if (jDeviceName != NULL) {
|
||||
env->DeleteLocalRef(jDeviceName);
|
||||
}
|
||||
if (jSamplingRates != NULL) {
|
||||
env->DeleteLocalRef(jSamplingRates);
|
||||
}
|
||||
if (jChannelMasks != NULL) {
|
||||
env->DeleteLocalRef(jChannelMasks);
|
||||
}
|
||||
if (jChannelIndexMasks != NULL) {
|
||||
env->DeleteLocalRef(jChannelIndexMasks);
|
||||
}
|
||||
if (jEncapsulationModes != NULL) {
|
||||
env->DeleteLocalRef(jEncapsulationModes);
|
||||
}
|
||||
if (jEncapsulationMetadataTypes != NULL) {
|
||||
env->DeleteLocalRef(jEncapsulationMetadataTypes);
|
||||
}
|
||||
if (cFormats != NULL) {
|
||||
delete[] cFormats;
|
||||
}
|
||||
if (jFormats != NULL) {
|
||||
env->DeleteLocalRef(jFormats);
|
||||
if (jAudioProfiles != NULL) {
|
||||
env->DeleteLocalRef(jAudioProfiles);
|
||||
}
|
||||
if (jGains != NULL) {
|
||||
env->DeleteLocalRef(jGains);
|
||||
@@ -1436,7 +1412,7 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
|
||||
unsigned int generation;
|
||||
unsigned int numPorts;
|
||||
jint *nGeneration;
|
||||
struct audio_port *nPorts = NULL;
|
||||
struct audio_port_v7 *nPorts = nullptr;
|
||||
int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
|
||||
jint jStatus;
|
||||
|
||||
@@ -1448,11 +1424,8 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
|
||||
}
|
||||
|
||||
numPorts = 0;
|
||||
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
|
||||
AUDIO_PORT_TYPE_NONE,
|
||||
&numPorts,
|
||||
NULL,
|
||||
&generation1);
|
||||
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
|
||||
nullptr, &generation1);
|
||||
if (status != NO_ERROR) {
|
||||
ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
|
||||
break;
|
||||
@@ -1461,13 +1434,10 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
|
||||
jStatus = (jint)AUDIO_JAVA_SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
|
||||
nPorts = (struct audio_port_v7 *)realloc(nPorts, numPorts * sizeof(struct audio_port_v7));
|
||||
|
||||
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
|
||||
AUDIO_PORT_TYPE_NONE,
|
||||
&numPorts,
|
||||
nPorts,
|
||||
&generation);
|
||||
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
|
||||
nPorts, &generation);
|
||||
ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
|
||||
numPorts, generation, generation1);
|
||||
} while (generation1 != generation && status == NO_ERROR);
|
||||
@@ -2671,6 +2641,7 @@ int register_android_media_AudioSystem(JNIEnv *env)
|
||||
|
||||
jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
|
||||
gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
|
||||
gArrayListMethods.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
|
||||
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, "toArray", "()[Ljava/lang/Object;");
|
||||
|
||||
@@ -2733,7 +2704,7 @@ int register_android_media_AudioSystem(JNIEnv *env)
|
||||
gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass);
|
||||
gAudioDevicePortCstor =
|
||||
GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
|
||||
"(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I"
|
||||
"(Landroid/media/AudioHandle;Ljava/lang/String;Ljava/util/List;"
|
||||
"[Landroid/media/AudioGain;ILjava/lang/String;[I[I)V");
|
||||
|
||||
// When access AudioPort as AudioDevicePort
|
||||
@@ -2743,8 +2714,10 @@ int register_android_media_AudioSystem(JNIEnv *env)
|
||||
|
||||
jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
|
||||
gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
|
||||
gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
|
||||
"(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V");
|
||||
gAudioMixPortCstor =
|
||||
GetMethodIDOrDie(env, audioMixPortClass, "<init>",
|
||||
"(Landroid/media/AudioHandle;IILjava/lang/String;Ljava/util/List;"
|
||||
"[Landroid/media/AudioGain;)V");
|
||||
|
||||
jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain");
|
||||
gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass);
|
||||
@@ -2846,6 +2819,10 @@ int register_android_media_AudioSystem(JNIEnv *env)
|
||||
jclass listClass = FindClassOrDie(env, "java/util/List");
|
||||
gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z");
|
||||
|
||||
jclass audioProfileClass = FindClassOrDie(env, "android/media/AudioProfile");
|
||||
gAudioProfileClass = MakeGlobalRefOrDie(env, audioProfileClass);
|
||||
gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "<init>", "(I[I[I[I)V");
|
||||
|
||||
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
|
||||
|
||||
RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.util.SparseIntArray;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -497,6 +498,13 @@ public final class AudioDeviceInfo {
|
||||
return encodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of {@link AudioProfile} supported by the audio devices.
|
||||
*/
|
||||
public @NonNull List<AudioProfile> getAudioProfiles() {
|
||||
return mPort.profiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of supported encapsulation modes for the device.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The AudioDevicePort is a specialized type of AudioPort
|
||||
@@ -57,6 +58,18 @@ public class AudioDevicePort extends AudioPort {
|
||||
mEncapsulationMetadataTypes = encapsulationMetadataTypes;
|
||||
}
|
||||
|
||||
AudioDevicePort(AudioHandle handle, String deviceName, List<AudioProfile> profiles,
|
||||
AudioGain[] gains, int type, String address, int[] encapsulationModes,
|
||||
@AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes) {
|
||||
super(handle,
|
||||
AudioManager.isInputDevice(type) ? AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
|
||||
deviceName, profiles, gains);
|
||||
mType = type;
|
||||
mAddress = address;
|
||||
mEncapsulationModes = encapsulationModes;
|
||||
mEncapsulationMetadataTypes = encapsulationMetadataTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,8 @@ package android.media;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The AudioMixPort is a specialized type of AudioPort
|
||||
* describing an audio mix or stream at an input or output stream of the audio
|
||||
@@ -42,6 +44,12 @@ public class AudioMixPort extends AudioPort {
|
||||
mIoHandle = ioHandle;
|
||||
}
|
||||
|
||||
AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName,
|
||||
List<AudioProfile> profiles, AudioGain[] gains) {
|
||||
super(handle, role, deviceName, profiles, gains);
|
||||
mIoHandle = ioHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a specific configuration of this audio mix port for use by methods
|
||||
* like AudioManager.connectAudioPatch().
|
||||
|
||||
@@ -19,6 +19,13 @@ package android.media;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* An audio port is a node of the audio framework or hardware that can be connected to or
|
||||
* disconnect from another audio node to create a specific audio routing configuration.
|
||||
@@ -78,6 +85,7 @@ public class AudioPort {
|
||||
private final int[] mChannelMasks;
|
||||
private final int[] mChannelIndexMasks;
|
||||
private final int[] mFormats;
|
||||
private final List<AudioProfile> mProfiles;
|
||||
@UnsupportedAppUsage
|
||||
private final AudioGain[] mGains;
|
||||
@UnsupportedAppUsage
|
||||
@@ -87,7 +95,6 @@ public class AudioPort {
|
||||
AudioPort(AudioHandle handle, int role, String name,
|
||||
int[] samplingRates, int[] channelMasks, int[] channelIndexMasks,
|
||||
int[] formats, AudioGain[] gains) {
|
||||
|
||||
mHandle = handle;
|
||||
mRole = role;
|
||||
mName = name;
|
||||
@@ -96,6 +103,39 @@ public class AudioPort {
|
||||
mChannelIndexMasks = channelIndexMasks;
|
||||
mFormats = formats;
|
||||
mGains = gains;
|
||||
mProfiles = new ArrayList<>();
|
||||
if (mFormats != null) {
|
||||
for (int format : mFormats) {
|
||||
mProfiles.add(new AudioProfile(
|
||||
format, samplingRates, channelMasks, channelIndexMasks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioPort(AudioHandle handle, int role, String name,
|
||||
List<AudioProfile> profiles, AudioGain[] gains) {
|
||||
mHandle = handle;
|
||||
mRole = role;
|
||||
mName = name;
|
||||
mProfiles = profiles;
|
||||
mGains = gains;
|
||||
Set<Integer> formats = new HashSet<>();
|
||||
Set<Integer> samplingRates = new HashSet<>();
|
||||
Set<Integer> channelMasks = new HashSet<>();
|
||||
Set<Integer> channelIndexMasks = new HashSet<>();
|
||||
for (AudioProfile profile : profiles) {
|
||||
formats.add(profile.getFormat());
|
||||
samplingRates.addAll(Arrays.stream(profile.getSampleRates()).boxed()
|
||||
.collect(Collectors.toList()));
|
||||
channelMasks.addAll(Arrays.stream(profile.getChannelMasks()).boxed()
|
||||
.collect(Collectors.toList()));
|
||||
channelIndexMasks.addAll(Arrays.stream(profile.getChannelIndexMasks()).boxed()
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
mSamplingRates = samplingRates.stream().mapToInt(Number::intValue).toArray();
|
||||
mChannelMasks = channelMasks.stream().mapToInt(Number::intValue).toArray();
|
||||
mChannelIndexMasks = channelIndexMasks.stream().mapToInt(Number::intValue).toArray();
|
||||
mFormats = formats.stream().mapToInt(Number::intValue).toArray();
|
||||
}
|
||||
|
||||
AudioHandle handle() {
|
||||
@@ -162,6 +202,13 @@ public class AudioPort {
|
||||
return mFormats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of supported audio profiles
|
||||
*/
|
||||
public List<AudioProfile> profiles() {
|
||||
return mProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of gain descriptors
|
||||
* Empty array if this port does not have gain control
|
||||
|
||||
68
media/java/android/media/AudioProfile.java
Normal file
68
media/java/android/media/AudioProfile.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* AudioProfile is a class used by {@link AudioDeviceInfo} to list the audio formats, sampling rates
|
||||
* and channel masks supported.
|
||||
* Each AudioProfile is specific to an audio format and lists all sampling rates and channel masks
|
||||
* supported for this format.
|
||||
*/
|
||||
public class AudioProfile {
|
||||
private final int mFormat;
|
||||
private final int[] mSamplingRates;
|
||||
private final int[] mChannelMasks;
|
||||
private final int[] mChannelIndexMasks;
|
||||
|
||||
AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks,
|
||||
@NonNull int[] channelIndexMasks) {
|
||||
mFormat = format;
|
||||
mSamplingRates = samplingRates;
|
||||
mChannelMasks = channelMasks;
|
||||
mChannelIndexMasks = channelIndexMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encoding format for this AudioProfile.
|
||||
*/
|
||||
public @AudioFormat.Encoding int getFormat() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an array of channel position masks that are associated with the encoding format.
|
||||
*/
|
||||
public @NonNull int[] getChannelMasks() {
|
||||
return mChannelMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an array of channel index masks that are associated with the encoding format.
|
||||
*/
|
||||
public @NonNull int[] getChannelIndexMasks() {
|
||||
return mChannelIndexMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an array of sample rates that are associated with the encoding format.
|
||||
*/
|
||||
public @NonNull int[] getSampleRates() {
|
||||
return mSamplingRates;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user