5、ffplay音频解码模块源码分析

ffplay音频解码模块源码原理分析

一、初始化

二、音频数据写入输出设备

sdl_audio_callback(…)输出数据的回调函数,将被SDL循环调用。

//参数stream为音频缓冲区,len为缓冲区长度,将音频数据拷贝到stream,由SDL将stream中的数据送入硬件播放
//opaque 为userdata,在函数audio_open(...)中赋值给SDL_AudioSpec结构体的userdata字段 
static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
{
    VideoState *is = opaque;
    int audio_size, len1;
    audio_callback_time = av_gettime_relative();
    //该while循环,从AVFrame中解码出原始音频数据,拷贝到stream缓冲中,直到填满音频缓冲区
    //若填满stream缓冲后数据有剩余将存放在is->audio_buf中,并由is->audio_buf_index指定开始位置
    while (len > 0) {
        if (is->audio_buf_index >= is->audio_buf_size) {
            //if语句:上次解码出的原始音频数据已全部送入stream或已播放完成,开始从FrameQueue中取出AVFrame解码音频数据
            //从一帧AVFrame中解码出原始的音频数据,返回字节大小
           audio_size = audio_decode_frame(is);
           if (audio_size < 0) {
                /* if error, just output silence */
               is->audio_buf = NULL;
               is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size;
           } else {
               if (is->show_mode != SHOW_MODE_VIDEO)
                   update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
               is->audio_buf_size = audio_size;
           }
           is->audio_buf_index = 0;
        }
        len1 = is->audio_buf_size - is->audio_buf_index;//未播放音频数据字节大小
        if (len1 > len)
            len1 = len;
        if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
            memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
        else {
            memset(stream, 0, len1);
            if (!is->muted && is->audio_buf)
                SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume);
        }
        len -= len1;//音频缓冲区大小减小
        stream += len1;//推进缓冲区地址
        is->audio_buf_index += len1;//推进音频缓冲区开始索引
    }
    //is->audio_buf中剩余的数据,及填满stream后剩余的数据
    is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;
    /* Let's assume the audio driver that is used by SDL has two periods. */
    if (!isnan(is->audio_clock)) {
        //此时的is->audio_clock存放的是当前解码的所有AVFrame播放完成后对应的pts(单位秒)
        //其值在audio_decode_frame(...)函数中修改
        //此处的set_clock_at(...)修改音频时钟的pts等信息,就要计算当前audio的pts
        //当前audio的pts=所有解码的AVFrame播放完成后的pts - 音频缓冲区数据播放需花费的时间 - 剩余的音频数据(is->audio_write_buf_size)播放需花费的时间。这里2 * is->audio_hw_buf_size是一个估计值,认为stream中的数据到达硬件驱动播放需要2倍的时间
        set_clock_at(&is->audclk,
			is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec,
			is->audio_clock_serial, audio_callback_time / 1000000.0);
        sync_clock_to_slave(&is->extclk, &is->audclk);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值