GStreamer在我看来更像是视频编解码领域的内容。
JPEG和MPEG
先区分一下这两个格式[1]。
JPEG全称Joint Photographic Experts Group,文件拓展名一般为.jpg或者.jpeg,是一种静态图像压缩标准,压缩比能达到10:1。
MPEG全称Moving Picture Experts Group,分为mpeg-1、mpeg-2、mpeg-4三种格式,,是一种视音频编码标准,压缩比能达到20:1。
GStreamer
GStreamer主要是通过插件的方式去构建pipeline,本文用ppl简写指代。ppl相当于一个大的容器,里面每一个元素都是element,也叫做plugin(插件)。
NVIDIA 的 deepstream 便是基于 GStreamer开发的 SDK。
一个最基本的GStreamer ppl如下,音视频流先经过demuxer解封装为视频流和音频流。视频流经过解码器 video decoder(如 mppvideodec 或 mppjpegdec)解码,音频流通过audio decoder解码。最后使用显示插件如 xvimagesink 和 waylandsink 显示图像,其中xvimagesink 会调用 X11接口对接X11显示架构,waylandsink 调用 Wayland 接口对接 Wayland 显示架构,使用音频播放插件如 alsasink 播放音频。
GStreamer基本命令
一些基本的GStreamer CLI。
gst-play-1.0
: 简化的媒体播放器,内部自动构建pplgst-launch-1.0
: 底层工具,需显式构建完整ppl,灵活性更高gst-inspect-1.0
: 打印指定element或plugin的信息gst-discoverer-1.0
: 可以对提供的URL进行分析,查看是否缺少plugin。比如gst-discoverer-1.0 rtsp://127.0.0.1:8554
顺便说一下URI(Uniform Resource Identifier)和URL(Uniform Resource Locator)。URI是统一资源标识符,用一个字符串用来标示抽象或物理资源;URL是统一资源定位符,是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。
URI 属于 URL 更高层次的抽象,一种字符串文本标准。URI 表示请求服务器的路径,定义这么一个资源。而 URL 同时说明要如何访问这个资源(rtsp://xxxxxxxxx)
GStreamer插件
GStreamer插件(元素)主要分为3类:
源插件source: 只产生数据,不接受数据。
过滤器插件filter:处理数据,然后向后一级传送
接收插件 sink:只接受数据,不再产生数据。比如保存文件,显示画面。
在 GStreamer 中,Pad 是连接不同元素之间的接口,允许它们相互通信和传递数据的概念。每个 GStreamer 元素都包含一个或多个 Pad,其中的数据流通过 Pad 传递。Pad 可以是输入 Pad,用来接收数据,也可以是输出 Pad,用于发送数据。Pads 是元素间数据流动的接口,而Pad Capabilities 是这些接口所能处理的数据类型和属性的详细描述[5]。
上下游 element 间通过Event(事件)同步状态,通过query(询问)来同步信息。
一些内部机制
GStreamer的内部机制除了上面提及的还有:
Message:全称Bus Message,用于pipeline和Application之间的交互。默认情况下,每个pipeline都会包含一个Bus,因此在编写Application程序时不需要创建Bus。Application唯一需要做的是在Bus上设置Message处理函数。Message具体类型可以参照 GstMessage,随便列几个:
GST_MESSAGE_UNKNOWN
– an undefined messageGST_MESSAGE_EOS
– end - of - stream reached in a pipeline. The application will only receive this message in the PLAYING state and every time it sets a pipeline to PLAYING that is in the EOS state. The application can perform a flushing seek in the pipeline, which will undo the EOS state again.GST_MESSAGE_ERROR
– an error occurred. When the application receives an error message it should stop playback of the pipeline and not assume that more data will be played. It is possible to specify a redirection url to the error messages by setting a redirect - location field into the error message, application or high level bins might use the information as required.GST_MESSAGE_WARNING
– a warning occurred.GST_MESSAGE_INFO
– an info message occurred
Signal:Signal源于GObject体系,属于GObject通用机制,用于特定交互。在GStreamer中,element也属于GObject,所以通过Signal就可以将Application和element联系起来。
Event:Event与Buffer一起在pipeline的up and downstream中传播,在 elements 之间进行传递events,换句话说,Message用于app和和ppl的通信;Event用于ppl内部的通信。具体类型可以参照 GstEvent,也是随便列几个:
GST_EVENT_FLUSH_START
: data is to be discarded
GST_EVENT_FLUSH_STOP
: data is allowed againGST_EVENT_CAPS
: Format information about the following buffersGST_EVENT_SEGMENT
: timing information for the following buffersGST_EVENT_TAG
: Stream metadata.GST_EVENT_BUFFERSIZE
: Buffer size requirements. Currently not used yet.
Query:Query是向一个 element 或者 Pad 查询信息。若我们有一个display,可以在屏幕上显示 video(假设只支持RGB格式),而decoder的输出大多是NV12或者I420格式的。所以,我们要在decoder跟display之间接一个videoproc(video post processing视频后处理)的element来进行格式转换。在此,我们并不需要指定videoproc的输入输出格式,它会自动的通过query的方式询问上下游所支持的格式,从而判断出其要做一个NV12→RGB的格式转换。这种方式也就是Gstreamer里面的的自动协商[2]。
Property:属性也是GObject的一种机制,就是对象的属性,在GStreamer中用于行为和参数控制。
插件介绍
Core Elements
fakesink
:将收到的数据全部丢弃。filesrc
:从文件读取数据。e.g.
gst-launch-1.0 filesrc location=nv12_640x320.yuv blocksize=307200! video/x-raw,format=NV12,width=640,height=320! mpph264enc! filesink location=out.h264
blocksize
:帧大小,读取YUV格式裸流编码时会用到。location
:文件地址,支持绝对/相对路径。
filesink
:将收到的数据保存为文件。e.g.
gst-launch-1.0 filesrc location=/tmp/test ! filesink location=/tmp/test2
gst1-plugins-base
appsrc
:从外部导入数据,在CLI用使用很少,主要是在代码中使用。appsink
:用于导出gst数据,同样很少在CLI中使用。通常配合appsrc
使用。decodebin/decodebin3
:自动动查找合适的解码插件。bin
可以将一系列elements组合形成一个逻辑上的element。举个例子,!a!b!c!d
这4个element放到一个bin
里,然后调用这个bin
就等同于调用!a!b!c!d
。playbin/playbin3
:集成了decodebin
,会自动查找合适的sink插件。uridecodebin/uridecodebin3
:集成了decodebin
,根据提供的URI自动查找source插件。videoconvert
:非常常用的插件,用于图像格式转换。在我使用的RK3588开发板上,适配了RGA(Raster Graphic Acceleration Unit)加速,可用于加速点/线绘制,执行图像缩放、旋转、bitBlt、alpha混合等常见的2D图形操作。videotestsrc
:生成不同格式和分辨率的视频流。xvimagesink
:在XV环境下渲染图像,由GPU进行合成。
gst1-plugins-good
v4l2src
:从v4l2设备获取视频数据。e.g.
gst-launch-1.0 v4l2src! video/x-raw,width=1920,height=1080,format=NV12! waylandsink
device
:指定设备节点。Default:"/dev/video-camera0"
min-buffers
:驱动最低缓存大小。num-buffers
:指定输出固定帧数的数据。
rtspsrc
:从RTSP服务器获取视频流。e.g.
gst-launch-1.0 rtspsrc location=rtsp://192.168.1.105:8554/! rtph264depay! h264parse! mppvideodec! waylandsink
location
:指定码流地址
videoflip
:视频翻转。e.g.
gst-launch-1.0 videotestsrc ! videoflip video-direction=90r ! waylandsink
C++ 例程
创建一个GStreamer element
// 创建新的 GStreamer 管道
capture_pipeline = gst_pipeline_new("capture-pipeline");
// 创建管道中的各个元素
GstElement *src = gst_element_factory_make("v4l2src", "src");
GstElement *jpegdec = gst_element_factory_make("mppjpegdec", "dec");
GstElement *videoconvert = gst_element_factory_make("videoconvert", "videoconvert");
GstElement *capsfilter = gst_element_factory_make("capsfilter", "caps");
GstElement *sink = gst_element_factory_make("appsink", "sink");
if (!src || !jpegdec || !videoconvert || !capsfilter || !sink) {
LOG_ERR("Not all elements could be created.\n");
return;
}
gst_pipeline_new
是 GStreamer 库提供的一个函数,参数是设置ppl的名称。gst_element_factory_make
的定义为:GstElement gst_element_factory_make(const gchar factoryname, const gchar *name);
返回一个
GstElement
类型的变量,如果创建成功返回创建元素的指针;如果创建失败返回NULL。factoryname
代表要创建的 GStreamer element的工厂名称,每个element都有一个工厂。e.g."v4l2src"
指的是 Video4Linux2 source的元素工厂,能创建从 Video4Linux2 设备捕获视频数据的元素。name
用于为新创建的元素指定一个名称,以用于Debug和Log。
// 配置 v4l2src 元素,指定视频设备
g_object_set(src, "device", device, NULL);
// 配置 capsfilter 元素,设置视频的格式、宽度和高度
GstCaps *caps = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGB", // 使用RGB、BGR、NV12格式
"width", G_TYPE_INT, user_data->FRAME_WIDTH,
"height", G_TYPE_INT, user_data->FRAME_HEIGHT,
"framerate", GST_TYPE_FRACTION, 30, 1, // 设置帧率为 30fps
NULL);
g_object_set(capsfilter, "caps", caps, NULL);
gst_caps_unref(caps);
// 配置 appsink 元素
g_object_set(sink,
"emit-signals", TRUE,
"sync", FALSE,
"max-buffers", 20,
"drop", TRUE,
NULL);
g_object_set
:先看GObject的继承关系,可以看出,GstElement 是 GObject 的派生类,因此可以使用g_object_set
来设置 GStreamer element的属性。g_object_set
可以接受一个以NULL
结束的属性名-属性值
键值对列表,可以一次性设置多个属性。在这里,是为已经被创建为v4l2src
的src
元素设置"device"
属性。
GObject ╰──GInitiallyUnowned ╰──GstObject ╰──GstElement ╰──GstBin ╰──GstPipeline
gst_caps_new_simple
:用于创建一个简单的caps
。也接受可变参数列表,后面的参数成对出现。"video/x-raw"
代表未经压缩的视频。G_TYPE_STRING
代表字符串类型,G_TYPE_INT
代表整型,GST_TYPE_FRACTION
代表分数类型,后面的两个参数分别是分子和分母。
// 连接 appsink 的 "new-sample" 信号到回调函数
g_signal_connect(sink, "new-sample", G_CALLBACK(on_new_sample), (gpointer)user_data);
// 将所有元素添加到管道中
gst_bin_add_many(GST_BIN(capture_pipeline), src, jpegdec, videoconvert, capsfilter, sink, NULL);
// 链接管道中的各个元素
if (!gst_element_link_many(src, jpegdec, videoconvert, capsfilter, sink, NULL)) {
LOG_ERR("Failed to link elements in the pipeline\n");
return;
}
// 启动管道
if (gst_element_set_state(GST_ELEMENT(capture_pipeline), GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
LOG_ERR("Failed to set pipeline to playing state\n");
return;
}
g_signal_connect
:将appsink
元素(sink
)的"new-sample"
信号与回调函数on_new_sample
相连接。当appsink
接收到新的视频样本时,就会触发"new-sample"
信号,进而调用on_new_sample
回调函数来处理该样本。gst_bin_add_many
:把多个 GStreamer 元素src
、jpegdec
、videoconvert
、capsfilter
、sink
添加到ppl中。GST_BIN
是一个宏,用于将GstElement
指针转换为GstBin
指针。