linux 下 poll 函数的调用返回结果为什么不准确

linux 下 poll 函数的调用返回结果为什么不准确,第1张

fds[0].fd 与 fds[1].fd 同样是sock这个描述符, 其中在fds[0] 中注册POLLIN和POLLRDNORM事件,

在樱手兄fds[1]中监听POLLOUT和POLLWRNORM事件, 这样做就成功了, 不会出现上述 "同时在一个脊袭sock描述符注册可读可写事件,

导致监听结薯销果与实际不符"的现象。

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~

细跟高跟凉鞋

精选推荐

广告


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

原文地址:https://54852.com/tougao/12174044.html

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

发表评论

登录后才能评论

评论列表(0条)

    保存