从Android5.0之后,AOSP引入了AudioPatch概念,用于表示音频中端到端的连接关系。
从代码中推测,AudioPatch主要用于连接source与sink。这里的source,既可以是实实在在的音频输入设备,如MIC,也可以是底层中混音后的音频流;这里的sink则表示输出设备,如扬声器、耳机等。
引入这个概念以后,对音频来讲,显然抽象程度更高,更容易理解宏观上的概念,如插拔耳机时,只要更换连接就可以,但更不易理解实现的细节,如音频数据在插拔耳机时如何运送到新设备上。
在AudioManager中获取全部的音频设备时,会列举出当前设备中支持的全部的AudioPatch。
static int updateAudioPortCache(ArrayList ports, ArrayList patches,
ArrayList previousPorts) {
sAudioPortEventHandler.init();
synchronized (sAudioPortGeneration) {
if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
int[] patchGeneration = new int[1];
int[] portGeneration = new int[1];
int status;
ArrayList newPorts = new ArrayList();
ArrayList newPatches = new ArrayList();
do {
newPorts.clear();
status = AudioSystem.listAudioPorts(newPorts, portGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
return status;
}
newPatches.clear();
//列出全部的AudioPatch,AudioPatch即为port中sink和source之间的关系
status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
return status;
}
// Loop until patch generation is the same as port generation unless audio ports
// and audio patches are not null.
} while (patchGeneration[0] != portGeneration[0]
&& (ports == null || patches == null));
// If the patch generation doesn't equal port generation, return ERROR here in case
// of mismatch between audio ports and audio patches.
if (patchGeneration[0] != portGeneration[0]) {
return ERROR;
}
for (int i = 0; i < newPatches.size(); i++) {
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
newPorts);
newPatches.get(i).sources()[j] = portCfg;
}
for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
newPorts);
newPatches.get(i).sinks()[j] = portCfg;
}
}
for (Iterator i = newPatches.iterator(); i.hasNext(); ) {
AudioPatch newPatch = i.next();
boolean hasInvalidPort = false;
for (AudioPortConfig portCfg : newPatch.sources()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
for (AudioPortConfig portCfg : newPatch.sinks()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
if (hasInvalidPort) {
// Temporarily remove patches with invalid ports. One who created the patch
// is responsible for dealing with the port change.
i.remove();
}
}
sPreviousAudioPortsCached = sAudioPortsCached;
sAudioPortsCached = newPorts;
sAudioPatchesCached = newPatches;
sAudioPortGeneration = portGeneration[0];
}
if (ports != null) {
ports.clear();
ports.addAll(sAudioPortsCached);
}
if (patches != null) {
patches.clear();
patches.addAll(sAudioPatchesCached);
}
if (previousPorts != null) {
previousPorts.clear();
previousPorts.addAll(sPreviousAudioPortsCached);
}
}
return SUCCESS;
}
调用的是AudioSystem.listAudioPatches( )接口
/**
* List all existing connections between audio ports.
* @param patches An AudioPatch array where the list will be returned.
* @hide
*/
public static int listAudioPatches(ArrayList patches) {
return updateAudioPortCache(null, patches, null);
}
然后调用updateAudioPortCache
static int updateAudioPortCache(ArrayList ports, ArrayList patches,
ArrayList previousPorts) {
sAudioPortEventHandler.init();
synchronized (sAudioPortGeneration) {
if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
int[] patchGeneration = new int[1];
int[] portGeneration = new int[1];
int status;
ArrayList newPorts = new ArrayList();
ArrayList newPatches = new ArrayList();
do {
newPorts.clear();
status = AudioSystem.listAudioPorts(newPorts, portGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
return status;
}
newPatches.clear();
status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
return status;
}
// Loop until patch generation is the same as port generation unless audio ports
// and audio patches are not null.
} while (patchGeneration[0] != portGeneration[0]
&& (ports == null || patches == null));
// If the patch generation doesn't equal port generation, return ERROR here in case
// of mismatch between audio ports and audio patches.
if (patchGeneration[0] != portGeneration[0]) {
return ERROR;
}
for (int i = 0; i < newPatches.size(); i++) {
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
newPorts);
newPatches.get(i).sources()[j] = portCfg;
}
for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
newPorts);
newPatches.get(i).sinks()[j] = portCfg;
}
}
for (Iterator i = newPatches.iterator(); i.hasNext(); ) {
AudioPatch newPatch = i.next();
boolean hasInvalidPort = false;
for (AudioPortConfig portCfg : newPatch.sources()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
for (AudioPortConfig portCfg : newPatch.sinks()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
if (hasInvalidPort) {
// Temporarily remove patches with invalid ports. One who created the patch
// is responsible for dealing with the port change.
i.remove();
}
}
sPreviousAudioPortsCached = sAudioPortsCached;
sAudioPortsCached = newPorts;
sAudioPatchesCached = newPatches;
sAudioPortGeneration = portGeneration[0];
}
if (ports != null) {
ports.clear();
ports.addAll(sAudioPortsCached);
}
if (patches != null) {
patches.clear();
patches.addAll(sAudioPatchesCached);
}
if (previousPorts != null) {
previousPorts.clear();
previousPorts.addAll(sPreviousAudioPortsCached);
}
}
return SUCCESS;
}
在这里会调用AudioSystem.listAudioPatches(),也就是通过AudioSystem直接调到了jni
static jint
android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
jobject jPatches, jintArray jGeneration)
{
ALOGV("listAudioPatches");
if (jPatches == NULL) {
ALOGE("listAudioPatches NULL AudioPatch ArrayList");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
ALOGE("listAudioPatches not an arraylist");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
return (jint)AUDIO_JAVA_BAD_VALUE;
}
status_t status;
unsigned int generation1;
unsigned int generation;
unsigned int numPatches;
jint *nGeneration;
struct audio_patch *nPatches = NULL;
jobjectArray jSources = NULL;
jobject jSource = NULL;
jobjectArray jSinks = NULL;
jobject jSink = NULL;
jobject jPatch = NULL;
int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
jint jStatus;
// get the patch count and all the patches until they both return the same generation
do {
if (attempts-- < 0) {
status = TIMED_OUT;
break;
}
numPatches = 0;
status = AudioSystem::listAudioPatches(&numPatches,
NULL,
&generation1);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
status);
break;
}
if (numPatches == 0) {
jStatus = (jint)AUDIO_JAVA_SUCCESS;
goto exit;
}
nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
status = AudioSystem::listAudioPatches(&numPatches,
nPatches,
&generation);
ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
numPatches, generation, generation1);
} while (generation1 != generation && status == NO_ERROR);
jStatus = nativeToJavaStatus(status);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
for (size_t i = 0; i < numPatches; i++) {
jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
nPatches[i].id);
if (patchHandle == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d",
i, nPatches[i].num_sources, nPatches[i].num_sinks);
env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
// load sources
jSources = env->NewObjectArray(nPatches[i].num_sources,
gAudioPortConfigClass, NULL);
if (jSources == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
for (size_t j = 0; j < nPatches[i].num_sources; j++) {
jStatus = convertAudioPortConfigFromNative(env,
NULL,
&jSource,
&nPatches[i].sources[j]);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
env->SetObjectArrayElement(jSources, j, jSource);
env->DeleteLocalRef(jSource);
jSource = NULL;
ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d",
i, j,
nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
nPatches[i].sources[j].id);
}
// load sinks
jSinks = env->NewObjectArray(nPatches[i].num_sinks,
gAudioPortConfigClass, NULL);
if (jSinks == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
jStatus = convertAudioPortConfigFromNative(env,
NULL,
&jSink,
&nPatches[i].sinks[j]);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
env->SetObjectArrayElement(jSinks, j, jSink);
env->DeleteLocalRef(jSink);
jSink = NULL;
ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d",
i, j,
nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
nPatches[i].sinks[j].id);
}
jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
patchHandle, jSources, jSinks);
env->DeleteLocalRef(jSources);
jSources = NULL;
env->DeleteLocalRef(jSinks);
jSinks = NULL;
if (jPatch == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
env->DeleteLocalRef(jPatch);
jPatch = NULL;
}
exit:
nGeneration = env->GetIntArrayElements(jGeneration, NULL);
if (nGeneration == NULL) {
jStatus = AUDIO_JAVA_ERROR;
} else {
nGeneration[0] = generation1;
env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
}
if (jSources != NULL) {
env->DeleteLocalRef(jSources);
}
if (jSource != NULL) {
env->DeleteLocalRef(jSource);
}
if (jSinks != NULL) {
env->DeleteLocalRef(jSinks);
}
if (jSink != NULL) {
env->DeleteLocalRef(jSink);
}
if (jPatch != NULL) {
env->DeleteLocalRef(jPatch);
}
free(nPatches);
return jStatus;
}
通过jni我们看到调用了两次 AudioSystem::listAudioPatches,其实只有第二次才是真正的调用,我们来具体看下AudioPolicyManager的listAudioPatches过程
status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches,
unsigned int *generation)
{
if (generation == NULL) {
return BAD_VALUE;
}
*generation = curAudioPortGeneration();
return mAudioPatches.listAudioPatches(num_patches, patches);
}
最终又调用到了AudioPatch中
status_t AudioPatchCollection::listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches) const
{
if (num_patches == NULL || (*num_patches != 0 && patches == NULL)) {
return BAD_VALUE;
}
ALOGV("listAudioPatches() num_patches %d patches %p available patches %zu",
*num_patches, patches, size());
if (patches == NULL) {
*num_patches = 0;
}
size_t patchesWritten = 0;
size_t patchesMax = *num_patches;
*num_patches = 0;
for (size_t patchIndex = 0; patchIndex < size(); patchIndex++) {
// do not report patches with AUDIO_DEVICE_IN_STUB as source or
// AUDIO_DEVICE_OUT_STUB as sink as those devices are used by stub HALs by convention
const sp patch = valueAt(patchIndex);
bool skip = false;
for (size_t srcIndex = 0; srcIndex < patch->mPatch.num_sources && !skip; srcIndex++) {
if (patch->mPatch.sources[srcIndex].type == AUDIO_PORT_TYPE_DEVICE &&
patch->mPatch.sources[srcIndex].ext.device.type == AUDIO_DEVICE_IN_STUB) {
skip = true;
}
}
for (size_t sinkIndex = 0; sinkIndex < patch->mPatch.num_sinks && !skip; sinkIndex++) {
if (patch->mPatch.sinks[sinkIndex].type == AUDIO_PORT_TYPE_DEVICE &&
patch->mPatch.sinks[sinkIndex].ext.device.type == AUDIO_DEVICE_OUT_STUB) {
skip = true;
}
}
if (skip) {
continue; // to next audio patch
}
if (patchesWritten < patchesMax) {
patches[patchesWritten] = patch->mPatch;
patches[patchesWritten++].id = patch->mHandle;
}
(*num_patches)++;
ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d",
patchIndex, patch->mPatch.num_sources, patch->mPatch.num_sinks);
}
ALOGV("listAudioPatches() got %zu patches needed %d", patchesWritten, *num_patches);
return NO_ERROR;
}