使用Gstreamer 作為數(shù)據(jù)源輸出視頻數(shù)據(jù)

在虛擬攝像頭程序的設(shè)計(jì)中,我們將用gstreamer作為數(shù)據(jù)源,為directshow注冊攝像頭程序提供數(shù)據(jù)源。

創(chuàng)建數(shù)據(jù)源類型

我們創(chuàng)建一個(gè)數(shù)據(jù)源vcamsource, 這個(gè)vcamsource將包含gststream的實(shí)例, 在VcamSource的私有屬性_VcamSourcePriv 中我們聲明了四個(gè)gstElement元素雏赦,分別代表pipeline,pipeline,capsfilter 星岗,sink:

/**** 注釋掉這部分填大,因?yàn)镚_DECLARE_DERIVABLE_TYPE 中已經(jīng)定義了這個(gè)類
struct  _VcamSource {
    GObject parent_instance;
};
***/
struct _VcamSourcePrivate {
    gboolean status;
    GstElement* pipeline, * source,   *capsfilter , * sink;
    
};

pipeline時(shí)streamer的容器伍茄,source時(shí)streamer的產(chǎn)生源栋盹,capsfilter是媒體格式過濾去,sink是stream的最終渲染器敷矫。我們將使用videotestsrc插件作為source例获,他會隨機(jī)生成stream, 同時(shí)使用appsink插件作為sink,它可以提供外部程序訪問stream中的數(shù)據(jù)曹仗。
需要注意的是在.h頭文件中榨汤,G_DECLARE_DERIVABLE_TYPE已經(jīng)包含了對_VcamSource 的定義,所以不要重復(fù)定義怎茫,另外_VcamSourcePrivate是一個(gè)私有屬性收壕,我們將用下面的宏將它加入,并且它的名字不可以改變必須是ModuleObjName##Private:

G_DEFINE_TYPE_WITH_CODE(VcamSource, vcam_source, G_TYPE_OBJECT, G_ADD_PRIVATE(VcamSource) )


#define VCAM_SOURCE_GET_PRIVATE(obj) ( G_TYPE_INSTANCE_GET_PRIVATE((obj), VCAM_TYPE_SOURCE, VcamSourcePrivate))

新的私有函數(shù)的定義轨蛤,需要使用G_ADD_PRIVATE宏蜜宪,他被作為額外的執(zhí)行代碼放在G_DEFINE_TYPE_WITH_CODE中,所以這里要注意祥山,使用G_DEFINE_TYPE_WITH_CODE替換G_DEFINE_TYPE圃验; 并在在class_init文件中不在需要, VCAM_SOURCE_GET_PRIVATE宏仍然保留,用于獲取私有的屬性缝呕,仍舊需要注意第三個(gè)參數(shù)澳窑,VcamSourcePrivate,他的格式是ModuleObjName##Private供常,不要更改摊聋。

vcam_source_class_init(VcamSourceClass *kclass) {
    //使用G_ADD_PRIVATE配合G_DEFINE_TYPE_WITH_CODE宏,不在需要g_type_class_add_private自己添加私有屬性栈暇, commnet out it
    //g_type_class_add_private(kclass, sizeof(VcamSourcePriv));

gstreamer在VcamSource的工作步驟

在VcamSource實(shí)現(xiàn)gstreamer流程需要以下的步驟:

  1. Gstreamer在執(zhí)行前需要初始化(gst_init (&argc, &argv);)麻裁,但這個(gè)初始化操作最好在系統(tǒng)入口處,所以在vcamsource中我們不進(jìn)行g(shù)streamer的初始化源祈,而是在初次引入VcamSource的時(shí)候煎源,先對gist系統(tǒng)做初始化。
  2. 分別創(chuàng)建source, pipleline, sink等元素
    我們將為每一個(gè)VcamSource實(shí)例分別創(chuàng)建一個(gè)gstreamer實(shí)例新博,有多個(gè)VcamSource時(shí)薪夕,就會對應(yīng)多個(gè)實(shí)例脚草。所以這些元素的創(chuàng)建赫悄,被我們放在object_init方法里:
 static void
vcam_source_init(VcamSource *d) {
      ......
      /* Create the elements */
      VcamSourcePriv* priv = VCAM_SOURCE_GET_PRIVATE(d);
      priv ->source= gst_element_factory_make("videotestsrc", "source");
      priv ->capsfilter = gst_element_factory_make("capsfilter ", "caps");
      priv->sink = gst_element_factory_make ("appsink", "app_sink");
      /* Create the empty pipeline */
      priv->pipeline = gst_pipeline_new("test-pipeline");
}

source在gstreamer中代表的是數(shù)據(jù)源元素,我們使用videotestsrc來作為初始驗(yàn)證源(測試方案用),他將產(chǎn)生測試用vedio數(shù)據(jù)埂淮。 同時(shí)姑隅,我們將采用GstAppSink作為gststream視頻的render(實(shí)際只輸出數(shù)據(jù),并不渲染窗口)倔撞。
appSink的不同之處在于它提供了額外的API讲仰,可以允許外部程序提取stream內(nèi)的數(shù)據(jù)(sample). appSink使用queue來儲存來自streamer線程的數(shù)據(jù),可以設(shè)置drop屬性痪蝇,當(dāng)queue滿了的時(shí)候鄙陡,就及時(shí)得drop掉數(shù)據(jù);相對于自己寫一個(gè)滿足需要的sink插件躏啰,appsink還是很方便的趁矾。
capsfilter 作為過濾器,并不能修改數(shù)據(jù)给僵,但是能夠限制數(shù)據(jù)的格式毫捣,在這里可以限制videotestsrc 產(chǎn)生的數(shù)據(jù)格式。

  1. 組裝pipleLine
    在使用pipleLine前我們需要將所有的gstreamer 元素組裝起來:
 static void
vcam_source_init(VcamSource *d) {
     ......
      /* Create the elements */
      VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(d);
      priv ->source= gst_element_factory_make("videotestsrc", "source");
      priv->sink = gst_element_factory_make("appsink", "app_sink");
      priv->capsfilter = gst_element_factory_make("capsfilter ", "caps");
      /* Create the empty pipeline */
      priv->pipeline = gst_pipeline_new("test-pipeline");

      if (!priv->pipeline || !priv->source || !priv->sink) {
          g_printerr("Not all elements could be created.\n");
          return -1;
      }

      /* Build the pipeline */
    gst_bin_add_many(GST_BIN(priv->pipeline), priv->source, priv->capsfilter, priv->sink, NULL);
    //if (gst_element_link(priv->source,  priv->sink) != TRUE) {
    if (gst_element_link(priv->source, priv->capsfilter) != TRUE || gst_element_link(priv->capsfilter, priv->sink) != TRUE) {
         g_printerr("Elements could not be linked.\n");
        gst_object_unref(priv->pipeline);
        return -1;
    }

}

gstreamer 元素的組裝也是在 vcam_source對象初始化的函數(shù)中來完成帝际, 連接的路線是 videotestsrc | capsfilter | appsink.

  1. 配置filter和appsink
    capsfilter主要用來限制testsrc的輸出格式蔓同,我們需要在pipleline調(diào)用前,先給提供一個(gè)默認(rèn)的格式:
    video/x-raw, width=640, height=480, format=RGB //表示640X480 RGB frame
    filter 和appsink的設(shè)置也是在初始化函數(shù)中完成:
static void
vcam_source_init(VcamSource *d) {
      ....
      /* Create the elements */
      ....
      /**設(shè)置 fitler caps**/ 
      GstCaps* caps = gst_caps_new_simple("video/x-raw",
          "format", G_TYPE_STRING, "RGB",
          "framerate", GST_TYPE_FRACTION, 25, 1,
          "width", G_TYPE_INT, 640,
          "height", G_TYPE_INT, 480,
          NULL);
      g_object_set(priv->capsfilter, "caps", caps, NULL);
      /**設(shè)置appsink的max-buffers來限制buffer大小蹲诀,
         設(shè)置drop屬性斑粱,允許queue滿意后自動blockthread或drop舊的buffer**/
      gst_app_sink_set_max_buffers(priv->sink, 500); // limit number of buffers queued
      gst_app_sink_set_drop(priv->sink, TRUE); // drop old buffers in queue when full
      /* Build the pipeline */
    ....
}
  1. 啟動pipleLine
    pipleline的啟動就是獎(jiǎng)狀臺設(shè)置為GST_STATE_PLAYING, 但是并不是創(chuàng)建完就啟動的侧甫,相反VcamSource中我們需要提供一個(gè)啟動函數(shù)珊佣,讓用戶或調(diào)用程序,顯示的啟動pipleLine.
    在.h頭文件中披粟,我們聲明一個(gè)start函數(shù)咒锻,以及一個(gè)幫助函數(shù):
struct _VcamSourceClass {
    GObjectClass parent_class;
   .......
   gshort (*start)(VcamSource* self);
  .....
};

void vcam_source_getMediaTyp(VcamSource *self, VcamIMediaType* typeptr);

gshort vcam_source_start(VcamSource* self);

在.c文件中,我們需要?jiǎng)?chuàng)建start的默認(rèn)實(shí)現(xiàn)函數(shù)守屉,以及vcam_source_start幫助函數(shù)惑艇, 并且將start指針指向默認(rèn)實(shí)現(xiàn)函數(shù):

//start pipleline
 static gshort startimpl(VcamSource* self) {
     
     VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(self);
     GstStateChangeReturn ret;
     /* Start playing */
     ret = gst_element_set_state(priv->pipeline, GST_STATE_PLAYING);
     if (ret == GST_STATE_CHANGE_FAILURE) {
         g_printerr("Unable to set the pipeline to the playing state (check the bus for error messages).\n");
         return -1;  //啟動失敗
     }
     else {
         priv->status = TRUE;
         return 0;
     }
 }

 gshort vcam_source_start(VcamSource* self) {
     VcamSourceClass *klass = VCAM_SOURCE_GET_CLASS(self);
     if (klass->start == NULL) return -2; //函數(shù)未初始化
      return klass->start(self);
 }



static void
vcam_source_class_init(VcamSourceClass *kclass) {
    //不在需要手動注冊私有屬性
   // g_type_class_add_private(kclass, sizeof(VcamSourcePriv));
     .....
    kclass->start = startimpl;
    .......
}

在pipleline成功start以后,我們會將vcamsource的狀態(tài)設(shè)置為true.

  1. 獲取來自gst appsink的數(shù)據(jù)
    appsink可以提供外部程序訪問stream中數(shù)據(jù)的能力拇泛。通過appsink訪問視頻samples的方法通常包括 GstApp.AppSink.prototype.pull_sampleGstApp.AppSink.prototype.pull_preroll滨巴, 它們是同步方式,這兩個(gè)方法會一致出去block狀態(tài)俺叭, 除非新的samples已經(jīng)可以被訪問恭取,或者sink關(guān)閉里,stream流到了末尾熄守,它們才會返回蜈垮。
    當(dāng)然耗跛, appsink對這兩個(gè)函數(shù)也提供了timeout的版本,GstApp.AppSink.prototype.try_pull_sampleGstApp.AppSink.prototype.try_pull_preroll攒发,它們連個(gè)額外接收一個(gè)timeout的參數(shù)调塌,用于限制等待的時(shí)間。
    pull_sample主要用于appsink在PlAYING狀態(tài)下獲取sample, 而pull_preroll則是當(dāng)PlAYING處于PAUSED狀態(tài)時(shí)惠猿,獲取最近的預(yù)撥(preroll)sample羔砾。特別是當(dāng)發(fā)生seek后,調(diào)用該方法可以立刻獲得seek位置的數(shù)據(jù)偶妖。
    vcamsource將同樣提供兩個(gè)方法姜凄,讓使用者可以方便的訪問視頻數(shù)據(jù),同時(shí)忽略內(nèi)部的實(shí)現(xiàn):
    void vcam_source_pull_sample(VcamSource self, Gst.Sample sample)
    void vcam_source_pull_preroll(VcamSource self, Gst.Sample sample)

并在.c文件中完成它們的實(shí)現(xiàn)趾访,在當(dāng)前階段檀葛,我們只是簡單的返回gstsample:

  void  vcam_source_pull_sample(VcamSource* self, GstSample* sample) {
      

      VcamSourcePriv* private = VCAM_SOURCE_GET_PRIVATE(self);

      g_return_if_fail(priv->sink != NULL);

      sample = gst_app_sink_pull_sample(priv->sink);

      if (sample == NULL && gst_app_sink_is_eos(priv->sink)) {
          g_print("the stream reach the ond \n");
          //send out singal for eof of stream
      }
      else
      {
          g_print("gst_app_sink_pull_sample returned null\n");
          return NULL;
      }
  }

  void  vcam_source_pull_preroll(VcamSource* self, GstSample* sample) {
      
      VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(self);

      g_return_if_fail(priv->sink != NULL);
      
      sample = gst_app_sink_pull_preroll(priv->sink);
      
      if (sample == NULL && gst_app_sink_is_eos(priv->sink)) {
          g_print("the stream reach the ond \n");
          //send out singal for eof of stream
      }else{
          g_print("gst_app_sink_pull_preroll returned null\n");
         
      }
  }

gststreamer元素的銷毀
每一個(gè)vcamsource元素,包含一個(gè)stream pipleline, 當(dāng)vcamsource由于任何原因走向銷毀得時(shí)候腹缩,我們需要同時(shí)銷毀stream得所有元素屿聋。
在VcamSource中我們增肌內(nèi)存管理所需的函數(shù),這些函數(shù)將幫我們最終銷毀VcamSource中創(chuàng)建的所有g(shù)streamer 元素:

 static void
      vcam_source_dispose(GObject* gobject)
  {
      VcamSourcePriv* priv = VCAM_SOURCE_GET_PRIVATE(gobject);

      /* In dispose(), you are supposed to free all types referenced from this
       * object which might themselves hold a reference to self. Generally,
       * the most simple solution is to unref all members on which you own a
       * reference.
       */

       /* dispose() might be called multiple times, so we must guard against
        * calling g_object_unref() on an invalid GObject by setting the member
        * NULL; g_clear_object() does this for us.
        */
      gst_element_set_state(priv->pipeline, GST_STATE_NULL);
      g_clear_object(&priv->pipeline);

      /* Always chain up to the parent class; there is no need to check if
       * the parent class implements the dispose() virtual function: it is
       * always guaranteed to do so
       */
      G_OBJECT_CLASS(vcam_source_parent_class)->dispose(gobject);
  }


  static void
      vcam_source_finalize(GObject* gobject)
  {
      /* Always chain up to the parent class; as with dispose(), finalize()
       * is guaranteed to exist on the parent's class virtual function table
       */
      G_OBJECT_CLASS(vcam_source_parent_class)->finalize(gobject);
  }

以上的代碼中藏鹊,不需要組個(gè)unreference source, sink, capsfilter. 由于它們都在pipleline中润讥,pipleline會自己處理好。如果手動處理盘寡,會報(bào)錯(cuò)誤楚殿。

最后,在修改class_init函數(shù)竿痰,將上邊的函數(shù)與gobject對象中定義的dispose與finalize函數(shù)bind起來:

static void
vcam_source_class_init(VcamSourceClass *kclass) {
    //注冊私有屬性
   ......
    /**memory manage**/
    base_class->dispose = vcam_source_dispose;
    base_class->finalize = vcam_source_finalize;
    /* install properties */
  ......
}

測試代碼:
在.c文件中我們寫一個(gè)測試程序脆粥,創(chuàng)建一個(gè)新的vcamsource對象,并且啟動它影涉,然后獲取preroll sample, 如果獲取成功变隔,打印 ****:

int main(int argc, char* argv) {
    gst_init(&argc, &argv);
    g_print("start gst test\n");
    VcamSource * source = g_object_new(VCAM_TYPE_SOURCE,NULL);
    vcam_source_start(source);
    VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(source);
    GstSample* sample;
    sample = gst_app_sink_pull_preroll(priv->sink);
    if(sample!=NULL)
        g_print("*********\n");
    g_print("start unrefercene \n");
    g_object_unref(source);
    g_print("program stop!!!!!!! \n");
}

上面的代碼包含了一行 gst_init(&argc, &argv);, 用于啟動gst系統(tǒng)蟹倾,它是我們上文提到的第一步的工作匣缘。測試打印結(jié)果如下:

image.png

完成代碼:
VcamSource.h

#ifndef VCAM_SOURCE_H
#define VCAM_SOURCE_H
#include <gst/gst.h>

#define VCAM_TYPE_SOURCE (vcam_source_get_type())
G_DECLARE_DERIVABLE_TYPE(VcamSource, vcam_source, VCAM, SOURCE, GObject)

typedef struct _VcamSourcePrivate VcamSourcePrivate;



struct _VcamSourceClass {
    GObjectClass parent_class;

    gshort(*start)(VcamSource* self);
};


gshort vcam_source_start(VcamSource* self);

void  vcam_source_pull_sample(VcamSource* self, GstSample* sample);

void  vcam_source_pull_preroll(VcamSource* self, GstSample* sample);

#endif /* __VCAM_SOURCE_H__ */

VcamSource.c


#include "VcamSource.h"
#include <gst/app/gstappsink.h>

struct _VcamSourcePrivate {
    gboolean status;
    GstElement* pipeline;
    GstElement* source;
    GstElement* capsfilter;
    GstElement* sink;
};


G_DEFINE_TYPE_WITH_CODE(VcamSource, vcam_source, G_TYPE_OBJECT, G_ADD_PRIVATE(VcamSource) )


#define VCAM_SOURCE_GET_PRIVATE(obj) ( G_TYPE_INSTANCE_GET_PRIVATE((obj), VCAM_TYPE_SOURCE, VcamSourcePrivate))



enum _VCAM_SOURCE_PROPERTY {
    VCAM_SOURCE_PROPERTY0,
    VCAM_SOURCE_STATUS,
    VCAM_SOURCE_VEDIO
};




static void  vcam_source_finalize(GObject* gobject);
static void  vcam_source_dispose(GObject* gobject);
static gshort startimpl(VcamSource* self);
static void   vcam_source_set_property(GObject* object, guint property_id,
                                       const GValue* value, GParamSpec* pspec);
static void    vcam_source_get_property(GObject* object, guint property_id,
                                         GValue* value, GParamSpec* pspec);

static void
vcam_source_class_init(VcamSourceClass* kclass) {
    g_print("start the class init \n");
    //注冊私有屬性
    //g_type_class_add_private(kclass, sizeof(VcamSourcePriv));

    kclass->start = startimpl;

    /* override base object methods */
    GObjectClass* base_class = G_OBJECT_CLASS(kclass);
    base_class->set_property = vcam_source_set_property;
    base_class->get_property = vcam_source_get_property;
    /**memory manage**/
    base_class->dispose = vcam_source_dispose;
    base_class->finalize = vcam_source_finalize;

    /* install properties */
   // g_object_class_install_property(base_class, VCAM_SOURCE_STATUS,
  //      g_param_spec_uint("status", "source status", "desscribe server's status", \
   //         0, 10, 0, G_PARAM_READWRITE));
 
}

static void
vcam_source_init(VcamSource* d) {
    g_print("start the object init \n");
    /* Create the elements */
    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(d);
    priv->source = gst_element_factory_make("videotestsrc", "source");
    priv->sink = gst_element_factory_make("appsink", "app_sink");
    priv->capsfilter = gst_element_factory_make("capsfilter", "caps_filter");
    /* Create the empty pipeline */
    priv->pipeline = gst_pipeline_new("test-pipeline");
    if (!priv->pipeline || !priv->source || !priv->sink) {
        g_printerr("Not all elements could be created.\n");
        return;
    }
    /**設(shè)置 fitler caps**/
    GstCaps* caps = gst_caps_new_simple("video/x-raw",
        "format", G_TYPE_STRING, "I420",
        "framerate", GST_TYPE_FRACTION, 25, 1,
        "width", G_TYPE_INT, 320,
        "height", G_TYPE_INT, 240, 
        NULL);
    g_object_set(priv->capsfilter, "caps", caps, NULL);
    /**設(shè)置appsink的max-buffers來限制buffer大小,設(shè)置drop屬性鲜棠,允許queue滿意后自動blockthread或drop舊的buffer**/
    gst_app_sink_set_max_buffers(priv->sink, 500); // limit number of buffers queued
    gst_app_sink_set_drop(priv->sink , TRUE); // drop old buffers in queue when full
   

    /* Build the pipeline */
    gst_bin_add_many(GST_BIN(priv->pipeline), priv->source, priv->capsfilter, priv->sink, NULL);
    //if (gst_element_link(priv->source,  priv->sink) != TRUE) {
    if (gst_element_link(priv->source, priv->capsfilter) != TRUE || gst_element_link(priv->capsfilter, priv->sink) != TRUE) {
         g_printerr("Elements could not be linked.\n");
        gst_object_unref(priv->pipeline);
        return -1;
    }

}


//start pipleline
static gshort startimpl(VcamSource* self) {

    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(self);
    GstStateChangeReturn ret;
    /* Start playing */
    ret = gst_element_set_state(priv->pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set the pipeline to the playing state (check the bus for error messages).\n");
        return -1;  //啟動失敗
    }
    else {
        priv->status = TRUE;
        return 0;
    }
    g_object_unref(priv); //結(jié)束引用
}

gshort vcam_source_start(VcamSource* self) {
    VcamSourceClass* klass = VCAM_SOURCE_GET_CLASS(self);
    if (klass->start == NULL) return -2; //函數(shù)未初始化
    return klass->start(self);
    g_object_unref(klass); //結(jié)束引用
}

                         

static void
vcam_source_set_property(GObject* object, guint property_id,
    const GValue* value, GParamSpec* pspec)
{
    VcamSource* self = VCAM_SOURCE(object);
    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(self);
    switch (property_id) {
    case VCAM_SOURCE_STATUS:

        priv->status = g_value_get_boolean(value);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        break;
    }
    g_object_unref(self); //結(jié)束引用
    g_object_unref(priv); //結(jié)束引用
}

static void
vcam_source_get_property(GObject* object, guint property_id,
    GValue* value, GParamSpec* pspec)
{
    VcamSource* self = VCAM_SOURCE(object);
    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(self);
    switch (property_id) {
    case VCAM_SOURCE_STATUS:
        g_value_set_boolean(value, priv->status);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        break;
    }
    g_object_unref(self); //結(jié)束引用
    g_object_unref(priv); //結(jié)束引用
}


void  vcam_source_pull_sample(VcamSource* self, GstSample* sample) {


    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(self);

    g_return_if_fail(priv->sink != NULL);

    sample = gst_app_sink_pull_sample(priv->sink);

    if (sample == NULL && gst_app_sink_is_eos(priv->sink)) {
        g_print("the stream reach the ond \n");
        sample = NULL;
        //send out singal for eof of stream
    }
    else
    {
        g_print("gst_app_sink_pull_sample returned null\n");
        sample = NULL;
        return NULL;
    }
    g_object_unref(priv); //結(jié)束引用
}

void  vcam_source_pull_preroll(VcamSource* self, GstSample* sample) {

    VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(self);

    g_return_if_fail(priv->sink != NULL);

    sample = gst_app_sink_pull_preroll(priv->sink);

    if (sample == NULL && gst_app_sink_is_eos(priv->sink)) {
        g_print("the stream reach the ond \n");
        //send out singal for eof of stream
    }
    else {
        g_print("gst_app_sink_pull_preroll returned null\n");

    }
    g_object_unref(priv); //結(jié)束引用
}


static void
vcam_source_dispose(GObject* gobject)
{
    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(gobject);

    /* In dispose(), you are supposed to free all types referenced from this
     * object which might themselves hold a reference to self. Generally,
     * the most simple solution is to unref all members on which you own a
     * reference.
     */

     /* dispose() might be called multiple times, so we must guard against
      * calling g_object_unref() on an invalid GObject by setting the member
      * NULL; g_clear_object() does this for us.
      */
    gst_element_set_state(priv->pipeline, GST_STATE_NULL);
    //source, capsfitler, sink 都有pipeline 管理肌厨, 清除工作有pipleline來完成
    g_clear_object(&priv->pipeline);
    /* Always chain up to the parent class; there is no need to check if
     * the parent class implements the dispose() virtual function: it is
     * always guaranteed to do so
     */
    G_OBJECT_CLASS(vcam_source_parent_class)->dispose(gobject);
}


static void
vcam_source_finalize(GObject* gobject)
{
    VcamSourcePrivate * priv = VCAM_SOURCE_GET_PRIVATE(gobject);

    /* Always chain up to the parent class; as with dispose(), finalize()
     * is guaranteed to exist on the parent's class virtual function table
     */
    G_OBJECT_CLASS(vcam_source_parent_class)->finalize(gobject);
}


int main(int argc, char* argv) {
    gst_init(&argc, &argv);
    g_print("start gst test\n");
    VcamSource * source = g_object_new(VCAM_TYPE_SOURCE,NULL);
    vcam_source_start(source);
    VcamSourcePrivate* priv = VCAM_SOURCE_GET_PRIVATE(source);
    GstSample* sample;
    sample = gst_app_sink_pull_preroll(priv->sink);
    if(sample!=NULL)
        g_print("*********\n");
    g_print("start unrefercene \n");
    g_object_unref(source);
    g_print("program stop!!!!!!! \n");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市豁陆,隨后出現(xiàn)的幾起案子柑爸,更是在濱河造成了極大的恐慌,老刑警劉巖盒音,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件表鳍,死亡現(xiàn)場離奇詭異何址,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)进胯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來原押,“玉大人胁镐,你說我怎么就攤上這事≈钕危” “怎么了盯漂?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笨农。 經(jīng)常有香客問我就缆,道長,這世上最難降的妖魔是什么谒亦? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任竭宰,我火速辦了婚禮,結(jié)果婚禮上份招,老公的妹妹穿的比我還像新娘切揭。我一直安慰自己,他們只是感情好锁摔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布廓旬。 她就那樣靜靜地躺著,像睡著了一般谐腰。 火紅的嫁衣襯著肌膚如雪孕豹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天十气,我揣著相機(jī)與錄音励背,去河邊找鬼。 笑死砸西,一個(gè)胖子當(dāng)著我的面吹牛椅野,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播籍胯,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竟闪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了杖狼?” 一聲冷哼從身側(cè)響起炼蛤,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝶涩,沒想到半個(gè)月后理朋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體絮识,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年嗽上,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了次舌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兽愤,死狀恐怖彼念,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浅萧,我是刑警寧澤逐沙,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站洼畅,受9級特大地震影響吩案,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帝簇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一徘郭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丧肴,春花似錦崎岂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至途样,卻和暖如春江醇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背何暇。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工陶夜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裆站。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓条辟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宏胯。 傳聞我的和親對象是個(gè)殘疾皇子羽嫡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容