ffmpeg轉碼步驟源碼實現的一點點淺析

ffmpeg轉碼步驟源碼實現的一點點淺析

ffmpeg轉碼過程對解碼的處理封裝在process_input()中(process_input()->decode_video()->decode()->avcodec_send_packet()),轉碼過程中ffmpeg會通過avformat庫一包一包的讀取avpacket經過avcodec_send_packet()往內部解碼器送原始音視頻壓縮包、這裏也提一下,我們都知道
avpacket 和 avframe 是ffmpeg的通用幀封裝 ,
avpacket是壓縮幀,avframe是原始圖像幀,
在解碼端,avpacket會送到解碼器,產出avframe
在編碼端,avframe會送進編碼器,產出avpacket
在濾鏡端,avframe 入,avframe 出

4.2.1ffmpeg內部做了並行解碼,簡圖要如下:

avcodec->internal內部維繫了一個環形線程列表,默認工作線程數量爲nb_cpu + 1 個,主線程通過avcodec_send_pkt()—>…->submit_packet()通過條件變量提交一個任務給一個空閒的工作線程,空閒線程收到通知後調用對應的解碼器回調函數code->decode()開始解碼,同時此線程的狀態機會切換到工作狀態(假定爲灰色格子)
next_decoded總是指向下一個空閒線程,
next_finnished總是指向第一個工作線程,這樣解碼器幀出來的順序即幀送進解碼器的順序,
next_finnished指向的工作線程解碼完成後,會存儲在avcodec->internal->buffer_frame中,avcodec_receive_frame()中會判斷它是否有數據有則取走,沒有則走一遍內部調用取幀,internal一般都是ffmpeg內部結構,不建議開發人員訪問.

關於ffmpeg的多線程編解碼分爲 frame級和slice級 兩類, 當然應該不是所有的編解碼器都支持.
我在測試過程中發現 ffmpeg n4.2.1的版本 解碼 h264 默認是打開了幀級多線程解碼的, 類 -threads 0 -thread_type frame -i xxx.mp4

ffmpeg -y -threads 0 -thread_type frame -i xxx.mp4  -f null -an -

同時掃了一下x264編碼部分,實現方式和解碼形同,但測試過程中卻發現-threads x -thread_type frame 即x264幀級多線程編碼並不支持, 而是轉成了x264內部的多線程編碼參數,可見是因編解碼器而異.

另外ffmpeg transcode_step 中,即便不加filter也會畢走了一個null filter,
add_buffersrc會將avframe添加進內部filter graph link鏈的一個fifo隊列中、
buffsersink_get會從自己buffsink的link fifo裏面取已經產出的frame,如果還沒有,則會激活跑一遍filter graph的濾鏡鏈圖再來取.
簡化如下,未經調試,實際也許有區別.

static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, int samples)
{
    BufferSinkContext *buf = ctx->priv;
    AVFilterLink *inlink = ctx->inputs[0];
    int status, ret;
    AVFrame *cur_frame;
    int64_t pts;

    if (buf->peeked_frame)
        return return_or_keep_frame(buf, frame, buf->peeked_frame, flags);

    while (1) {
        ret = samples ? ff_inlink_consume_samples(inlink, samples, samples, &cur_frame) :
                        ff_inlink_consume_frame(inlink, &cur_frame);
        if (ret < 0) {
            return ret;
        } else if (ret) {
            /* TODO return the frame instead of copying it */
            return return_or_keep_frame(buf, frame, cur_frame, flags);
        } else if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
            return status;
        } else if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST)) {
            return AVERROR(EAGAIN);
        } else if (inlink->frame_wanted_out) {
            ret = ff_filter_graph_run_once(ctx->graph);
            if (ret < 0)
                return ret;
        } else {
            ff_inlink_request_frame(inlink);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章