snd_pcm_open需要包含什么头文件

snd_pcm_open需要包含什么头文件,第1张

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解析流程结束。

音视频

点赞文章给优秀博主打call~

细跟高跟凉鞋

精选推荐

广告

android使用Alsa Aloop录制系统内部声音 原创

2022-04-24 19:12:49

这歌声无聊可是辉煌

码龄10年

关注

alsa aloop是alsa提供的内部loopback功能,可以用来实现录制系统内部声音,在没有硬件支持loopback功能时可以采用这种软件loopback的实现来代替。

alsa aloop实现了一个虚拟的声卡。

在kernel的编译配置选项文件中加入CONFIG_SND_ALOOP=y之后烧录到平丛坦带台中声卡会多出两个pcm设备:

00-00: Loopback PCM : Loopback PCM : playback 8 : capture 8

00-01: Loopback PCM : Loopback PCM : playback 8 : capture 8

注意到此时原先板载的实际声卡的index将变为1,因为aloop注册比较早

有了这两个节点之后就可以使用tinyplay和tinycap来验证。

使用方法为:

tinyplay 写入card 0 device 0,同时tinycap从card 0 device 1录制

tinyplay /data/8ch_16bit.wav -d 0&tinycap /data/test.wav -d 1 -c 8 -r 48000 -b 16 <

[1] 8228

Playing sample: 8 ch, 48000 hz, 16 bit

Capturing sample: 8 ch, 48000 hz, 16 bit

注意的是打开的参数如采样率 位深和ch数要与录制的时候保持信搭一致,否则会提示参数非法而打开失败。

这一步验证完成之后要实现在android里使用aloop录制系统声音则需要在audo hal中pcm_open,pcm_close,pcm_write实际声卡的地方一样加上 *** 作aloop的虚拟声卡0,

在android P上调试发现使用pcm_open打开aloop device输出时传递的alsa config参数也渗芦有限制:

period_size = 1024

period_count = 4

period_size 和period_count 需要,不超过1024和4,否则一样会open失败 提示参数非法。

最后贴下alsa aloop的数据流向:

提示连接庆念被拒绝,

1,首先需要确认你的声卡驱动是否正常,

2,确认函数参数是否正确

3,确认构建配置树是否配誉哗困置成功

由于提主提供的资料有限,没法提供更芦察多建议!


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12329786.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存