走向必然王国:如何有把握地构建 GStreamer 管道?

本文转载自许野平的博客

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

GStreamer 是一款非常优秀的媒体流构建工具。由于相关参考资料缺乏,官网介绍不够详尽,初学者往往费尽周折,也搞不定一个定制的管道架构,最后只好从网络上东拼西凑,摸着别人的例子过河。

如果您有类似的困惑,本文将带领您走出这个困境,迈出正确构建 GStreamer 管道的第一步。

1. 最简单的视频管道

由于一个管道至少有一个起点和一个终点,因此最简单的管道至少有两个元素,一个用来做 src(流的起点),另一个做 sink(流的终点)。

  • videotestsrc:提供测试视频信号源。它绝对不会出错,可以提供可靠、稳定的信号源。
  • autovideosink:可以把为压缩编码的视频信号显示在屏幕上。我用的是 Jetson Nano 硬件平台,这个元素直接把画面全屏显示了。

我们把它两个连接起来,用下面的命令测试一下。

$ gst-launch-1.0 videotestsrc ! autovideosink
  • 1

屏幕显示如下信号:
在这里插入图片描述

这个让我想起三十多年前当电视机维修工的时候,用的那个视频信号发生器,输出的就是类似这样的信号。人生如梦,当年手拿烙铁和万用表的时候,老子怎么也没想到将来有一天会靠敲键盘在机器视觉行业混饭。

2. 自己创建一个问题

我打算做个简单的实验,在 videotestsrc 后面加上一个 h.264 编码器,然后再紧跟一个 h.264 解码器。我觉得这个效果应该和上面的例子一样——因为图像编码后再解码,等于啥也没做。

输入下面的命令后,一大堆错误信息,让我瞬间崩溃了:

$ gst-launch-1.0 videotestsrc ! omxh264enc ! omxh264dec ! autovideosink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Framerate set to : 30 at NvxVideoEncoderSetParameterNvMMLiteOpen : Block : BlockType = 4
===== NVMEDIA: NVENC =====
NvMMLiteBlockCreate : Block : BlockType = 4
H264: Profile = 66, Level = 40
ERROR: from element /GstPipeline:pipeline0/GstOMXH264Enc-omxh264enc:omxh264enc-omxh264enc0: Internal data stream error.
Additional debug info:
/dvs/git/dirty/git-master_linux/3rdparty/gst/gst-omx/omx/gstomxvideoenc.c(1383): gst_omx_video_enc_loop (): /GstPipeline:pipeline0/GstOMXH264Enc-omxh264enc:omxh264enc-omxh264enc0:
stream stopped, reason not-negotiated
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
Freeing pipeline ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3. 有用的 fakesink

GStreamer 提供了一个万能的管道终点元素 fakesink。这个元素对输入数据没有任何要求,来者不拒,同时,它啥工作也不做,感觉就是个黑洞,任何数据到它这里都接受,也不做任何处理,然后就没有然后了。因此,它前面可以对接任何合理的元素。

fakesink 的作用在于,可以在任何位置让你正在构建的管道合法地结束。这可以让我们采用增量方式,步步为营,慢慢增加管道长度,直到完整地构建出管道。

例如,为了正确构建上面的例子,我输入下面这两行命令,都是可以正常执行的。这说明管道的 videotestsrc ! omxh264enc 这一部分是可以正常工作的。

$ gst-launch-1.0 videotestsrc ! fakesink
$ gst-launch-1.0 videotestsrc ! omxh264enc ! fakesink
  • 1
  • 2

但是下面这一行就不行了:

$ gst-launch-1.0 videotestsrc ! omxh264enc ! omxh264dec ! fakesink
  • 1

这说明,元素 omxh264dec 与管道前面的那个部分不能衔接。接下来我们分析一下原因。

4. gst-inspect-1.0:随手可得的元素/插件说明书

我们用下面两行命令查看 omxh264enc 和 omxh264dec 的说明,重点查看 omxh264enc 的输出数据(src pad caps)和 omxh264dec 的输入数据 (sink pad caps) 的兼容性。为了便于理解,下面的屏幕输出内容我做了删减。

$ gst-inspect-1.0 omxh264enc 
...
Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-h264
                  width: [ 16, 4096 ]
                 height: [ 16, 4096 ]
          stream-format: { (string)byte-stream, (string)avc }
              alignment: au
...

$ gst-inspect-1.0 omxh264dec
...
Pad Templates:
SINK template: 'sink'
Availability: Always
Capabilities:
video/x-h264
parsed: true
alignment: au
stream-format: byte-stream
width: [ 1, 2147483647 ]
height: [ 1, 2147483647 ]
...

  • 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

看出区别了吗?omxh264dec 要求输入数据必须满足 parsed=true,而元素 omxh264enc 却不提供这个保障。

顾名思义,这说明 omxh264enc 的输出结果需要进一步解析后,才能发送给 omxh264dec 解码。查找一些 h.264 数据解析插件,发现 h265parse 符合要求。于是我们构造下面的命令:

$ gst-launch-1.0 videotestsrc ! omxh264enc ! h264parse ! omxh264dec ! autovideosink
  • 1

运行后,非常完美。忽然又突发奇想,再长一些行不行?输入下面的命令:

$ gst-launch-1.0 videotestsrc ! omxh264enc ! h264parse ! omxh264dec ! omxh264enc ! h264parse ! omxh264dec ! autovideosink
  • 1

依旧完美!OK,从今天起,我们可以借助 gst-inspect-1.0 和 fakesink 这两个工具,逐步排查管道连接中存在的 caps 协商异常问题,建立可靠的媒体流管道。如果本文对您有帮助,请点赞、收藏,谢谢!

posted @ 2023-03-10 10:44  thammer  阅读(203)  评论(0编辑  收藏  举报