overrun
和underrun
通过snd_pcm_period_elapsed()接口进行控制
流程: stop->start->stop->start->
overrun 在录音时由于录音数据过快会产生overrun现象
underrun 在放音时由于用户层的音频数据到DDR中的速度比控制放的速度满,出现underrun
snd_pcm_period_elapsed正常更新位置 正常录音: 应用层使用poll的调用关系
1 2 3 4 5 6 7 8 9 snd_pcm_period_elapsed | |-> snd_pcm_update_hw_ptr0 | |-> pos = substream->ops->pointer(substream); | |-> snd_pcm_update_state |-> snd_pcm_capture_avail |-> wake_up(&runtime->sleep);
read进程 overrun以后不能通过wakeup唤醒, 正常要通过wake_up(&runtime->sleep)唤醒什么?
通过wake_up(&runtime->sleep)唤醒核心层core中的read
进程
read进程的由来 1 2 3 4 5 6 7 用户空间 arecord -f cd 1. wav --- 产生read进程 ioctl - SNDRV_PCM_IOCTL_READI_FRAMES | ------------------------------------------------- | 内核空间 snd_pcm_capture_ioctl
内核空间的数据处理 数据的调用关系:
1 2 3 4 5 6 7 snd_pcm_capture_ioctl |-> snd_pcm_capture_ioctl1 |-> snd_pcm_lib_read (SNDRV_PCM_IOCTL_READI_FRAMES) |-> snd_pcm_lib_read1 |-> snd_pcm_capture_avail |------> wait_for_avail |-> transfer(substream, appl_ofs, data, offset, frames)
wait_for_avail的实现逻辑:
1 2 3 4 5 6 7 8 9 10 wait_for_avail |-> init_waitqueue_entry(&wait, current); |-> set_current_state(TASK_INTERRUPTIBLE); |-> add_wait_queue(&runtime->tsleep, &wait); | |-> for (;;) |-> signal_pending(current) | |-> tout = schedule_timeout(wait_time); |-> 上报用户空间当前stream流的状态
sleep队列的工作 通过打印调试信息可以判断在使用snd_pcm_period_elapsed
更新位置信息后,是通过wake_up(&runtime->sleep)
将当前进程唤醒
什么时候对sleep队列进行的初始化,什么时候将当前进程加入的sleep队列
初始化 1 2 3 4 5 6 snd_pcm_capture_open |-> snd_pcm_open |-> snd_pcm_open_file |-> snd_pcm_open_substream |-> snd_pcm_attach_substream |-> init_waitqueue_head(&runtime->sleep);
将当前进程加入sleep队列 1 2 3 .poll = snd_pcm_capture_poll |-> poll_wait(file, &runtime->sleep, wait);
poll()该接口用户空间arecord应用有调用
poll的作用; read进程的控制机制 通过源码中的wakeup可以判断对read进程的唤醒存在两种方式
1 2 3 4 5 if (runtime->twake) { if (avail >= runtime->twake) wake_up(&runtime->tsleep); } else if (avail >= runtime->control->avail_min) wake_up(&runtime->sleep);
通过核心层core中自己控制(唤醒)read进程,必须runtime->twake=1
1 2 3 4 5 6 7 8 9 10 11 12 1. snd_pcm_lib_read1 |-> runtime->twake = runtime->control->avail_min ? : 1 ; 2. snd_pcm_hw_params |-> runtime->period_size = params_period_size(params); |-> runtime->control->avail_min = runtime->period_size; 3. params_period_size(params) |-> 解析用户空间参数SNDRV_PCM_HW_PARAM_PERIOD_SIZE 4. arecord参数选项: --period-size=# distance between interrupts is # frames
stop 出现overrun或underrun后,alsa什么时候在那进行stop
时间 在对每个DMA描述符进行处理时,也就是DMA描述符中断的callback中.
位置 1 snd_pcm_period_elapsed()
sound/core/pcm_lib.c
Description: This function is called from the interrupt handler when the PCM has processed the period size. It will update the current pointer, set up the tick, wake up sleepers, etc. Even if more than one periods have elapsed since the last call, you have to call this only once.
作用: 通知缓冲区空闲(对应回放)或者有效(对应录音)
条件 实现逻辑: 主要以overrun
为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 snd_pcm_period_elapsed | |-> snd_pcm_update_hw_ptr0 | |-> pos = substream->ops->pointer(substream); if (pos == SNDRV_PCM_POS_XRUN) { xrun(substream); return -EPIPE; } |-> 更新指针(hw_ptr_base,hw_ptr_interrupt, status->hw_ptr), (hw_ptr_jiffies) |-> snd_pcm_update_state | |-> snd_pcm_playback_avail(/snd_pcm_capture_avail) |--> snd_pcm_drain_done()
当pos == SNDRV_PCM_POS_XRUN
时,出现overrun
出现overrun后的处理 1 2 xrun |-> snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
start 出现overrun或underrun, 并且数据准备完成后,alsa什么时候在那进行start
时间 位置