Get gstreamer bus messages using non-static message handler - c++

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;
}

Related

gstreamer callback not working GMainContext

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.

How to run multiple pipelines with appsrc in Gstreamer?

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 )

Make h264 source seekable

I have a h.264 stream that I’m trying to make it seakable
Once the stream is ended video is recorded however by default, it won't be seekable. I know by making the source streamable it disables seekability however I was wondering if there’s a way to inject index info at the end of the stream or is there a anyway to make the video seekable in some other way?
How I'm setting up GSTBus:
eosBus = gst_element_get_bus(pipeline);
gst_bus_add_signal_watch (eosBus);
g_signal_connect (eosBus, "message", (GCallback) message_cb, pipeline);
The way I'm handling EOS:
static void gst_native_stop_recording (JNIEnv* env, jobject thiz) {
gst_element_send_event(pipeline, gst_event_new_eos());
gst_element_set_state (pipeline, GST_STATE_PAUSED);
//unlink elements
Here's how I setup the calback message function (which is never triggered)
static void message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
{
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS: {
GST_DEBUG("Got EOS\n");
break;
}
default:
break;
}
}

Gstreamer EOS message handling in filesink to change location on the fly

Trying to switch output files on the fly, but can't handle EOS.
http://gstreamer-devel.966125.n4.nabble.com/Dynamically-updating-filesink-location-at-run-time-on-the-fly-td4660569.html
Quote:
Assuming you have a pipeline that looks like this:
audiosrc --> encoder --> mux --> filesink
then you'll need to change it to:
audiosrc --> encoder --> queue --> muxsink_bin
where muxsink_bin is a bin
ghostpad --> mux --> filesink
then the procedure is:
1 - Block the queue srcpad using gst_pad_set_blocked_async()
2 - In the blocked callback:
2a - unlink muxsink_bin with gst_pad_unlink()
2b - send an EOS event to the muxsink_bin sink pad with gst_pad_send_event()
2b - create a new muxsink_bin
2c - set filesink location
2d - add the new bin to the pipeline with gst_bin_add()
2e - sync with parent using gst_element_sync_state_with_parent()
2f - link it to the queue srcpad with gst_pad_link()
2g - unblock the queue srcpad with gst_pad_set_blocked_async(). When the unblocked callback occurs you're recording again & no data has been lost. No action is required in the unblocked callback
3 - handle the EOS & delete the old muxsink_bin. I had a msg handler that I installed in my bin_init() function using "gstbin_class->handle_message = GST_DEBUG_FUNCPTR(msg_handler)" & in the handler:
3a - lock the bin state with gst_element_set_locked_state()
3b - set the state to NULL with gst_element_set_state()
3c - remove it from the pipeline with gst_bin_remove()
That's it. The only thing to be mindful of is that data must be flowing thru the pipeline for this to work.
Paddy
The main sequence works except for the finalization of the old pipeline.
The difficulty is with the point 3: I can send EOS to the ghostpad, and the filesink gets it. But how to catch that EOS?
What does it mean "install msg handler using gstbin_class->handle_message = GST_DEBUG_FUNCPTR(msg_handler)"?
There is message forwarding.
Must be enabled on the bus:
g_object_set(G_OBJECT(bin), "message-forward", TRUE, 0);
Handling:
case GST_MESSAGE_ELEMENT:
{
const GstStructure *s = gst_message_get_structure (msg);
if (gst_structure_has_name (s, "GstBinForwarded"))
{
GstMessage *forward_msg = NULL;
gst_structure_get (s, "message", GST_TYPE_MESSAGE, &forward_msg, NULL);
if (GST_MESSAGE_TYPE (forward_msg) == GST_MESSAGE_EOS)
{
g_print ("EOS from element %s\n",
GST_OBJECT_NAME (GST_MESSAGE_SRC (forward_msg)));
DestroyBin();
CreateNewBin();
RemovePad();
}
gst_message_unref (forward_msg);
}
}
Full code:
#include <gst/gst.h>
#include <iostream>
#include <cstring>
#include <cstdio>
static gchar *opt_effects = NULL;
#define DEFAULT_EFFECTS "identity,exclusion,navigationtest," \
"agingtv,videoflip,vertigotv,gaussianblur,shagadelictv,edgetv"
static GstElement *pipeline;
static GstElement * muxer;
static GstElement * sink;
static GstElement * q2;
static int i=0;
GstElement * bin;
GstPad * muxerSinkPad;
gulong probeId;
static GQueue effects = G_QUEUE_INIT;
void CreateNewBin();
void DestroyBin();
void ChangeLocation();
void RemovePad();
static GstPadProbeReturn
pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstPad *sinkPad = gst_element_get_static_pad(bin, "sink");
gst_pad_unlink(pad, sinkPad);
gst_pad_send_event(sinkPad, gst_event_new_eos());
gst_object_unref(sinkPad);
return GST_PAD_PROBE_OK;
}
static gboolean
timeout_cb (gpointer user_data)
{
static int i=0;
if(i==0)
{
GstPad * q2SrcPad;
q2SrcPad = gst_element_get_static_pad(q2, "src");
std::cout << "Timeout: " << q2SrcPad << std::endl;
probeId = gst_pad_add_probe (q2SrcPad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
pad_probe_cb, user_data, NULL);
gst_object_unref(q2SrcPad);
return TRUE;
}
return FALSE;
}
static gboolean
bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GMainLoop *loop = (GMainLoop*)user_data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
gst_object_default_error (msg->src, err, dbg);
g_error_free (err);
g_free (dbg);
g_main_loop_quit (loop);
break;
}
case GST_EVENT_EOS:
std::cout << "EOS message is got" << std::endl;
break;
case GST_MESSAGE_ELEMENT:
{
const GstStructure *s = gst_message_get_structure (msg);
if (gst_structure_has_name (s, "GstBinForwarded"))
{
GstMessage *forward_msg = NULL;
gst_structure_get (s, "message", GST_TYPE_MESSAGE, &forward_msg, NULL);
if (GST_MESSAGE_TYPE (forward_msg) == GST_MESSAGE_EOS)
{
g_print ("EOS from element %s\n",
GST_OBJECT_NAME (GST_MESSAGE_SRC (forward_msg)));
DestroyBin();
CreateNewBin();
RemovePad();
}
gst_message_unref (forward_msg);
}
}
break;
default:
break;
}
return TRUE;
}
int
main (int argc, char **argv)
{
GError *err = NULL;
GMainLoop *loop;
GstElement *src, *q1,/* *q2,*/ /**effect,*/ /**filter1*//*, *filter2*/ *encoder;/*, *sink*/;
gst_init(&argc, &argv);
pipeline = gst_pipeline_new ("pipeline");
src = gst_element_factory_make ("videotestsrc", NULL);
//Create a caps filter between videosource videoconvert
std::string capsString = "video/x-raw,format=YV12,width=320,height=240,framerate=30/1";
GstCaps * dataFilter = gst_caps_from_string(capsString.c_str());
q1 = gst_element_factory_make ("queue", NULL);
encoder = gst_element_factory_make ("x264enc", NULL);
q2 = gst_element_factory_make("queue", NULL);
gst_bin_add_many(GST_BIN(pipeline), src, q1, encoder, q2, 0);
gboolean link = gst_element_link_filtered(src, q1, dataFilter);
link &= gst_element_link(q1, encoder);
link &= gst_element_link(encoder, q2);
CreateNewBin();
gst_element_set_state (pipeline, GST_STATE_PLAYING);
loop = g_main_loop_new (NULL, FALSE);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, loop);
g_timeout_add_seconds (10, timeout_cb, loop);
g_main_loop_run (loop);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return 0;
}
void RemovePad()
{
GstPad * q2SrcPad;
q2SrcPad = gst_element_get_static_pad(q2, "src");
gst_pad_remove_probe(q2SrcPad, probeId);
gst_object_unref(q2SrcPad);
}
void DestroyBin()
{
gst_element_set_state(bin, GST_STATE_NULL);
gst_bin_remove(GST_BIN(pipeline), bin);
}
void CreateNewBin()
{
static std::string fileLocPattern = "deneme%d.mkv";
char buffer[12];
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, fileLocPattern.c_str(), i++);
//Create Muxer Element
muxer = gst_element_factory_make("matroskamux", "MatroskaMuxer");
//Create File Sink Element
sink = gst_element_factory_make("filesink", buffer);
g_object_set(G_OBJECT(sink), "location", buffer, 0);
//Create muxsinkBin
bin = gst_bin_new(buffer);
g_object_set(G_OBJECT(bin), "message-forward", TRUE, 0);
//Add a src pad to the bin
gst_bin_add_many(GST_BIN(bin), muxer, sink, 0);
gboolean linkState = TRUE;
//Connect elements within muxsink_bin
//Link: matroskamuxer -> filesink
linkState &= gst_element_link_many(muxer, sink, 0);
//Add this bin to pipeline
gst_bin_add(GST_BIN(pipeline), bin);
//Create ghostpad and manually link muxsinkBin and remaining part of the pipeline
{
GstPadTemplate * muxerSinkPadTemplate;
if( !(muxerSinkPadTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(muxer), "video_%u")) )
{
std::cout << "Unable to get source pad template from muxing element" << std::endl;
}
//Obtain dynamic pad from element
muxerSinkPad = gst_element_request_pad(muxer, muxerSinkPadTemplate, 0, 0);
//Add ghostpad
GstPad * ghostPad = gst_ghost_pad_new("sink", muxerSinkPad);
gst_element_add_pad(bin, ghostPad);
gst_object_unref(GST_OBJECT(muxerSinkPad));
gst_element_sync_state_with_parent(bin);
//Get src pad from queue element
GstPad * queueBeforeBinSrcPad = gst_element_get_static_pad(q2, "src");
//Link queuebeforebin to ghostpad
if (gst_pad_link(queueBeforeBinSrcPad, ghostPad) != GST_PAD_LINK_OK )
{
std::cout << "QueueBeforeBin cannot be linked to MuxerSinkPad." << std::endl;
}
gst_object_unref(queueBeforeBinSrcPad);
}
}
http://gstreamer-devel.966125.n4.nabble.com/Listening-on-EOS-events-for-GstBin-td4669126.html
http://gstreamer-devel.966125.n4.nabble.com/file/n4669476/main.cpp
Depending on your use case you can use multifilesink element. It will switch files on the fly on certain events. A file for each buffer, a file for each segment... Check its properties and see if there is anything that would work for you.
It also serves as a good code base in case you want to write something similar (or maybe extend it?)
I'll post the code of actual custom GstBin aka 'muxsink_bin' that I ended up implementing to do that forwarding and EOS handling for the 'detachable sink part' of the pipeline.
plisolatedbin.h:
#pragma once
#include <gst/gst.h>
#include <gst/gstbin.h>
G_BEGIN_DECLS
#define PL_TYPE_ISOLATED_BIN (pl_isolated_bin_get_type ())
#define PL_IS_ISOLATED_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PL_TYPE_ISOLATED_BIN))
#define PL_IS_ISOLATED_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PL_TYPE_ISOLATED_BIN))
#define PL_ISOLATED_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PL_TYPE_ISOLATED_BIN, PlIsolatedBinClass))
#define PL_ISOLATED_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PL_TYPE_ISOLATED_BIN, PlIsolatedBin))
#define PL_ISOLATED_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PL_TYPE_ISOLATED_BIN, PlIsolatedBinClass))
#define PL_ISOLATED_BIN_CAST(obj) ((PlIsolatedBin*)(obj))
typedef struct _PlIsolatedBin PlIsolatedBin;
typedef struct _PlIsolatedBinClass PlIsolatedBinClass;
/**
* Does not forward EOS to parent by default.
*/
struct _PlIsolatedBin
{
GstBin bin;
};
struct _PlIsolatedBinClass
{
GstBinClass parent_class;
};
GType pl_isolated_bin_get_type();
GstElement* pl_isolated_bin_new();
G_END_DECLS
plisolatedbin.c:
#include "plisolatedbin.h"
#include <assert.h>
G_DEFINE_TYPE(PlIsolatedBin, pl_isolated_bin, GST_TYPE_BIN)
static void pl_isolated_bin_init(PlIsolatedBin *plisolatedbin)
{
}
static void pl_isolated_bin_handle_message_func(GstBin *bin, GstMessage *message)
{
if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_EOS)
{
GST_BIN_CLASS(pl_isolated_bin_parent_class)->handle_message(bin, message);
}
else
{
GstMessage *forwarded = gst_message_new_element(GST_OBJECT_CAST(bin), gst_structure_new("PlIsolatedBinForwarded", "message", GST_TYPE_MESSAGE, message, NULL));
gst_element_post_message(GST_ELEMENT_CAST(bin), forwarded);
}
}
static void pl_isolated_bin_class_init(PlIsolatedBinClass *class)
{
class->parent_class.handle_message = GST_DEBUG_FUNCPTR(pl_isolated_bin_handle_message_func);
}
GstElement* pl_isolated_bin_new()
{
return g_object_new(PL_TYPE_ISOLATED_BIN, NULL);
}

Pushing images into a gstreamer pipeline

After playing around with some toy applications, exploring the
documentation and googling around (including the mailing list
archives) I am still puzzled for what I would think is a rather common
use case.
I have an existing code that generates images (in memory) and I would
like to push these images into a gstreamer pipeline (to create a flv
video at the end).
I could not find an "obvious way to do it". My best guess will be to
dig in the source code of GstMultiFileSrc and its parent GstPushSrc,
to figure it out.
Could any of you point me out to the "obvious way" of doing this ?
Is it there any related piece of documentation/tutorial/example on this ?
Once I have the input right, the rest is a piece of cake, thanks to
Gstreamer awesomeness !
(something like "my magic input -> ffmpegcolorspace ! ffenc_flv !
flvmux ! filesink location=desktop.flv" )
Thanks for your answers.
GStreamer uses plugins to do everything. Plugins that create data or take it from an external source are called "src" plugins.
The generic src plugin for injecting application-generated data into a pipeline is called appsrc. The API provided by appsrc is documented as part of the App Library.
Here's one example that demonstrates feeding appsrc with generated images: gdk-gstappsrc-stream.c. It seems to be derived from some test code in the GStreamer source tree: here.
Another approach would be to create your own src plugin. Look at the goom music visualization plugin for an example that seems to work in a way similar to what you have specified.
I found a solution (maybe) to this (i get the images with OpenCV) ... but i have an error with the pipeline: ERROR from element mysource: Error en el flujo de datos interno.
Debugging info: gstbasesrc.c(2574): gst_base_src_loop (): /GstPipeline:pipeline0/GstAppSrc:mysource:
streaming task paused, reason not-negotiated (-4)
this is the code:
typedef struct _App App;
struct _App{
GstElement *pipeline;
GstElement *appsrc;
GMainLoop *loop;
guint sourceid;
GTimer *timer;
};
App s_app;
CvCapture *capture;
static gboolean read_data(App *app){
GstFlowReturn ret;
GstBuffer *buffer = gst_buffer_new();
IplImage* frame = cvQueryFrame(capture);
GST_BUFFER_DATA(buffer) = (uchar*)frame->imageData;
GST_BUFFER_SIZE(buffer) = frame->width*frame->height*sizeof(uchar*);
g_signal_emit_by_name(app->appsrc,"push-buffer",buffer,&ret);
gst_buffer_unref(buffer);
if(ret != GST_FLOW_OK){
GST_DEBUG("Error al alimentar buffer");
return FALSE;
}
return TRUE;
}
static void start_feed(GstElement* pipeline,guint size, App* app){
if(app->sourceid == 0){
GST_DEBUG("Alimentando");
app->sourceid = g_idle_add((GSourceFunc) read_data, app);
}
}
static void stop_feed(GstElement* pipeline, App* app){
if(app->sourceid !=0 ){
GST_DEBUG("Stop feeding");
g_source_remove(app->sourceid);
app->sourceid = 0;
}
}
static gboolean
bus_message (GstBus * bus, GstMessage * message, App * app)
{
GST_DEBUG ("got message %s",
gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR: {
GError *err = NULL;
gchar *dbg_info = NULL;
gst_message_parse_error (message, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (message->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
g_main_loop_quit (app->loop);
break;
}
case GST_MESSAGE_EOS:
g_main_loop_quit (app->loop);
break;
default:
break;
}
return TRUE;
}
int main(int argc, char* argv[]){
App *app = &s_app;
GError *error = NULL;
GstBus *bus;
GstCaps *caps;
capture = cvCaptureFromCAM(0);
gst_init(&argc,&argv);
/* create a mainloop to get messages and to handle the idle handler that will
* feed data to appsrc. */
app->loop = g_main_loop_new (NULL, TRUE);
app->timer = g_timer_new();
app->pipeline = gst_parse_launch("appsrc name=mysource ! video/x-raw-rgb,width=640,height=480,bpp=24,depth=24 ! ffmpegcolorspace ! videoscale method=1 ! theoraenc bitrate=150 ! tcpserversink host=127.0.0.1 port=5000", NULL);
g_assert (app->pipeline);
bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
g_assert(bus);
/* add watch for messages */
gst_bus_add_watch (bus, (GstBusFunc) bus_message, app);
/* get the appsrc */
app->appsrc = gst_bin_get_by_name (GST_BIN(app->pipeline), "mysource");
g_assert(app->appsrc);
g_assert(GST_IS_APP_SRC(app->appsrc));
g_signal_connect (app->appsrc, "need-data", G_CALLBACK (start_feed), app);
g_signal_connect (app->appsrc, "enough-data", G_CALLBACK (stop_feed), app);
/* set the caps on the source */
caps = gst_caps_new_simple ("video/x-raw-rgb",
"bpp",G_TYPE_INT,24,
"depth",G_TYPE_INT,24,
"width", G_TYPE_INT, 640,
"height", G_TYPE_INT, 480,
NULL);
gst_app_src_set_caps(GST_APP_SRC(app->appsrc), caps);
/* go to playing and wait in a mainloop. */
gst_element_set_state (app->pipeline, GST_STATE_PLAYING);
/* this mainloop is stopped when we receive an error or EOS */
g_main_loop_run (app->loop);
GST_DEBUG ("stopping");
gst_element_set_state (app->pipeline, GST_STATE_NULL);
gst_object_unref (bus);
g_main_loop_unref (app->loop);
cvReleaseCapture(&capture);
return 0;
}
Any idea???
You might try hacking imagefreeze to do what you want. appsrc might also do it.