I am trying to create a circular buffer with gstreamer in C. Currently the source is my PC webcam 'v4l2src'. Once the buffer list hits the predefined size, I flush the oldest buffer in the list and insert a new buffer. I am using gst_buffer_list to acheive the same. I need to this circular buffer to run continuously and when any call back is received, I copy this buffer list and send it to the another pipeline's appsrc using the emit signal property. Both the pipelines are under below link:
For the testing purposes I am directly written the code that if buffer_length == 50 ---> then create a copy(deep) of the buffer_list and send to pipeline2 through the g_emit_signal(push_buffer_list). I am successfully able to get the buffers(with some warnings that I dont understand why) and save to a file. I can see the filesize also reached few MBs but the file is not playable. When I play the file with ffplay <file_location>, I am able to see 2-3 frames.
I would like to understand if I am using the push_buffer_list signal correctly. What canges can be done to these pipeline2 to make it work properly.
#include <gst/gst.h>
#include<stdio.h>
#include <gst/app/gstappsrc.h>
#include <time.h>
static GstElement *pipeline, *video_src, *video_sink, *videoconvert, *videoencode,*identity, *identity1, *identity2,*queue, *fake_sink, *tee, *muxer;
static GstElement *pipeline2, *appsrc, *file_sink2;
GstBufferList *buflist, *copy_buflist;
static GstPad *blockpad, *probepad,*probepad2, *id2sink_pad, *fakesink_pad;
// GstPad *tee_src1_pad, *tee_src2_pad;
GstPadTemplate *templ;
GMainLoop *loop, *loop2;
GstBuffer *buff;
static gboolean
bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GMainLoop *loop = 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_clear_error (&err);
g_free (dbg);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
g_print ("End-Of-Stream reached.\n");
// g_main_loop_quit (loop2);
// gst_element_set_state (pipeline2, GST_STATE_NULL);
// g_main_loop_unref (loop2);
// gst_object_unref (pipeline2);
exit(0);
break;
}
case GST_MESSAGE_WARNING:{
GError *err = NULL;
gchar *name, *debug = NULL;
name = gst_object_get_path_string (msg->src);
gst_message_parse_warning (msg, &err, &debug);
g_printerr ("ERROR: from element %s: %s\n", name, err->message);
if (debug != NULL)
g_printerr ("Additional debug info:\n%s\n", debug);
g_error_free (err);
g_free (debug);
g_free (name);
break;
}
default:
break;
}
return TRUE;
}
static GstPadProbeReturn
block_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
g_print("in block prode\n");
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
debug_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
g_print("coming in fakesink \n");
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
pad_probe_buflist_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstFlowReturn retval;
g_print("coming in filesink Senfing EOS NOW\n");
// g_signal_emit_by_name (appsrc, "end-of-stream", &retval);
gst_element_send_event(pipeline2, gst_event_new_eos());
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstFlowReturn retval;
static GstClockTime timestamp = 0;
g_print("pointer comes in pad probe\n");
// g_print("probe type %d \n",info->type);
buff = gst_pad_probe_info_get_buffer(info);
// GST_BUFFER_PTS (buff) = timestamp;
// GST_BUFFER_DURATION (buff) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
// timestamp += GST_BUFFER_DURATION (buff);
guint buflen = gst_buffer_list_length (buflist);
g_print("buffer length is %d \n",buflen);
if (buflen==50) // This is custom
{
copy_buflist = gst_buffer_list_copy_deep (buflist);
gst_element_set_state (pipeline2, GST_STATE_PLAYING);
g_signal_emit_by_name (appsrc, "push-buffer-list", copy_buflist, &retval);
//GstFlowReturn retval = gst_app_src_push_buffer_list((GstAppSrc*)appsrc,copy_buflist);
g_print("RETVAL %d\n", retval);
g_signal_emit_by_name (appsrc, "end-of-stream", &retval);
}
// retval = gst_app_src_end_of_stream ((GstAppSrc*)appsrc);
// g_print("RETVAL %d\n", retval);
gst_buffer_list_insert(buflist, buflen,buff);
// gst_buffer_unref (buff);
// gst_buffer (copy_buflist);
return GST_PAD_PROBE_OK;
}
int tutorial_main(int argc, char *argv[])
{
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret,ret2;
GstStateChangeReturn sret;
GstStateChangeReturn pause_flag =0;
GstState state;
GstPad *srcpad;
gst_init (&argc, &argv);
video_src = gst_element_factory_make ("v4l2src", "source");
appsrc = gst_element_factory_make ("appsrc", "source");
videoconvert = gst_element_factory_make("videoconvert","convert");
videoencode = gst_element_factory_make("x264enc","encode");
video_sink = gst_element_factory_make ("filesink", "sink");
fake_sink = gst_element_factory_make ("fakesink", "fakesink");
identity1 = gst_element_factory_make ("identity", "identity1");
identity2 = gst_element_factory_make ("identity", "identity2");
muxer = gst_element_factory_make("flvmux", "muxer");
queue = gst_element_factory_make ("queue", "queue");
tee = gst_element_factory_make("tee","tee");
file_sink2 = gst_element_factory_make ("filesink", "filesink");
pipeline = gst_pipeline_new ("test-pipeline");
pipeline2 = gst_pipeline_new ("filesink-pipeline");
g_object_set(file_sink2,"location","/home/jafar/gps.mp4",NULL); /// YOU NEED TO UPDATE THE FILE LOCATION
if (!pipeline || !video_src || !video_sink ||!videoconvert ||!videoencode ||!identity1 ||!identity2 ||!fake_sink ) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
//Pipeline1 v4l2---->createbuffer---->fakesink
gst_bin_add_many (GST_BIN (pipeline), video_src,videoconvert,videoencode,identity1,identity2,fake_sink, NULL);
if ((gst_element_link_many(video_src,videoconvert,videoencode,identity1, identity2, fake_sink, NULL) != TRUE )) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
//pipeline2 buffer--->appsrc---->filesink
gst_bin_add_many (GST_BIN (pipeline2), appsrc,file_sink2, NULL);
if ((gst_element_link_many(appsrc,file_sink2, NULL) != TRUE )) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline2);
return -1;
}
fakesink_pad = gst_element_get_static_pad(fake_sink, "sink");
id2sink_pad = gst_element_get_static_pad(identity2, "sink");
//Get forurce pad
probepad = gst_element_get_static_pad (identity1, "src");
probepad2 = gst_element_get_static_pad (file_sink2, "sink");
buflist = gst_buffer_list_new(); //init bufffer list
//install a probe on identity1 src pad
gst_pad_add_probe (probepad, GST_PAD_PROBE_TYPE_BUFFER, pad_probe_cb, loop, NULL);
g_print("COde blocks here!!!\n");
time_t now = time(NULL);
/* Start playing */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
ret2 = gst_element_set_state (pipeline2, GST_STATE_PAUSED);
bus = gst_element_get_bus (pipeline);
loop = g_main_loop_new (NULL, FALSE);
loop2 = g_main_loop_new (NULL, FALSE);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, loop);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline2), bus_cb, loop2);
// g_timeout_add_seconds (20, timeout_cb, loop);
g_main_loop_run (loop);
g_main_loop_run (loop2);
/* Free resources */
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_element_set_state (pipeline2, GST_STATE_NULL);
gst_object_unref (blockpad);
gst_object_unref (probepad);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline);
gst_object_unref (pipeline2);
g_main_loop_unref (loop);
g_main_loop_unref (loop2);
return 0;
}
int
main (int argc, char *argv[])
{
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
return gst_macos_main (tutorial_main, argc, argv, NULL);
#else
return tutorial_main (argc, argv);
#endif
}
To compile the code execute below:
gcc buffer-tutorial-2.c -o buffer-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-audio-1.0` -lgstapp-1.0
The output of the code is under this link: Code output
Looks like the answer here was to find out the format of the video stream through the
typefindfunctionality provided byGstreamerand set thecapsof theappsrcelement accordingly in the second pipeline. Also instead of using theg_signal_emit_by_name (appsrc, "push-buffer-list", copy_buflist, &retval)I now usedGstFlowReturn retval = gst_app_src_push_buffer_list((GstAppSrc*)appsrc,copy_buflist). The second fucntion is provided specifically by the appsrc API in the gstreamer docs. I was assuming that these both functions would behave same but I dont know what is the difference. This needs to be looked in the Gst source Code. Also an interesting point to note is when sending an EOS withgst_pad_send_event(appsrccpad, gst_event_new_eos())the video creation failed however, If I sent the EOS with thisg_signal_emit_by_name (appsrc, "end-of-stream", &retval)I got the resultant video from the buffer list. I am posting the above refactored and corrected code here. There are many extra variables and no unrefs are used, dont mind them.The code can be compiled and executed with the same command as described in the question.