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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章