ALSADMA缓冲区分析

来源:互联网 发布:返利网在淘宝上买手机 编辑:程序博客网 时间:2024/06/08 09:25

    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接口: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 *substream,                         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;    int err = 0;    if (size == 0)        return 0;    snd_pcm_stream_lock_irq(substream);    switch (runtime->status->state) {    case SNDRV_PCM_STATE_PREPARED:    case SNDRV_PCM_STATE_RUNNING:    case SNDRV_PCM_STATE_PAUSED:        break;    case SNDRV_PCM_STATE_XRUN:        err = -EPIPE;        goto _end_unlock;    case SNDRV_PCM_STATE_SUSPENDED:        err = -ESTRPIPE;        goto _end_unlock;    default:        err = -EBADFD;        goto _end_unlock;    }    while (size > 0) {        snd_pcm_uframes_t frames, appl_ptr, appl_ofs;        snd_pcm_uframes_t avail;        snd_pcm_uframes_t 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 < 0)                goto _end_unlock;        }        frames = size > avail ? avail : size;        cont = runtime->buffer_size - runtime->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(substream, appl_ofs, data, offset, frames)) < 0)            goto _end;        snd_pcm_stream_lock_irq(substream);        switch (runtime->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 = 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)                goto _end_unlock;        }    } _end_unlock:    snd_pcm_stream_unlock_irq(substream); _end:    return xfer > 0 ? (snd_pcm_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_ptr;    printk("runtime->status->hw_ptr:%lu  runtime->buffer_size:%lu,  runtime->control->appl_ptr:%lu runtime->boundary:%lu",             runtime->status->hw_ptr,  runtime->buffer_size, runtime->control->appl_ptr, runtime->boundary);    if (avail < 0)        avail += runtime->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末尾,这一段是可以写入的(注意这个函数名中有playback,只用于播放),这一段长度为runtime->buffer_size - runtime->control->appl_ptr;另外hw_ptr之前的空间也是可写的(其中的数据已经被播放),所以整个可用的长度为 runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr。

原创粉丝点击