gstreamer中playbin的播放原理

1. 目錄結構 
gstreamer-0.10.21 目錄
gst 核心文件,實現gst 元件工廠等功能
plugins gstreamer-0.10.21提供的元件
lib 不可被應用調用的元件,但他是某些元件的parent class
pkgconfig install tools 都是make install需要拷貝的東西
po 各種語言的翻譯文件
tests 測試gstreamer-0.10.21的元件

gst -plugins-base-0.10.21 目錄
gst base提供的元件
gst -libs base提供的元件的parent class,應用不可用的元件
ext 需要額外源碼庫的元件
sys 系統相關的元件(x11,v4l)這2個window就沒有

2.細數元件 
看 gst 有哪些元件就 哪些文件調用了gst_element_register
grep -nr 'gst_element_register' --include=*.c ./

gstreamer的元件統一註冊,別的元件基本都是單獨註冊
static struct _elements_entry _elements[] = {
{"capsfilter", GST_RANK_NONE, gst_capsfilter_get_type},
{"fakesrc", GST_RANK_NONE, gst_fake_src_get_type},
{"fakesink", GST_RANK_NONE, gst_fake_sink_get_type},
#ifdef HAVE_SYS_SOCKET_H
{"fdsrc", GST_RANK_NONE, gst_fd_src_get_type},
{"fdsink", GST_RANK_NONE, gst_fd_sink_get_type},
#endif
{"filesrc", GST_RANK_PRIMARY, gst_file_src_get_type},
{"identity", GST_RANK_NONE, gst_identity_get_type},
{"queue", GST_RANK_NONE, gst_queue_get_type},
{"filesink", GST_RANK_PRIMARY, gst_file_sink_get_type},
{"tee", GST_RANK_NONE, gst_tee_get_type},
{"typefind", GST_RANK_NONE, gst_type_find_element_get_type},
{"multiqueue", GST_RANK_NONE, gst_multi_queue_get_type},
{NULL, 0},
};

3.playbin (只說playbin播放視頻的情況)
playbin元件算是整合的元件,內部建立工廠管道,用到很多元件。

3.1元件流程圖 
filesrc --> (typefind) -->decodebin -->|--> abin (audioconvert-->audioresample-->volume-->osssink)
··························································|--> vbin (identity-->ffmpegcolorspace-->videoscale-->fbdevsink)

3.2代碼分析 
應 用程序建立playbin元件後就調用get_element_set_state將playbin設置爲 NULL,READY,PAUSED,PLAYING, 在READY-->PAUSED時候(GST_STATE_CHANGE_READY_TO_PAUSED),playbin就把管道建立起來了

gst_play_base_bin_change_state
|----setup_source
|----|----gen_source_element 建立source元件,也就是filesrc
|----|----|----if (play_base_bin->suburi) setup_subtitle 如果playbin的suburi屬性被設置了就配置字幕元件
|----|----|----gst_element_make_from_uri
|----|----|----|----get_element_factories_from_uri_protocol根據字符串頭uri協議search_by_entry確定工廠類型,比如file://則建立filesrc
|----|----|----gst_element_factory_create根據確定的工廠類型建立元件(filesrc)

|----|----analyse_source分析源是否是raw數據/有動態pad/有輸出pad再做相應處理
|----|----|----幾種情況都會走到group_commit
|----|----|----|----GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin)->
setup_output_pads 也就是gstplaybin.c的setup_sinks

|----|----|----|----|----有音頻需要可視化調用gen_vis_element
|----|----|----|----|----有音頻不需要可視化調用gen_audio_element
|----|----|----|----|----有視頻調用gen_video_element

|----|----make_decoder
|----|----|----gst_element_factory_make("decodebin", NULL)建立decodebin元件
|----|----gst_element_link (play_base_bin->source, decoder)把filesrc和decodebin鏈接起來

3.3 playbin目錄 
目錄是gst -plugins-base-0.10.21/gst /playback/
原本 着文件挺多,不過是2套playbin,其中一套是非穩定版本playbin2,所以其實真正要 的文件不多 。
gstplayback.c註冊playbin和playbin2
playbin 用到的C文件有(主要是playbin, playbasebin, decodebin)
gstplaybin.c
gstplaybasebin.c
gstdecodebin.c
gststreaminfo.c
gststreamselector.c
gstplaymarshal.c

playbin2 用到的C文件有(與playbin,playbasebin, decodebin等價的是playbin2,playsink, decodebin2)
gstplaybin2.c
gstplaysink.c
gstdecodebin2.c
及剩下的一些文件,gsturidecodebin.c和gstdecodebin2.c也是非穩定版本

3.4 playbin元件用到的parent class 

filesrc 的parent_class是basesrc ,從下面可以 出來,
GST_BOILERPLATE_FULL (GstFileSrc, gst_file_src, GstBaseSrc, GST_TYPE_BASE_SRC,
_do_init);

當用戶調用set_state時對應內部change_state, 會調用該元件的parent class的change_state。basesrc就像是filesrc的基類 

osssink 的parent_class是audiosink ,從下面可以 出來,
osssink_type = g_type_register_static (GST_TYPE_AUDIO_SINK, "GstOssSink", &osssink_info, 0);

osssink只是做跟底層相關的工作,音頻的邏輯層還是在audiosink和它的parent audiobasesink裏面,他們再調用osssink的函數指針做具體讀寫聲卡的操作 

3.5 元件的parent class 
因爲change_state會分發到管道中的每個元件,元件的change_state裏都會調用它parent_class的change_state,所以不免想看 看 playbin的parent_class
struct _GstPlayBin {
GstPlayBaseBin parent;

struct _GstPlayBaseBin {
GstPipeline pipeline;

playbin-->playbasebin-->pipeline已經到管道了


set_state能發送給管道中所有元件的change_state函數 
每個元件的change_state函數中都會調用parent_class的change_state,
playbin--(parent)-->playbasebin
playbasebin--(parent)-->pipeline
pipeline--(parent)-->bin
最上一級是bin, bin的change_state函數,

while (!done) { 
gpointer data; 

switch (gst_iterator_next (it, &data)) { 
case GST_ITERATOR_OK: 

GstElement *child; 

child = GST_ELEMENT_CAST (data); 

/* set state and base_time now */ 
ret = gst_bin_element_set_state (bin, child, base_time, current, next);

把bin裏的元件一個個取出來發送set_state命令,這樣所有元件都會改變狀態了

gstclock獲得時鐘的3種方式 
1 systemclock系統時鐘
2 音頻設備 基於採樣率而知道的時鐘
3 網絡包中包含的時鐘信息

我只 過systemclock, gstclock->gstsystemtclock->g_get_current_time->gettimeofday->(system call)

音視頻時間同步到系統時間 
並不是音頻和視頻誰同步誰,而是大家都跟系統時間同步
NS...B...B...EOS NS=new segment
C.running_time = absolute_time - base_time (系統)
B.running_time = (B.timestamp - NS.start)/NS.abs_rate + NS.accum (元件)
同步就是讓B,C的running_time相等, B等待
base_time從NEW_SEGMENT事件發出爲基準,比如seek後就是NEW SEGMENT

_class_init給klass掛函數指針 

gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
把一個klass轉換爲3種數據結構,可以的原因是一級級都是數據結構的第一個成員,傳進來的kclass是最大的數據結構,往小轉當然可以

struct _GstBaseAudioSink {
GstBaseSink element;

struct _GstBaseSink {
GstElement element;

struct _GstElement 
{
GstObject object;

狀態變遷 
NULL-->READY 
1 probe device
2 open device

READY-->PAUSED 
1 激活pad,返回ASYNC,然後起stream thread線程才把狀態改變的事情做完,直到sink pad收到first buffer,阻塞住,這時才真正算狀態改變完,用get_state可以查詢到
2 管道running_time歸零
3 如果是live source返回NO_PREROLL,不產生數據(live source是即使暫停也會產生數據的源,比如net和camera)

PAUSED-->PLAYING (大部分元件忽略這個狀態)
1 管道選擇時鐘分發到每個子元件,也就是同步時鐘只發生在PLAYING時
2 管道把clock running_time計算出來的base_time分發到每個子元件(change_state時)
3 sink襯墊不再阻塞buffer/event,開始render數據
4 live source開始產生數據

PLAYING-->PAUSED (大部分元件忽略這個狀態)
1 如果sink此時無buffer在手一定要等收到buffer才能完成狀態改變。 上面的READY->PAUSED也是sink在進入PAUSED前一定要有buffer,爲了PLAYING時不至於讓用戶等待前面的一小撮時間
2 EOS事件無效,但到PLAYING時會repost
3 sink把preroll上的所有等待都解除阻塞
4 live source停止產生數據

PAUSED-->READY 
1 sink解除阻塞preroll,元件解除阻塞設備
2 chain和get_range 返回WRONG_STATE,(buffer相關的函數)
3 pad使無效(deactivate),stream thread停止
4 sink忘記所有的協商格式(negotiation caps)

READY-->NULL 
1 close device
2 刪除所有動態建立的pads


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章