I have this sample application where i create GMainContext and uses it while creating g_main_loop.
GstElement *pipeline;
GstBus *bus;
guint bus_watch_id;
/* init */
gst_init(&argc, &argv);
/* create pipeline, add handler */
pipeline = gst_pipeline_new("my_pipeline");
/* adds a watch for new message on our pipeline's message bus to
* the default GLib main context, which is the main context that our
* GLib main loop is attached to below
*/
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
GstElement *filesrc = gst_element_factory_make("filesrc", "filesrc");
g_assert(filesrc);
g_object_set(filesrc, "location", "sample.wav", NULL);
GstElement *udpsink = gst_element_factory_make("udpsink", "udpsink");
g_assert(udpsink);
g_object_set(udpsink, "port", 6666, NULL);
gst_bin_add_many(GST_BIN(pipeline), filesrc, udpsink, NULL);
gst_element_link_many(filesrc, udpsink, NULL);
gst_bus_add_signal_watch(bus);
g_signal_connect(bus, "message::error", G_CALLBACK(my_bus_callback), NULL);
g_signal_connect(bus, "message::eos", G_CALLBACK(my_bus_callback), NULL);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
GMainContext *context = g_main_context_new();
loop = g_main_loop_new(context, FALSE);
g_main_loop_run(loop);
/* clean up */
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
// g_source_remove (bus_watch_id);
g_main_loop_unref(loop);
With this, I do not get any callback. So g_signal_connect does not work. However, if I don't pass context while creating g_main_loop, it works as expected. How do I make it work with GMainContext?
gst_bus_add_signal_watch works on default main context.
g_main_context_push_thread_default needs to be called in order to use newly created context.
Related
I've got running already a working gstreamer pipeline in an embedded C linux application. The pipeline looks like this:
appsrc-> queue - > h264encode -> queue -> h264parse -> mp4mux -> filesink
The source is a video memory buffer which is pushed into a appscr element using the "need-data" standard method.
The code is similar to the gstreamer examples and looks like this:
static void
cb_need_data (GstElement *appsrc,
guint unused_size,
gpointer user_data)
{
static gboolean white = FALSE;
static GstClockTime timestamp = 0;
GstBuffer *buffer;
guint size;
GstFlowReturn ret;
size = 1024 * 768 * 2;
buffer = gst_buffer_new_and_alloc (size);
/* this makes the image black/white */
memset (buffer->data, white ? 0x55 : 0xaa, size);
white = !white;
GST_BUFFER_TIMESTAMP (buffer) = timestamp;
GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
timestamp += GST_BUFFER_DURATION (buffer);
g_print("push-buffer\n");
//g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
ret = gst_app_src_push_buffer(GST_APP_SRC(appsrc), buffer);
if (ret != GST_FLOW_OK) {
/* something wrong, stop pushing */
g_print("ret fail\n");
g_main_loop_quit (loop);
}
}
gint
main (gint argc,
gchar *argv[])
{
GstElement *pipeline, *appsrc;
/* init GStreamer */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* setup pipeline */
pipeline = gst_parse_launch("appsrc name=mysource ! fakesink silent=0", NULL);
appsrc = gst_bin_get_by_name_recurse_up (GST_BIN (element), "source");
GstCaps *caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_UYVY, 1024, 768, 0, 1, 4, 3);
gst_app_src_set_caps(GST_APP_SRC(appsrc), caps);
/* setup appsrc */
g_object_set (G_OBJECT (appsrc),
"stream-type", 0,
"format", GST_FORMAT_TIME, NULL);
g_signal_connect (appsrc, "need-data", G_CALLBACK (cb_need_data), NULL);
/* play */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print("PLAY\n");
g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
g_main_loop_unref (loop);
return 0;
}
Now I need to run multiple identical pipelines in parallel. Since the pipeline elements will be identical, I'm planning to create a data structure containing all necessary gstreamer elements, like that I should be able to create different instances of the same pipeline type:
appsrc1-> ...... -> filesink1
appsrc2-> ...... -> filesink2
appsrc3-> ...... -> filesink3
.....
The question:
The problem is that I don't know how to instantiate the cb_need_data callback function for each the different appsrc elements, would it be possible to instantiate this function? Or do I need to create a copy for each pipeline? (cb_need_data1, cb_need_data2,...etc )
I have created a program using gstreamer which listens to different ports (say 5) for rtp packets.
Now I have created a class (say GstClass) which creates the pipeline, and has a Callback function which listens to the bus messages (I need this message system to shut down the pipeline after a certain timeout).
The main function looks like this - 2 threads are created with 2 objects and the GstFunc is called in both threads. The first function would listen to port 5000 and the second would listen to port 5008
int main() {
char filepath1[ ]= "/home/rohan/Tornado1.raw";
char filepath2[ ]= "/home/rohan/Tornado2.raw";
unsigned int port1 = 5000;
unsigned int port2 = 5008;
GstClass GstObj1;
GstClass GstObj2;
boost::thread thrd1 { &GstClass::GstFunc, &GstObj1, filepath1, &port1 };
boost::thread thrd2 { &GstClass::GstFunc, &GstObj2, filepath2, &port2 };
thrd1.join();
thrd2.join();
return 0;
}
the class GstClass looks like this -
class GstClass {
protected:
//some other variables...
GMainLoop *msLoop;
public:
gboolean bus_call(GstBus *bus, GstMessage *message,
gpointer data);
void GstFunc(char *filepath, unsigned int *port);
};
For detailed function view please look at this example. Replace the function int main (int argc, char *argv[]) with void GstFunc(char *filepath, unsigned int *port) with appropriate changes.
The GstFunc looks like
void GstFunc(char *filepath, unsigned int *port)
GMainLoop *loop;
GstElement *pipeline, *source, *conv, *sink;
GstBus *bus;
guint bus_watch_id;
gst_init (NULL, NULL);
loop = g_main_loop_new (NULL, FALSE);
/* Create gstreamer elements */
pipeline = gst_pipeline_new ("audio-player");
source = gst_element_factory_make ("autoaudiosrc", "audiosource");
conv = gst_element_factory_make ("audioconvert", "converter");
sink = gst_element_factory_make ("autoaudiosink", "audio-output");
if (!pipeline || !source || !conv || !sink) {
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
/* we add a message handler */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, bus_call, NULL);
gst_object_unref (bus);
gst_bin_add_many (GST_BIN (pipeline), source, conv, sink, NULL);
gst_element_link_many (GST_BIN (pipeline), source, conv, sink, NULL);
g_main_loop_run (loop);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
return 0;
}
Now the dilemma I am facing is with the static function (in example) bus_call(...).
Since I am creating 2 pipelines in 2 different threads which are listening to 2 different ports, I cannot have this function as static (shared between the objects). how can I make these 2 pipelines dis-joint from each other? Or how can I get this static bus_call(...) to become non-static?
simply removing static keyword didn't help and giving this error
error: invalid use of non-static member function ‘gboolean GstClass::bus_call(GstBus*, GstMessage*, gpointer)‘
Few Imp points
I have referred to this document which says To use a bus, attach a message handler to the bus of a pipeline using gst_bus_add_watch()
The gst_bus_add_watch() in the GstClass::GstFunc() (which callbacks the bus_call) is mapped to the header file gstbus.h and the the declaration is simply
GST_API
guint gst_bus_add_watch(GstBus * bus, GstBusFunc func, gpointer user_data);
My initial guess is that the gst_bus_add_watch is expecting the 2nd parameter to be a static function. I am not sure why though. What can be done here?
*********************** Question Edit 2 ***********************
Is it possible to add an argument to the bus_call like gboolean bus_call(GstBus *bus, GstMessage *message,gpointer data,**SOME POINTER TO THE OBJECT**)?
This way the function will remain static while having a pointer to the object calling it, and acting upon the objects (say close pipeline of that object).
I don't think you can get away with what you want. The signature for GstBusFunc(), the callback, is for a pointer to function, not pointer to member function. They're different things. (I've also failed with std::bind, fwiw).
I've done something very similar to what you describe, though not quite the same, but I took a different approach that might help you. You can use a static method, but you must pass a pointer to your pipeline class to gst_bus_add_watch. Inside your busCallback you dereference the pointer and off you go! You may need to implement some kind of locking scheme as well.
class MyPipeline {
GstElement *m_pipeline;
public:
MyPipeline(...);
static void gboolean busCallback(GstBus *bus, GstMessage *msg, gpointer p);
}
MyPipeline::MyPipeline(...)
{
// create pipeline...
m_pipeline = ...;
// bus callback, pass 'this' as arg for callback
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline));
gst_bus_add_watch(bus, &MyPipeline::busCallback, this);
gst_object_unref(bus);
// ...
}
gboolean MyPipeline::busCallback(GstBus *, GstMessage *msg, gpointer p)
{
// get lock if needed...
// recover your class instance
MyPipeline *myPipeline = (MyPipeline *)p;
// do what you need to, free lock
return TRUE;
}
Now I'm trying to play mp4 files.
After trying to play mp4 file to demux, a stream error occurs.
My code is below, please let me know if there are any problems.
Any advises? Thank you
error -> encountered a general stream error
int main(int argc, char* argv[]){
GMainLoop *loop;
GstElement *pipeline, *vsource, *demux, *vqueue, *ssource, *muxer, *filesink;
GstElement *vplay, *conv;
//GstBin *recording;
GstBus *bus;
GstPad *srcpad, *sinkpad;
guint bus_watch_id;
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
pipeline = gst_pipeline_new("Live Recording");
vsource = gst_element_factory_make("filesrc","v-file-source");
demux = gst_element_factory_make("qtdemux","v-file-demux");
vqueue = gst_element_factory_make("queue2","v-file-queue");
vplay = gst_element_factory_make("autovideosink","play");
conv = gst_element_factory_make("videoconvert","converter");
if(!pipeline)
g_print("no pipe\n");
if(!vsource)
g_print("no video source\n");
if(!demux)
g_print("no video demux\n");
if(!vqueue)
g_print("no video queue\n");
if(!vplay)
g_print("no video play\n");
if(!conv)
g_print("no conv");
g_object_set (G_OBJECT (vsource), "location", "./sample.mp4", NULL);
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
gst_object_unref(bus);
gst_bin_add_many(GST_BIN(pipeline),vsource,demux,vqueue, conv, vplay,NULL);
if(!gst_element_link(vsource, demux)){
g_print("no link");
}
if(!gst_element_link_many(vqueue, conv, vplay,NULL)){
g_print("no link many");
}
g_print("now play\n");
g_signal_connect (demux, "pad-added", G_CALLBACK (on_pad_added), vqueue);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_main_loop_run(loop);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
g_source_remove(bus_watch_id);
g_main_loop_unref(loop);
return 0;
}
I am trying a basic pipeline below. If I run the pipeline without the caps function it passes straight through (assuming same I/O format). Once I add the caps the pre-roll starts but the video does not continue through to the sink output.
Am I coding this wrong?
thx
Art
#include <gst/gst.h>
#include <glib.h>
static void
on_pad_added (GstElement *element,
GstPad *pad,
gpointer data)
{
GstPad *sinkpad;
GstElement *decoder = (GstElement *) data;
g_print ("Dynamic pad created, linking out/in \n");
sinkpad = gst_element_get_static_pad (decoder, "sink");
gst_pad_link (pad, sinkpad);
gst_object_unref (sinkpad);
}
int
main (int argc,
char *argv[])
{
GMainLoop *loop;
gboolean link_ok;
GstElement *pipeline, *source, *decoder, *ffcs, *vidsc, *capsfout, *sink;
GstBus *bus;
GstCaps *caps;
/* Initialisation */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* Create gstreamer elements */
pipeline = gst_pipeline_new ("video-player");
source = gst_element_factory_make ("filesrc", "file-source");
decoder = gst_element_factory_make ("decodebin2", "dec-bin2");
ffcs = gst_element_factory_make ("ffmpegcolorspace", "ffcs");
vidsc = gst_element_factory_make ("videoscale", "vidsc");
capsfout = gst_element_factory_make ("capsfilter", "capsfout");
sink = gst_element_factory_make ("filesink", "vidout");
if (!pipeline || !source || !decoder || !ffcs || !vidsc || !capsfout || !sink) {
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
/* we set the input/output filename to the source element */
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
g_object_set (G_OBJECT (sink), "location", argv[2], NULL);
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bin_add_many (GST_BIN (pipeline),
source, decoder, ffcs, vidsc, capsfout, sink, NULL);
/* we link the elements together */
gst_element_link (source, decoder);
gst_element_link (decoder, ffcs);
gst_element_link (ffcs, vidsc);
caps = gst_caps_new_simple("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
"width", G_TYPE_INT, 384,
"height", G_TYPE_INT, 216,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL);
link_ok = gst_element_link_filtered(vidsc,sink,caps);
gst_caps_unref (caps);
if (!link_ok) {
g_warning ("Failed to link vidsc to sink!");
}else{
g_print("seems ok, no error reported?\n");
}
/* Set the pipeline to "playing" state*/
g_print ("Now playing: %s\n", argv[1]);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* Iterate */
g_print ("Running...\n");
g_main_loop_run (loop);
/* Out of the main loop, clean up nicely */
g_print ("Returned, stopping playback\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
g_print ("Deleting pipeline\n");
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}
It may be late to answer but you maybe forgot to do the dynamic pad link between some elements and you never call the on_pad_added function. I had the same problem.
Have you tried to add it since then? For example this line to dynamically link two elements instead of the gst_element_link:
g_signal_connect (decoder, "pad-added", G_CALLBACK (on_pad_added), ffcs);
For what reasons do you need the capsfilter? Only constrain the properties you need. E.g. you set a framerate, but don't have a videorate element before the capsfilter. If you don't care about the framerate (e.g. just want to force a size) remove the framerate from the caps you set on capsfilter..
Following code is written to play a .wav file but it doesn't seem to work.
I would like to know if i am missing something in it.
Code:
#include <gst/gst.h>
#include <glib.h>
int main(int argc , char *argv[])
{
GMainLoop *loop;
GstElement *source,*audioparser,*sink,*pipeline;
GstBus *bus;
gst_init(&argc,&argv);
// create a pipeline
loop = g_main_loop_new (NULL, FALSE);
pipeline = gst_pipeline_new ("wav-player");
source = gst_element_factory_make("filesrc","file-source");
audioparser = gst_element_factory_make("wavparse","wav-parser");
sink = gst_element_factory_make("alsasink","sink1");
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
gst_element_set_state (pipeline, GST_STATE_NULL);
// set location to current sourceg_object_set(G_OBJECT(source),"location",argv[1],NULL);
// add elements to bin
gst_bin_add_many(GST_BIN(pipeline),source,audioparser,sink,NULL);
gst_element_link_many(source,audioparser,sink,NULL);
// create bus
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_main_loop_run (loop);
return 1;
}
Please compile this using following command:
gcc -Wall $(pkg-config --cflags --libs gstreamer-0.10) wav.c -o wavparser
Thanks in advance
Just use playbin2 instead of a hand-crafted pipeline
that is, replace everything from "pipeline = gst_pipeline_new()" to "gst_element_link_many" by:
pipeline = gst_element_factory_make("playbin2", NULL);
g_object_set(pipeline, "uri", "file:///the/file/I/want.wav", NULL);
maybe playbin2 is exactly what you need, but answerring question: wavparse does not have static src-pad, so you must handle "pad-added" signal from this element on runtime. something like the code:
gst_bin_add_many (GST_BIN (pipeline), wavsrc, wavparse,audioconvert, audiosink, NULL);
g_object_set (G_OBJECT (wavsrc), "location", "sound.wav", NULL);
gst_element_link(wavsrc, wavparse);
gst_element_link(audioconvert, audiosink);
g_signal_connect (wavparse, "pad-added", G_CALLBACK (on_pad_added), audioconvert);
where: wavsrc is filesrc, wavparse is wavparse, audioconvert is audioconvert, audiosink is alsasink (i'm not sure alsasink works for you, so you can choose another)
void on_pad_added (GstElement *src_element, GstPad *src_pad, gpointer data)
{
g_print ("Linking dynamic pad...\n");
GstElement *sink_element = (GstElement *) data; // is audioconvert
GstPad *sink_pad = gst_element_get_static_pad (sink_element, "sink");
gst_pad_link (src_pad, sink_pad);
gst_object_unref (sink_pad);
src_element = NULL; // yup, i don't want "unused" warning here
}