gstreamer简介

当前GStreamer主要有两个大的版本分支:

1)0.10.x系列。这个版本系列的历史较久,相关资源比较丰富。但目前官方已经不再发展和支持该版本。该系列有中文版的用户手册。

2)1.x系列。2012年以来发布的版本系列,也是官方推荐的版本系列。只有英文的用户手册,但手册的内容与0.10.x相差不大,尽管API已经不再兼容旧版本。以下的描述以1.x系列为准。1.x系列被设计为可以和0.10.x系列在系统中共存,因此在同一台电脑上,同时安装0.10.x系列和1.x系列是完全没有冲突的。

GStreamer本质上只是一个多媒体应用框架,具体的多媒体播放功能由插件来完成。
这个网页就是gstreamer的插件列表。表中列出的插件,分属4个不同的插件集:
    gst-plugins-base。这类插件格式规范,维护的也很好。
    gst-plugins-good。这类插件有高质量的代码(但格式未必规范),而且许可证也符合要求(LGPL或与LGPL兼容的许可证)。
    gst-plugins-ugly。这类插件有高质量的代码,但许可证方面有问题。
    gst-plugins-bad。这类插件尚不成熟,需要更多文档、测试和应用。
插件安装方法,以gst-plugins-base为例。
sudo apt-get install gstreamer1.0-plugins-base
除了上面列出的插件之外,目前的做法,更倾向于使用ffmpeg作为后端编解码库,尤其是编解码更复杂的视频文件。因此在0.10.x时代,提供了gstreamer0.10-ffmpeg插件,而1.x时代,则有gstreamer1.0-libav提供对avcodec、avformat等ffmpeg库的支持。
核心插件
核心插件又称gst-plugins-core,目前已经集成到gstreamer代码中,没有独立的库。
和上面提到的四类插件不同。它不提供具体的多媒体编解码功能,而是配合框架,搭建完整的多媒体流水线。
这里仅对其中一部分插件的功能描述如下:
fakesink:一个数据只进不出的“黑洞”。例如,一个视频文件一般包括视频流和音频流。如果设备只能播放音频(例如音箱),那么视频流对于设备来说,就是没有意义的东西。这时可以用fakesink插件将之吃掉。否则,由于GStreamer会在视频流和音频流之间进行同步,如果视频流没有被消耗,音频流也无法向前进。
万能插件
GStreamer除了那些完成具体功能的插件以外,还有一些抽象的高级插件,如playbin插件。该插件使用了GStreamer的自动加载(Auto plugging)机制,可以自动根据媒体类型,选择不同的管道播放,相当于是个万能播放插件。对于GStreamer应用开发人员来说,是个相当好用的东西。
playbin插件负责媒体播放的全过程,还有其他一些只负责某个步骤的全能插件:
decodebin:解码插件。
autoaudiosink:音频播放插件
autovideosink:视频播放插件

GStreamer应用
相关工具软件
GStreamer提供了一个工具软件集——gstreamer-tools。其安装方法如下:
sudo apt-get install gstreamer-tools gstreamer1.0-tools
它包括以下工具:
gst-launch:创建GStreamer管道的原型验证工具。它是其中用的最广泛的工具——网上关于GStreamer的问题讨论,多数并不贴出代码,而是给出gst-launch形式的命令。
下面是一个播放mp3文件的示例:
gst-launch filesrc location=1.mp3 ! mad ! audioconvert ! autoaudiosink
需要注意的是上面的命令中,!两边都要留空格,不然命令会执行错误。
gst-inspect:用于查询GStreamer插件的相关信息,也非常常用。比如,如果某个媒体文件无法播放,首先使用gst-inspect查询一下相关插件是否正确安装。
其他的还有gst-typefind、gst-xmllaunch、gst-feedback。
注意1.x系列的工具名和0.10.x系列的略有不同,例如gst-launch在1.x系列中叫做gst-launch-1.0。

一些概念

 bus

 消息总线,例:为你的管道(pipeline)添加一个GstBus的处理函数:

  /* watch for messages on the pipeline's bus (note that this will only
  * work like this when a GLib main loop is running) */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

    GstElement
    对于需要应用 GStreamer 框架的程序员来讲,GstElement 是一个必须理解的概念,因为它是组成管道的基本构件,也是框架中所有可用组件的基础,这也难怪 GStreamer 框架中的大部分函数都会涉及到对 GstElement 对象的操作。从 GStreamer 自身的观点来看,GstElement 可以描述为一个具有特定属性的黑盒子,它通过连接点(link point)与外界进行交互,向框架中的其余部分表征自己的特性或者功能。

    不是对每个element都创建一个线程,thread的边界必须是queue element。

bin
    箱柜(bin)是GStreamer框架中的容器元件,包含多个element或者bin,它通常被用来容纳其它的元件对象,但由于其自身也是一个GstElement对象,因此实际上也能够被用来容纳其它的箱柜对象。利用箱柜可以将需要处理的多个元件组合成一个逻辑元件,由于不再需要对箱柜中的元件逐个进行操作,因此能够很容易地利用它来构造更加复杂的管道。在GStreamer框架中使用箱柜还有另外一个优点,那就是它会试着对数据流进行优化,这对于多媒体应用来讲是很具吸引力的。 GStreamer框架提供了两种方法来创建箱柜:一种是借助工厂方法,另一种则是使用特定的函数。

    pipline是顶级的bin。

    pad
    衬垫(pad)是GStreamer框架引入的另外一个基本概念,它指的是元件(element)与外界的连接通道,对于框架中的某个特定元件来说,其能够处理的媒体类型正是通过衬垫暴露给其它元件的。成功创建GstElement对象之后,可以通过gst_element_get_pad()获得该元件的指定衬垫。

  1     pad可以有2种被激活的模式:PUSH和PULL。PUSH是默认的模式,souce pad通过调用sink pad的gst_pad_push来传输数据;PULL模式下,sink pad通过调用source pad的gst_pad_pull_range来请求数据

 2       按这种模式工作的Pad有一个loop函数,loop函数被重复调用直到返回false。Loop函数无论什么时候需要数据时,可以阻塞。当Pad被取消激活时,loop函数应该取消阻塞。 

 3      当上游元素调用 _push()函数,下游Pad的Chain函数被调用。

4       一个element的实现, 需要注册一个或多个GstPadTemplate,然后才能够通过这些template创建pad。

4   push方法:该方法的函数原型是GstFlowReturngst_pad_push (GstPad * pad, GstBuffer * buffer)。在该函数的实现里,通过调用 GST_PAD_CHAINFUNC (peer) (peer, buffer)来把数据推送给下一个元件,同时下一个元件根据他来接受数据。downstream elements的sink pad上需要定义chain函数(gst_pad_set_chain_function ),upstream elements调用这个chain函数来完成将buffer从upstream(source pad)到downstream elements(sink pad)的传递 。这种scheduling方式中source elements递归调用downstream elements的chain函数,最后一直调用到目的elements的才能函数。

    ghost pad
它是从箱柜里面所有元件的衬垫中推举出来的,通常来讲会同时选出输入衬垫和输出衬垫。如果仔细研究一下箱柜,会发现它没有属于自己的输入衬垫和输出衬垫,因此显然是无法作为一个逻辑整体与其它元件交互的。为了解决这一问题,GStreamer引入了精灵衬垫(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");

    元件连接
在引入了元件和衬垫的概念之后,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);

元件状态
 当GStreamer框架中的元件通过管道连接好之后,它们就开始了各自的处理流程,期间一般会经历多次状态切换,其中每个元件在特定时刻将处于如下四种状态之一:
    NULL 这是所有元件的默认状态,表明它刚刚创建,还没有开始做任何事情。
    READY 表明元件已经做好准备,随时可以开始处理流程。
    PAUSED 表明元件因某种原因暂时停止处理数据。
    PLAYING 表明元件正在进行数据处理。
所有的元件都从NULL状态开始,依次经历NULL、READY、PAUSED、PLAYING等状态间的转换。元件当前所处的状态可以通过调用gst_element_set_state()函数进行切换:    
GstElement *bin;
/* 创建元件,并将其连接成箱柜bin */
gst_element_set_state (bin, GST_STATE_PLAYING);

例子:

  在理解了一些基本概念和处理流程之后,下面来看看如何利用GStreamer框架提供的组件,来实现一个简单的MP3播放器。在图1中描述的结构能够很容易地映射成MP3播放器,其中数据源元件负责从磁盘上读取数据,过滤器元件负责对数据进行解码,而接受器元件则负责将解码后的数据写入声卡。

与其它众多GNOME项目一样,GStreamer也是用C语言实现的。如果想要在程序中应用GStreamer提供的各种功能,首先必须在主函数中调用gst_init()来完成相应的初始化工作,以便将用户从命令行输入的参数传递给GStreamer函数库。一个典型的GStreamer应用程序的初始化如下所示:

#include <gst/gst.h>
int main (int argc, char *argv[])
{
  gst_init (&argc, &argv);
  /* ... */
}

接下去需要创建三个元件并连接成管道,由于所有GStreamer元件都具有相同的基类GstElement,因此能够采用如下方式进行定义:

  GstElement *pipeline, *filesrc, *decoder, *audiosink;

管道在GStreamer框架中是用来容纳和管理元件的,下面的代码将创建一条名为pipeline的新管道:

  /* 创建用来容纳元件的新管道 */
  pipeline = gst_pipeline_new ("pipeline");

数据源元件负责从磁盘文件中读取数据,它具有名为location的属性,用来指明文件在磁盘上的位置。使用标准的GObject属性机制可以为元件设置相应的属性:

/* 创建数据源元件 */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);

过滤器元件负责完成对MP3格式的数据进行解码,最简单的办法是安装mad这一插件,借助它来完成相应的解码工作:

/* 创建过滤器元件 */
decoder = gst_element_factory_make ("mad", "decoder");

接收器元件负责将解码后的数据利用声卡播放出来:

/* 创建接收器元件 */
audiosink = gst_element_factory_make ("audiosink", "play_audio");

已经创建好的三个元件需要全部添加到管道中,并按顺序连接起来:

/* 添加元件到管道中 */
gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
/* 通过衬垫连接元件 */
gst_element_link_many (filesrc, decoder, audiosink, NULL);

所有准备工作都做好之后,就可以通过将管道的状态切换到PLAYING状态,来启动整个管道的数据处理流程:

/* 启动管道 */
gst_element_set_state (pipeline, GST_STATE_PLAYING);

由于没有用到线程,因此必须通过不断调用gst_bin_iterate()函数的办法,来判断管道的处理过程会在何时结束:

while (gst_bin_iterate (GST_BIN (pipeline)));

只要管道内还会继续有新的事件产生,gst_bin_iterate()函数就会一直返回TRUE,只有当整个处理过程都结束的时候,该函数才会返回FALSE,此时就该终止管道并释放占用的资源了:

/* 终止管道 */
gst_element_set_state (pipeline, GST_STATE_NULL);
/* 释放资源 */
gst_object_unref (GST_OBJECT (pipeline));

用GStreamer实现的MP3播放器的源代码如下所示:

#include <gst/gst.h>
int main (int argc, char *argv[])
{
    GstElement *pipeline, *filesrc, *decoder, *audiosink;
    gst_init(&argc, &argv);
    if (argc != 2) {
        g_print ("usage: %s <mp3 filename>\n", argv[0]);
        exit (-1);
    }
    /* 创建一条新的管道 */
    pipeline = gst_pipeline_new ("pipeline");
    /* 生成用于读取硬盘数据的元件 */
    filesrc = gst_element_factory_make ("filesrc", "disk_source");
    g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
    /* 创建解码器元件 */
    decoder = gst_element_factory_make ("mad", "decoder");
    /* 创建音频回放元件 */
    audiosink = gst_element_factory_make ("osssink", "play_audio");
    /* 将生成的元件添加到管道中 */
    gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
    /* 连接各个元件 */
    gst_element_link_many (filesrc, decoder, audiosink, NULL);
    /* 开始播放 */
    gst_element_set_state (pipeline, GST_STATE_PLAYING);
    while (gst_bin_iterate (GST_BIN (pipeline)));
    /* 停止管道处理流程 */
    gst_element_set_state (pipeline, GST_STATE_NULL);
    /* 释放占用的资源 */
    gst_object_unref (GST_OBJECT (pipeline));
    exit (0);
    }

g_signal_connect

gulong g_signal_connect(gpointer instance,const gchar *detailed_signal,GCallback c_handler,gpointer data );
g_signal_connect(button, "pressed",G_CALLBACK(callback), NULL);
instance:信号发出者,可以认为我们操作的控件,如按下按钮,这个就为按钮指针
detailed_signal:信号标志,如"pressed"
c_handler:回调函数的名称,需要用G_CALLBACK()进行转换
data:给回调函数传的参数,gpointer 相当于C语言的 void *
返回值:注册函数的标志当按下button按钮时,就会自动调用回调函数callback(相当于处理中断任务),回调函数callback可以是任意函数,函数名字我们根据需要自行命名,如果不是库函数,我们还得定义这个回调函数,这里需要注意的是,回调函数的写法(返回值,参数),不是我们想怎么写就怎么写,帮助文档里已经规定好了回调函数应该如何写,如果不按规定来写,可能产生意想不到的错误。
GSignal是GStreamer的一个重要部分。它会让你在你感兴趣的事情发生时收到通知。信号是通过名字来区分的,每个GObject都有它自己的信号。使用g_signal_connect()方法把“pad-added”信号和我们的源(uridecodebin)联系了起来,并且注册了一个回调函数。GStreamer把&data这个指针的内容传给回调函数,这样CustomData这个数据结构中的数据也就传递了过去。

猜你喜欢

转载自blog.csdn.net/evsqiezi/article/details/82466267