I'm new to GStreamer, I followed the Basic tutorial 2 on GStreamer website and try to make it work with local mp4 file.
My problem is that I can't link "uridecodebin" and "autovideosink", here is my related code :
GstElement *pipeline, *source, *sink;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Create the elements */
source = gst_element_factory_make ("uridecodebin", "source");
sink = gst_element_factory_make ("autovideosink", "sink");
/* Create the empty pipeline */
pipeline = gst_pipeline_new ("pipeline");
if (!pipeline || !source || !sink) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
if (gst_element_link (source, sink) != TRUE) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
It always return false with gst_element_link (source, sink), but it worked well if I simply use gst-launch-1.0 uridecodebin uri=file://MyPathToVideo/test.mp4 ! autovideosink command, what am I doing wrong?
Lots of thanks.
Try using gst_parse_launch() and giving it your pipeline. It is shorter this way. And I believe it takes care of some particularities in your case.
The main issue your approach is not working is that uridecodebin does not expose any pads because at that point in time it does not know anything about your MP4 file. So it can contain audio, video, both - or whatever. The correct approach is to implement delayed linking.
So instead of linking it directly you implement the pad-added signal on uridecodebin:
https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html?gi-language=c#GstElement::pad-added
Then you start the pipeline with the elements disconnected.
This pad-added signal is triggered when uridecodebin has scanned your media file and exposes pads which can be linked. In case it is your video pad you can connect it to the autovideosink.
gst_parse_launch() if I'm not mistaken will take of this automatically for you (at least that is what gst-lauch-1.0 is doing - not sure if that specific functionality moved to that API as well).
P.S. You jumped the gun. Tutorial 2 does not use uridecodebin but more basic elements. Tutorial 3 will cover dynamic pipelines.
Related
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?
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.
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.
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.
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.
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
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...
...
I am trying to play my first video in GSTreamer, by using GstElement, without pre-configured things like gst_parse_launch etc
I dont understand why my pipeline cant be linked and I get an error "unable to set the pipeline to playing state" ?
How can I fix it? What is missed?
#include <iostream>
#include <gst/gst.h>
int main(int argc, char *argv[])
{
GstElement *pipeline;
GstElement *source, *sink;
gst_init(&argc, &argv); //! Initialize GStreamer
pipeline = gst_pipeline_new("my-pipeline"); //! Creating pipeline
source = gst_element_factory_make("filesrc", "file-source"); //! Creating source
g_object_set(G_OBJECT(source), "location", "file:///D:/workspace/rocket.mp4", NULL);
sink = gst_element_factory_make("autovideosink", "sink"); //! Creating sink
if (sink == NULL)
{
g_error("Could not create neither 'autovideosink' element");
}
gst_bin_add_many(GST_BIN(pipeline), source, sink, NULL); //! Adding elements to pipeline container
if (!gst_element_link_many(source, sink, NULL)) //! Linking all elements together
{
g_warning("Unable to link elements!");
}
auto ret = gst_element_set_state(pipeline, GST_STATE_PLAYING); //! Turning pipeline in PLAYING STATE
if (ret == GST_STATE_CHANGE_FAILURE)
{
g_printerr("unable to set the pipeline to playing state");
gst_object_unref(pipeline);
return -1;
}
return 0;
}
Thanks in advance!
Try to change "file:///D:/workspace/rocket.mp4" to "D:/workspace/rocket.mp4".
And also maybe you need to change gst_element_link_many(source, sink, NULL) to gst_element_link(source, sink).
Here is an answer
First of all, here is github link to my solution with comments.
Explanation:
If you want to play video file, without predefined pipelines, like playbin, gst_parse_launch etc, you have two options
Dynamically link uridecodebin with 2 pipeline sleeves (one for audio and second for video)
Dynamically link by using avidemux and make it on 'atomic' level. Its very complicated, and there is no working help in net. Maybe I will update this branch later with working solution.
In my github link, you will find first solution with 2 pipelines and uridecodebin
I want to read a file and playback. very simple.
*windows10, visual studio 2017 community.
in command prompt, this is working correctly.
gst-launch-1.0 filesrc location="C:/test.webm" ! decodebin ! autovideosink
but my code is not working, this.
int main(int argc, char *argv[]) {
..... declare variable
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Create the elements */
source = gst_element_factory_make ("filesrc", "source");
decode = gst_element_factory_make("decodebin", "decode");
sink = gst_element_factory_make ("autovideosink", "sink");
pipeline = gst_pipeline_new ("test-pipeline");
gst_bin_add_many (GST_BIN (pipeline), source, decode, sink, NULL);
g_object_set (G_OBJECT(source), "location", "C:/test.webm", NULL);
bus = gst_element_get_bus (pipeline);
... error processing
}
and my error is this
Error received from element sink-actual-sink-d3dvideo: Output window was closed
Debugging information: ../sys/d3dvideosink/d3dhelpers.c(1911): d3d_render_buffer (): /GstPipeline:test-pipeline/GstAutoVideoSink:sink/GstD3DVideoSink:sink-actual-sink-d3dvideo
please help me what is my problem.
my code is almost same to official tutorial. I just changed videotestsrc to filesrc, add the decodebin between source and sink, set the property for giving media file location
I have constructed a custom GStreamer pipeline that I will use to play RTSP streams. At the same time I'd like to create a new GstPlayer to use this pipeline. The problem is that there isn't a way that I can see to set a GstPlayer's pipeline (the only related method is gst_player_get_pipeline(). I don't understand how there is no way to customize a pipeline for a GstPlayer. This seems like basic functionality, so I must be missing something.
My pipeline:
GstElement *pipeline, *source, *filter, *sink;
// Create pipeline elements
pipeline = gst_pipeline_new ("vdi-pipeline");
source = gst_element_factory_make ("rtspsrc", "vdi-source");
filter = gst_element_factory_make ("decodebin", "vdi-filter");
sink = gst_element_factory_make ("appsink", "vdi-sink");
if (!source || !filter || !sink)
{
__android_log_print (ANDROID_LOG_ERROR, "Error", "A GstElement could not be created. Exiting.");
return;
}
// Add elements to pipeline
gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL);
// Link elements together
if (!gst_element_link_many (source, filter, sink, NULL)) {
__android_log_print (ANDROID_LOG_ERROR, "Warning", "Failed to link elements!");
}
But you can play rtsp via GstPlayer out of the box.. why do you want custom pipeline?
The player is using playbin which accept any kind of url.. and it will create pipeline dynamically according to what is being played..
What about patching the player itself, if you really cannot use playbin? I dont think it is intended for custom pipelines.. but you can hack it here.
You will then have hook the newpads and other callback on the rtspsrc instead of playbin.. and other stuff - I guess you do not want this.
The other way is - when the playbin constructs pipeline it uses rtspsrc inside - you can get this element from pipeline object and change some parameters.. but be carefull as changing parameters during playback is very tricky..
UPDATE:
Hm I think I overlook the appsink somehow.. well I think you can set playbin property audio-sink or video-sink to override it to use appsink.
But still you will have to somehow get the playbin element out of GstPlayer or set the playbin parameter upon initialization (I dont know how) - in this case I would ask on IRC (freenode, #gstreamer) if you are going the right direction.
Maybe better way would be to create your own application using decodebin and or even playbin and pass there the appsink element.. why do you want to use GstPlayer if you are not playing but processing buffers?
HTH
I'm currently working on a gstreamer pipeline that begins with a uridecodebin that opens a png file and that I hope to eventually link to an imagefreeze element (although in the future I may want to link it to any arbitrary element). I've connected to the "pad-added" signal, but it appears that uridecodebin doesn't ever actually create the pad.
After looking closely at the logs, it appears that it successfully opens the png file and links the filesrc to the decodebin, but there doesn't appear to be any pad created (the callback is never called, and when I iterate over the src pads of the uridecodebin after the file is opened, there are none). Does anyone know why this might be the case?
Unfortunately it's part of a much larger codebase so I can't share the full code but I can give excerpts of the relevant samples:
GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
GstElement *imagefreeze = gst_element_factory_make ("imagefreeze", NULL);
GstElement *sink = gst_element_factory_make ("fakesink", NULL);
g_object_set (G_OBJECT (uridecodebin), "uri"
"file:///test.png", NULL);
g_signal_connect (uridecodebin, "pad-added",
G_CALLBACK (uri_pad_added_cb), NULL);
gst_bin_add_many (GST_BIN (bin), uridecodebin, imagefreeze, sink, NULL);
gst_element_link (imagefreeze, sink);
And then, the callback (at this point, just a stub):
static void
uri_pad_added_cb (GstElement * element, GstPad * pad, gpointer data)
{
GST_WARNING ("uri_pad_added_cb");
}