Android audio 输出设备选择过程分析(下)

阅读: 评论:0

Android audio 输出设备选择过程分析(下)

Android audio 输出设备选择过程分析(下)

上篇的分析到audio_route,现在接个力,也算是7月的作业,再不交这个月就落下了。

audio_route_apply_and_update_path分两个过程,从函数名字都能看出来,一个是apply,一个是updata:

audio_route_apply_path

/* Apply an audio route path by name */
int audio_route_apply_path(struct audio_route *ar, const char *name)
{struct mixer_path *path;...path = path_get_by_name(ar, name);...path_apply(ar, path);return 0;
}

path是一个struct:

struct mixer_path {char *name;unsigned int size;unsigned int length;struct mixer_setting *setting;
};

内含struct mixer_setting:

struct mixer_setting {unsigned int ctl_index;unsigned int num_values;unsigned int type;union ctl_values value;
};

好吧,还套了个ctl_values:

union ctl_values {int *enumerated;long *integer;void *ptr;unsigned char *bytes;
};

背景知识先来这么多,先继续看代码

path_get_by_name(ar, name)这个调用返回了一个mixer_path指针。

static struct mixer_path *path_get_by_name(struct audio_route *ar,const char *name)
{unsigned int i;
​for (i = 0; i < ar->num_mixer_paths; i++)if (strcmp(ar->mixer_path[i].name, name) == 0)return &ar->mixer_path[i];
​return NULL;
}

看到代码简单,老夫长舒一口气!

无论怎么样我们还是需要去查证两个参数,ar和name.

name简单一些:

//audio_hw.c
strlcpy(mixer_path, use_case_table[usecase->id], MIXER_PATH_MAX_LENGTH);
...
audio_route_apply_and_update_path(adev->audio_route, mixer_path);//name的源头

mixer_path来自use_case_table[usecase->id],列举几个常见的(我常见的<(__)>):

const char * const use_case_table[AUDIO_USECASE_MAX] = {...[USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",[USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",[USECASE_AUDIO_RECORD] = "audio-record",[USECASE_AUDIO_PLAYBACK_FM] = "play-fm",[USECASE_AUDIO_HFP_SCO_UPLINK] = "hfp-sco",[USECASE_VOICE_CALL] = "voice-call",        ...
}

播放录音,电话,甚至FM都包括在其间了。从概念上来讲,这个就是一个使用场景,从代码上来讲,就是一个字符串而已。。。

再去看复杂一些的第一个参数ar。

首先,从audio_hw.c来看,ar = adev->audio_route, adev 来自 out_set_parameters函数的out->dev。

而out:

struct stream_out *out = (struct stream_out *)stream;//stream类型audio_stream

简单做个小结就是ar即:

stream->dev->audio_route

stream一路追溯上去,来自:

audio_hw.c

adev_open_output_stream函数。完全没找到ar被赋值,被构造的地方!郁闷。。。

前面说到path_get_by_name,完全不知道ar的num_mixer_paths哪来的?

一咬牙一跺脚,来个不要脸的方法,看看num_mixer_paths在哪赋值的,好家伙,又找出来个线索:

//audio_route.c
audio_route_initstart_tagpath_createnum_mixer_paths赋值

还偷偷发现,audio_route_init里用到了XML_Parser。一阵阵窃喜)——)十有八九,mixer_path就是在这个过程中被解析的。

我们现在搜索下/在哪调用的吧:

小插曲,碰到一个问题:

.///msm8960/platform.c

我们的平台是msm8996,audio hal用的到底是哪个platform.c?

只好打开audio/audio/hal/Android.mk看看了

...
#filter的结果不为空(TARGET_BOARD_PLATFORM为msm 8996)
ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994 msm8992 msm8996 msm8996_gvmq,$(TARGET_BOARD_PLATFORM)),)//注意逗号后面,第二个参数是空的
AUDIO_PLATFORM = msm8974
...
LOCAL_SRC_FILES := audio_hw.c voice.c platform_info.c $(AUDIO_PLATFORM)/platform.c
...

好的,我知道了,用的就是./msm8974/platform.c

随着时间一点点过去,我又捋出来这么一个过程:

AudioFlinger::loadHwModule_l
...audio_hw/adev_open//刚才百思不得其解的adev原来是这个时候初始化的...adev->platform = platform_init(adev);

嗯,qcom

void *platform_init(struct audio_device *adev)
{...int retry_num = 0, snd_card_num = 0, key = 0;...//MAX_SND_CARD为8.while (snd_card_num < MAX_SND_CARD) {//external/tinyalsa/mixer.c//打开/dev/snd/controlC(snd_card_num)节点,关联到mixer上adev->mixer = mixer_open(snd_card_num);...//取到mixer的名字赋值给snd_card_name//本例中是apq8096-adp-agave-snd-cardsnd_card_name = strdup(mixer_get_name(adev->mixer));...//_data->hw_info = hw_info_init(snd_card_name);        //本例中is_i2s_ext_modem:0//i2s外置?if (platform_is_i2s_ext_modem(snd_card_name, my_data)) {adev->audio_route = audio_route_init(snd_card_num,MIXER_XML_PATH_I2S);} else {//(翻译一下这里的一段注释)//从声卡名称中获取编解码器内部名称,并动态形成混音器路径文件名.//这是将来挑选任何基于编解码器名称的混音器文件的通用方法,无需更改代码。//此代码假设混合器文件的格式为mixer_l。//如果此动态读取混音器文件无法打开,则它将回退到默认混音器文件,即l.//这样做是为了保持向后兼容性,但不是强制性的,只要混合器文件按照上述假设命名即可。if (platform_is_guardian(snd_card_name, my_data)) {//平台是监护人?护卫者?ALOGD("%s:%s test", __func__, MIXER_XML_PATH_GUARDIAN);}//snd_internal_name=“apq8096” 剩下的tmp="adp-agave-snd-card"snd_internal_name = strtok_r(snd_card_name, "-", &tmp);if (snd_internal_name != NULL)//snd_internal_name=“adp” tmp="agave-snd-card"snd_internal_name = strtok_r(NULL, "-", &tmp);if (snd_internal_name != NULL) {//mixer_xml_file = "/vendor/etc/mixer_paths"strlcpy(mixer_xml_file, MIXER_XML_BASE_STRING,MIXER_PATH_MAX_LENGTH);//strcat, 连接字符串//mixer_xml_file = "/vendor/etc/mixer_paths_"strlcat(mixer_xml_file, MIXER_FILE_DELIMITER,MIXER_PATH_MAX_LENGTH);//mixer_xml_file = "/vendor/etc/mixer_paths_adp"strlcat(mixer_xml_file, snd_internal_name,MIXER_PATH_MAX_LENGTH);//mixer_xml_file = "/vendor/etc/mixer_l"strlcat(mixer_xml_file, MIXER_FILE_EXT,MIXER_PATH_MAX_LENGTH);} else {strlcpy(mixer_xml_file, MIXER_XML_DEFAULT_PATH,MIXER_PATH_MAX_LENGTH);}//access()检查该进程是否将被允许读,写或测试存在的文件if (F_OK == access(mixer_xml_file, 0)) {ALOGD("%s: Loading mixer file: %s", __func__, mixer_xml_file);if (audio_extn_read_xml(adev, snd_card_num, mixer_xml_file,MIXER_XML_PATH_AUXPCM) == -ENOSYS)adev->audio_route = audio_route_init(snd_card_num,mixer_xml_file);} else {//由于mixer_l文件并不存在,所以还是用默认的xml文件ALOGD("%s: Loading default mixer file", __func__);//AUXPCM_BT_ENABLED没定义,所以这个方法直接等同于-ENOSYSif(audio_extn_read_xml(adev, snd_card_num, MIXER_XML_DEFAULT_PATH,MIXER_XML_PATH_AUXPCM) == -ENOSYS)//终于要给audio_route赋值了。。。前文找了半天啊。//在audio_route_init中完成了对l的解析adev->audio_route = audio_route_init(snd_card_num,MIXER_XML_DEFAULT_PATH);}}...adev->snd_card = snd_card_num;break;}retry_num = 0;snd_card_num++;mixer_close(adev->mixer);...//my_data初始化  my_data->adev = adev;my_data->fluence_in_spkr_mode = false;...//高通的双mic降噪相关的一些设置项,涉及不到的就可以暂时不看了property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, "");...//acdb处理my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);...platform_acdb_init(my_data);...//底下的就是看着眼熟,不知道干嘛用的了}

终于可以回到audio_route_apply_path

第一步:path = path_get_by_name(ar, name);

ar对应着l,path_get_by_name,name即前文分析到的usecase,比如录音时候的"audio_record".

取到的结果对应l中的:

<path name="audio-record"><ctl name="MultiMedia1 Mixer SEC_MI2S_TX" value="1" />
</path>

简单做个解释:

  • MultiMedia1:audio-record这个usecase对应的FE(front end) PCM.

  • SEC_MI2S_TX : device连接的BE(back end) DAI.

  • Mixer:表示 DSP 路由功能

  • value:1 表示连接,0 表示断开连接

这里有个很专业的文章可供参考(可能看完底下这个,你会发现我这篇文章没有任何存在的价值了!):

这个ctl的意思是把MultiMedia1 PCM与SEC_MI2S_TX这个DAI连接起来。

第二步:path_apply(ar, path);

将第一步中取到的path传给path_apply

static int path_apply(struct audio_route *ar, struct mixer_path *path)
{unsigned int i;unsigned int ctl_index;struct mixer_ctl *ctl;enum mixer_ctl_type type;
​ALOGD("Apply path: %s", path->name != NULL ? path->name : "none");for (i = 0; i < path->length; i++) {ctl_index = path->setting[i].ctl_index;ctl = index_to_ctl(ar, ctl_index);type = mixer_ctl_get_type(ctl);if (!is_supported_ctl_type(type))continue;size_t value_sz = sizeof_ctl_type(type);//通过memcpy直接将path->setting[i].value放入ar->mixer_state[ctl_index].new_valuememcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,path->setting[i].num_values * value_sz);}
​return 0;
}

前面apply的过程到这里算勉强结束了,接下来该update了。

audio_route_update_path

/** Operates on the specified path .. controls will be updated in the* order listed in the XML file*/
static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)                                              
{struct mixer_path *path;int32_t i, end;unsigned int j;...path = path_get_by_name(ar, name);//再取一遍...i = reverse ? (path->length - 1) : 0;//前面传的false,所以这里i = 0;end = reverse ? -1 : (int32_t)path->length;//这里等于path的长度while (i != end) {unsigned int ctl_index;enum mixer_ctl_type type;
​ctl_index = path->setting[i].ctl_index;
​struct mixer_state * ms = &ar->mixer_state[ctl_index];
​type = mixer_ctl_get_type(ms->ctl);if (!is_supported_ctl_type(type)) {continue;}size_t value_sz = sizeof_ctl_type(type);/* if any value has changed, update the mixer */for (j = 0; j < ms->num_values; j++) {//用memcpy的方式更新ar->mixer_state中对应的值。}...
}

 

本文发布于:2024-01-31 07:38:40,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170665792326756.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:Android Wi
标签:过程   输出设备   Android   audio
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23