Gstreamer 中的playback插件
标签:
Playbin2 provides a stand-alone everything-in-one abstraction for an audio and/or video player.
playbin2 is considered stable now. It is the prefered playback API now, and replaces the old playbin element, which is no longer supported.
上面的例子中就是没有描述audio/videosink,playbin要负责找到最好的element。
基于下面这个图,gst-lauch就是这里的application,pipeline 就是playbin
也可以不使用playbin,而是直接描述自己的pipeline:
gst-launch gnlfilesource location=file:///tmp/myfile.
mov start=0 duration=2000000000 ! autovideosink
在gst-lauch中就是来控制pipeline状态机的状态跳转来驱动pipeline工作,并且接收pipeline发出的message
进行相应的处理,当EOS message发出后跳出处理。application通过event来驱动pipeline工作,并根据
pipeline发出的message确定如何对pipeline进行控制。
可见,playbin只是一个pipeline,而pipeline需要一个application来进行创建和驱动,gst-launch就是这样
的一个application。
1. 创建pipeline
int main (int argc, char *argv[])
{
pipeline =
(GstElement *) gst_parse_launchv ((const gchar **) argvn, &error);//从上面的调用来看,
gst-launch需要再命令行上描述pipeline的组成,因此通过解析命令行可以知道pipeline内的
element的情况,gst-launch解析这些element并且从注册的plugin中找到这些element来创建
出pipeline,得到pipeline后也可以通过pipeline的数据结构得到内部的element的指针,因此
application是可以直接调用element内实现的函数比如change_state等。
}
2. pipeline和element对外函数接口
gstreamer为element定义了一些需要实现的函数,这些函数可以被application调用。在类定义中
申明了这些函数,并且通常都是在class_init()中定义。另外,pipeline是一个多重继承的子类:
继承了Gobject,Gelement, GBin类,
struct _GstPlayBinClass
{
GstPipelineClass parent_class;
.....
}
struct _GstPipelineClass {
GstBinClass parent_class;
...
}
struct _GstBinClass {
GstElementClass parent_class;
....
}
struct _GstElementClass
{
GstObjectClass parent_class;
....
}
struct _GstObjectClass {
GObjectClass parent_class;
。。。。
}
继承关系:
GObjectClass ->GstObjectClass->GstElementClass->GstBinClass->GstPipelineClass->GstPlayBinClass
举个例子:
在gst-lauch中,通过对gstElement调用set_state()函数,
static GstElement *pipeline = NULL;
...
gst_element_set_state (pipeline,GST_STATE_PLAYING);
-> result = (oclass->set_state) (element, state);
-> klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func);
-> transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
ret = gst_element_change_state (element, transition);
-> ret = (oclass->change_state) (element, transition);
因为当前状态和target状态可能不是一次就能跳转到的,而是要符合状态机跳转的
规则,可能产生中间跳转比如从null->playing,需要经过null->ready->pause->play
因此需要处理这些中间状态,通过对ret返回值进行处理(target_state),需要根据
target_state来建立中间跳转,通过下面的函数来完成中间跳转:
gst_element_continue_state
-> message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
old_state, old_next, pending);
gst_element_post_message (element, message);
set_state()要满足状态机跳转的要求:
typedef enum
{
GST_STATE_CHANGE_NULL_TO_READY = (GST_STATE_NULL<<3) | GST_STATE_READY,
GST_STATE_CHANGE_READY_TO_PAUSED = (GST_STATE_READY<<3) | GST_STATE_PAUSED,
GST_STATE_CHANGE_PAUSED_TO_PLAYING = (GST_STATE_PAUSED<<3) | GST_STATE_PLAYING,
GST_STATE_CHANGE_PLAYING_TO_PAUSED = (GST_STATE_PLAYING<<3) | GST_STATE_PAUSED,
GST_STATE_CHANGE_PAUSED_TO_READY = (GST_STATE_PAUSED<<3) | GST_STATE_READY,
GST_STATE_CHANGE_READY_TO_NULL = (GST_STATE_READY<<3) | GST_STATE_NULL
} GstStateChange;
因此具有这些类的函数接口:
static void
gst_play_bin_class_init (GstPlayBinClass * klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gstbin_klass = (GstBinClass *) klass;
}
struct _GstPlayBinClass
{
GstPipelineClass parent_class;
void (*about_to_finish) (GstPlayBin * playbin);
void (*video_changed) (GstPlayBin * playbin);
void (*audio_changed) (GstPlayBin * playbin);
void (*text_changed) (GstPlayBin * playbin);
void (*video_tags_changed) (GstPlayBin * playbin, gint stream);
void (*audio_tags_changed) (GstPlayBin * playbin, gint stream);
void (*text_tags_changed) (GstPlayBin * playbin, gint stream);
GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
GstPad *(*get_audio_pad) (GstPlayBin * playbin, gint stream);
GstPad *(*get_text_pad) (GstPlayBin * playbin, gint stream);
};
static void
gst_play_bin_class_init (GstPlayBinClass * klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
gobject_klass = (GObjectClass *) klass;
gstelement_klass = (GstElementClass *) klass;
gstbin_klass = (GstBinClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_klass->set_property = gst_play_bin_set_property;
gobject_klass->get_property = gst_play_bin_get_property;
gobject_klass->finalize = gst_play_bin_finalize;
klass->get_video_tags = gst_play_bin_get_video_tags;
klass->get_audio_tags = gst_play_bin_get_audio_tags;
klass->get_text_tags = gst_play_bin_get_text_tags;
klass->convert_frame = gst_play_bin_convert_frame;
klass->get_video_pad = gst_play_bin_get_video_pad;
klass->get_audio_pad = gst_play_bin_get_audio_pad;
klass->get_text_pad = gst_play_bin_get_text_pad;
gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
gstbin_klass->handle_message =
GST_DEBUG_FUNCPTR (gst_play_bin_handle_message); }
application可以通过pipeline指针和element指针去调用各个element的类函数比如:
bus = gst_element_get_bus (pipeline);
gst_element_set_index (pipeline, index);
gst_element_set_state (pipeline, GST_STATE_PAUSED);
如何创建message:
//Create a new element-specific message创建一个element相关的message
GstMessage *gst_message_new_element (GstObject * src, GstStructure * structure)
{
return gst_message_new_custom (GST_MESSAGE_ELEMENT, src, structure);//message创建函数 }
所有支持的message类型包括:
static GstMessageQuarks message_quarks[] = {
{GST_MESSAGE_UNKNOWN, "unknown", 0},
{GST_MESSAGE_EOS, "eos", 0},
{GST_MESSAGE_ERROR, "error", 0},
{GST_MESSAGE_WARNING, "warning", 0},
{GST_MESSAGE_INFO, "info", 0},
{GST_MESSAGE_TAG, "tag", 0},
{GST_MESSAGE_BUFFERING, "buffering", 0},
{GST_MESSAGE_STATE_CHANGED, "state-changed", 0},
{GST_MESSAGE_STATE_DIRTY, "state-dirty", 0},
{GST_MESSAGE_STEP_DONE, "step-done", 0},
{GST_MESSAGE_CLOCK_PROVIDE, "clock-provide", 0},
{GST_MESSAGE_CLOCK_LOST, "clock-lost", 0},
{GST_MESSAGE_NEW_CLOCK, "new-clock", 0},
{GST_MESSAGE_STRUCTURE_CHANGE, "structure-change", 0},
{GST_MESSAGE_STREAM_STATUS, "stream-status", 0},
{GST_MESSAGE_APPLICATION, "application", 0},
{GST_MESSAGE_ELEMENT, "element", 0},
{GST_MESSAGE_SEGMENT_START, "segment-start", 0},
{GST_MESSAGE_SEGMENT_DONE, "segment-done", 0},
{GST_MESSAGE_DURATION, "duration", 0},
{GST_MESSAGE_LATENCY, "latency", 0},
{GST_MESSAGE_ASYNC_START, "async-start", 0},
{GST_MESSAGE_ASYNC_DONE, "async-done", 0},
{GST_MESSAGE_REQUEST_STATE, "request-state", 0},
{GST_MESSAGE_STEP_START, "step-start", 0},
{GST_MESSAGE_QOS, "qos", 0},
{GST_MESSAGE_PROGRESS, "progress", 0},
{0, NULL, 0}
};
//把message发到bus上
gboolean gst_bus_post (GstBus * bus, GstMessage * message)
例子:
s = gst_structure_new ("test_message", "msg_id", G_TYPE_INT, i, NULL);
m = gst_message_new_element (NULL, s);
GST_LOG ("posting element message");
gst_bus_post (test_bus, m);
在gst-launch中,由application直接去处理message,
static EventLoopResult
event_loop (GstElement * pipeline, gboolean blocking, GstState target_state)
{
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
while (TRUE) {
message = gst_bus_poll (bus, GST_MESSAGE_ANY, blocking ? -1 : 0);//从bus上获取message再处理
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_NEW_CLOCK:
{
GstClock *clock;
gst_message_parse_new_clock (message, &clock);
PRINT ("New clock: %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL"));
break;
}
case GST_MESSAGE_CLOCK_LOST:
PRINT ("Clock lost, selecting a new one\n");
gst_element_set_state (pipeline, GST_STATE_PAUSED);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
break;
。。。。。。
}
另外一种message处理方法:
由于bus的存在,而message都需要通过bus传输给application,另外一种方法就是在bus上增加watch函数
来处理pipeline发给application的message:
bus = gst_pipeline_get_bus (GST_PIPELINE (play));
gst_bus_add_watch (bus, my_bus_callback, loop);
gst_object_unref (bus);
static gboolean
my_bus_callback (GstBus *bus,
GstMessage *message,
gpointer data)
{
GMainLoop *loop = data;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
gst_message_parse_error (message, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
g_free (debug);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:
g_main_loop_quit (loop);
break;
.....
}
3. playbin2
playbin2是一个pipeline,受到application的驱动。pipeline也是一个element,因此包含一个4状态机:
NULL,ready,pause,play
NULL);
(playbin),gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0,
g_signal_emit (G_OBJECT
NULL);
g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0,
当某个signal发出以后,该handler函数自动调用:
};
LAST_SIGNAL
SIGNAL_GET_TEXT_PAD,
SIGNAL_GET_AUDIO_PAD,
SIGNAL_GET_VIDEO_PAD,
SIGNAL_GET_TEXT_TAGS,
SIGNAL_GET_AUDIO_TAGS,
SIGNAL_GET_VIDEO_TAGS,
SIGNAL_TEXT_TAGS_CHANGED,
SIGNAL_AUDIO_TAGS_CHANGED,
SIGNAL_VIDEO_TAGS_CHANGED,
SIGNAL_TEXT_CHANGED,
SIGNAL_AUDIO_CHANGED,
SIGNAL_VIDEO_CHANGED,
SIGNAL_CONVERT_FRAME,
SIGNAL_ABOUT_TO_FINISH,
{
enum
可以定义一些signal来绑定信号处理函数的方式:(基于event的方式)
如果需要和APPLICATION进行通信,发出message信息通过bus传递给application。在element中是如何发出message。
状态跳转图来维护状态机的正常跳转。
首先playbin2和普通的plugin的element一样要实现相关的类函数供Application来调用,并且自动按照