Gstreamer的playbin2 插件分析

原文地址:

blog.csdn.net/android_lee/article/details/6789272

自己的一点补充:

playbin2有两个属性:audio-sink和video-sink。应用只需要实例化合适的element然后通过这两个属性传给playbin2就行了

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


你可能感兴趣的:(Gstreamer的playbin2 插件分析)