iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言多媒体框架GStreamer使用教程深讲
  • 365
分享到

C语言多媒体框架GStreamer使用教程深讲

2024-04-02 19:04:59 365人浏览 薄情痞子
摘要

目录1、GStreamer简介2、GStreamer基本概念2.1、元件(Element)2.2、箱柜(Bin)2.3、管道(Pipeline)2.4、衬垫(Pad)2.5、能力集(

之前参与的万能视频播放器项目采用了多媒体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: Always
    Capabilities:
      audio/x-raw-float
                   rate: [ 8000, 50000 ]
               channels: [ 1, 2 ]
             endianness: 1234
                  width: 32
          buffer-frames: 0
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      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);
 

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); //初始化gstreamer
    loop = 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--libsgstreamer-0.10)-gtest2.c-otest2

./test2/home/phinecos/test.mp3

8、最后

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

到此这篇关于C语言多媒体框架GStreamer使用教程深讲的文章就介绍到这了,更多相关C语言GStreamer内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言多媒体框架GStreamer使用教程深讲

本文链接: https://www.lsjlt.com/news/164647.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言多媒体框架GStreamer使用教程深讲
    目录1、GStreamer简介2、GStreamer基本概念2.1、元件(Element)2.2、箱柜(Bin)2.3、管道(Pipeline)2.4、衬垫(Pad)2.5、能力集(...
    99+
    2024-04-02
  • C语言多媒体框架GStreamer入门和概述
    目录一.概述二.Gstreamer 的特点:三.Gstreamer源码下载地址:四.Gstreamer二进制发布库:五.Gstreamer命令行显示一.概述 在音视频领域接触最多实现...
    99+
    2024-04-02
  • C语言深入讲解指针与结构体的使用
    目录1 啥是指针1.1指针与指针变量1.2总结2 指针和指针类型2.1指针+-整数3 野指针3.1 野指针的成因1指针未初始化2指针越界访问3指针指向的空间释放3.2 如何避免野指针...
    99+
    2024-04-02
  • C语言深入讲解函数的使用
    目录关于函数1. 函数的定义形式2. 函数的声明3. 返回语句4. 函数参数4.1 形式参数(传值调用)4.2 实际参数(传址调用)4.3 无参数5. 函数的调用5.1 嵌套调用5....
    99+
    2024-04-02
  • C语言深入讲解链表的使用
    目录一、链表的概念二、链表的分类1. 单向或者双向链表2. 带头或者不带头(是否有自带哨兵位头结点)3. 循环或者非循环链表4. 无头单向非循环链表和带头双向循环链表3、链表的实现(...
    99+
    2024-04-02
  • C语言联合体Union特点及运用全面讲解教程
    目录前言一、联合(共用体)定义二、联合的特点及运用三、联合的大小计算总结前言 上一期C语言快速入门我们学习了枚举类型及相关知识点 这期我们来跟着笔者学习一下联合体相关知识,相信聪明的...
    99+
    2024-04-02
  • C语言共用体union作用使用示例教程
    目录共用体 union开锅解构小结一手共用体 union 什么是共用体 union这个共用体,估计大家平时在代码也比较少见,我去看了;其实这个共用体 u...
    99+
    2024-04-02
  • C语言深入浅出讲解指针的使用
    目录一、利用指针倒序字符串二、题目实例三、总结一、利用指针倒序字符串 void _reversal(char* left, char* right) { while (lef...
    99+
    2024-04-02
  • C语言深入讲解函数参数的使用
    目录一、函数参数二、程序的顺序点三、小结-上四、调用约定五、可变参数六、可变参数的限制七、小结-下一、函数参数 函数参数在本质上与局部变量相同在栈上分配空间函数参数的初始值是函数调用...
    99+
    2024-04-02
  • Flutter路由框架Fluro使用教程详细讲解
    目录1.Navigator使用简介2.fluro1.配置2.使用方法3.路由拦截3.封装1.Navigator使用简介 使用Flutter 的Navigator 导航器可以实现页面的...
    99+
    2022-11-13
    Flutter路由框架Fluro Flutter Fluro
  • C语言中结构体和共用体实例教程
    目录一、实验目的二、实验内容三、实验记录3.1 候选人选票统计3.2 print函数3.3 链表总结一、实验目的 掌握结构体类型变量的定义和使用; 掌握结构体类型...
    99+
    2024-04-02
  • C语言深入讲解语句与选择结构的使用
    目录知识点1-语句1.1 语句1.表达式语句2.控制语句3.空语句4.复合语句知识点2-选择结构2.1 if语句1.if语句的单分支2.if语句的双分支3.if语句的多分支4.if语...
    99+
    2024-04-02
  • C语言超详细讲解结构体与联合体的使用
    目录结构体offsetof-宏位段枚举联合体(共用体)结构体 结构体内存对齐问题: 当我们在计算结构体的大小时,我们便需要清楚的知道结构体内存对齐是什么。 存在内存对齐的原因可细分为...
    99+
    2024-04-02
  • 使用emacs编写C语言教程
    如何使用emacs编写c语言程序,并编译运行 vi和emacs被分别被称为编辑器之神和神之编辑器。vi的入门精通都很难,emacs入门容易,精通难;vi使用起来不停地切换模式,而em...
    99+
    2024-04-02
  • C语言深入讲解宏的定义与使用方法
    目录一、C语言中的宏定义二、宏定义表达式三、宏表达式与函数的对比四、有趣的问题五、强大的内置宏六、小结一、C语言中的宏定义 #define是预处理器处理的单元实体之一#define ...
    99+
    2024-04-02
  • c语言怎么使用多线程
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-21
  • google c++程序测试框架googletest使用教程详解
    目录什么是googletest?googletest简介谁在使用 GoogleTest?相关开源项目googletest的下载与编译cmake gui编译在vs2019中使用...
    99+
    2024-04-02
  • VS2017使用教程(使用VS2017编写C语言程序)
    以下是使用VS2017编写C语言程序的简单教程:步骤1:安装VS2017首先,你需要从官方网站下载并安装Visual Studio ...
    99+
    2023-09-13
    C语言
  • 详解Dev C++使用教程(使用Dev C++编写C语言程序)
    前面我们给出了一段完整的C语言代码,就是在显示器上输出“C语言中文网”,如下所示: #include <stdio.h> int main() { puts("C...
    99+
    2024-04-02
  • C语言深入讲解动态内存分配函数的使用
    目录一、malloc二、free(用于释放动态开辟的空间)三、calloc四、realloc五、常见的动态内存分配错误六、柔性数组局部变量和函数的形参向栈区申请空间 全局变量和sta...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作