FFMPEG-0.11.1分析之ffmpeg結構(簡單涉及)與代碼流程(資料流、多線程)【很亂,建議直接看其中包含的原文串連】

來源:互聯網
上載者:User

1.      【資料流】【這裡暫省略,記憶體的管理,只記錄簡單流程,不說每個資料包在哪裡出生,怎麼流向,在哪裡銷毀了。】

可參考連結:http://www.rosoo.net/a/201207/16135.html

 

位元流:流:

這裡說的是URL、IO端,僅僅是個人給他取個名字說是,位元流、流,沒有特殊意義。

 

包:

 

前端是demux,檔案以流的形式在IO開啟,opt_input_file—avformat_open_input中調用s->iformat->read_header(s)進行檔案頭資訊的解析,然後再transcode中,從input_files[file_index]->ctx擷取格式上下文,av_read_frame(is,&pkt)從檔案中將資料以包的形式讀出來,單個包中是整幀資料還是片,看具體容器格式(rvmb中多包一幀見得較多)。接著在output_packet(InputStream *ist,
const AVPacket *pkt)中,拷貝一下當前包,然後轉入解碼端。

 

後端是mux,比如轉碼時音視頻編解碼資訊,有些手動設定,有些預設,在transcode_init—avformat_write_header,調用AVOutputFormat. write_header,檔案的頭資訊(比如,flv、avi、3gp在檔案起始,儲存了很多資訊,有些標記音視頻編解碼參數,有些指示媒體資料或其他資料在接下來檔案中的排布,它們遵從各自的檔案協議。在後端編碼沒有完全結束時,有些類型的檔案頭部資訊也是沒有完結的,其中flv不是,它頭資訊與媒體負載是無關的)。

output_packet—do_streamcopy—write_frame—av_interleaved_write_frame—s->oformat->write_packet中寫入包資料,而IO緊接著就在mux的下層,順理成章即可。

幀:

 

解碼器的開啟avcodec_open2在init_input_stream invoked by transcode_init,同時其中AVCodecContext.get_buffer/release_buffer得到static intcodec_get_buffer(AVCodecContext *s, AVFrame *frame)/ static voidcodec_release_buffer(AVCodecContext *s, AVFrame *frame),get_buffer中從InputStream.
buffer_pool中取幀,InputStream是管理解碼後的資料的結構,buffer_pool是一個FrameBuffer鏈表。

decode_video(InputStream *ist, AVPacket *pkt, int*got_output)中,在InputStream中取AVFrame,avcodec_decode_video2中解碼,單線程和多線程片解碼走vctx->codec->decode,多線程幀解碼走ff_thread_decode_frame。 

 

箭頭是指標參考關聯性,直線是平行關係。呵呵,大家可以看下代碼,這裡畫一下,就是說明他們有關聯。

 

另外,同步問題,大家也要注意下。

 

這有一張圖插在這裡,有用,來自:http://www.rosoo.net/a/201207/16135.html

 

解碼出來的資料,經過pre_process_video_frame(去邊?在解碼中會有加邊處理),OutputStream是用來管理將要編碼之前的資料,在new_output_stream中ost->sync_ist = input_streams[source_index]將兩者聯絡在一起。在transcode—init_simple_filtergraph中,ist->filters[ist->nb_filters- 1] = fg->inputs[0]符合了。下面還是看一下filters,不然很難理清頭緒,這是ffmpeg中新的機制。

 

ffmpeg新版本中的filter可參考:http://blog.csdn.net/nkmnkm/article/details/7219641

AVFilterGraph:幾乎完全等同與directShow中的fitlerGraph,代表一串聯接起來的filter們.【對照起來看,容易理解,很快可以上手】
AVFilter:代表一個filter.
AVFilterPad:代表一個filter的輸入或輸出口,等同於DShow中的Pin.只有輸出pad的filter叫source,只有輸入pad的filter叫sink.
AVFilterLink:代表兩個串連的fitler之間的粘合物.

1 產生graph: AVFilterGraph*graph = avfilter_graph_alloc();
2 建立source
AVFilterContext *filt_src; avfilter_graph_create_filter(&filt_src,&input_filter, "src",NULL, is, graph);
3 建立sink
AVFilterContext *filt_out;
ret = avfilter_graph_create_filter(&filt_out,avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts,graph);
4 串連source和sink:avfilter_link(filt_src, 0, filt_out, 0);
5 對graph做最後的檢查: avfilter_graph_config(graph, NULL);
我們是從sink中取出處理完成的幀,所以最好把sink的引用儲存下來,比如:
AVFilterContext *out_video_filter=filt_out;
6實現input_filter

 

在transcode_init中configure_simple_filtergraph—configure_video_filters做了配置。在poll_filters中調用av_buffersink_read或av_buffersink_get_buffer_ref,這裡縮放就是用這個機制實現的,取代了先前的swscale,不過這個版本的代碼有些亂。

 

編碼器的開啟是在transcode_init中,avcodec_open2(ost->st->codec, codec, &ost->opts),avctx->codec->init初始化。在output_packet中,但有if(!check_output_constraints(ist, ost) || ost->encoding_needed)判斷,poll_filters—do_video_out—avcodec_encode_video2中進行編碼,接著write_frame打包入容器。

 

2.      【多線程】(-threads n 、解碼多線程)(如果要真正支援多線程,需要編譯的時候,加入線程庫pthread)

建議參考:http://www.360doc.com/content/12/0416/11/474846_204064235.shtml

 

解碼多線程:

Avcodec_open2中,ff_lockmgr_cb互斥鎖,相關函數av_lockmgr_register、avpriv_lock_avformat、avpriv_unlock_avformat,entangled_thread_counter變數在

avcodec_open2做簡單標記。ff_thread_init中validate_thread_parameters注意下,如h264解碼的AVCodec .capabilities=/*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |CODEC_CAP_SLICE_THREADS |CODEC_CAP_FRAME_THREADS,這裡說明其能支援的類型,並且在ifelse選擇是FF_THREAD_FRAME先於FF_THREAD_SLICE。

 

(linux)線程相關的函數在pthread.c中,可以參閱以下,ff_thread_init下面會調用到frame_thread_init,裡面有frame_worker_thread背景工作執行緒。

 

要注意AVCodecContext.thread_opaque以及execute、execute2的賦值。

 

SLICE_THREADING:

Ffmpeg中,dvvideo_decoder,ffv1_decoder,h264_decoder,mpeg2_video_decoder和mpeg_video_decoder均支援FF_THREAD_SLICE(slice可以是整幀,可也以幀的分割)。如果線程類型是片,則在ff_thread_init是調用thread_init(AVCodecContext *avctx)做初始化,(順便說一下,這個函數裡可以看到,線程的auto選擇是先擷取cpu數目,FFMIN(nb_cpus + 1, MAX_AUTO_THREADS)),接著pthread_create(&c->workers[i],NULL,worker,
avctx)建立線程,avcodec_thread_park_workers,再然後execute =avcodec_thread_execute、execute2 = avcodec_thread_execute2。

這裡註冊完AVCodecContext的多執行緒函數excute,Codec解碼過程中處理slice時調用avctx->excute(),excute啟動slice解碼背景工作執行緒開始多線程解碼,同時快速返回開始下一slice的解析和解碼。FrameThreading主線程和解碼線程的同步如:

 

FRAME_THREADING:

目前支援Frame Threading的解碼器有h264_decoder,huffyuv_decoder, ffvhuff_decoder, mdec_decoder, mimic_decoder, mpeg4_decoder,theora_decoder, vp3_decoder和vp8_decoder。

Frame Threading有如下限制:使用者函數draw_horiz_band()必須是安全執行緒的;為了提升效能,使用者應該為codec提供安全執行緒的get_buffer()回呼函數,使用者必須能處理多線程帶來的延時。另外,支援frame Threading的codec要求每個包包含一個完整幀。Buffer內容在ff_thread_report_progress()調用之後,buffer內容不能寫。

每個線程都有以下四個狀態,2所示,為了保證安全執行緒,若codec未實現update_thread_context()和安全執行緒的get_buffer(),則必須解碼完成後才能將狀態轉換為STATUS_SETUP_FINISHED,意味著下一個線程只能在當前線程解碼完成後才能開始解碼。

而入兔三所示,如果codec實現update_thread_context()和安全執行緒的get_buffer(),線程狀態可以再解碼開始之前轉換為STATUS_SETUP_FINISHED,這樣,下一個線程就可能與當前線程並行。

解碼主線程通過調用submit_packet將碼流交給對應的解碼線程。主線程和解碼線程的同步4所示。

圖中可以看到,主線程按序遞交packet,由work線程處理。

【文中已標出參考文章,建議大家看原文,本文只是鄙人理思路記錄的片段】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.