Looping audio file section using segments

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Looping audio file section using segments

Ing. Gabriele Oberhammer
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;
}



_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
Reply | Threaded
Open this post in threaded view
|

Re: Looping audio file section using segments

Tim Müller
Hi Gabriele,

It will depend a bit on the file type and the demuxer/parser elements
involved whether segment seeks work well or not. But they should work
fine with common elements.

One thing you might want to remove is the queue element between filesrc
and decodebin (or just use uridecodebin/uridecodebin3 directly and set
a file:// uri like you did with playbin). The queue element after
filesrc will force the demuxer/parser into a suboptimal code path for
seeking.

Also, the initial set_state() should probably be to PAUSED state, so
you can then do a flushing segment seek to the start position without
outputting anything yet.

Cheers
 Tim


_______________________________________________
gstreamer-devel mailing list
[hidden email]
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel