Splitmuxsink: split-now introduces unstable extra delay

This piece of code below creates a pipeline that shows test video while saving encoded content to files with splitmuxsink.

#include <gst/gst.h>

GMainLoop* loop = NULL;
GstElement* pipeline = NULL;
GstElement* splitmuxsink = NULL;

#pragma warning(disable:4996)

gboolean split(gpointer data) {
    GTimeVal timeval;
    g_get_current_time(&timeval);
    g_printerr("[%ld.%03ld] split-now\n", timeval.tv_sec, timeval.tv_usec / 1000);
    g_signal_emit_by_name(splitmuxsink, "split-now");
    return FALSE;
}

gchar* format_location_callback(GstElement* splitmux, guint fragment_id, gpointer data)
{
    static int index = 0;
    gchar* location = g_strdup_printf("output-%05d.mp4", index);
    if (index++ == 1) {
        GTimeVal timeval;
        g_get_current_time(&timeval);
        g_printerr("[%ld.%03ld] pause\n", timeval.tv_sec, timeval.tv_usec / 1000);
        gst_element_set_state(pipeline, GST_STATE_PAUSED);
    }
    return location;
}


int main(int argc, char* argv[])
{
    gst_init(NULL, NULL);

    // create the pipeline
    pipeline = gst_parse_launch(
        "videotestsrc ! video/x-raw,width=800,height=600,framerate=30/1 ! timeoverlay time-mode=buffer-count ! tee name=t"
        " t. ! queue ! autovideosink"
        " t. ! queue ! videoconvert ! nvh264enc ! h264parse ! splitmuxsink name=sink max-size-time=30000000000", NULL);
    if (!pipeline) exit(-1);

    // splitmuxsink: pause the pipeline when the second file is to be created
    splitmuxsink = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
    g_signal_connect(splitmuxsink, "format-location", G_CALLBACK(format_location_callback), NULL);

    // create the main loop
    loop = g_main_loop_new(NULL, FALSE);

    // pause at 00:00:20
    g_timeout_add_seconds(20, split, NULL);

    // start playing
    gst_element_set_state(pipeline, GST_STATE_PLAYING);
    g_main_loop_run(loop);

    // cleanup omitted: gst_element_set_state(pipeline, GST_STATE_NULL) ...
    gst_deinit();
    return 0;
}

The pipeline needs to be paused at certain points, which is triggerred dynamically by users.

The last video file is not finished and cannot be played, if I just simply pause the pipeline. To make the last video file playable, we have to send a split signal to splitmuxsink and wait until the split is finished.

When the pipeline is paused, I hope that video files created by splitmuxsink contains the latest visible frame (i.e., if the user opens the video file with a player and seeks to the end, he will see an image same as the one in autovideosink).

So I send a split-now signal to splitmuxsink, and wait until the file is splitted with splitmuxsink’s format_location_callback. This callback is used because it seems to be the only one relavant in splitmuxsink’s documentation.

The pause is triggerred at 00:00:20. I find that the video file indeed contains exactly 600 frames, but autovideosink bahaves unexpectedly. Most of the time, the location callback is triggerred 2.2 seconds after signaling split-now, and autovideosink stops at frame-670. Sometimes, the location callback is triggerred 0.2 seconds after split-now.

It’s weird that the video source is paused at frame-670, while the video file containing 600 frames. The delay is too high.

How can I make it behave consistently? How to split video more quickly when requested?

(tested under Windows & GStreamer 1.24.12)

In general, i think that the problem is in the overall approach.

  1. You should know, that file operations are generally much slower that ones in memory, So, you should not rely on the timings of file operations - 'cos OS can be busy with some other things and etc.
  2. To make the last segment playable try to send EOS to the pipeline after your stream ends.
  3. I would suggest changing the approach a bit and not pausing the pipeline when splitting. Maybe add one more queue before the splitmuxsink (in case of frames being dropped)

An interesting observation is that, if I set “splitmuxsink max-size-time=100000000 send-keyframe-requests=true”, splitmuxsink is able to split video every 100ms.

In this case, it sends key frame requests to the video encoder, and the video encoder reponds immediately. But I guess that in “split-now”, it does not send the key frame request.

BTW, I cannot send EOS because the entire pipeline is something like a ring buffer. The application can be paused, but not stopped.