Skip to content

fmp4: Add split-at-running-time signal

Jendrik Weise requested to merge jewe37/gst-plugins-rs:main into main

While consistently timed fragments are usually preferred for fmp4 streams, restrictions on the fragment boundaries due to other variants or renditions of the stream may require custom fragmentation. To address this issue, a signal is added allowing sending a time at which the stream will next be fragmented.

In practice, this is realized by moving out the calculation of the fragment_end_pts into a function that determines it from the fragment_duration and manually specified fragment boundaries.

For easy testing, the following python script can be used:

import gi
import sys
gi.require_version('Gst', '1.0')
gi.require_version('GstPbutils', '1.0')
from gi.repository import Gst, GLib, GstPbutils

Gst.init(None)

def on_bus_message(bus, message):
    t = message.type
    if t == Gst.MessageType.EOS:
        print("End-of-stream")
        loop.quit()
    elif t == Gst.MessageType.ERROR:
        err, debug = message.parse_error()
        print(f"Error: {err}, {debug}")
        loop.quit()
    elif t == Gst.MessageType.WARNING:
        err, debug = message.parse_warning()
        print(f"Warning: {err}, {debug}")
    return True

pipeline = Gst.Pipeline.new("my_pipeline")

videotestsrc = Gst.ElementFactory.make("videotestsrc", "source")
encoder = Gst.ElementFactory.make("x264enc", None)
cmafmux = Gst.ElementFactory.make("cmafmux", None)
filesink = Gst.ElementFactory.make("filesink", None)

if not all([videotestsrc, encoder, cmafmux, filesink]):
    print("Not all elements could be created.")
    sys.exit(1)

videotestsrc.set_property("pattern", "smpte")
videotestsrc.set_property("num-buffers", 30 * 60)

### UNCOMMENT TO TEST CHUNK MODE
# cmafmux.set_property("chunk-duration", 5000000000)

cmafmux.set_property("fragment-duration", 10000000000000) #3-ish hours
filesink.set_property("location", "test.mp4")

for element in [videotestsrc, encoder, cmafmux, filesink]:
    pipeline.add(element)

if not videotestsrc.link(encoder):
    print("Failed to link videotestsrc to encodebin")
    sys.exit(1)

if not encoder.link(cmafmux):
    print("Failed to link encodebin to cmafmux")
    sys.exit(1)

if not cmafmux.link(filesink):
    print("Failed to link cmafmux to filesink")
    sys.exit(1)

bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", on_bus_message)

ret = pipeline.set_state(Gst.State.PLAYING)
if ret == Gst.StateChangeReturn.FAILURE:
    print("Unable to set the pipeline to the playing state.")
    sys.exit(1)

print("Pipeline is playing...")

cmafmux.emit("request-fragment", 10000000000) # 10 seconds
cmafmux.emit("request-fragment", 20000000000) # 20 seconds

loop = GLib.MainLoop()
try:
    loop.run()
except KeyboardInterrupt:
    pass

pipeline.set_state(Gst.State.NULL)

Merge request reports