APP下载

基于Gstreamer框架的ffmpeg流媒体编解码设计

2019-01-30王锋陆凯

电子技术与软件工程 2019年1期
关键词:编解码插件调用

文/王锋 陆凯

把采集的yuv格式通过ffmpeg编解码库编码成h264格式,再通过网络传输到室内播放终端,在室内机终端设备再通过ffmpeg解码器转换为yuv420p格式,最终转换为RGB格式,并在Linux系统的ARM平台上利用QT图形化界面显示。最终实现了数字可视对讲系统功能实现的整个流程。

Gstreamer是一个基于管道Pipeline的多媒体应用框架,采用C语言编程,但是通过gObject,将各插件封装成面向对象编程的工具。元件 Element是Gstreamer最重要和基本的对象类,通过插件Plugin的形式提供,多个元件Elements可以组合为箱柜bin,并进一步聚合形成一个管道Pipeline完成一个多媒体应用处理。目前是嵌入式Linux最为常用的处理多媒体应用框架。我们主要是在ffmpeg多媒体编解码的过程中加入Gstreamer 的应用框架。

1 基于gstreamer的流媒体可视对讲系统开发过程

Gstreamer框架中使用gst-launch命令进行流媒体播放,我们在开发过程中,主要使用gst-launch 在终端编译和运行一条pipeline用于播放多媒体。gst-launch-0.10 或gst-launch-1.0一般ubuntu系统自带,相关插件包可通过wget下载opky安装。由于是基于嵌入式ARM芯片的流媒体开发,还需交叉编译相关gstreamer动态库移植到下位机平台,如glib库、gstreamer插 件 库libgstqt5videosink.so、qt5lib库libQt5GLib-2.0.so、libQt5GStreamer-1.0.so等。 开发过程中调用的gst代码有gstffmpeg-0.11.2、gst-libav-1.14.4等。

1.1 用GStreamer作v4l2摄像头采集和输出到YUV文件

由于需要做摄像头的视频采集,所以首先在内核中添加视频采集模块Video4Linux2,它是一种内核设备驱动,主要为Linux 下的应用程序编程提供视频设备接口函数,同时,由于我们是基于GStreamer 框架开发,故在v4l2摄像头采集中加入GStreamer 插件的方式进行开发。其中 Video4Linux2插件是一个用于捕捉和播放视频的API和驱动框架,支持一般的摄像头设备。

v4l2本身不仅仅是支持视频采集功能,它还支持其他的视频功能,元件v4l2src属于Video4Linux2插件,用于读取Video4Linux2设备的视频帧,这里即为摄像头。v4l2src是使用v4l2接口的视频源插件,只是用来做视频采集的,支持多种格式的视频采集,例如rgb格式和yuv格式。

在Linux系统中V4L2驱动的摄像头数据采集我们采用内存映射方式(mmap)进行图像采集。数据的采集从/dev/video0设备文件。

视频采集过程如下:

(1)创建一条名为pipe的新管道 pipe=gst_pipeline_new("pipe");管 道 在GStreamer框架中是用来容纳和管理元件的。

(2)调用gst_element_factory_make函数,创建v4l2src、jpegenc、f ilesink插件,分别作为输入数据源元件、过滤器元件、输出数据源元件,并调用g_object_set设置元件的属性,输入源v4l2src的device属性设置一下,指定采集设备的名称:并对输入源指定帧数量,最后创建f ilesink插件后设置文件的保存路径。如图1所示。

(3)判断管道与元件创建无问题,调用gst_bin_add_many()函数添加已创建好的三个元件到pipe管道中,并按顺序连接起来,可以更好的让数据流动。如图2所示。

(4)调用gst_pipeline_get_bus()获取管道的消息总线,并添加消息总线监视器,释放线资源。如图3所示。

(5) 在管道创建完成并添加消息监视器后,切换管道状态PLAYING状态,来启动整个管道的数据传输处理流程,处理完成停止管道并释放占用的资源。在创建管道之前先创建了一个 loop=g_main_loop_new(NULL,FALSE);循环体,g_main_loop_run()则是进入主循环在这里我们调用启动它。有事件时,它就处理事件,没事件时就睡眠状态。如图4所示。

1.2 用GStreamer作Ffmpeg视频编码和解码

V4l2摄像头视频采集完成后生成的yuv格式数据量很大,便于传输我们需要把YUV422的像素数据编码为H.264的压缩编码数据,传输完成后再进行数据的解码。在此采取Gstreamer管道的方式进行Ffmpeg编解码,所以需要安装ffmpeg库、x264库之外,还需安装Gstreamer ffmpeg插件等。

1.2.1 插件的初始化

图1

图2

图3

图4

图5

ffpmepg插件的初始化直接就是通过plugin_init()函数注册到Gstreamer中的,每个plugin都是在plugin_init()函数中通过gst_element_register()函数将plugin的相应信息注册到gstreamer中。首先调用av_register_all()函数注册编码器,该函数是所有使用编码器、复用器的基础,在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。

接着调用ffmepeg编解码注册函数gst_ffmpegenc_register (plugin); gst_ffmpegdec_register (plugin)等。通过gst_element_register()函数将plugin的相应信息注册到gstreamer中。通过该函数,可以创建一个名称为name、优先级为rank的type类型elementfactory,并将elementfactory添加到registry。在我们自己编写的插件中,将元件等级rank的值设置为比GST_RANK_PRIMARY大即大于256就可,这样就将会优先选择我们编写的plugin。

1.2.2 gst-ffmpeg视频编码

(1)pad的定义创建、连接、流动设置。

首先调用gst_ffmpegenc_init()函数进行gst-ffmpeg编码的初始化,主要完成对pad的定义创建、连接、流动设置。指定元件对外接口sink pad 和src pad,创建pad 的作用是使数据流通过这些接口流入流出元件,它相当于element的接口,是element间传输数据的通道。

指 定sinkpad接 口 后,调 用gst_ffmpegenc_getcaps()从sink pad 中获取Gstcaps信息,这个函数的作用就是产生一个新的caps,并设置这个caps。并调用avcodec_alloc_context()为编解码器上下文分配空间。并设置分辨率、像素格式、波特率等帧参数。 gst_ffmpeg_avcodec_open()初始化一个视音频编解码器的AVCodecContext。Gstcaps代表pad能处理的媒体类型,调用caps插件的作用是协商caps所支持的格式。

(2)Sinkpad的调度模式设置。

指定pad初始化视音频编解码完成。调用Chain链条函数对sinkpad调度模式进行设置,我们采用推送模式,推送模式是实现把src pad产生的数据“推送”给下游元件即sink pad。推送模式下,源元件发起数据传输,是管道中的驱动力量;下游元件在chain函数中接收buffer。这样,就完成了从上游元件到下游元件的buffer传递。

紧接着给初始化ffmpeg x264参数赋值给ffmpegenc指针,赋值参数有编码输出比特率、GOP关键帧的最大间隔帧数、帧大小、rtp负载尺寸、运动侦测的方式等。参数赋值完成调用g_object_set_property()函数把参数信息写入object结构体。在创建pad、调度模式设计级从参数的配置完成后,调用gst_element_add_pad(),添加srcpad、sinkpad到元件中。所有流程完成为element间的数据传输鉴定了基础。

(3)element的注册。

在上面pad添加完成后,下面进行element的注册。首先调用gst_ffmpeg_cfg_init()构建ffmpeg参数信息,声明AVCodec类型的结构体指针in_plugin,主要用于存储编码器的信息。通过判断in_plugin结构体成员如类型、ID、name从而判断是否是初始化的编码信息,如不符合,调用av_codec_next()函数传入相关编码信息。紧接着调用g_type_register_static()相关的类型GType衍生一个新的glib静态类型。g_type_add_interface_static()实现接口,并通过gst_element_register()注册插件支持的所有element类型。

上面完成gst-ffmpeg元件的注册,采用同样的方法对ffmpegmux整流器进行注册。在对ffmpegmux的注册过程中,调用gst_element_add_pad()添加mux的src pad,并配置videopads、audiopads、preload等参数。

(4)Pipe管道运行。

在所有视音频初始化信息完成后,pad指定配置完成,调用gst_element_factory_make ()完成element的创建,将多个不同功能的元件(element)装进一个箱柜(bin)中来管理元件,再通过gst_bin_iterate ()函数运行管道。

1.2.3 gst-ffmpeg视频解码

gst-ffmpeg视频解码主要是调用gst_ffmpegdemux_loop()函数来完成的。原始数据流的获取我们首先可以通过libavformat来实现对各种文件的分离,libavformat能够依次读取数据包,过滤掉所有那些视频流中不需要的部分。后gst-ffmpeg调用ffmpeg的API即 av_read_frame()来读取一个完整的帧数据 ffmpeg,av_read_frame()的好处是可以从一个简单的包里返回一个包含所有数据的视频帧。

文件系统的读操作由ffmpeg调用gstf ilesrc插件的read操作来实现 。一帧数据读出以后,调用avcodec_send_packet()和avcodec_receive_frame()函数进行解码,解码器解码完,调用dec->callbacks.FillBufferDone通知gst-omx插件。通知gst-omx插件是采用pad push机制进行完成的,实质上采用异步的方式 ,gst-omx的优势是在不同的硬件下支持不同的编解码。完成解码解码H264视频数据成YUV2格式。

1.3 视音频传输与媒体播放

传输视音频利用tcpclientsink和udpsink插件主动发送编码后的数据到播放端。gst_element_factory_make()函数创建插件,g_object_set()方法来设置插件网络属性。并使用gst-launch-0.10工具完成管道的创建。如图5所示。

视频解码后是YUV空间的,我们采用两种方式进行显示播放,一种是QT GUI的方式,而QT GUI需要RGB空间的数据,所以需要用sws_scale转换颜色格式到RGB,采用paintEvent(QPaintEvent *)函数进行绘制事件,视频显示可以简单的通过videoout元素来完成视频显示。

另外一种方式是用imxv4l2sink元件进行播放,由于我们硬件平台采用了Freescale的i.MX,是基于Linux下的开发,所以可以采用imxv4l2sink 进行视频的播放。QtGStreamer可以通过overlay嵌入到其他窗口中。imxv4l2sink元件播放视频利用GPU加速器。所以显示速度及效果更加显著。

4 总结

Gsteramer流媒体的ffmpeg编解码开发主要还是基于插件的开发,通过插件的形式把各个元件连接起来,形成管道的方式便于数据的传输。首先需要创建基本元件,v4l2src、pipeline,f ilesrc,encoder,imxv4l2sink等,然后把这些元件加入到pipeline中,并链接起来,最后改变pipeline的状态,就可以启动对媒体数据的处理了。

猜你喜欢

编解码插件调用
1553B总线控制器编解码设计
自编插件完善App Inventor与乐高机器人通信
核电项目物项调用管理的应用研究
大型民机试飞遥测视频编解码方法研究
LabWindows/CVI下基于ActiveX技术的Excel调用
基于H.265编解码的高清视频传输系统研究
基于系统调用的恶意软件检测技术研究
MapWindowGIS插件机制及应用
基于Revit MEP的插件制作探讨
主流视频编解码软件的硬件性能分析与设计