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線程處理。
【文中已標出參考文章,建議大家看原文,本文只是鄙人理思路記錄的片段】