目录

1、GStreamer简介

2、GStreamer基本概念

2.1、元件(Element)

2.2、箱柜(Bin)

2.3、管道(Pipeline)

2.4、衬垫(Pad)

2.5、能力集(Caps)

2.6、幽灵pad(ghost pad)

2.7、Bus

2.8、缓冲区(Buffer)

2.9、插件(Plugin)

3、GStreamer基本架构

4、GStreamer通信机制

4.1、Message

4.2、Event

4.3、Signal

4.4、Probe

4.5、Quary

5、GStreamer元件状态

6、GStreamer中的几个关键概念

6.1、识别流的MIME类型

6.2、媒体流类型检测(typefind)

6.3、数据探测

6.4、插件加载流程

7、GStreamer开发示例-MP3文件播放器

8、最后


之前参与的万能视频播放器项目采用了多媒体GStreamer开源框架,在最上层业务层通过连接各个插件形成一个pipeline来完成相应的业务需求。但之前没接触过GStreamer框架,所以从项目的前期预研开始,就从GStreamer最基本的概念开始熟悉,逐步解决项目中遇到的多个问题。本文根据此次项目实践,对GStreamer多媒体框架做一个相对全面的总结。

1、GStreamer简介

GStreamer是GNOME桌面环境下用来创建流媒体应用的多媒体框架,其基本设计思想来自于俄勒冈(Oregon)研究生学院有关视频管道的创意,同时也借鉴了DirectShow的设计思想。

GStreamer是用c语言实现的,使用了面向对象的思维。GStreamer 框架是基于插件和管道的,所有的插件都能够被链接到任意的已经定义的数据流管道中,数据通过管道机制进行统一交换。GStreamer的很多优点来源于其框架的模块化,使得新的插件能够无缝合并。

GStreamer能够处理任意类型的数据流,其目标是要简化音/视频应用程序的开发,其最显著的用途是在构建音视频播放器、编辑音视频文件、音视频格式转换和流媒体服务上,GStreamer已经能够被用来处理像 MP3、Ogg、MPEG1、MPEG2、AVI、Quicktime 等多种格式的多媒体数据。

GStreamer核心库函数是一个处理插件、数据流和媒体操作的框架。另外,其还提供了一套API,用于程序员使用其它插件来编写他所需要的应用程序时使用。但是,由于追求模块化和高效率,使得GStreamer在整个框架上变的复杂,也同时因为复杂度的提高,使得开发一个新的应用程序显得不是那么的简单。

2、GStreamer基本概念

2.1、元件(Element)

元件是GStreamer的核心,是具有一定功能的基本单元,可将其描述为一个具有特定属性的黑盒子。其在代码里面的类型是GstElement,可以理解为Gstreamer里面的基类。Gstreamer默认安装了很多有用的元件,按照功能上的差异,element分为以下几类:

(1)source element 数据源元件,只有输出端,用来产生供管道消费的数据,例如,音频捕捉单元,它从声卡读取原始音频数据,供其它模块用;
(2)filter(/filter-like) element 中间元件,包括过滤器、转换器、复用器、解复用器、编解码器等,其既有输入端又有输出端,从输入端获得相应数据,经过处理之后传递给输出端,有的element可能有一个source pad多个sink pads(demux),有的可能有多个source pads一个sink pad(mux),有的有一个source pad一个sink pad,例如,音频编码单元,它从外界获得音频数据之后,根据压缩算法编码后,给其它模块使用;
(3)sink elements 接收器元件,只有输入端,仅有消费数据的能力,是整条媒体管道的终端,例如,音频回放单元,负责将接收到的数据写到声卡上;

2.2、箱柜(Bin)

由多个基本单元组成的一个高级的功能单元,是装载元件的容器,可以通过改变一个Bin的状态来改变其内部所有元件的状态,Bin可以发送总线消息(bus message)给其子集元件。

Bin和pipeline的区别就是pipeline肯定是bin,但bin不一定是pipeline,bin就像一个盒子,里面放了什么东西,功能具体是怎么实现的,用户可以不关心,bin是元件的集合,而pipeline更强调应用的可执行性。

2.3、管道(Pipeline)

最高等级的Bin,是一种允许对所包含的元件进行安排(scheduling)的普通容器。顶层(toplevel)箱柜必须为一个管道,因此每个GStreamer应用程序都至少需要一个管道。当应用程序启动后,管道会自动运行在后台线程中,下面是一个典型的pipeline示例:

2.4、衬垫(Pad)

不同Elements之间的链接点,数据流在元件之间流动就是依靠Pads。Pads有处理特殊数据的能力,也就是其支持特定媒体类型的能力,一个Pads能够限制数据流类型的通过,链接成功的条件是,两个Pads允许通过的数据类型一致时才能建立(数据类型协商)。

Pads按照数据导向,可分为source pads(element的输出),sink pads(element 的输入),按照时效性可分为,永久型(always)、随机型(sometimes)、请求型(on request),三种时效性的意义顾名思义: 永久型的衬垫一直会存在,随机型的衬垫只在某种特定的条件下才存在(会随机消失的衬垫也属于随机型),请求型的衬垫只在应用程序明确发出请求时才出现。

Pads通过GstCaps对象进行描述,一个GstCaps对象包括一个或者多个GstStructure对象,一个GstStructure描述一种媒体类型,其结构中只包含功能集中规定的固定值。

2.5、能力集(Caps)

Pad的属性描述,例如:

  SRC template: 'src'Availability: AlwaysCapabilities:audio/x-raw-floatrate: [ 8000, 50000 ]channels: [ 1, 2 ]endianness: 1234width: 32buffer-frames: 0SINK template: 'sink'Availability: AlwaysCapabilities:audio/x-vorbis

2.6、幽灵pad(ghost pad)

bin本身没有pad,所以就没有办法把两个bin链接起来。但可以用bin中的一个元件的pad构造一个代理pad,这样bin就有一个代理pad了。这个pad实际指向被代理的那个单元的pad,示例如下:

2.7、Bus

Bus采用自己的线程机制,负责pipeline线程和应用程序程序之间的通信。每个pipeline缺省创建一个Bus,应用程序在总线上设置一个类似于对象的信号处理的消息处理器,当主循环运行的时候,总线将会轮询这个消息处理器是否有新的消息,当消息被采集到后,总线将呼叫相应的回调函数来完成相关操作。

应用程序有两种方法使用Bus,第一种是使用 GLib/Gtk+ main loop及gst_bus_add_watch () or gst_bus_add_signal_watch()事件回调函数机制,第二种是程序通过gst_bus_peek () /gst_bus_poll ()主动检查Bus中的消息;

2.8、缓冲区(Buffer)

管道的数据流由一组缓冲区和事件组成,缓冲区包括实际的管道数据,事件包括控制信息,如寻找信息和流的终止信号。所有这些数据流在运行的时候自动的流过管道。
缓冲区包含了你创建的管道里的数据流,通常一个源元件会创建一个新的缓冲区,同时元件还将会把缓冲区的数据传递给下一个元件。一个缓冲区主要由以下一个组成:

(1)指向某块内存的指针;
(2)内存的大小;
(3)缓冲区的时间戳;
(4)一个引用计数,指出了缓冲区所使用的元件数。没有元件可引用的时候,这个引用将用于销毁缓冲区。

buffer的创建有2种方式,一种是由当前的element自己创建,然后把这个buffer传递给下一个element;另外一种方式就是dwonstream-allocated buffers,就是由下一个element来创建要求大小的buffer,并提供buffer操作函数,当前element通过调用buffer操作函数将数据写入这个buffer中完成buffer数据传递。其区别在于buffer的创建是在数据传输的源端element创建还是在数据接收端element来创建。

2.9、插件(Plugin)

元件必须封装在插件中才能被使用,一个插件是一块可以加载的代码,通常被称为共享对象文件(shared object file)或动态链接库(dynamically linked library),一个插件中可以包含一个或若干element。

3、GStreamer基本架构

GStreamer core、Plugins以及依赖的第三方开源库的架构关系,如下图所示,

Gstreamer的组成结构如下图所示:

4、GStreamer通信机制

Gstreamer的通信机制示意图及解释如下:

4.1、Message

pipeline用来主动向外报告自己的运行状态。这些Message被发送到一个消息队列,也就是pipeline的Bus,应用程序可以从Bus中获取Message,并作出自定义的反应。Message是GST提供的,属于异步操作;

4.2、Event

pipeline中插件之间进行通信的机制,分为下行事件,上行事件和双向事件。也可以由应用程序直接向某一个插件发送事件,但起作用的前提是:该插件定义了该事件的响应操作。 通过事件可以控制整个pipeline的运行状态。

下行事件是由source插件向sink插件方向传输,例如,

GST_EVENT_EOS (流的终止信号)
GST_EVENT_NEWSEGMENT

上行事件是由sink插件向source插件方向传输,用于改变管道中数据流的状态,例如:

GST_EVENT_QOS
GST_EVENT_SEEK(查找)

双向事件,例如:

GST_EVENT_FLUSH_START
GST_EVENT_FLUSH_STOP

4.3、Signal

应用程序控制某一插件的运行状态,signal可以看做Glib对象的一个属性,是由Gobject系统提供的,属于同步操作,与linux中的系统信号有差别。通过信号可以让某个插件做一些对插件本身变量的操作,比如增加或者删除一些维护信息等等。

4.4、Probe

应用程序可以通过探针Probe来探测某个插件的pad中流过的数据,比如:在audioconert 插件的src pad 加一个探针,每当有buf到达时,就调用callback_have_data(),这里这个函数只是打印一下buf的大小,统计一下buf流过的个数;

//main
GstPad *m_pad_concert_src = gst_element_get_static_pad(m_gst_convert, "src");
gst_pad_add_buffer_probe(m_pad_concert_src, G_CALLBACK(callback_have_data), NULL);
gst_object_unref(m_pad_concert_src);/*******Callback handler when probe date received***********/
static gboolean callback_have_data(GstPad *padsrc, GstBuffer *buffer, gpointer data)
{gint    iBufSize = 0;gchar*     pBuffer = NULL;iBufSize = GST_BUFFER_SIZE(buffer);pBuffer = (gchar*)GST_BUFFER_DATA(buffer);static gint numBuf = 0;g_print("\rBUF %d  Size=%d   ", numBuf++, iBufSize);return TRUE;
}

4.5、Quary

应用程序可以查询pipline当前的运行状态,比如:以下代码用来查询当前播放的位置,和总的播放时间。

GstFormat m_format = GST_FORMAT_TIME;
gint64 m_position , m_length;
if( gst_element_query_position(pipeline, &m_format,&m_position) &&gst_element_query_duration(pipeline, &m_format, &m_length))
{g_print("Current: %"GST_TIME_FORMAT"   Total: %" GST_TIME_FORMAT "\r", GST_TIME_ARGS(m_position),GST_TIME_ARGS(m_length));
}

5、GStreamer元件状态

一个元件在被创建后,它不会执行任何操作,通过改变元件的状态,才能使它做某些事情。元件有四种状态,每种状态都有其特定的意义,具体如下:

GST_STATE_NULL 默认状态:没有分配任何资源,没有载入插件,不能处理数据;
GST_STATE_READY 预备状态:分配或载入所有与流无关的资源(非硬件资源),所有数据流的位置信息应该自动置0,如果数据流先前被打开过,它应该被关闭,并且其位置信息、特性信息应该被重新置为初始状态;
GST_STATE_PAUSED 暂停状态:准备好全部资源,接受数据流,只是sink element暂停,收到数据不处理,只是block;
GST_STATE_PLAYING 播放状态:准备好全部资源,接受并处理数据流;其实这个状态除了当前运行时钟外,其它与PAUSED状态一样,可以通过gst_element_set_state()来改变一个元件的状态,当元件处于GST_STATE_PLAYING状态,管道会开始自动处理数据。

6、GStreamer中的几个关键概念

6.1、识别流的MIME类型

元件通过caps来描述其能处理的媒体格式,元件之间交互数据流通过caps协商,caps是一个mime类型或者一些特性集的组合。

一个加载进系统的元件必须提供其源衬垫和接收衬垫支持的mime类型。通过Gstreamer注册中心可以知道目前注册的不同的元件,以及他们所期望得到的与他们能够产生的媒体类型,下图显示了管道中每个Pads所处理的MIME类型。

6.2、媒体流类型检测(typefind)

通常当加载一个新的媒体流时,媒体的类型并不明了。这意味着选择一条管道来对媒体流进行解码之前,首先需要检测媒体流的类型。 GStreamer 使用了类型检测 (typefinding) 来达到此目的。类型检测是构建管道所必经的步骤。

首先它会一直读取数据流,在此期间,它会把数据提供给所有的实现了类型检测器 (typefinder) 的插件,当其中任何一个类型检测器识别出数据流,这个类型检测器元件将会发送一个信号,并开始像一个关卡 (passthrough)模块一样工作。如果数据流的类型没有被任何类型检测器识别出来,管道会发送一个错误信息,并终止所有正在处理该数据流的动作。一旦类型检测元件找到一个类型,应用程序将会使用该元件作为管道的一部分来解码媒体流。

6.3、数据探测

探测是衬垫监听器的形象比喻,从技术上,探针仅仅是一个可以依附于衬垫的回调信号。这些信号默认是没有被发射(fired)的(不然的话会降低性能),但是可以通过附加探针调用gst_pad_add_data_probe() 或类似的函数被激活,这些函数附加了信号处理器,并激活实际信号的发射。

同样地,你可以用 gst_pad_remove_data_probe () 或相关函数来删除信号处理器,也可以只是监听时间或缓冲区。 探针在管道的线程context运行,所以回调不应该阻塞,而且通常不能有异常的阻塞,否则会降低管道的性能,如果出现这样的缺陷,会导致死锁甚至崩溃。

6.4、插件加载流程

如下图所示,基于插件的程序,其工作原理本质上都是通过读取动态库实现的,只需要每个动态库中实现某一个特定的接口就可以了,比如XX_init等,这里就是plugin_init。里面会有个像注册表一样的数据结构会存储所有的插件的信息。

7、GStreamer开发示例-MP3文件播放器

利用GStreamer框架提供的组件,来实现一个简单的MP3播放器。数据源元件负责从磁盘上读取数据,过滤器元件负责对数据进行解码,而接受器元件则负责将解码后的数据写入声卡,示例代码和注释如下:

#include <gst/gst.h>
#include <glib.h>//定义消息处理函数,
static gboolean bus_call(GstBus *bus,GstMessage *msg,gpointer data)
{GMainLoop *loop = (GMainLoop *) data;//主循环的指针,接受EOS消息时退出switch (GST_MESSAGE_TYPE(msg)){case GST_MESSAGE_EOS:g_print("End of stream\n");g_main_loop_quit(loop);break;case GST_MESSAGE_ERROR:{gchar *debug;GError *error;gst_message_parse_error(msg,&error,&debug);g_free(debug);g_printerr("ERROR:%s\n",error->message);g_error_free(error);g_main_loop_quit(loop);break;}default:break;}return TRUE;
}int main(int argc,char *argv[])
{GMainLoop *loop;GstElement *pipeline,*source,*decoder,*sink;//定义组件GstBus *bus;gst_init(&argc,&argv); //初始化gstreamerloop = g_main_loop_new(NULL,FALSE);//创建主循环,在执行 g_main_loop_run后正式开始循环if(argc != 2){g_printerr("Usage:%s <mp3 filename>\n",argv[0]);return -1;}//创建管道和元件pipeline = gst_pipeline_new("audio-player"); //管道用来容纳元件source = gst_element_factory_make("filesrc","file-source");//数据源元件decoder = gst_element_factory_make("mad","mad-decoder");//过滤器元件sink = gst_element_factory_make("autoaudiosink","audio-output");//接收器元件if(!pipeline||!source||!decoder||!sink){g_printerr("One element could not be created.Exiting.\n");return -1;}//设置 source的location 参数,即文件地址.g_object_set(G_OBJECT(source),"location",argv[1],NULL);//得到管道的消息总线bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));//添加消息监视器gst_bus_add_watch(bus,bus_call,loop);gst_object_unref(bus);//把元件添加到管道中。管道是一个特殊的组件,可以更好的让数据流动gst_bin_add_many(GST_BIN(pipeline),source,decoder,sink,NULL);//通过衬垫依次连接元件gst_element_link_many(source,decoder,sink,NULL);//启动管道,开始播放gst_element_set_state(pipeline,GST_STATE_PLAYING);g_print("Running\n");//开始循环g_main_loop_run(loop);g_print("Returned,stopping playback\n");//终止管道,释放资源gst_element_set_state(pipeline,GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return 0;
}

编译运行

gcc -Wall $(pkg-config --cflags --libs gstreamer-0.10) -g test2.c -o test2
./test2 /home/phinecos/test.mp3

8、最后

本文总结了多媒体框架GStreamer一些基本概念及流程,希望能给使用GStreamer开源库的朋友提供一个借鉴或参考。

全面解析C语言多媒体开源框架GStreamer相关推荐

  1. Json解析开源框架--Gson 的基本使用

    Gson 是google解析Json的一个开源框架,同类的框架fastJson,JackJson等等 根据一个GsonDemo工程来说明一下: 首先使用豆瓣图书的API接口:https://api.d ...

  2. Android开源框架【集合】

    Android开源框架[集合] 图解一览 Square& JakeWharton 框架名称 功能描述 Picasso 一个强大的图片下载与缓存的库 OkHttp Square出品,一个Http ...

  3. 25类Android常用开源框架

    1.图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 F ...

  4. java 大厂面试指南:性能优化 + 微服务 + 并发编程 + 开源框架 + 分布式

    秋招面试,我相信有人欢喜有人愁,大厂的面试题千奇百怪,不知道他会问到哪方面的知识点,我也是秋招大部队里面的一员,给大家整理出了 18 个大厂经常会问到 200 多道面试问题,涉及的知识点有,性能优化, ...

  5. java xml开源操作类,xml解析和操作的开源工具项目涵盖java c++ php 等语言

    XML解析器-Xerces    XML操作库-dom4j    XML文档解析器-Digester    J2ME-的XML-解析器-kXML XML解析类库-MXP1    XML解析器-LibX ...

  6. 微服务开源框架TARS的RPC源码解析 之 初识TARS C++服务端

    作者:Cony 导语:微服务开源框架TARS的RPC调用包含客户端与服务端,<微服务开源框架TARS的RPC源码解析>系列文章将从初识客户端.客户端的同步及异步调用.初识服务端.服务端的工 ...

  7. GIS开源框架:ArcGIS文件地理数据库(GDB)解析与入库

    对于GIS专业毕业的同学,想必对于ArcGIS软件不会太陌生,对于地理数据库也有一定的了解和使用经验.但是,撇开软件操作层面不谈,作为一个WebGIS/GIS开发人员,我们如何通过GIS开源框架去完成 ...

  8. Android 学习笔记之Volley开源框架解析(一)

    PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍...   1.Http请求...   这里只是简单的说一下Http请求的过程.. ...

  9. 开源框架完美组合之Spring.NET + NHibernate + ASP.NET MVC + jQuery + easyUI 中英文双语言小型企业网站Demo项目分析

    开源框架完美组合之Spring.NET + NHibernate + ASP.NET MVC + jQuery + easyUI 中英文双语言小型企业网站Demo,这个是一个在网上流传比较多的Spri ...

最新文章

  1. CSS设置样式时,鼠标移动到div上,发生抖动问题解决办法
  2. ajax callback 在什么时候运行
  3. boost::detail模块fwd容器的测试程序
  4. linux系列(二十):find命令
  5. Golang——HTTP编程请求和响应实现
  6. tcpdump命令速查
  7. XX基金 机器学习平台使用情况访谈总结
  8. ios 类别(category)
  9. 阿里舆情︱舆情热词分析架构简述(Demo学习)
  10. CONTEXT_CREATION_ERROR: Renderer: Error creating Canvas3D graphics context/Not a GL2 implementation
  11. java快速查找素数_Java实现快速查找某个范围内的所有素数
  12. 计算机四级 网络工程师 考过指南
  13. 【Matlab】求解函数导数
  14. 互联网江湖野史 - 氪金玩家
  15. JavaScript 进阶技能,中高级前端必备
  16. Qt实战案例(18)——Qt位置相关函数汇总实例
  17. Jmeter常用插件下载
  18. 联盟链系列 - 中间CA颁发证书
  19. qq不能发说说显示服务器错误,qq为什么发送不成word服务器拒绝发送离线请求. - 卡饭网...
  20. 教程篇(7.0) 03. FortiGate安全 防火墙策略 ❀ Fortinet 网络安全专家 NSE 4

热门文章

  1. Asterisk的MusicOnHold设置(1.8)
  2. 匝间短路故障学习历程(四) -- 频谱分析
  3. haskell基本语法
  4. Attempting to deserialize object on a CUDA device but torch.cuda.is_available()的可能原因
  5. 科大讯飞 唤醒python_用科大讯飞API实现本地语音文件识别
  6. 计算机辅助设计师考试卷理论知识,《计算机辅助设计》考试试卷B(1)
  7. Apple M1与英特尔芯片:两个强大处理器的比较
  8. winhex界面使用经验分享
  9. 华为OD机试真题:欢乐的周末
  10. android 系统儿童玩具,AR卡派(儿童玩具)