当前位置:网站首页>ALSA音频架构 -- snd_pcm_open函数分析
ALSA音频架构 -- snd_pcm_open函数分析
2022-08-11 04:30:00 【c-coder】
引言
alsa-lib主要是给抽象出来的一套ALSA应用程序的用户空间库,供具体的应用程序调用。alsa-utils 主要是相关的操作APP,可以充当官方demo,供开发人员参考。前文已经给出ALSA音频架构。本文主要详细分析snd_pcm_open。
snd_pcm_open顺序图
代码详细分析 (以播放为例)
问题引入
alsa_utils aplay.c 中的播放接口采用函数指针实现,具体定义如下
static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
赋值如下
writei_func = snd_pcm_writei;
readi_func = snd_pcm_readi;
writen_func = snd_pcm_writen;
readn_func = snd_pcm_readn;
snd_pcm_writei通过调用_snd_pcm_writei写入PCM数据流,_snd_pcm_writei函数原型如下
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
/* lock handled in the callback */
if (!pcm->fast_ops->writei)
return -ENOSYS;
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size); // 播放函数指针
}
_snd_pcm_writei会调用pcm->fast_ops->writei进行实际操作。
查看aplay.c源码始终没有发现PCM设备中的结构体const snd_pcm_fast_ops_t *fast_ops
在哪里初始化,极大可能在snd_pcm_open中进行了相应的操作。
snd_pcm_open 具体分析
alsa_utils aplay.c 中调用 snd_pcm_open 如下
...
char *pcm_name = "default";
...
err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
if (err < 0) {
error(_("audio open error: %s"), snd_strerror(err));
return 1;
}
snd_pcm_open 函数原型如下
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
snd_config_t *top;
int err;
assert(pcmp && name);
if (_snd_is_ucm_device(name)) {
name = uc_mgr_alibcfg_by_device(&top, name);
if (name == NULL)
return -ENODEV;
} else {
err = snd_config_update_ref(&top);
if (err < 0)
return err;
}
err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0);
snd_config_unref(top);
return err;
}
pcmp,即打开的PCM设备句柄; name,要打开的PCM设备名称,默认default
stream,对应的PCM流类型,播放PCM流(SND_PCM_STREAM_PLAYBACK)和录音PCM流(SND_PCM_STREAM_CAPTURE)
mode,打开方式,阻塞、非阻塞及异步等
snd_pcm_open通过调用snd_config_update_ref来获取als.conf中的配置信息,参数保存至snd_config_t 。
通过snd_pcm_open_noupdate 解析 snd_config_t 配置,snd_pcm_open_noupdate 函数原型如下
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
const char *name, snd_pcm_stream_t stream,
int mode, int hop)
{
int err;
snd_config_t *pcm_conf;
const char *str;
err = snd_config_search_definition(root, "pcm", name, &pcm_conf);
if (err < 0) {
SNDERR("Unknown PCM %s", name);
return err;
}
if (snd_config_get_string(pcm_conf, &str) >= 0)
// 循环递归解析
err = snd_pcm_open_noupdate(pcmp, root, str, stream, mode,
hop + 1);
else {
snd_config_set_hop(pcm_conf, hop);
err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
}
snd_config_delete(pcm_conf);
return err;
}
snd_pcm_open_conf 提取 snd_config_t 参数
static const char *const build_in_pcms[] = {
"adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",
"linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share",
"shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul",
NULL
};
static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
snd_config_t *pcm_root, snd_config_t *pcm_conf,
snd_pcm_stream_t stream, int mode)
{
...
sprintf(buf, "_snd_pcm_%s_open", str); //open_name即“_snd_pcm_hw_open”
...
const char *const *build_in = build_in_pcms;
sprintf(buf1, "libasound_module_pcm_%s.so", str);
...
// 通过open_name在lib中获取对应的动态库函数
open_func = snd_dlobj_cache_get(lib, open_name,
SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION), 1);
if (open_func) {
err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);
...
snd_pcm_open_conf 调用snd_dlobj_cache_get在动态库中libasound_module_pcm_hw.so获取函数指针_snd_pcm_hw_open
_snd_pcm_hw_open通过调用snd_pcm_hw_open来创建hw_pcm设备。snd_pcm_hw_open函数原型如下
int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
int card, int device, int subdevice,
snd_pcm_stream_t stream, int mode,
int mmap_emulation ATTRIBUTE_UNUSED,
int sync_ptr_ioctl)
{
...
if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0)
return ret;
...
fd = snd_open_device(filename, fmode);
...
return snd_pcm_hw_open_fd(pcmp, name, fd, sync_ptr_ioctl);
_err:
if (fd >= 0)
close(fd);
snd_ctl_close(ctl);
return ret;
}
snd_pcm_hw_open主要完成如下工作:
调用snd_ctl_hw_open创建了一个hw control设备,并设置回调const snd_ctl_ops_t *ops
,回调参数为snd_ctl_hw_ops
,具体操作接口如下:
static const snd_ctl_ops_t snd_ctl_hw_ops = {
.close = snd_ctl_hw_close,
.nonblock = snd_ctl_hw_nonblock,
.async = snd_ctl_hw_async,
.subscribe_events = snd_ctl_hw_subscribe_events,
.card_info = snd_ctl_hw_card_info,
.element_list = snd_ctl_hw_elem_list,
.element_info = snd_ctl_hw_elem_info,
.element_add = snd_ctl_hw_elem_add,
.element_replace = snd_ctl_hw_elem_replace,
.element_remove = snd_ctl_hw_elem_remove,
.element_read = snd_ctl_hw_elem_read,
.element_write = snd_ctl_hw_elem_write,
.element_lock = snd_ctl_hw_elem_lock,
.element_unlock = snd_ctl_hw_elem_unlock,
.element_tlv = snd_ctl_hw_elem_tlv,
.hwdep_next_device = snd_ctl_hw_hwdep_next_device,
.hwdep_info = snd_ctl_hw_hwdep_info,
.pcm_next_device = snd_ctl_hw_pcm_next_device,
.pcm_info = snd_ctl_hw_pcm_info,
.pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice,
.rawmidi_next_device = snd_ctl_hw_rawmidi_next_device,
.rawmidi_info = snd_ctl_hw_rawmidi_info,
.rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice,
.set_power_state = snd_ctl_hw_set_power_state,
.get_power_state = snd_ctl_hw_get_power_state,
.read = snd_ctl_hw_read,
};
调用snd_pcm_hw_open_fd创建hw PCM设备并配置对应的回调,snd_pcm_hw_open_fd函数原型如下
int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
int sync_ptr_ioctl)
ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);
...
// 配置回调接口
pcm->ops = &snd_pcm_hw_ops;
pcm->fast_ops = &snd_pcm_hw_fast_ops;
pcm->private_data = hw;
pcm->poll_fd = fd;
pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
pcm->tstamp_type = tstamp_type;
...
}
回调接口如下
static const snd_pcm_ops_t snd_pcm_hw_ops = {
.close = snd_pcm_hw_close,
.info = snd_pcm_hw_info,
.hw_refine = snd_pcm_hw_hw_refine,
.hw_params = snd_pcm_hw_hw_params,
.hw_free = snd_pcm_hw_hw_free,
.sw_params = snd_pcm_hw_sw_params,
.channel_info = snd_pcm_hw_channel_info,
.dump = snd_pcm_hw_dump,
.nonblock = snd_pcm_hw_nonblock,
.async = snd_pcm_hw_async,
.mmap = snd_pcm_hw_mmap,
.munmap = snd_pcm_hw_munmap,
.query_chmaps = snd_pcm_hw_query_chmaps,
.get_chmap = snd_pcm_hw_get_chmap,
.set_chmap = snd_pcm_hw_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
.status = snd_pcm_hw_status,
.state = snd_pcm_hw_state,
.hwsync = snd_pcm_hw_hwsync,
.delay = snd_pcm_hw_delay,
.prepare = snd_pcm_hw_prepare,
.reset = snd_pcm_hw_reset,
.start = snd_pcm_hw_start,
.drop = snd_pcm_hw_drop,
.drain = snd_pcm_hw_drain,
.pause = snd_pcm_hw_pause,
.rewindable = snd_pcm_hw_rewindable,
.rewind = snd_pcm_hw_rewind,
.forwardable = snd_pcm_hw_forwardable,
.forward = snd_pcm_hw_forward,
.resume = snd_pcm_hw_resume,
.link = snd_pcm_hw_link,
.link_slaves = snd_pcm_hw_link_slaves,
.unlink = snd_pcm_hw_unlink,
.writei = snd_pcm_hw_writei, //播放数据流回调
.writen = snd_pcm_hw_writen,
.readi = snd_pcm_hw_readi,
.readn = snd_pcm_hw_readn,
.avail_update = snd_pcm_hw_avail_update,
.mmap_commit = snd_pcm_hw_mmap_commit,
.htimestamp = snd_pcm_hw_htimestamp,
.poll_descriptors = NULL,
.poll_descriptors_count = NULL,
.poll_revents = NULL,
};
上文中的pcm->fast_ops->writei即snd_pcm_hw_writei。
至此alsa-lib中的snd_pcm_open解析流程结束。
边栏推荐
- What are port 80 and port 443?What's the difference?
- "104 Maximum Depth of Binary Trees" in LeetCode's Day 12 Binary Tree Series
- Map中的getOrDefualt方法
- shell监视gpu使用情况
- .NET 服务注册
- jwsManager服务接口实现类-jni实现
- send_sig: 内核执行流程
- 优先级队列
- uni-app - city selection index list / city list sorted by A-Z (uview component library IndexList index list)
- MQ框架应用比较
猜你喜欢
随机推荐
Three 】 【 yolov7 series of actual combat from 0 to build training data sets
AVH 动手实践 (二) | 在 Arm 虚拟硬件上部署 PP-OCR 模型
WPF DataGrid 使用数据模板(2)
MySQL数据库存储引擎以及数据库的创建、修改与删除
Common layout effect realization scheme
Snap - rotate the smallest number of an array
机器学习中什么是集成学习?
Use Navicat Premium to export database table structure information to Excel
[FPGA] day19- binary to decimal (BCD code)
[C Language] Getting Started
"104 Maximum Depth of Binary Trees" in LeetCode's Day 12 Binary Tree Series
"239 Sliding Window Maximum Value" on the 16th day of LeetCode brushing
1815. Get the maximum number of groups of fresh donuts state compression
How to add icons to web pages?
LeetCode刷题第12天二叉树系列之《104 二叉树的最大深度》
力扣——旋转数组的最小数字
如何给网页添加icon图标?
Jetson Orin平台4-16路 GMSL2/GSML1相机采集套件推荐
LeetCode刷题第10天字符串系列之《125回文串验证》
redis按照正则批量删除key