Dynamically adding new queue to a tee element

I have a predefined pipeline with tee element. I also have a filesink element which writes video to output.mp4 file. Additionally, I want to start a pipeline and attach extra filesink element, which writes the video to output2.mp4, at some time in the future. However, I can’t get readable output2.mp4 file.

pipeline = Gst.ElementFactory.make("pipeline")
src = Gst.ElementFactory.make("videotestsrc")
convert = Gst.ElementFactory.make("videoconvert")
encode = Gst.ElementFactory.make("x264enc")
mux = Gst.ElementFactory.make("mp4mux")
tee = Gst.ElementFactory.make("tee")
q = Gst.ElementFactory.make("queue")
sink = Gst.ElementFactory.make("filesink")
sink.set_property("location", "output.mp4")

pipeline.add(src, convert, encode, mux, tee, q, sink)
src.link(convert)
convert.link(encode)
encode.link(mux)
mux.link(tee)
tee.link(q)
q.link(sink)

# Create extra elements to start recording later
# But do not link them with tee
q2 = Gst.ElementFactory.make("queue")
sink2 = Gst.ElementFactory.make("filesink")
sink2.set_property("location", "output2.mp4")
pipeline.add(q2, sink2)
q2.link(sink2)

...

# Called at some time in the future:
def start_recording():
    tee_pad_templ = tee.get_pad_template("src_%u")
    tee_pad = tee.request_pad(tee_pad_templ)
    sink_pad = q2.get_static_pad("sink")
    tee_pad.link(sink_pad)

Should I do some additional manipulations in start_recording, e.g. change the states of the elements or the whole pipeline?

P.S.: my full code is at Delayed video recording in gstreamer · GitHub

I wrote about this a few years ago: GStreamer Dynamic Pipelines – coaxion.net – slomo's blog . The second example is more or less what you’re doing.

1 Like

@slomo thank you for tyou answer! I’ve managed to create a dynamic pipeline from my code. However, it seems some concepts steel elude me.

I try to dynamically build two pipelines inside my application:

  1. videotestsrc ! tee name=t ! videoconvert ! x264enc ! mp4mux ! filesink location=out.mp4 t. ! queue ! videoconvert ! x264enc ! mp4mux ! filesink location=out2.mp4
  2. videotestsrc ! videoconvert ! x264enc ! mp4mux ! tee name=t ! filesink location=out.mp4 t. ! queue ! filesink location=out2.mp4

In both cases pipeline is constructed dynamically starting from the second queue element. However, I don’t get the correct out2.mp4 in the second pipeline. Both of those pipelines work fine if I construct them statically via gst-launch-1.0. Is there any significant difference between pipeline 1 and 2? Should I perform some extra work in my code to make second pipeline work?

The second pipeline also does not work in gst-launch-1.0 for me. Putting a tee after mp4mux seems quite optimistic as it will have to re-write both files in the end.

In either case, you should always put suitably-sized queue elements after each of the branches of a tee.

Hi! To me it looks like conceptionally there’s a problem: if you dynamically add one more output to the mp4mux when it already have started muxing, the output file won’t contain complete mp4 headers, because the beginning of the file will be lost.

What you can do is to consider muxing to mp4 after the tee, having a pipeline like

videotestsrc ! videoconvert ! x264enc ! tee name=t ! queue ! mp4mux ! filesink location=out.mp4
t. ! queue ! mp4mux ! filesink location=out2.mp4

Or using another format, that allows cutting the file, such as mpegts or just a raw h264 bytestream. But seeking on those files will have low performance and precision.

gst-launch-1.0 videotestsrc ! videoconvert ! x264enc  ! tee name=t ! queue ! filesink location=out.h264 t. ! queue ! filesink location=out2.h264
2 Likes

Thank you for the answer @Sasha . However, I still have some questions. When the dynamically attached part of pipeline is detached, are there any extra actions necessary? Like sending the EOS event, etc. I guess it’s not necessary for simple formats, such as wav. But is it required for mp4mux?

Yes, anything that has to (re-)write headers or otherwise finalize its output will need an EOS event. It’s also needed to ensure that everything still in that branch is actually written out.

1 Like