Looping audio file section using segments

Ing. Gabriele Oberhammer gabriele at naustech.com
Mon Nov 16 11:37:30 UTC 2020


About loop/segment play

Hi all.

I'm experimenting with gstreamer segment seek to play a short file
section in loop seamlessly, but it seems to work only when using
a pipeline with a single 'playbin' element.
This is the sequence of steps I do in the code to loop a file from 20s
to 24s:

1. setup pipeline
2. set to playing state
3. in the first ASYNC_DONE callaback do a segment seek from 20s to 24s
4. on every consequent SEGMENT_DONE signal repeat the seek till EOF

This seems to work fine for the simple pipeline with only 'playbin' element.
(0 branch of the #ifdef macro)
When building a little more complex pipeline with a 'filesrc' a 'decodebin'
and a couple o queues elements the GST_SEGMENT* signals are never fired.
(1 branch of the #ifdef macro).
The seek to the 20s mark is correctly performed, but when play time reaches
the 24s mark I get an EOF instead of a SEGMENT_DONE signal.

Am I I missing something obvious ? I've read a lot of documentation and emails
but I can't find a clear response.

Moreover, on the few examples that I found and in the gstreamer documentation
about segments, it seems that I should only set the GST_SEEK_FLAG_FLUSH on
the first seek and not in the following SEGMENT_DONE callbacks... but doing
that way after the first SEGMENT_DONE, the same callback is called rapidly
multiple times and leads to app crash, which will overflow the sink and the
program crashes.

Can anyone shed some light on what I'm doing wrong or point me to some
working example or documentation ?

Any help will be really appreciated. Thanks :-)


Code:

// Compile with:
// gcc -Wall test_seek.c -o test_seek $(pkg-config --cflags --libs gstreamer-1.0)

#include <gst/gst.h>
#include <glib.h>

// Loop markers
#define START_SEC   20
#define END_SEC     24

typedef struct {
     GMainLoop *loop;
     GstElement *seek_element;
     gboolean first_seek;
} bus_data_t;

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
{
     bus_data_t *bus_data = (bus_data_t *)data;
     gchar  *debug;
     gboolean ret;
     GError *error;

     switch (GST_MESSAGE_TYPE (msg)) {

         case GST_MESSAGE_EOS:
             g_print("EOS\n");
             g_main_loop_quit(bus_data->loop);
             break;

         case GST_MESSAGE_ASYNC_DONE:
             if (bus_data->first_seek) {
                 g_print("First seek\n");
                 bus_data->first_seek = FALSE;
                 ret = gst_element_seek(bus_data->seek_element, 1.0,
                         GST_FORMAT_TIME,
                         GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
                         GST_SEEK_TYPE_SET, START_SEC*GST_SECOND,
                         GST_SEEK_TYPE_SET, END_SEC*GST_SECOND);
                 if (!ret)
                     g_print("First seek error\n");
             }
             break;

         case GST_MESSAGE_SEGMENT_DONE:
             g_print("Segment done\n");
             g_print("Loop seek\n");
             ret = gst_element_seek(bus_data->seek_element, 1.0,
                     GST_FORMAT_TIME,
                     /* According to documentation, the flag FLUSH should not be set
                        here, but without it the SEGMENT_DONE callback is called rapidly
                        multiple times and leads to app crash */
                     GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
                     GST_SEEK_TYPE_SET, START_SEC*GST_SECOND,
                     GST_SEEK_TYPE_SET, END_SEC*GST_SECOND);
             if (!ret)
                 g_print("Loop seek error\n");
             break;

         case GST_MESSAGE_ERROR:
             gst_message_parse_error (msg, &error, &debug);
             g_free (debug);
             g_printerr ("Error: %s\n", error->message);
             g_error_free (error);
             g_main_loop_quit(bus_data->loop);
             break;

         default:
             break;
     }

     return TRUE;
}

int main(int argc, char *argv[])
{
     GMainLoop *loop;

     GstElement *pipeline;
     gchar *file;
     GstBus *bus;
     guint bus_watch_id;

     // Init
     gst_init (&argc, &argv);

     loop = g_main_loop_new (NULL, FALSE);

     // Get args
     if (argc != 2) {
         g_printerr ("Usage: %s <audio file>\n", argv[0]);
         return -1;
     }

#if 0
     // Not working, getting EOS insetad of GST_MESSAGE_SEGMENT_DONE
     GstElement *source;
     file = g_strdup (argv[1]);
     g_print("File: %s\n", file);
     pipeline = gst_parse_launch("filesrc name=src ! queue ! decodebin name=dec "
             "! queue ! pulsesink", NULL);
     source = gst_bin_get_by_name(GST_BIN(pipeline), "src");
     g_object_set(source, "location", file, NULL);
     gst_object_unref(source);
#else
     // Working (almost)
     file = gst_filename_to_uri(argv[1], NULL);
     g_print("File: %s\n", file);
     pipeline = gst_parse_launch("playbin", NULL);
     g_object_set(pipeline, "uri", file, NULL);
#endif

     bus_data_t bus_data = {
         .loop = loop,
         .seek_element = pipeline,
         .first_seek = TRUE
     };

     // Bus handler
     bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
     bus_watch_id = gst_bus_add_watch(bus, bus_call, &bus_data);
     gst_object_unref (bus);

     g_print("Play start\n");
     gst_element_set_state(pipeline, GST_STATE_PLAYING);

     // Loop
     g_print("Loop...\n");
     g_main_loop_run(loop);

     // End
     g_print("Loop ended\n");
     gst_element_set_state(pipeline, GST_STATE_NULL);

     // Cleanup
     g_print("Cleanup\n");
     gst_object_unref(GST_OBJECT(pipeline));
     g_source_remove(bus_watch_id);
     g_main_loop_unref(loop);

     return 0;
}





More information about the gstreamer-devel mailing list