基于GStreamer的视频框架-01-Overview

在多媒体尤其是视频处理领域,FFmpeg和GStreamer是最为流行的工具,它们在音视频处理领域有着广泛的应用。FFmpeg简单易用,通过一条命令即可快速完成多媒体处理任务,使用最为广泛,搞过音视频处理的人几乎都知道它。而GStreamer是一个多媒体处理框架,其管道化的设计更适合构建复杂的多媒体应用程序,且模块化的设计和插件系统使其更具灵活性和扩展性

目前主流的嵌入式平台,例如Nvidia Jetson系统、Rockchip、Zynq对GStreamer都有不错的支持,基于GStreamer进行视频处理应用程序开发变得既灵活又简单。然而GStreamer虽然提供了大量视频处理相关元素,但是数据流各节点的连接关系仍然需要开发人员手动绑定。现实开发场景中,通常数据流管道的结构可能会随着需求的变化随时调整,如何能够更加灵活的构建多分支多流的管道成为一件棘手的事情。我借鉴了Hi3559视频框架中VI、VPASS、VO的概念,在GStreamer管道概念的基础上进行了再次抽象,构建了基于GStreamer的视频框架VPI,将元素链接关系抽象为视频输入V1、视频处理VP和视频输出VO三个部分。如下图所示,在一个复杂Pipeline中可以有多个VI、VP和VO的存在,一个VI可以连接多个VP,一个VP也可以连接多个VO,VO可以导出数据到应用程序中进行外部处理,例如转换为OpenCV的Mat,或者进行神经网络推理,外部处理完成后可再送给另一路VI进行后续处理

而以上所有内容,都仅仅通过以下几个API进行构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建管道
pipeline = vpi_pipeline_new();
// 创建视频输入设备VI,命名为vi0
vpi_vi_device_new(pipeline, "vi0", vi0cfg);
// 创建应用程序输入VI,命名为vi0
vpi_vi_app_new(pipeline, "vi1", vi1cfg);
// 创建视频编码VP,命名为vp0
vpi_vp_enc_new(pipeline, "vp0", enc0cfg);
// 创建视频编码VP,命名为vp1
vpi_vp_enc_new(pipeline, "vp1", enc1cfg);
// 创建应用程序输出VO,命名为vo0
vpi_vp_vo_new(pipeline, "vo0", vo0_callback);
// 创建应用程序输出VO,命名为vo1
vpi_vp_vo_new(pipeline, "vo1", vo1_callback);
// 创建应用程序输出VO,命名为vo2
vpi_vp_vo_new(pipeline, "vo2", vo2_callback);
// 连接vi0和vp0,vi0和vo0
vpi_vi_vp_link(pipeline, "vi0", "vp0");
vpi_vi_vo_link(pipeline, "vi0", "vo0");
// 连接vp0和vo1
vpi_vp_vo_link(pipeline, "vp0", "vo1");
// 连接vi1和vp1
vpi_vi_vp_link(pipeline, "vi1", "vp1");
// 连接vp1和vo2
vpi_vp_vo_link(pipeline, "vp1", "vo2");
// 运行管道
vpi_run(pipeline);

// 停止管道
vpi_stop(pipeline);
// 销毁管道
vpi_pipeline_destroy(pipeline);

本系列文章将会对VPI视频框架进行原理和源码的介绍,通过阅读本系列文章,您可以了解到

  • 什么是GStreamer?

  • 如何安装和在工程中引用GStreamer库?

  • 如何在程序中使用GStreamer构建自己的视频流?

  • 如何使用gst-launch快速验证管道是否正确?

  • 如何查询所有已安装的插件,以及查询某个元素的属性和接收数据的能力?

  • 如何调试GStreamer管道,如何将构建好的管道可视化?

  • 如何构建多分支流?

  • 如何将应用程序作为输入或将管道数据导出到应用程序中?

  • 如何将多个元素的结构抽象为一个VI、VP或者VO?

  • 如何实现VI、VP、VO的绑定关系?