《ALSADMA缓冲区分析.docx》由会员分享,可在线阅读,更多相关《ALSADMA缓冲区分析.docx(4页珍藏版)》请在三一办公上搜索。
1、ALSADMA缓冲区分析ALSA应用程序调用ALSA lib库中的函数snd_pcm_writei向声卡硬件写入交错(write后的i代表interleaved)数据。在ALSA lib中最后会调到snd_pcm_hw_writen(snd_pcm_t *pcm, void *bufs, snd_pcm_uframes_t size)函数,这个函数调用通用的ioctl接口: err = ioctl(fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern); 在ALSA驱动层中 snd_pcm_playback_ioctl1函数响应ioctl,在这个函数中某个位置会调
2、用 result = snd_pcm_lib_writev(substream, bufs, xfern.frames);它会继续调用: snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_writev_transfer)函数,其中snd_pcm_lib_writev_transfer是个函数指针。这个函数很关键,其中包括下面的代码: static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substr
3、eam, unsigned long data, snd_pcm_uframes_t size, int nonblock, transfer_f transfer) struct snd_pcm_runtime *runtime = substream-runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; . while (size 0) snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t
4、 cont; if (runtime-status-state = SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); avail = snd_pcm_playback_avail(runtime); if (!avail) if (nonblock) err = -EAGAIN; goto _end_unlock; err = wait_for_avail_min(substream, &avail); if (err avail ? avail : size; cont = runtime-buffer_size - run
5、time-control-appl_ptr % runtime-buffer_size; if (frames cont) frames = cont; snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); appl_ptr = runtime-control-appl_ptr; appl_ofs = appl_ptr % runtime-buffer_size; snd_pcm_stream_unlock_irq(substream); if (err = transfer(substre
6、am, appl_ofs, data, offset, frames) status-state) case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: break; appl_ptr += frames; if (appl_ptr = runtime-boundary) appl_ptr -= runtime-boundary; runtime-control-appl_ptr
7、= appl_ptr; if (substream-ops-ack) substream-ops-ack(substream); offset += frames; size -= frames; xfer += frames; if (runtime-status-state = SNDRV_PCM_STATE_PREPARED & snd_pcm_playback_hw_avail(runtime) = (snd_pcm_sframes_t)runtime-start_threshold) err = snd_pcm_start(substream); if (err 0 ? (snd_p
8、cm_sframes_t)xfer : err; snd_pcm_playback_avail是个内联函数,在include/sound/pcm.h中定义: /* * result is: 0 . (boundary - 1) */ static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime) snd_pcm_sframes_t avail = runtime-status-hw_ptr + runtime-buffer_size - runtime-control-appl_pt
9、r; if (avail boundary; else if (snd_pcm_uframes_t) avail = runtime-boundary) avail -= runtime-boundary; return avail; 上面的函数代码,可以帮助我们理解 alsa 中的数据结构成员所代表的含义。很显然,上面的函数的目的就是返回当前hardware buffer中用户程序可写的帧(frames)数。runtime-status-hw_ptr应该表示硬件指针,指向硬件已经处理(播放/录音)过的数据的位置,runtime-control-buffer_size是整个buffer的大小,而runtime-control-appl_ptr是用户程序已经处理(读/写)过的数据的位置。appl_ptr之后的空间,一直到buffer末尾,这一段是可以写入的,这一段长度为runtime-buffer_size - runtime-control-appl_ptr;另外hw_ptr之前的空间也是可写的,所以整个可用的长度为 runtime-status-hw_ptr + runtime-buffer_size - runtime-control-appl_ptr。