除非我有两个队列,否则为什么屏幕上的视频无法更新?

Why does on-screen video fail to update unless i have two queues?

gst-launch-1.0 -v -e \
    videotestsrc ! tee name=t0 \
    t0. ! queue ! x264enc ! matroskamux ! filesink location="test.mkv" \
    t0. ! queue ! queue ! autovideosink

有效,文件和屏幕显示都有效

gst-launch-1.0 -v -e \
    videotestsrc ! tee name=t0 \
    t0. ! queue ! x264enc ! matroskamux ! filesink location="test.mkv" \
    t0. ! queue ! autovideosink

无效。

这是另一组示例。

gst-launch-1.0 -v -e \
    videotestsrc ! tee name=t0 \
    t0. ! queue ! autovideosink \
    t0. ! queue ! autovideosink

有效。

gst-launch-1.0 -v -e \
    videotestsrc ! tee name=t0 \
    t0. ! queue ! autovideosink \
    t0. ! autovideosink

不会。为什么不?为什么发球台的两个输出都需要排队?在最坏的情况下,我希望一个 autovideosink 可以工作而另一个是空白的,但一个显示单个帧而另一个是黑色的。

但以下确实有效。怎么回事?

gst-launch-1.0 -v -e \
    videotestsrc ! tee name=t0 \
    t0. ! queue ! autovideosink \
    t0. ! queue ! autovideosink \
    t0. ! autovideosink

为什么添加第三个输出就不需要在所有输出上排队?

gst-launch-1.0 --version
gst-launch-1.0 version 1.12.4
GStreamer 1.12.4
https://packages.gentoo.org/package/media-libs/gstreamer

有谁知道为什么队列会这样?

这是我要制作的管道。以上只是缩小的例子。

(注意:管道第一行中的奇怪上限是为了确保我的 Logitech c920 相机输出 h264 而不是 raw,以及我的 Logitech BRIO 输出 1080p 的视频 jpeg,而不是 720p 的 raw。这已经过测试,效果比简单 "decodebin")

好得多
gst-launch-1.0 -e \
    v4l2src device=/dev/video0 ! 'video/x-h264;image/jpeg;video/x-raw' ! decodebin ! 'video/x-raw' ! tee name=t0 \
    v4l2src device=/dev/video1 ! 'video/x-h264;image/jpeg;video/x-raw' ! decodebin ! 'video/x-raw' ! tee name=t1 \
    v4l2src device=/dev/video2 ! 'video/x-h264;image/jpeg;video/x-raw' ! decodebin ! 'video/x-raw' ! tee name=t2 \
    v4l2src device=/dev/video3 ! 'video/x-h264;image/jpeg;video/x-raw' ! decodebin ! 'video/x-raw' ! tee name=t3 \
    matroskamux name=mux \
    t0.  ! queue ! autovideoconvert ! x264enc ! mux. \
    t1.  ! queue ! autovideoconvert ! x264enc ! mux. \
    t2.  ! queue ! autovideoconvert ! x264enc ! mux. \
    t3.  ! queue ! autovideoconvert ! x264enc ! mux. \
    mux. ! queue ! filesink location="test.mkv" \
    videomixer name=mix \
        sink_0::zorder=1 sink_0::alpha=1.0 sink_0::ypos=0   sink_0::xpos=0    \
        sink_1::zorder=1 sink_1::alpha=1.0 sink_1::ypos=0   sink_1::xpos=960  \
        sink_2::zorder=1 sink_2::alpha=1.0 sink_2::ypos=540 sink_2::xpos=0    \
        sink_3::zorder=1 sink_3::alpha=1.0 sink_3::ypos=540 sink_3::xpos=960  \
    t0.  ! queue ! autovideoconvert ! video/x-raw, width=960, height=540 ! mix.sink_0 \
    t1.  ! queue ! autovideoconvert ! video/x-raw, width=960, height=540 ! mix.sink_1 \
    t2.  ! queue ! autovideoconvert ! video/x-raw, width=960, height=540 ! mix.sink_2 \
    t3.  ! queue ! autovideoconvert ! video/x-raw, width=960, height=540 ! mix.sink_3 \
    mix. ! queue ! autovideosink sync=false



通过将 max-size-bytes=0 max-size-buffers=0 max-size-time=10000000000 添加到队列中解决了这个问题。

对于未启动 gstreamer 低级别位的任何人来说,这难以置信 违反直觉。但如果它有效,我想它会起作用。

了解 GStreamer 中 PREROLLING 的概念:

https://gstreamer.freedesktop.org/documentation/design/preroll.html

A sink element can only complete the state change to PAUSED after a buffer has been queued on the input pad or pads.

文档中没有强调的是,管道只会在所有接收器都具有 PREROLLED.

后从 PAUSED 过渡到 PLAYING

另请注意,tee 不是线程化的,因此它会按顺序向下游推送样本。

发生的情况是这样的:接收器 1 收到样本,但不会开始播放,因为它会等到管道中的所有其他接收器都收到样本,这样 audio/video 同步才能得到遵守。

所以现在 sink 1 正在等待,它有效地阻止了 tee 阻止它发送更多数据 - 在这种情况下,sink 2 也是如此。因为没有数据会到达 sink 2,所以你陷入了僵局。

队列会自动在管道路径中添加一个线程作为副作用 - 防止死锁。

如果您只有一个队列,它实际上可能有效 - 取决于您将水槽连接到发球台的顺序。如果有队列的路径先被传递,它就不会死锁,并且发球台可以向另一个发球台传递数据,并且状态更改将成功。 (与三个接收器的示例相同,如果所有路径都有一个队列但不是最后一个你可能会逃脱它)

对所有 tee 输出使用队列是一种很好的做法。

x264enc 示例特别棘手。你在这里面临的问题是编码器消耗了太多数据但没有产生任何东西(还)有效地拖延了管道。

两种修复方式:

  1. x264enc 元素使用 tune=zerolatency
  2. 增加 non-encoder 路径队列中的缓冲区大小以补偿编码器延迟。

对于 queue ! queue,您实际上是通过将缓冲区大小加倍来处理案例 2.