close

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来调用,并且自动按照

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 丘猴子 的頭像
    丘猴子

    轉貼部落格

    丘猴子 發表在 痞客邦 留言(0) 人氣()