gstreamer 订阅
GStreamer 是用来构建流媒体应用的开源多媒体框架(framework),其目标是要简化音/视频应用程序的开发,目前已经能够被用来处理像 MP3、Ogg、MPEG1、MPEG2、AVI、Quicktime 等多种格式的多媒体数据。 展开全文
GStreamer 是用来构建流媒体应用的开源多媒体框架(framework),其目标是要简化音/视频应用程序的开发,目前已经能够被用来处理像 MP3、Ogg、MPEG1、MPEG2、AVI、Quicktime 等多种格式的多媒体数据。
信息
外文名
gstreamer
领    域
计算机技术
技    术
解码和过滤技术
属    性
开源的多媒体框架库
桌面环境
GNOME
gstreamer发展历史
1999年Erik Walthinsen创建了GStreamer,2001年1月11日发表了第一个主要版本是0.1。没过多久,GStreamer出现了第一个商业版本,由RidgeRun公司发行,这是一家嵌入式Linux 公司。RidgeRun后来遇到了财务困难,工作人员大多离去,包括Walthinsen。 GStreamer的进展并未受影响。2001年7月发表了0.2.0的版本,2002年9月,发表了0.4.0,2004年3月又发表了0.8.0。2004年,新公司Fluendo成立,并使用GStreamer编写一个流媒体服务器Flumotion, 并提供多媒体解决方案。2005年12月发表了0.10.0版本。日后GStreamer渐渐普及,2006年,Totem , Rhythmbox 和 Banshee等媒体巨头都使用 GStreamer。GStreamer日后在商业上获取巨大成功有许多不同的公司采用(诺基亚、摩托罗拉、德州仪器、 飞思卡尔、英特尔等等),并已成为一个非常强大的跨平台多媒体框架。其跨平台设计,使其能够在Linux(包括x86,PowerPC和ARM架构),Solaris(Intel和SPARC)以及OpenSolaris,FreeBSD,OpenBSD,NetBSD,Mac OS X,Microsoft Windows和OS/400上运行。GStreame也有针对其他语言的绑定如Python,Vala,C++,Perl,GNU Guile和Ruby。GStreamer依据GNU宽通用公共许可证授权。
收起全文
精华内容
参与话题
问答
  • 详细的GStreamer开发教程

    万次阅读 多人点赞 2019-11-04 15:10:36
    详细的GStreamer开发教程 文章目录详细的GStreamer开发教程1. 什么是GStreamer?2. GStreamer架构2.1 Media Applications2.2 Core Framework2.3 Plugins3. GStreamer组件3.1 Element创建一个 GstElement3.2 箱柜...

    详细的GStreamer开发教程



    本文主要参考GStreamer官方Tutorials:gstreamer Tutorials

    1. 什么是GStreamer?

    Gstreamer是一个用于开发流媒体应用的开源框架,采用了基于插件(plugin)和管道(pipeline)的体系结构,框架中的所有的功能模块都被实现成可以插拔的组件(component), 并且能够很方便地安装到任意一个管道上。由于所有插件都通过管道机制进行统一的数据交换,因此很容易利用已有的各种插件“组装”出一个功能完善的流媒体应用程序。

    由于deepstream是基于gstreamer的,所以要想在deepstream上做拓展,需要对gstreamer有一定的认识。

    2. GStreamer架构

    GStreamer的核心功能是为插件,数据流和媒体类型处理/协商提供框架。 下图是对基于Gstreamer框架的应用的简单分层:
    在这里插入图片描述

    2.1 Media Applications

    最上面一层为应用,比如gstreamer自带的一些工具(gst-launch,gst-inspect等),以及基于gstreamer封装的库(gst-player,gst-rtsp-server,gst-editing-services等)根据不同场景实现的应用。

    2.2 Core Framework

    中间一层为Core Framework,主要提供:

    • 上层应用所需接口
    • Plugin的框架
    • Pipline的框架
    • 数据在各个Element间的传输及处理机制
    • 多个媒体流(Streaming)间的同步(比如音视频同步)
    • 其他各种所需的工具库

    2.3 Plugins

    最下层为各种插件,实现具体的数据处理及音视频输出,应用不需要关注插件的细节,会由Core Framework层负责插件的加载及管理。主要分类为:

    • Protocols:负责各种协议的处理,file,http,rtsp等。
    • Sources:负责数据源的处理,alsa,v4l2,tcp/udp等。
    • Formats:负责媒体容器的处理,avi,mp4,ogg等。
    • Codecs:负责媒体的编解码,mp3,vorbis等。
    • Filters:负责媒体流的处理,converters,mixers,effects等。
    • Sinks:负责媒体流输出到指定设备或目的地,alsa,xvideo,tcp/udp等。

    3. GStreamer组件

    3.1 Element

    对于需要应用 GStreamer 框架的程序员来讲,GstElement 是一个必须理解的概念,因为它是组成管道的基本构件,也是框架中所有可用组件的基础。因此,GStreamer 框架中的大部分函数都会涉及到对 GstElement 对象的操作。

    从 GStreamer 自身的观点来看,GstElement 可以描述为一个具有特定属性的黑盒子,它通过连接点(link point)与外界进行交互,向框架中的其余部分表征自己的特性或者功能。 一个Element实现了一个功能(如读取文件,解码,输出等),一个程序需要创建多个element,并按顺序将其串连起来,构成一个完整的pipeline。

    按照各自功能上的差异,GstElement 可以细分成如下几类:

    • Source Element 数据源元件 只有输出端,它仅能用来产生供管道消费的数据,而不能对数据做任何处理。一个典型的数据源元件的例子是音频捕获单元,它负责从声卡读取原始的音频数据,然后作为数据源提供给其它模块使用。

    在这里插入图片描述

    • Filter Element 过滤器元件 既有输入端又有输出端,它从输入端获得相应的数据,并在经过特殊处理之后传递给输出端。 一个典型的过滤器元件的例子是音频编码单元,它首先从外界获得音频数据,然后根据特定的压缩算法对其进行编码,最后再将编码后的结果提供给其它模块使用。

    在这里插入图片描述或者 img

    • Sink Element 接收器元件 只有输入端,它仅具有消费数据的能力,是整条媒体管道的终端。一个典型的接收器元件的例子是音频回放单元,它负责将接收到的数据写到声卡上,通常这也是音频处理过程中的最后一个环节。

    在这里插入图片描述

    下图将有助于你更好地理解数据源元件、过滤器元件和接收器元件三者的区别,同时也不难看出它们是如何相互配合形成管道的:
    在这里插入图片描述

    创建一个 GstElement

    要想在应用程序中创建GstElement对象,办法是借助于工厂对象GstElementFactory。由于GStreamer框架提供了多种类型的GstElement对象,因此对应地提供了多种类型的GstElementFactory对象,它们是通过特定的工厂名称来进行区分的。例如,下面的代码通过gst_element_factory_find()函数获得了一个名为mad的工厂对象,它之后可以用来创建与之对应的MP3解码器元件:

    GstElementFactory *factory;
    factory = gst_element_factory_find ("mad");
    

    成功获得工厂对象之后,接下来就可以通过gst_element_factory_create()函数来创建特定的GstElement对象了,该函数在调用时有两个参数,分别是需要用到的工厂对象,以及即将创建的元件名称。元件名称可以用查询的办法获得,也可以通过传入空指针(NULL)来生成工厂对象的默认元件。下面的代码示范了如何利用已经获得的工厂对象,来创建名为decoder的MP3解码器元件:

    GstElement *element;
    element = gst_element_factory_create (factory, "decoder");
    

    此外,创建元件的最简单方法是使用 gst_element_factory_make()gst_element_factory_make实际上是两个功能组合的简写,此函数为新创建的元件采用工厂名称和元件名称。

    element = gst_element_factory_make ("mad", "decoder");
    if (!element) {
        g_print ("Failed to create element of type 'mad'\n");
        return -1;
    }
    

    当创建的GstElement不再使用的时候,还必须调用gst_element_unref()函数释放其占用的内存资源,gst_element_unref()会将元件的引用计数减少1。元件在创建时的引用计数为1。当refcount减少到0时,元件将被完全销毁。

    gst_element_unref (element);
    

    GStreamer使用了与GObject相同的机制来对属性(property)进行管理,包括查询(query)、设置(set)和读取(get)等。所有的GstElement对象都从其父对象GstObject那里继承了名称(name)这一最基本的属性,这是因为像gst_element_factory_make()gst_element_factory_create()这样的函数在创建元件对象时都会用到名称name属性,通过调用gst_object_set_name()和gst_object_get_name()函数可以设置和读取GstElement对象的名称属性。

    一个完整地创建GstElement的例子如下所示。

    #include <gst/gst.h>
    
    int main (int argc, char *argv[])
    {
      GstElementFactory *factory;
      GstElement * element;
    
      /* init GStreamer */
      gst_init (&argc, &argv);
    
      /* create element, method #2 */
      factory = gst_element_factory_find ("fakesrc");
      if (!factory) {
        g_print ("Failed to find factory of type 'fakesrc'\n");
        return -1;
      }
      element = gst_element_factory_create (factory, "source");
      if (!element) {
        g_print ("Failed to create element, even though its factory exists!\n");
        return -1;
      } 
      
      /* get name */
      g_object_get (G_OBJECT (element), "name", &name, NULL);
      g_print ("The name of the element is '%s'.\n", name);
      g_free (name);
      
      gst_object_unref (GST_OBJECT (element));
      gst_object_unref (GST_OBJECT (factory));
    
      return 0;
    }
    

    3.2 箱柜(bin)

    箱柜(bin)是GStreamer框架中的容器元件,它通常被用来容纳其它的元件对象,但由于其自身也是一个GstElement对象,因此实际上也能够被用来容纳其它的箱柜对象。利用bin可以将需要处理的多个元件组合成一个逻辑元件,你不再需要对箱柜中的元件逐个进行操作,而只需处理一个bin元件即可。

    例如:你有很多个元件用来解码视频并对其使用一些效果,要使事情变得简单一些, 你可以把这些元件放进一个bin(就像一个容器)中,以后你就可以使用bin来引用这些元件了。 这样其实bin变成了一个元件,如你的管道是 a ! b ! c ! d ,你可以把他们放进 mybin,这样当你使用mybin时其实是引用了 a ! b ! c ! d

    下图描述了箱柜在GStreamer框架中的典型结构:
    在这里插入图片描述

    在GStreamer应用程序中使用的箱柜主要有两种类型:

    • GstPipeline 管道是最常用到的容器,对于一个GStreamer应用程序来讲,其顶层箱柜必须是一条管道。
    • GstThread 线程的作用在于能够提供同步处理能力,如果GStreamer应用程序需要进行严格的音视频同步,一般都需要用到这种类型的箱柜。

    GStreamer框架提供了两种方法来创建箱柜:一种是借助工厂方法,另一种则是使用特定的函数。下面的代码示范了如何使用工厂方法创建线程对象,以及如何使用特定函数来创建管道对象:

    GstElement *thread, *pipeline;
    // 创建线程对象,同时为其指定唯一的名称。
    thread = gst_element_factory_make ("thread", NULL);
    // 根据给出的名称,创建一个特定的管道对象。
    pipeline = gst_pipeline_new ("pipeline_name");
    

    箱柜成功创建之后,就可以调用gst_bin_add()函数将已经存在的元件添加到其中来了:

    GstElement *element;
    GstElement *bin;
    bin = gst_bin_new ("bin_name");
    element = gst_element_factory_make ("mad", "decoder");
    gst_bin_add (GST_BIN (bin), element);
    

    而要从箱柜中找到特定的元件也很容易,可以借助gst_bin_get_by_name()函数实现:

    GstElement *element;
    element = gst_bin_get_by_name (GST_BIN (bin), "decoder");
    

    由于GStreamer框架中的一个箱柜能够添加到另一个箱柜之中,因此有可能会出现箱柜嵌套的情况,gst_bin_get_by_name()函数在查找元件时会对嵌套的箱柜作递归查找。元件有添加到箱柜之中以后,在需要的时候还可以从中移出,这是通过调用gst_bin_remove()函数来完成的:

    GstElement *element;
    gst_bin_remove (GST_BIN (bin), element);
    

    请注意,您添加元件的箱柜将获得该元件的所有权,如果销毁箱柜,则该元件将被取消引用,如果从箱柜容器中删除元件,则将自动取消引用该元件。

    下面是创建pipeline Bin容器的代码示例:

    #include <gst/gst.h>
    
    int main (int argc, char *argv[])
    {
      GstElement *bin, *pipeline, *source, *sink;
    
      /* init */
      gst_init (&argc, &argv);
    
      /* create */
      pipeline = gst_pipeline_new ("my_pipeline");
      bin = gst_bin_new ("my_bin");
      source = gst_element_factory_make ("fakesrc", "source");
      sink = gst_element_factory_make ("fakesink", "sink");
    
      /* First add the elements to the bin */
      gst_bin_add_many (GST_BIN (bin), source, sink, NULL);
      /* add the bin to the pipeline */
      gst_bin_add (GST_BIN (pipeline), bin);
    
      /* link the elements */
      gst_element_link (source, sink);
      [...]
    }
      
    

    元件的状态

    当GStreamer框架中的元件通过管道连接好之后,它们就开始了各自的处理流程,期间一般会经历多次状态切换,其中每个元件在特定时刻将处于如下四种状态之一:

    • GST_STATE_NULL:这是默认状态。在这种状态下没有分配资源,因此,转换到此状态将释放所有资源。当元件的引用计数达到0并被释放时,该元件必须处于此状态。
    • GST_STATE_READY:在就绪状态下,元件已分配了其所有全局资源,即可以保留在流中的资源。你可以考虑打开设备,分配缓冲区等。但是在这种状态下不会打开流,因此流位置自动为零。如果先前已打开流,则应在此状态下将其关闭,并应重置位置,属性等。
    • GST_STATE_PAUSED:在此状态下,元件已打开流,但未对其进行处理。此时元件可以修改流的位置,读取和处理数据,状态一旦更改为PLAYING,即可开始播放。总之,PAUSED与PLAYING相同,只是PAUSED没有运行时钟。
    • GST_STATE_PLAYING:在该PLAYING状态下,与该PAUSED状态下完全相同,只是时钟现在运行。

    所有的元件都从NULL状态开始,依次经历NULL、READY、PAUSED、PLAYING等状态间的转换。元件当前所处的状态可以通过调用gst_element_set_state()函数进行切换:

    GstElement *bin;
    /* 创建元件,并将其连接成箱柜bin */
    gst_element_set_state (bin, GST_STATE_PLAYING);
    

    默认情况下,管道及其包含的所有元件在创建之后将处于NULL状态,此时它们不会进行任何操作。当管道使用完毕之后,不要忘记重新将管道的状态切换回NULL状态,让其中包含的所有元件能够有机会释放它们正在占用的资源。

    管道真正的处理流程是从第一次将其切换到READY状态时开始的,此时管道及其包含的所有元件将做好相应的初始化工作,来为即将执行的数据处理过程做好准备。对于一个典型的元件来讲,处于READY状态时需要执行的操作包括打开媒体文件和音频设备等,或者试图与位于远端的媒体服务器建立起连接。

    处于READY状态的管道一旦切换到PLAYING状态,需要处理的多媒体数据就开始在整个管道中流动,并依次被管道中包含的各个元件进行处理,从而最终实现管道预先定义好的某种多媒体功能。GStreamer框架也允许将管道直接从NULL状态切换到PLAYING状态,而不必经过中间的READY状态。

    正处于播放状态的管道能够随时切换到PAUSED状态,暂时停止管道中所有数据的流动,并能够在需要的时候再次切换回PLAYING状态。如果需要插入或者更改管道中的某个元件,必须先将其切换到PAUSED或者NULL状态,元件在处于PAUSED状态时并不会释放其占用的资源。

    3.3 衬垫(Pad)

    衬垫(pad)是GStreamer框架引入的另外一个基本概念,它指的是元件(element)与外界的连接通道, 衬垫pad是元件的输入和输出,它们用于协商GStreamer中元件之间的链接和数据流。可以将pad视为元件上的“插头”或“端口”,与其他元件建立链接后,数据可以通过pad流入或流出其他元件。

    绝大多数元件有一个输入pad(sink)和一个输出pad(src)。 因此,我们上面的管道看起来是这样的:

    [src] ! [sink src]  ! [sink]
    

    最左边的元件只有一个src pad用来提供数据,接下来的元件接收信息并做一些处理后,传给下一个元件,因此他们有sink和src pad,最后一个元件sink pad只接收数据。

    下面看一下如何获取元件的衬垫pad。

    成功创建GstElement对象之后,可以通过gst_element_get_pad()获得该元件的指定衬垫。例如,下面的代码将返回element元件中名为src的衬垫:

    GstPad *srcpad;
    srcpad = gst_element_get_pad (element, "src");
    

    如果需要的话也可以通过gst_element_get_pad_list()函数,来查询指定元件中的所有衬垫。例如,下面的代码将输出element元件中所有衬垫的名称:

    GList *pads;
    pads = gst_element_get_pad_list (element);
    while (pads) {
      GstPad *pad = GST_PAD (pads->data);
      g_print ("pad name is: %s\n", gst_pad_get_name (pad));
      pads = g_list_next (pads);
    }
    

    与元件一样,衬垫的名称也能够动态设置或者读取,这是通过调用gst_pad_get_name ()gst_pad_set_name()函数来完成的。

    所有元件的衬垫都可以细分成输入衬垫(sink)和输出衬垫(src)两种,其中输入衬垫只能接收数据但不能产生数据,而输出衬垫则正好相反,只能产生数据但不能接收数据。GStreamer框架中的所有衬垫都必然依附于某个元件之上,调用gst_pad_get_parent()可以获得指定衬垫所属的元件,该函数的返回值是一个指向GstElement的指针。

    元件链接(Pad link)

    在引入了元件和衬垫的概念之后,GStreamer对多媒体数据的处理过程就变得非常清晰了:通过将不同元件的衬垫依次连接起来构成一条媒体处理管道,使数据在流经管道的过程能够被各个元件正常处理,最终实现特定的多媒体功能。

    GStreamer框架中的元件是通过各自的衬垫连接起来的,下面的代码示范了如何将两个元件通过衬垫连接起来,以及如何在需要的时候断开它们之间的连接:

    GstPad *srcpad, *sinkpad;
    srcpad = gst_element_get_pad (element1, "src");
    sinpad = gst_element_get_pad (element2, "sink");
    // 连接
    gst_pad_link (srcpad, sinkpad);
    // 断开
    gst_pad_unlink (srcpad, sinkpad);
    

    如果需要建立起连接的元件都只有一个输入衬垫和一个输出衬垫,那么更简单的做法是调用gst_element_link()函数直接在它们之间建立起连接,或者调用gst_element_unlink()函数断开它们之间的连接:

    // 连接
    gst_element_link (element1, element2);
    // 断开
    gst_element_unlink (element1, element2);
    

    Pad Capability

    衬垫从某种程度上可以看成是元件的代言人,因为它要负责向外界描述该元件所具有的能力。GStreamer框架提供了统一的机制来让衬垫描述元件所具有的能力(Capability)。Capabilities简称caps,它描绘了两个pad之间可以支持的数据类型,具体是通过数据结构_GstCaps来实现的:

    struct _GstCaps {
      gchar *name; /* the name of this caps */
      guint16 id; /* type id (major type) */
      guint refcount; /* caps are refcounted */
      GstProps *properties; /* properties for this capability */
      GstCaps *next; /* caps can be chained together */
    };
    

    当你使用 gst-inspect-1.0 命令查看一个元件的详细信息时,就会列出该元件的pad信息。

    以下是对mad元件的能力描述,不难看出该元件中实际包含sink和src两个衬垫,并且每个衬垫都带有特定的功能信息。名为sink的衬垫是mad元件的输入端,它能够接受MIME类型为audio/mp3的媒体数据,此外还具有layer、bitrate和framed三种属性。名为src的衬垫是mad元件的输出端,它负责产生MIME类型为audio/raw媒体数据,此外还具有format、depth、rate和channels等多种属性。

    Pads:
      SINK template: ’sink’
        Availability: Always
        Capabilities:
        ’mad_sink’:
          MIME type: ’audio/mp3’:
      SRC template: ’src’
        Availability: Always
        Capabilities:
          ’mad_src’:
            MIME type: ’audio/raw’:
            format: String: int
            endianness: Integer: 1234
            width: Integer: 16
            depth: Integer: 16
            channels: Integer range: 1 - 2
            law: Integer: 0
            signed: Boolean: TRUE
            rate: Integer range: 11025 - 48000
    

    准确地说,GStreamer框架中的每个衬垫都可能对应于多个能力描述,它们能够通过函数gst_pad_get_caps()来获得。例如,下面的代码将输出pad衬垫中所有能力描述的名称及其MIME类型:

    GstCaps *caps;
    caps = gst_pad_get_caps (pad);
    g_print ("pad name is: %s\n", gst_pad_get_name (pad));
    while (caps) {
      g_print (" Capability name is %s, MIME type is %s\n",
      gst_caps_get_name (cap),
      gst_caps_get_mime (cap));
      caps = caps->next;
    }
    

    Pad Capability for filtering

    如果我想让两个元件之间的数据流变为某种媒体类型video/x-raw,并限制帧率等状态,可以通过Caps filter实现,最简单的方法是使用函数gst_caps_new_simple()

    static gboolean
    link_elements_with_filter (GstElement *element1, GstElement *element2)
    {
      gboolean link_ok;
      GstCaps *caps;
    
      caps = gst_caps_new_simple ("video/x-raw",
              "format", G_TYPE_STRING, "I420",
              "width", G_TYPE_INT, 384,
              "height", G_TYPE_INT, 288,
              "framerate", GST_TYPE_FRACTION, 25, 1,
              NULL);
    
      link_ok = gst_element_link_filtered (element1, element2, caps);
      gst_caps_unref (caps);
    
      if (!link_ok) {
        g_warning ("Failed to link element1 and element2!");
      }
    
      return link_ok;
    }
    

    这将迫使两个元件之间的数据流变为某种视频格式(宽度,高度和帧速率等)。如果在所涉及元件的上下文中无法实现链接,则链接将失败。

    注意:使用gst_element_link_filtered()它时,它将自动为您创建一个capsfilter元件,并将其插入到您要连接的两个元件之间的容器或管道中,如果您要断开这些元件的连接,就必须从capsfilter断开这两个元件连接。

    精灵衬垫(ghost pad)

    如果仔细研究一下箱柜,会发现bin没有属于自己的输入衬垫和输出衬垫,因此显然是无法作为一个逻辑整体与其它元件交互的。为了解决这一问题,GStreamer引入了精灵衬垫(ghost pad)的概念, ghost pad是与bin中某些元件的pad相互连接, 它与UNIX文件系统中的符号链接类似,如下图所示:在bin上使用ghost pad,链接到Element1的sink,这样可以通过操作bin上的ghost pad操作Element1 sink pad。

    在这里插入图片描述

    具有精灵衬垫(ghost pad)的箱柜在行为上与元件是完全相同的,所有元件具有的属性它都具有,所有针对元件能够进行的操作也同样能够针对箱柜进行,因此在GStreamer应用程序中能够像使用元件一样使用这类箱柜。

    下面的代码示范了如何为箱柜添加一个精灵衬垫:

    GstElement *bin;
    GstElement *element;
    element = gst_element_factory_create ("mad", "decoder");
    bin = gst_bin_new ("bin_name");
    gst_bin_add (GST_BIN (bin), element);
    gst_element_add_ghost_pad (bin, gst_element_get_pad (element, "sink"), "sink");
    

    动态衬垫( Dynamic pads )

    创建元件时,某些元件可能没有包含全部pad。例如,创建ogg demuxer元件时会发生src pad不会立即创建的情况。这是因为此时demuxer并不知道输入流经过demux后需要输出什么类型的流?输出几路流?因此demuxer的src pad不会立即创建。动态衬垫会根据输入文件的类型,自动创建衬垫pad。

    当demuxer元件检测到ogg流时,它会读取ogg流并根据ogg流的信息为每个流动态创建pad。当ogg流结束时,demuxer元件将会删除动态src pad。

    我们使用gst-inspect oggdemux命令来查看oggdemux的信息,结果显示该元件只有一个衬垫:“一个sink垫”。其他衬垫都是“ dormant(休眠)”状态。
    你可以在oggdemux的src pad caps中看到此内容,其中有一个“Exists: Sometimes”的属性,表示该pad是并不总是存在的。

    #include <gst/gst.h>
    
    static void cb_new_pad (GstElement *element, GstPad *pad, gpointer data)
    {
      gchar *name;
      name = gst_pad_get_name (pad);
      g_print ("A new pad %s was created\n", name);
      g_free (name);
      /* here, you would setup a new pad link for the newly created pad */
    [..]
    }
    
    int main (int argc, char *argv[])
    {
      GstElement *pipeline, *source, *demux;
      GMainLoop *loop;
        
      /* init */
      gst_init (&argc, &argv);
        
      /* create elements */
      pipeline = gst_pipeline_new ("my_pipeline");
      source = gst_element_factory_make ("filesrc", "source");
      g_object_set (source, "location", argv[1], NULL);
      demux = gst_element_factory_make ("oggdemux", "demuxer");
    
      /* you would normally check that the elements were created properly */
      /* put together a pipeline */
      gst_bin_add_many (GST_BIN (pipeline), source, demux, NULL);
      gst_element_link_pads (source, "src", demux, "sink");
    
      /* listen for newly created pads */
      g_signal_connect (demux, "pad-added", G_CALLBACK (cb_new_pad), NULL);
    
      /* start the pipeline */
      gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
      loop = g_main_loop_new (NULL, FALSE);
      g_main_loop_run (loop);
    
    [..]
    
    }
    

    这里通过g_signal_connect监听"pad-added"信号,并使用G_CALLBACK (cb_new_pad)回调处理监听到的信号。最后不要忘记使用gst_element_set_state ()gst_element_sync_state_with_parent () 将新添加的元素的状态设置为管道的目标状态。

    3.4 总线(Bus)

    总线是一个简单的系统,负责将消息从管道传递到应用程序。默认情况下,每个管道都包含一个总线,因此应用程序不需要创建总线或任何东西。应用程序唯一要做的就是在总线上设置消息处理程序,这类似于对对象的信号处理程序。当mainloop运行时,将定期检查总线上是否有新消息,并且在有任何消息可用时将调用回调。

    如何使用Bus?

    有两种总线使用方式:

    • 运行GLib/Gtk+ main loop,并将某种watch连接到总线。这样,GLib main loop将检查总线上是否有新消息,并在有消息时触发信号,此时可以使用回调函数进行处理。通常使用gst_bus_add_watch ()gst_bus_add_signal_watch ()来监听Bus,并将消息处理程序附加到管道的总线上,每当管道向总线发出消息时,都将调用此处理程序。
    • 自己检查总线上的消息。可以使用gst_bus_peek ()和/或完成此操作 gst_bus_poll ()
    #include <gst/gst.h>
    
    static GMainLoop *loop;
    
    static gboolean
    my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
    {
      g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
    
      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:
          /* end-of-stream */
          g_main_loop_quit (loop);
          break;
        default:
          /* unhandled message */
          break;
      }
    
      /* we want to be notified again the next time there is a message
       * on the bus, so returning TRUE (FALSE means we want to stop watching
       * for messages on the bus and our callback should not be called again)
       */
      return TRUE;
    }
    
    gint main (gint argc, gchar * argv[])
    {
      GstElement *pipeline;
      GstBus *bus;
      guint bus_watch_id;
    
      /* init */
      gst_init (&argc, &argv);
    
      /* create pipeline, add handler */
      pipeline = gst_pipeline_new ("my_pipeline");
    
      /* adds a watch for new message on our pipeline's message bus to
       * the default GLib main context, which is the main context that our
       * GLib main loop is attached to below
       */
      bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
      bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
      gst_object_unref (bus);
    
      /* [...] */
    
      /* create a mainloop that runs/iterates the default GLib main context
       * (context NULL), in other words: makes the context check if anything
       * it watches for has happened. When a message has been posted on the
       * bus, the default main context will automatically call our
       * my_bus_callback() function to notify us of that message.
       * The main loop will be run until someone calls g_main_loop_quit()
       */
      loop = g_main_loop_new (NULL, FALSE);
      g_main_loop_run (loop);
    
      /* clean up */
      gst_element_set_state (pipeline, GST_STATE_NULL);
      gst_object_unref (pipeline);
      g_source_remove (bus_watch_id);
      g_main_loop_unref (loop);
    
      return 0;
    }
    

    请注意,如果你使用gst_bus_add_signal_watch(),则可以连接总线上的“消息”信号,而不必switch()处理所有可能的消息类型。只需以 message::的形式连接到你需要处理的信号。

    上面的代码段也可以写成:

    GstBus *bus;
    
    [..]
    
    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
    gst_bus_add_signal_watch (bus);
    g_signal_connect (bus, "message::error", G_CALLBACK (cb_message_error), NULL);
    g_signal_connect (bus, "message::eos", G_CALLBACK (cb_message_eos), NULL);
    
    [..]
    

    Bus Message类型

    GStreamer具有一些可以通过总线传递的预定义消息类型。 所有消息都有消息源,类型和时间戳。消息源可以被用于查看哪些元件发出的消息。

    以下是所有消息的列表,并简要说明了它们的作用以及如何解析消息内容。

    • 错误,警告和信息通知:如果应向用户显示有关管道状态的消息,则元素使用这些通知。错误消息是致命的,并终止数据传递。警告不是致命的,但仍然暗示着问题。信息消息用于非问题通知。可以使用gst_message_parse_error()gst_message_parse_warning ()gst_message_parse_info ()来解析这些消息。使用后应释放错误和调试这些消息对象。
    • 流结束通知:流结束时发出。管道的状态不会改变,但是进一步的媒体处理将停止。
    • 标签:在流中找到元数据时发出。应用程序应在内部缓存元数据。gst_message_parse_tag()被用于解析tag列表,gst_tag_list_unref ()当不再需要时应进行释放。
    • 状态更改:状态更改成功后发出。 gst_message_parse_state_changed ()可用于解析旧状态和新状态的转变。
    • 缓冲:在网络流缓存期间发出。通过gst_message_get_structure()函数从返回的结构中提取“ buffer-percent”属性,可以从消息中手动提取媒体进度(以百分比为单位)。
    • 特定于应用程序的消息:可以通过获取消息结构并读取其字段来提取有关这些消息的任何信息。应用程序消息主要用于应用程序内部使用,以防应用程序需要将信息从某个线程封送到主线程中。

    3.5 最佳实战

    #include <gst/gst.h>
    
    /* Structure to contain all our information, so we can pass it to callbacks */
    typedef struct _CustomData {
      GstElement *pipeline;
      GstElement *source;
      GstElement *convert;
      GstElement *sink;
    } CustomData;
    
    /* Handler for the pad-added signal */
    static void pad_added_handler (GstElement *src, GstPad *pad, CustomData *data);
    
    int main(int argc, char *argv[]) {
      CustomData data;
      GstBus *bus;
      GstMessage *msg;
      GstStateChangeReturn ret;
      gboolean terminate = FALSE;
    
      /* Initialize GStreamer */
      gst_init (&argc, &argv);
    
      /* Create the elements */
      data.source = gst_element_factory_make ("uridecodebin", "source");
      data.convert = gst_element_factory_make ("audioconvert", "convert");
      data.sink = gst_element_factory_make ("autoaudiosink", "sink");
    
      /* Create the empty pipeline */
      data.pipeline = gst_pipeline_new ("test-pipeline");
    
      if (!data.pipeline || !data.source || !data.convert || !data.sink) {
        g_printerr ("Not all elements could be created.\n");
        return -1;
      }
    
      /* Build the pipeline. Note that we are NOT linking the source at this
       * point. We will do it later. */
      gst_bin_add_many (GST_BIN (data.pipeline), data.source, data.convert , data.sink, NULL);
      if (!gst_element_link (data.convert, data.sink)) {
        g_printerr ("Elements could not be linked.\n");
        gst_object_unref (data.pipeline);
        return -1;
      }
    
      /* Set the URI to play */
      g_object_set (data.source, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
    
      /* Connect to the pad-added signal */
      g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);
    
      /* Start playing */
      ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
      if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (data.pipeline);
        return -1;
      }
    
      /* Listen to the bus */
      bus = gst_element_get_bus (data.pipeline);
      do {
        msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
            GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
    
        /* Parse message */
        if (msg != NULL) {
          GError *err;
          gchar *debug_info;
    
          switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
              gst_message_parse_error (msg, &err, &debug_info);
              g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
              g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
              g_clear_error (&err);
              g_free (debug_info);
              terminate = TRUE;
              break;
            case GST_MESSAGE_EOS:
              g_print ("End-Of-Stream reached.\n");
              terminate = TRUE;
              break;
            case GST_MESSAGE_STATE_CHANGED:
              /* We are only interested in state-changed messages from the pipeline */
              if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data.pipeline)) {
                GstState old_state, new_state, pending_state;
                gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
                g_print ("Pipeline state changed from %s to %s:\n",
                    gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
              }
              break;
            default:
              /* We should not reach here */
              g_printerr ("Unexpected message received.\n");
              break;
          }
          gst_message_unref (msg);
        }
      } while (!terminate);
    
      /* Free resources */
      gst_object_unref (bus);
      gst_element_set_state (data.pipeline, GST_STATE_NULL);
      gst_object_unref (data.pipeline);
      return 0;
    }
    
    /* This function will be called by the pad-added signal */
    static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
      GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
      GstPadLinkReturn ret;
      GstCaps *new_pad_caps = NULL;
      GstStructure *new_pad_struct = NULL;
      const gchar *new_pad_type = NULL;
    
      g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));
    
      /* If our converter is already linked, we have nothing to do here */
      if (gst_pad_is_linked (sink_pad)) {
        g_print ("We are already linked. Ignoring.\n");
        goto exit;
      }
    
      /* Check the new pad's type */
      new_pad_caps = gst_pad_get_current_caps (new_pad);
      new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
      new_pad_type = gst_structure_get_name (new_pad_struct);
      if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
        g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
        goto exit;
      }
    
      /* Attempt the link */
      ret = gst_pad_link (new_pad, sink_pad);
      if (GST_PAD_LINK_FAILED (ret)) {
        g_print ("Type is '%s' but link failed.\n", new_pad_type);
      } else {
        g_print ("Link succeeded (type '%s').\n", new_pad_type);
      }
    
    exit:
      /* Unreference the new pad's caps, if we got them */
      if (new_pad_caps != NULL)
        gst_caps_unref (new_pad_caps);
    
      /* Unreference the sink pad */
      gst_object_unref (sink_pad);
    }
    

    编译方法:

    gcc basic-tutorial-3.c -o basic-tutorial-3 `pkg-config --cflags --libs gstreamer-1.0`
    

    代码注释:

    /* Structure to contain all our information, so we can pass it to callbacks */
    typedef struct _CustomData {
      GstElement *pipeline;
      GstElement *source;
      GstElement *convert;
      GstElement *sink;
    } CustomData;
    

    由于本教程(以及大多数实际应用程序)都涉及回调,因此我们将所有数据放在一个结构体中,以便于传递处理。

    /* Handler for the pad-added signal */
    static void pad_added_handler (GstElement *src, GstPad *pad, CustomData *data);
    

    这是信号处理函数,将在信号触发后调用,此处为函数预定义。

    /* Create the elements */
    data.source = gst_element_factory_make ("uridecodebin", "source");
    data.convert = gst_element_factory_make ("audioconvert", "convert");
    data.sink = gst_element_factory_make ("autoaudiosink", "sink");
    

    创建元素。uridecodebin将在内部实例化所有必要的元素(source,demuxer和decode),以将URI转换为原始音频或视频流。由于它包含demuxer,因此其src pad最初不可用,我们需要动态创建src pad。

    audioconvert 用于转换不同的音频格式。

    autoaudiosink用于将音频流呈现到声卡。

    if (!gst_element_link (data.convert, data.sink)) {
      g_printerr ("Elements could not be linked.\n");
      gst_object_unref (data.pipeline);
      return -1;
    }
    

    在这里,我们将audioconvert元素链接到autoaudiosink元素,但是我们不将它们与uridecodebin链接,因为此时uridecodebin 不包含src pad。

    /* Set the URI to play */
    g_object_set (data.source, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
    

    我们将文件的URI设置为通过属性播放。

    信号处理

    /* Connect to the pad-added signal */
    g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);
    

    GSignals是GStreamer的一个关键点,它们使你可以在发生某些事情的时候(通过回调)得到通知并进行处理。

    在这一行中,通过g_signal_connect监听"pad-added"信号,并提供要使用的回调函数pad_added_handler和数据指针。GStreamer对此数据指针不执行任何操作,它只是将其转发给回调,因此我们可以与其共享信息。在这种情况下,我们传递一个指向CustomData的专门构建的指针。

    GstElement生成的信号可以在其文档中找到,也可以使用gst-inspect-1.0找到。

    /* Start playing */
      ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
      if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (data.pipeline);
        return -1;
      }
    /* Listen to the bus */
      bus = gst_element_get_bus (data.pipeline);
    

    将管道设置为PLAYING状态,并开始侦听总线中是否有消息(例如ERROREOS)。

    回调

    uridecodebin元素最终有足够的流信息时,它将创建src pad,并触发“pad-added”信号。此时将执行回调函数。

    static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
    

    src是触发信号的GstElement,在此示例中,它只能是uridecodebin。信号处理程序的第一个参数始终是触发它的对象。

    new_pad是刚刚添加到src元素中的GstPad,通常这是我们要链接的pad。

    data是我们在连接信号时提供的指针。在此示例中,我们使用它传递CustomData指针。

    GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
    

    CustomData中,我们提取出convert元素,然后使用gst_element_get_static_pad ()检索其sink pad。我们需要把uridecodebin新生成的new_pad(src pad)和audioconvert的sink_pad链接到一起。

    /* If our converter is already linked, we have nothing to do here */
    if (gst_pad_is_linked (sink_pad)) {
      g_print ("We are already linked. Ignoring.\n");
      goto exit;
    }
    

    uridecodebin会尝试创建多个pad,并且对于每个pad,都会调用此回调。一旦我们已经链接成功,上面的代码将阻止继续链接。

    /* Check the new pad's type */
    new_pad_caps = gst_pad_get_current_caps (new_pad, NULL);
    new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
    new_pad_type = gst_structure_get_name (new_pad_struct);
    if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
      g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
      goto exit;
    }
    

    现在,我们将检查新创建的src pad将要输出的数据类型,我们仅对产生音频的pad感兴趣,因此使用g_str_has_prefix (new_pad_type, “audio/x-raw”)进行过滤。

    gst_pad_get_current_caps()检索pad 的当前capabilities(即其当前输出的数据类型)。gst_caps_get_structure()检索new_pad_caps的GstStructure结构体数据 。gst_structure_get_name()得到new pad的类型。

    如果名称不是audio/x-raw,则该名称不是经过解码的音频pad,因此我们忽略它。否则,尝试链接pad:

    /* Attempt the link */
    ret = gst_pad_link (new_pad, sink_pad);
    if (GST_PAD_LINK_FAILED (ret)) {
      g_print ("Type is '%s' but link failed.\n", new_pad_type);
    } else {
      g_print ("Link succeeded (type '%s').\n", new_pad_type);
    }
    

    gst_pad_link()尝试链接两个pad。与gst_element_link()`一样,必须从src链接到sink,并且两个pad都必须位于同一bin(或管道)中。

    展开全文
  • GStreamer基础教程01——Hello World

    万次阅读 多人点赞 2014-02-19 09:51:01
    目标  对于一个软件库来说,没有比在屏幕上打印出Hello World更近直观的第一印象了。因为我们是在和一个多媒体的framework打交道,所以我们准备播放一段...不多说了,准备你的第一个GStreamer应用吧…... Hello World

    目标

          对于一个软件库来说,没有比在屏幕上打印出Hello World更近直观的第一印象了。因为我们是在和一个多媒体的framework打交道,所以我们准备播放一段视频来代替Hello World。不要被下面的代码吓唬住了——真正起作用的也就四行而已。剩下的都是资源管理的代码,C语言嘛,就是有这个麻烦。不多说了,准备你的第一个GStreamer应用吧…


    Hello World

    把下面的代码copy到一个文本文件,并改名为basic-tutorial-1.c

    #include <gst/gst.h>
      
    int main(int argc, char *argv[]) {
      GstElement *pipeline;
      GstBus *bus;
      GstMessage *msg;
      
      /* Initialize GStreamer */
      gst_init (&argc, &argv);
      
      /* Build the pipeline */
      pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);
      
      /* Start playing */
      gst_element_set_state (pipeline, GST_STATE_PLAYING);
      
      /* Wait until error or EOS */
      bus = gst_element_get_bus (pipeline);
      msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
      
      /* Free resources */
      if (msg != NULL)
        gst_message_unref (msg);
      gst_object_unref (bus);
      gst_element_set_state (pipeline, GST_STATE_NULL);
      gst_object_unref (pipeline);
      return 0;
    }

          编译方面根据你的平台也有介绍,linux平台http://docs.gstreamer.com/display/GstSDK/Installing+on+Linux,Mac平台http://docs.gstreamer.com/display/GstSDK/Installing+on+Mac+OS+X,windows下是http://docs.gstreamer.com/display/GstSDK/Installing+on+Windows。如果编译出错的话请检查一下报错地方的语句,如果编译通过,那么可以运行了,会弹出一个窗口,播放网络上的一个视频。恭喜你,第一步成功了!


    代码分析

          我们看一下代码,分析一下工作流程。

        /* Initialize GStreamer */
        gst_init (&argc, &argv);

          这是所有GStreamer应用的第一句,在gst_init里面做了

          +初始化所有内部数据结构

          +检查所有可用的插件

          +运行所有的命令行选项

          如果你把argc和argv传入gst_init,在处理命令行上是由好处的(在GStreamer工具里面还会讲到这个方面)。

        /* Build the pipeline */
        pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);

          这一行是这个教程最重要的部分,里面有两个非常重要的点:gst_parse_launch和playbin2


    gst_parse_launch

          GStreamer是设计来处理多媒体流的框架。媒体流经过一系列的中间element,从source element流到sink element。这些相互作用的element构成了一整个的pipeline。

          使用GStreamer时你常常需要使用独立的elements来手动搭建一个pipeline,但是,在比较简单的情况下,我们也可以使用gst_parse_launch()。这个函数原本是描述一个pipeline的,但也可以很方便的用来建立一个pipeline。


    playbin2

          我们让gst_parse_launch()函数建立了一个怎么样的pipeline呢?这就是playbin2的用处了,我们建立了一个只包含playbin2的element的pipeline。

          playbin2是一个特殊的element,它既是一个source也是一个sink,同时也能处理整个pipeline的事务。在内部,他创建和链接了所有播放你的媒体所必须的elements,你完全不必担心。

          这个element相对于纯手工搭建的pipeline来说,控制粒度没有那么好,但也有足够的可定制了。

          在这个例子中,我们仅仅解析了playbin2得一个参数——我们希望播放的URI。试试其他的地址,比如http://或者file://开头的URI,playbin2都能良好的工作。

          如果你键入了错误的URI,或者URI不存在,或者你漏掉了某个插件,GStreamer提供了一些通知机制,但我们这个例子仅仅实现在出错时退出,所以就不展开了。

      /* Start playing */
      gst_element_set_state (pipeline, GST_STATE_PLAYING);
          这一行代码展示了另一个需要关注的点:状态。每一个GStreamer的element有一个状态,你可以理解成常见的DVD播放器上得播放/暂停按钮。播放器必须设置pipeline为PLAYING状态才能真正开始播放,这一行代码就是做了这件事。

      /* Wait until error or EOS */
      bus = gst_element_get_bus (pipeline);
      msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
          这几行是在等待发生一个错误或者流已经播放结束。gst_element_get_bus()会得到pipeline的总线,然后gst_bus_timed_pop_filtered()会阻塞直到你遇到一个错误或者流播放结束。这一点在下一讲还会继续介绍,这里先介绍这么多。

          就这样了,GStreamer处理了所有的事情,这个例子会在流播放结束或播放出错时停止,当然,任何时候你都可以用Ctrl+C来终止。


    清理

          在应用终止前,我们还有一些事情要做

      /* Free resources */
      if (msg != NULL)
        gst_message_unref (msg);
      gst_object_unref (bus);
      gst_element_set_state (pipeline, GST_STATE_NULL);
      gst_object_unref (pipeline);
          在你使用了一个函数后,一定要记得查阅文档来确定是否需要释放资源。在这个例子中,gst_bus_timed_pop_filtered()会返回一个message,这个需要调用gst_message_unref()来释放(下一讲会继续介绍)。

          gst_element_get_bus()会对总线增加一个引用,所以也需要调用get_object_unref()来释放。设置pipeline为NULL状态会让它释放掉所有的资源,最后,释放掉pipeline自身。


    展开全文
  • Gstreamer基础知识介绍

    万次阅读 2018-08-07 17:29:27
    由于deepstream是基于gstreamer的,所以要想在deepstream上做拓展,需要对gstreamer有一定的认识。以下主要介绍Gstreamer整体框架和Gstreamer基础概念。 一、Gstreamer整体框架 gstreamer是一个用于开发流式多媒体...

    由于deepstream是基于gstreamer的,所以要想在deepstream上做拓展,需要对gstreamer有一定的认识。以下主要介绍Gstreamer整体框架和Gstreamer基础概念。

    一、Gstreamer整体框架

    gstreamer是一个用于开发流式多媒体应用的开源框架。本身这个框架是为了更好的处理音视频而设计开发的,但gstreamer不限于处理音视频,只要是有明显的数据流特征的应用,gstreamer都能很好的应用。

    gstreamer的整体功能核心是pipeline框架以及用于扩展功能的插件:

    1、pipeline用于安排数据流图,明确数据流处理过程。通过gstreamer多媒体数据协商机制和同步机制,pipeline能够很好的地处理流式数据。

    2、插件用于扩展功能,开发者可以灵活利用已有插件,而且还可以自定义特定功能插件。

                   

    二、Gstreamer基础概念

    Gstreamer的基础概念有如下四个,了解这四个概念是我们将Gstreamer玩起来的前提

    1、Elements

    Elements是一个pipeline中的基本组成功能实体,通过将不同功能的elements连起来,就可以实现一个数据流处理功能。如图所示,通过将source filer sink三种element连起来,可以实现对pipeline中传输数据格式的过滤。

    element主要有三种:

    (1) source element,主要是作为一个pipeline中的source节点,是数据源。source element只有src pad,即只有输出口。

    (2) sink element ,作为pipline的end节点 。sink element 只有sink pad,即只有输入口。

    (3) common element,作为pipline中的中间数据处理单元,既有sink pad,也有src pad,通常有:Filters, convertors, demuxers, muxers and codecs等

    2、pad

    pad是element的输入和输出,作为协商链接和数据传输用,链接只能是两个pad之间的,每个Pad都可以限定支持的数据格式。当两个不同Pad之间的格式匹配,两个Pad就可以进行链接,并传输数据。

    3、bin和Pipeline

    bin是element的一个集合,bin中的element理应互联,从而使bin也能提供某种功能,由于bin是element的子类,所以所有的element的操作,bin几乎都继承了。

    pipeline是一个完整的流式数据处理流程,可以看成一个顶级的bin。pipeline如下图所示

               

    4、通信

    Gstreamer为应用提供了几种通信机制:

    (1)buffer 用于element之间数据传递,buffer中包含的是媒体数据,buffer的传输方向永远是从src pad 到sink pad的,即向下传输。

    (2) event element之间或者application到element的信息传输,包含的是控制数据。event既可以向下也可以向上传输。

    (3)message 由element向application传输,用于传输 errors,tags,state changes, buffering state, redirects等信息。

    (4)queries 通常由application向pipeline发送,用于请求一些持续时间和播放点等信息。queries的应答是同步的。element也可以使用queries来向相邻element请求相应的信息

    这边只做简单的介绍,详细的可以查阅Gstreamer 官方用户手册

    https://gstreamer.freedesktop.org/documentation/index.html

    展开全文
  • 什么是GstreamerGstreamer是一个支持Windows,Linux,Android, iOS的跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤串联起来,达到预期的效果。每个步骤通过元素...

    什么是Gstreamer?

    Gstreamer是一个支持Windows,Linux,Android, iOS的跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤串联起来,达到预期的效果。每个步骤通过元素(Element)基于GObject对象系统通过插件(plugins)的方式实现,方便了各项功能的扩展。

    下图是对基于Gstreamer框架的应用的简单分层:

     

    Media Applications

    最上面一层为应用,比如gstreamer自带的一些工具(gst-launch,gst-inspect等),以及基于gstreamer封装的库(gst-player,gst-rtsp-server,gst-editing-services等)根据不同场景实现的应用。

    Core Framework

    中间一层为Core Framework,主要提供:

    • 上层应用所需接口
    • Plugin的框架
    • Pipline的框架
    • 数据在各个Element间的传输及处理机制
    • 多个媒体流(Streaming)间的同步(比如音视频同步)
    • 其他各种所需的工具库

    Plugins

    最下层为各种插件,实现具体的数据处理及音视频输出,应用不需要关注插件的细节,会由Core Framework层负责插件的加载及管理。主要分类为:

    • Protocols:负责各种协议的处理,file,http,rtsp等。
    • Sources:负责数据源的处理,alsa,v4l2,tcp/udp等。
    • Formats:负责媒体容器的处理,avi,mp4,ogg等。
    • Codecs:负责媒体的编解码,mp3,vorbis等。
    • Filters:负责媒体流的处理,converters,mixers,effects等。
    • Sinks:负责媒体流输出到指定设备或目的地,alsa,xvideo,tcp/udp等。

    Gstreamer框架根据各个模块的成熟度以及所使用的开源协议,将core及plugins置于不同的源码包中:

    • gstreamer: 包含core framework及core elements。
    • gst-plugins-base: gstreamer应用所需的必要插件。
    • gst-plugins-good: 高质量的采用LGPL授权的插件。
    • gst-plugins-ugly: 高质量,但使用了GPL等其他授权方式的库的插件,比如使用GPL的x264,x265。
    • gst-plugins-bad: 质量有待提高的插件,成熟后可以移到good插件列表中。
    • gst-libav: 对libav封装,使其能在gstreamer框架中使用。

     

    Gstreamer基础概念

    在进一步学习Gstreamer前,我们需要掌握一些gstreamer的基础概念。

    Element

    Element是Gstreamer中最重要的对象类型之一。一个element实现一个功能(读取文件,解码,输出等),程序需要创建多个element,并按顺序将其串连起来,构成一个完整的pipeline。

    Pad

    Pad是一个element的输入/输出接口,分为src pad(生产数据)和sink pad(消费数据)两种。
    两个element必须通过pad才能连接起来,pad拥有当前element能处理数据类型的能力(capabilities),会在连接时通过比较src pad和sink pad中所支持的能力,来选择最恰当的数据类型用于传输,如果element不支持,程序会直接退出。在element通过pad连接成功后,数据会从上一个element的src pad传到下一个element的sink pad然后进行处理。
    当element支持多种数据处理能力时,我们可以通过Cap来指定数据类型.
    例如,下面的命令通过Cap指定了视频的宽高,videotestsrc会根据指定的宽高产生相应数据:

    gst-launch-1.0 videotestsrc ! "video/x-raw,width=1280,height=720" ! autovideosink

    Bin和Pipeline

    Bin是一个容器,用于管理多个element,改变bin的状态时,bin会自动去修改所包含的element的状态,也会转发所收到的消息。如果没有bin,我们需要依次操作我们所使用的element。通过bin降低了应用的复杂度。
    Pipeline继承自bin,为程序提供一个bus用于传输消息,并且对所有子element进行同步。当将pipeline的状态设置为PLAYING时,pipeline会在一个/多个新的线程中通过element处理数据。

    下面我们通过一个文件播放的例子来熟悉上述提及的概念:测试文件 sintel_trailer-480p.ogv

    gst-launch-1.0 filesrc location=sintel_trailer-480p.ogv ! oggdemux name=demux ! queue ! vorbisdec ! autoaudiosink demux. ! queue ! theoradec ! videoconvert ! autovideosink

    通过上面的命令播放文件时,会创建如下pipeline:

    可以看到这个pipeline由8个element构成,每个element都实现各自的功能:
    filesrc读取文件,oggdemux解析文件,分别提取audio,video数据,queue缓存数据,vorbisdec解码audio,autoaudiosink自动选择音频设备并输出,theoradec解码video,videoconvert转换video数据格式,autovideosink自动选择显示设备并输出。

    不同的element拥有不同数量及类型的pad,只有src pad的element被称为source element,只有sink pad的被称为sink element。

    element可以同时拥有多个相同的pad,例如oggdemux在解析文件后,会将audio,video通过不同的pad输出。

     

    Gstreamer数据消息交互

    在pipeline运行的过程中,各个element以及应用之间不可避免的需要进行数据消息的传输,gstreamer提供了bus系统以及多种数据类型(Buffers、Events、Messages,Queries)来达到此目的:

    Bus

    Bus是gstreamer内部用于将消息从内部不同的streaming线程,传递到bus线程,再由bus所在线程将消息发送到应用程序。应用程序只需要向bus注册消息处理函数,即可接收到pipline中各element所发出的消息,使用bus后,应用程序就不用关心消息是从哪一个线程发出的,避免了处理多个线程同时发出消息的复杂性。

    Buffers

    用于从sources到sinks的媒体数据传输。

    Events

    用于element之间或者应用到element之间的信息传递,比如播放时的seek操作是通过event实现的。

    Messages

    是由element发出的消息,通过bus,以异步的方式被应用程序处理。通常用于传递errors, tags, state changes, buffering state, redirects等消息。消息处理是线程安全的。由于大部分消息是通过异步方式处理,所以会在应用程序里存在一点延迟,如果要及时的相应消息,需要在streaming线程捕获处理。

    Queries

    用于应用程序向gstreamer查询总时间,当前时间,文件大小等信息。

     

    gstreamer tools

    Gstreamer自带了gst-inspect-1.0和gst-launch-1.0等其他命令行工具,我们可以使用这些工具完成常见的处理任务。
    gst-inspect-1.0
    查看gstreamer的plugin、element的信息。直接将plugin/element的类型作为参数,会列出其详细信息。如果不跟任何参数,会列出当前系统gstreamer所能查找到的所有插件。

    $ gst-inspect-1.0 playbin

    gst-launch-1.0
    用于创建及执行一个Pipline,因此通常使用gst-launch先验证相关功能,然后再编写相应应用。
    通过上面ogg视频播放的例子,我们已经看到,一个pipeline的多个element之间通过 “!" 分隔,同时可以设置element及Cap的属性。例如:
    播放音视频

    gst-launch-1.0 playbin file:///home/root/test.mp4

    转码

    gst-launch-1.0 filesrc location=/videos/sintel_trailer-480p.ogv ! decodebin name=decode ! \
    videoscale ! "video/x-raw,width=320,height=240" ! x264enc ! queue ! \
    mp4mux name=mux ! filesink location=320x240.mp4 decode. ! audioconvert ! \
    avenc_aac ! queue ! mux.

    Streaming

    #Server
    gst-launch-1.0 -v videotestsrc ! "video/x-raw,framerate=30/1" ! x264enc key-int-max=30 ! rtph264pay ! udpsink host=127.0.0.1 port=1234
    
    #Client
    gst-launch-1.0 udpsrc port=1234 ! "application/x-rtp, payload=96" ! rtph264depay ! decodebin ! autovideosink sync=false

     

    引用

    https://gstreamer.freedesktop.org/documentation/application-development/introduction/gstreamer.html
    https://gstreamer.freedesktop.org/documentation/application-development/introduction/basics.html
    https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html

     

    作者:John.Leng
    本文版权归作者所有,欢迎转载。商业转载请联系作者获得授权,非商业转载请在文章页面明显位置给出原文连接.

    转载于:https://www.cnblogs.com/xleng/p/10948838.html

    展开全文
  • GStreamer 应用开发手册 中文版。GStreamer 流媒体相关技术资料
  • Gstreamer的安装

    千次阅读 2018-11-12 15:58:09
    相关索引:... 官网: https://gstreamer.freedesktop.org/ 官网安装方法: https://gstreamer.freedesktop.org/documentation/frequently-asked-questions/getting.h...
  • GStreamer功能详解

    千次阅读 2018-08-15 15:49:26
    参考:https://blog.csdn.net/tx3344/article/details/7497434 参考:https://thebigdoc.readthedocs.io/en/latest/gstreamer/gst-concept.html 参考:... 什么是GStreamer...
  • gstreamer简介

    千次阅读 2018-09-06 18:51:08
    常用 gchar * caps_string = gst_caps_to_string (new_selected_caps); g_free (caps_string);...最后列一下Gstreamer中常见的时间宏,注意Gstreamer中的时间单位是:纳秒 #define G_USEC_PER_SEC 1000000 #defin...
  • GStreamer介绍和基本概念

    千次阅读 2016-11-18 11:05:45
    简单的介绍和一些基本的概念
  • GStreamer

    2018-06-20 18:40:55
    本文介绍如何使用GStreamer 编写一个简单的MP3播放器。1,需要使用mad解码插件,因此需要先安装gstreamer0.10-plugins-ugly2,编写mp3播放器下面来看看如何利用GStreamer框架提供的组件,来实现一个简单的MP3播放器。...
  • gstreamer用法

    2019-01-16 16:34:41
    随着ARM平台性能的日益强大和嵌入式设备的发展,对于多媒体处理如音视频播放,摄像头,流媒体处理等需求也日益增多,本文就通过几个基于嵌入式Linux下多媒体应用的示例来简单展示下使用Gstreamer框架进行多媒体处理的...
  • Jetson之GStreamer+OpenCV读取显示摄像头

    万次阅读 热门讨论 2018-05-14 11:04:41
    参考:http://blog.iotwrt.com/media/2017/08/23/opencv-gstreamer/ 硬解码就是利用硬件芯片来解码的,TX2有单独的解码模块,NVDEC。软解码是用软件程序来解码,比较占用CPU资源。截止当前,nvidia的硬件编码官方...
  • opencv gstreamer

    千次阅读 2019-12-03 19:36:03
    https://www.ardusub.com/developers/opencv.html... Top side computer To capture video stream with the python script and QGC at same time, it's necessary to modifygstreamer options, changing! udpsink h...
  • gstreamer

    2019-11-28 18:37:51
    pcl@pcl-desktop:~$ gst-inspect-1.0 | grep h264 pcl@pcl-desktop:~$ gst-launch-1.0 --version https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-16486181925.20.46ad2b88jVKbAo&id=563626613170...
  • 在之前的Gstreamer基础教程中,介绍了gstreamer基础相关的知识,目的是起到入门的作用。接下来在应用开发手册系列教程中,将更多的从技术角度,介绍更多的概念和功能,起到更好的学习效果。 1 什么是gstreamer ...
  • gstreamer介绍

    2016-06-20 08:40:18
    gstreamer介绍 Rainbow编码器是基于gstreamer实现的,webkit的video标签使用了gstreamergstreamer项目里还有一个rtsp的服务器,gstreamer的应用越来越多,而且它的确是一个不错的东西。 最近有几次需要向...
  • GStreamer基础教程10——GStreamer工具

    万次阅读 2014-03-19 12:00:06
    GStreamer提供了一系列方便使用的工具。
  • GStreamer SDK 1.0 Build Via Cerbero

    千次阅读 2016-07-20 16:36:54
    GStreamer SDK 1.0 Build Via Cerbero 粗略列出对Cerbero脚本的理解,希望能对他人有所帮助。具体代码解析暂未整理,如本篇确能对你有所帮助,且需要进一步了解,可留言。将会根据留言情况来选择是否花费时间整理。...
  • GStreamer(二)

    2018-05-25 20:00:21
    GStreamer应用(续) TCP远程播放 除了本地播放之外,GStreamer亦支持远程播放。以下仅以TCP远程播放为例。 TCP远程播放采用Client/Server模式。 step1 1.首先打开播放端软件。(Server端) gst-la...
  • Linux平台C语言利用GStreamer实现mp4格式视频播放代码,包括管道配置,连接,以及动态连接element的pad。难点:demux控件通过回调方式进行音视频的分流,连接解码器、sink输出。

空空如也

1 2 3 4 5 ... 20
收藏数 11,968
精华内容 4,787
关键字:

gstreamer