温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C语言多媒体框架GStreamer如何使用

发布时间:2022-07-19 17:08:14 来源:亿速云 阅读:226 作者:iii 栏目:开发技术

这篇文章主要介绍了C语言多媒体框架GStreamer如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C语言多媒体框架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示例:

C语言多媒体框架GStreamer如何使用

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,示例如下:

C语言多媒体框架GStreamer如何使用

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以及依赖的第三方开源库的架构关系,如下图所示,

C语言多媒体框架GStreamer如何使用

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

C语言多媒体框架GStreamer如何使用

4、GStreamer通信机制

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

C语言多媒体框架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类型或者一些特性集的组合。

C语言多媒体框架GStreamer如何使用

一个加载进系统的元件必须提供其源衬垫和接收衬垫支持的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。里面会有个像注册表一样的数据结构会存储所有的插件的信息。

C语言多媒体框架GStreamer如何使用

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

关于“C语言多媒体框架GStreamer如何使用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“C语言多媒体框架GStreamer如何使用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI