Looping a video with gstreamer and gst-launch?
Asked Answered
L

7

11

I am able to play a video on the command line with gstreamer's gst-launch like this:

gst-launch gnlfilesource location=file:///tmp/myfile.mov start=0 duration=2000000000 ! autovideosink

This plays the first 2 seconds of the file in /tmp/myfile.mov, afterwards the video playback stops. Is there anyway to get this to loop repeatidly? i.e. turn the 2 second long gnlfilesource into an infinite length video that plays those 2 seconds again and again and again?

Lathe answered 26/7, 2011 at 16:10 Comment(0)
B
1

Assuming bash...

Wrap it in a while-loop?

while true; do [your command]; done

where true does nothing sucessfully, i.e.

true: true
    Return a successful result.

    Exit Status:
    Always succeeds.

It allows you to create infinite loops, e.g.

$ while true; do echo "run..."; sleep 1; done
run...
run...
run...
run...
run...
...
Beffrey answered 26/7, 2011 at 16:32 Comment(4)
Pedically that would work, but I want to do it within gstreamer so I can process this infinite loop later...Lathe
@Rory - sorry, can't help you there, googling "gst-launch looping video" returns some interesting matches...Beffrey
This fails when looping videos of variable lenghts inside a videomixer.Wellwisher
This looks more like a quick fix rather then a solutionProductive
Y
11

If using gst-launch then you may have to use while true; do [your command]; done as Fredrik has stated. However if interested in C code, I have written a code which may help you. Looping of video every 2 seconds from the beginning of the file at the end of the stream of first run.

  //(c) 2011 enthusiasticgeek
  // This code is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

#include <gst/gst.h>

gboolean bus_callback(GstBus *bus, GstMessage *msg, gpointer data)
{
    GstElement *play = GST_ELEMENT(data);
    switch (GST_MESSAGE_TYPE(msg))
    {
    case GST_MESSAGE_EOS:
        /* restart playback if at end */
        if (!gst_element_seek(play, 
                    1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
                    GST_SEEK_TYPE_SET,  2000000000, //2 seconds (in nanoseconds)
                    GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
            g_print("Seek failed!\n");
        }
        break;
    default:
        break;
    }
    return TRUE;
}

gint
main (gint   argc,
      gchar *argv[])
{
  GMainLoop *loop;
  GstElement *play;
  GstBus *bus;

  /* init GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

  /* make sure we have a URI */
  if (argc != 2) {
    g_print ("Usage: %s <URI>\n", argv[0]);
    return -1;
  }

  /* set up */
  play = gst_element_factory_make ("playbin", "play");
  g_object_set (G_OBJECT (play), "uri", argv[1], NULL);

  bus = gst_pipeline_get_bus (GST_PIPELINE (play));
  gst_bus_add_watch (bus, bus_callback, play);
  gst_object_unref (bus);

  gst_element_set_state (play, GST_STATE_PLAYING);

  /* now run */
  g_main_loop_run (loop);

  /* also clean up */
  gst_element_set_state (play, GST_STATE_NULL);
  gst_object_unref (GST_OBJECT (play));

  return 0;
}

Update: See the following link http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/chapter-dataaccess.html

[Section 19.1.2. Play a region of a media file]. This could be used in conjugation with my code.

Yourself answered 9/8, 2011 at 14:34 Comment(2)
Interesting, however, the timestamp is not monotonically increasing with this solution.Dihydric
How do you use that to actually play the gstreamer video?! When I start that binary, nothing happens...Geraint
V
10

This seems to be possible with multifilesrc plugin,

gst-launch-1.0 multifilesrc location=alien-age.mpg loop=true ! decodebin ! autovideosink

Seems to be added back in June 2011.

Vanmeter answered 26/10, 2015 at 16:35 Comment(2)
Does not loop on Ubuntu 18.04 when streaming the video as webcam in a browser with v4l2loopback.Hemmer
Does not work on both Ubuntu18.04 and Ubuntu20.04 for me as wellProductive
W
8

multifilesrc is the easiest way, but it won't work on media files that have "Media length" known. you can loop on any video files only if file does not have any information about the time or length.

Open your file with any media player, if it shows media length or if you can seek the file forward or backward, that means it knows the media length and multifilesrc won't loop it.

How to convert video file into file without time track (stream file) with GStreamer:

you need to run two pipelines on command line, first run the recorder:

gst-launch-1.0 udpsrc port=10600 ! application/x-rtp-stream ! rtpstreamdepay name=pay1 ! rtph264depay ! h264parse ! video/x-h264,alignment=nal ! filesink location=my_timeless_file.mp4

it starts and waits for incoming stream.

on another terminal run the play pipeline:

gst-launch-1.0 filesrc location=my_file_with_time_track ! queue ! decodebin ! videoconvert ! x264enc ! h264parse config-interval=-1 ! rtph264pay pt=96 ! rtpstreampay name=pay0 ! udpsink host=127.0.0.1 port=10600

play pipeline starts and eventually terminates when it streamed whole file, now go back to the first command line and terminate recording pipeline with Ctrl+C.

(instead of udpsrc/udpsink you can use any other mechanisms to make the stream, like appsrc/appsink)

Now you have a new file which can be used in multifilesrc with loop:

gst-launch-1.0 multifilesrc location=my_timeless_file.mp4 loop=true ! queue ! decodebin ! videoconvert ! ximagesink

Why multifilesrc does not loop files with known length?

Because when length of media is known it sends EOS message downstream and causes whole pipeline going to state NULL, by removing that information when it reaches end of file (byte stream) it tries to find next file to play (remember it is "multi" file source, and by default can accept wildcard location like "image_%d.png"). When there is no wildcard to point to the next file, it loops back to only known file.

Wickman answered 31/10, 2021 at 8:39 Comment(0)
L
6

According to folks on the #gstreamer IRC channel, you can't do this with gstreamer itself, you'd need something outside the gstreamer pipeline to loop it.

Lathe answered 2/8, 2011 at 11:35 Comment(0)
D
3

It's not looping file in stream on gstreamer, but I was able to do it with ffmpeg -stream_loop option. https://ffmpeg.org/ffmpeg.html#Main-options

$ ffmpeg -re -stream_loop -1 -i /tmp/sample.mp4 -f rtsp rtsp://localhost:8554/stream
Discontented answered 17/9, 2020 at 6:19 Comment(2)
getting Connection to tcp://localhost:8554?timeout=0 failed (Connection refused), trying next address error when trying to work with this,Bergh
This answer is missing command with gstreamer pipeline definition something like gst-launch-1.0 tcpserversrc port=8554 ! rtpdec ! ...Inglebert
B
1

Assuming bash...

Wrap it in a while-loop?

while true; do [your command]; done

where true does nothing sucessfully, i.e.

true: true
    Return a successful result.

    Exit Status:
    Always succeeds.

It allows you to create infinite loops, e.g.

$ while true; do echo "run..."; sleep 1; done
run...
run...
run...
run...
run...
...
Beffrey answered 26/7, 2011 at 16:32 Comment(4)
Pedically that would work, but I want to do it within gstreamer so I can process this infinite loop later...Lathe
@Rory - sorry, can't help you there, googling "gst-launch looping video" returns some interesting matches...Beffrey
This fails when looping videos of variable lenghts inside a videomixer.Wellwisher
This looks more like a quick fix rather then a solutionProductive
V
0

You can use gstreamermm lib in C++. Note it's just an example.

Example of lopping video:

#include <gstreamermm.h>
#include <glibmm/main.h>
#include <glibmm/convert.h>
#include <iostream>
#include <stdlib.h>
#include <gstreamermm/playbin.h>

namespace
{

    Glib::RefPtr<Glib::MainLoop> mainloop;
    Glib::RefPtr<Gst::PlayBin> playbin;
    gboolean bus_callback(const Glib::RefPtr<Gst::Bus>& bus ,
        const Glib::RefPtr<Gst::Message>& message)
    {
        switch (message->get_message_type()) {
        case Gst::MESSAGE_EOS:
        {
            playbin->seek(1.0, Gst::FORMAT_TIME, Gst::SEEK_FLAG_FLUSH, Gst::SEEK_TYPE_SET, 0
                , Gst::SEEK_TYPE_NONE, 0);
            return true;
        }
        case Gst::MESSAGE_ERROR:
        {
            Glib::RefPtr<Gst::MessageError> msgError =
                Glib::RefPtr<Gst::MessageError>::cast_static(message);

            if (msgError)
            {
                Glib::Error err;
                err = msgError->parse_error();
                std::cerr << "Error: " << err.what() << std::endl;
            }
            else
                std::cerr << "Error." << std::endl;

            mainloop->quit();
            return false;
        }
        default:
            break;
        }

        return true;
    }
}

int main(int argc, char** argv)
{
    Gst::init(argc, argv);
    if (argc < 2)
    {
        std::cout << "Usage: " << argv[0] << " <media file or uri>" << std::endl;
        return EXIT_FAILURE;
    }
#ifndef GSTREAMERMM_DISABLE_DEPRECATED
    playbin = Gst::PlayBin::create();
#else
    Glib::RefPtr<Gst::Element> playbin = Gst::ElementFactory::create_element("playbin");
#endif

    if (!playbin)
    {
        std::cerr << "The playbin2 element could not be created." << std::endl;
        return EXIT_FAILURE;
    }

    Glib::ustring uri;

    if (gst_uri_is_valid(argv[1]))
        uri = argv[1];
    else
        uri = Glib::filename_to_uri(argv[1]);

    
    mainloop = Glib::MainLoop::create();

    playbin->set_property("uri", uri);

    Glib::RefPtr<Gst::Bus> bus = playbin->get_bus();

    bus->add_watch(sigc::ptr_fun(&bus_callback));

    playbin->set_state(Gst::STATE_PLAYING);

    mainloop->run();
    std::cout << "Returned. Setting state to NULL." << std::endl;
    playbin->set_state(Gst::STATE_NULL);
    return EXIT_SUCCESS;
}

Or you can use classic C lib gst. Example of lopping video:

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

typedef struct _CustomData {
    GstElement* playbin;
    GMainLoop* loop;
} CustomData;


gboolean bus_callback(GstBus* bus, GstMessage* msg, CustomData* data)
{
    GstElement* play = GST_ELEMENT(data->playbin);
    switch (GST_MESSAGE_TYPE(msg))
    {
    case GST_MESSAGE_EOS:
    {
        gboolean ret = gst_element_seek(play,
            1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
            GST_SEEK_TYPE_SET, 0,
            GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
        if (!ret)
        {
            g_print("Seek failed!\n");
            return FALSE;
        }
        return TRUE;
    }
    case GST_MESSAGE_ERROR:
    {
        GError* err;
        gchar* debug;
        gst_message_parse_error(msg, &err, &debug);
        g_print("Error: %s\n", err->message);
        g_error_free(err);
        g_free(debug);
        g_main_loop_quit(data->loop);
        return FALSE;
    }
    default:
        break;
    }
    return TRUE;
}

gint main(gint argc, gchar** argv)
{
    GMainLoop* main_loop;
    GstElement* playbin;
    GstBus* bus;
    GstStateChangeReturn ret;
    CustomData data;
    if (argc < 2)
    {
        g_print("Usage: %s <URI>\n", argv[0]);
        return EXIT_FAILURE;
    }
    gst_init(&argc, &argv);
    playbin = gst_element_factory_make("playbin", "playbin");

    gchar* uri;
    if (gst_uri_is_valid(argv[1]))
        uri = argv[1];
    else
        uri = gst_filename_to_uri(argv[1], NULL);
    g_object_set(G_OBJECT(playbin), "uri", uri, NULL);
    bus = gst_element_get_bus(playbin);
    ret = gst_element_set_state(playbin, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set the pipeline to the playing state.\n");
        gst_object_unref(playbin);
        return EXIT_FAILURE;
    }
    main_loop = g_main_loop_new(NULL, FALSE);
    data.loop = main_loop;
    data.playbin = playbin;
    gst_bus_add_watch(bus, (GstBusFunc)bus_callback, &data);

    g_main_loop_run(main_loop);
    
    g_main_loop_unref(main_loop);
    gst_element_set_state(playbin, GST_STATE_NULL);
    gst_object_unref(playbin);
    gst_object_unref(bus);
    return EXIT_SUCCESS;
}
Vie answered 11/6, 2024 at 12:21 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Paschal

© 2022 - 2025 — McMap. All rights reserved.