Skip to content

Instantly share code, notes, and snippets.

@groakat
Last active December 20, 2015 18:49
Show Gist options
  • Save groakat/6179024 to your computer and use it in GitHub Desktop.
Save groakat/6179024 to your computer and use it in GitHub Desktop.
Complementary file to Gstreamer bug #705531 (https://bugzilla.gnome.org/show_bug.cgi?id=705531) See first comment for complete description.
#include <string.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gdk/gdk.h>
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#elif defined (GDK_WINDOWING_WIN32)
#include <gdk/gdkwin32.h>
#elif defined (GDK_WINDOWING_QUARTZ)
#include <gdk/gdkquartz.h>
#endif
/* TODOS FOR TESTERS OF BUG */
/* SET MODE OF RECYCLING OF RECBINS
* define to recycle the recBins, comment out to
* replace the recBins with fresh factory made ones
*/
#define TEST_RECYCLE
/* SET LOCATION OF FILESINK
* go to line approx. 185 in function updateFilesinkLocation() and replace
* the file location
*/
/* TO SWAP TO NEXT FILE LOCATION CLICK PAUSE BUTTON */
/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {
gint32 cnt;
GstElement *main; /* pipeline */
GstElement *src; /*("uvch264src", "src") */
GstElement *prevQueue; /*("queue", "prevQueue") */
GstElement *vfcaps; /*("capsfilter", "vfcaps") */
GstElement *preview_sink; /*( "autovideosink", "previewsink") */
GstElement *vidQueue; /*( "queue", "vidQueue") */
GstElement *vidcaps; /*("capsfilter", "vidcaps") */
GstElement *t; /*( "tee", "t") */
GstElement *srcQueue; /*( "queue", "srcQueue") */
GstElement *recBin1; /*"recoding bin 1") */
GstElement *fileQueue1; /*( "queue", "fileQueue1") */
GstElement *ph264_1; /* ("h264parse", "ph264_1") */
GstElement *mux_1; /*("mp4mux", "mux1") */
GstElement *filesink1; /*( "filesink", "filesink1") */
GstElement *recBin2; /*("recoding bin 2") */
GstElement *fileQueue2; /*( "queue", "fileQueue2") */
GstElement *ph264_2; /* ("h264parse", "ph264_2") */
GstElement *mux_2; /*("mp4mux", "mux2") */
GstElement *filesink2; /*( "filesink", "filesink2") */
} CustomData;
static void createElements(CustomData *data){
data-> cnt = 0;
data->main = gst_pipeline_new("main");
data->src = gst_element_factory_make("uvch264src", "src");
data->prevQueue = gst_element_factory_make("queue", "prevQueue");
data->vfcaps = gst_element_factory_make("capsfilter", "vfcaps");
data->preview_sink = gst_element_factory_make( "autovideosink", "previewsink");
data->vidQueue = gst_element_factory_make( "queue", "vidQueue");
data->vidcaps = gst_element_factory_make("capsfilter", "vidcaps");
data->t = gst_element_factory_make( "tee", "t");
data->srcQueue = gst_element_factory_make( "queue", "srcQueue");
}
static void linkMainPipeline(CustomData *data){
gst_bin_add(GST_BIN (data->main), data->src);
gst_bin_add(GST_BIN (data->main), data->prevQueue);
gst_bin_add(GST_BIN (data->main), data->vfcaps);
gst_bin_add(GST_BIN (data->main), data->preview_sink);
gst_bin_add(GST_BIN (data->main), data->vidQueue);
gst_bin_add(GST_BIN (data->main), data->vidcaps);
gst_bin_add(GST_BIN (data->main), data->t);
gst_bin_add(GST_BIN (data->main), data->srcQueue);
/* self.log.debug("link self.elements in main pipeline"); */
/* self.log.debug("1. linking preview branch..."); */
GstPad *srcP2 = gst_element_get_static_pad(data->src,"vfsrc");
GstPad *tP2 = gst_element_get_static_pad(data->prevQueue, "sink");
gst_pad_link(srcP2, tP2);
gst_object_unref (GST_OBJECT (srcP2));
gst_object_unref (GST_OBJECT (tP2));
gst_element_link(data->prevQueue, data->vfcaps);
gst_element_link(data->vfcaps, data->preview_sink);
/* self.log.debug("2. linking H264 branch until tee...") */
GstPad *srcP = gst_element_get_static_pad(data->src, "vidsrc");
GstPad *tP = gst_element_get_static_pad(data->vidQueue, "sink");
gst_pad_link(srcP, tP);
gst_object_unref (GST_OBJECT (srcP));
gst_object_unref (GST_OBJECT (tP));
gst_element_link(data->vidQueue, data->vidcaps);
gst_element_link(data->vidcaps, data->t);
}
static void linkRecBin1(CustomData *data){
data->recBin1 = gst_bin_new("recoding bin 1");
data->fileQueue1 = gst_element_factory_make( "queue", "fileQueue1");
data->ph264_1 = gst_element_factory_make ("h264parse", "ph264_1");
data->mux_1 = gst_element_factory_make("mp4mux", "mux1");
data->filesink1 = gst_element_factory_make( "filesink", "filesink1");
gst_bin_add(GST_BIN (data->recBin1), data->fileQueue1);
gst_bin_add(GST_BIN (data->recBin1), data->ph264_1);
gst_bin_add(GST_BIN (data->recBin1), data->mux_1);
gst_bin_add(GST_BIN (data->recBin1), data->filesink1);
/* self.log.debug("link elements in recBin1") */
gst_element_link(data->fileQueue1, data->ph264_1);
gst_element_link(data->ph264_1, data->mux_1);
gst_element_link(data->mux_1, data->filesink1);
/* self.log.debug("create ghost pad for recBin1") */
GstPad *pad = gst_element_get_static_pad (data->fileQueue1, "sink");
gst_element_add_pad(data->recBin1, gst_ghost_pad_new("sink",pad));
gst_object_unref (GST_OBJECT (pad));
}
static void linkRecBin2(CustomData *data){
data->recBin2 = gst_bin_new("recoding bin 2");
data->fileQueue2 = gst_element_factory_make( "queue", "fileQueue2");
data->ph264_2 = gst_element_factory_make ("h264parse", "ph264_2");
data->mux_2 = gst_element_factory_make("mp4mux", "mux2");
data->filesink2 = gst_element_factory_make( "filesink", "filesink2");
gst_bin_add(GST_BIN (data->recBin2), data->fileQueue2);
gst_bin_add(GST_BIN (data->recBin2), data->ph264_2);
gst_bin_add(GST_BIN (data->recBin2), data->mux_2);
gst_bin_add(GST_BIN (data->recBin2), data->filesink2);
/* self.log.debug("link elements in recBin2") */
gst_element_link(data->fileQueue2, data->ph264_2);
gst_element_link(data->ph264_2, data->mux_2);
gst_element_link(data->mux_2, data->filesink2);
/* self.log.debug("create ghost pad for recBin2") */
GstPad *pad = gst_element_get_static_pad (data->fileQueue2, "sink");
gst_element_add_pad(data->recBin2, gst_ghost_pad_new("sink",pad));
gst_object_unref (GST_OBJECT (pad));
}
static void linkRecBin1ToMain(CustomData *data){
gst_bin_add(GST_BIN (data->main), data->recBin1);
/* self.log.debug("link srcQueue --> recBin to tee") */
GstPad *pad = gst_element_get_request_pad(data->t, "src_%u");
gst_pad_link(pad, gst_element_get_static_pad(data->srcQueue, "sink"));
gst_element_link(data->srcQueue, data->recBin1);
gst_object_unref (GST_OBJECT (pad));
}
static void updateFilesinkLocation(CustomData *data, GstElement* fs){
char loc[57];
sprintf(loc, "/run/media/peter/Elements/peter/data/tmp-20130801/%d.mp4", (int)data->cnt);
puts(loc);
g_object_set(G_OBJECT(fs), "location", loc, NULL);
}
static void setProperties(CustomData *data){
updateFilesinkLocation(data, data->filesink1);
printf("set source propertiess\n");
g_object_set(G_OBJECT(data->src), "auto-start", 1, NULL);
g_object_set(G_OBJECT(data->src), "fixed-framerate", 1, NULL);
g_object_set(G_OBJECT(data->src), "async-handling", 0, NULL);
g_object_set(G_OBJECT(data->src), "iframe-period", 30, NULL);
// g_object_set(G_OBJECT(data->src), "num-clock-samples", -1, NULL);
g_object_set(G_OBJECT(data->src), "device", "/dev/video1", NULL);
}
static void setCaps(CustomData *data){
GstCaps *caps = gst_caps_from_string("video/x-h264,width=1920,height=1080,framerate=30/1,profile=constrained-baseline");
g_object_set(G_OBJECT(data->vidcaps), "caps", caps, NULL);
GstCaps *caps2 = gst_caps_from_string("video/x-raw,width=320,height=240,framerate=15/1");
g_object_set(G_OBJECT(data->vfcaps), "caps", caps2, NULL);
gst_object_unref (GST_OBJECT (caps));
gst_object_unref (GST_OBJECT (caps2));
}
/* swapping functions */
static void resetBin(CustomData *data, GstElement *bin){
gst_element_set_state(GST_BIN(bin), GST_STATE_NULL);
#ifndef TEST_RECYCLE
if (bin == data->recBin1){
gst_object_unref (data->recBin1);
linkRecBin1(data);
}else{
gst_object_unref (data->recBin2);
linkRecBin2(data);
}
#endif
}
static GstPadProbeReturn preparePipeline(GstPad* pad, GstPadProbeInfo *probeInfo, gpointer *userData){
printf("preparePipeline\n");
CustomData *data = userData;
GstPad* pC;
GstElement* binC;
GstElement* binN;
GstElement* fs;
GstElement* mux;
GstElement* fqN;
GstElement* fqC;
gst_pad_remove_probe(pad, probeInfo->id);
data->cnt += 1;
pC = gst_pad_get_peer(pad);
binC = gst_pad_get_parent(pC);
if (binC == data->recBin1){
printf("replace recBin1 by recBin2\n") ;
resetBin(data, data->recBin2);
binN = data->recBin2;
fs = data->filesink2;
mux = data->mux_2;
fqN = data->fileQueue2;
fqC = data->fileQueue1;
}else{
printf("replace recBin2 by recBin1\n") ;
resetBin(data, data->recBin1);
binN = data->recBin1;
fs = data->filesink1;
mux = data->mux_1;
fqN = data->fileQueue1;
fqC = data->fileQueue2;
}
printf("prepare next recBin\n");
updateFilesinkLocation(data, fs);
printf("remove current recBin from main and prepare catch\n");
gst_element_unlink(data->srcQueue, fqC);
gst_bin_remove(GST_BIN(data->main), binC);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(data->main),GST_DEBUG_GRAPH_SHOW_ALL, "main-1");
printf("send EOS to current recBin\n");
gst_element_send_event(binC, gst_event_new_eos ());
printf("prepare next recBin\n");
gst_element_set_state(GST_BIN(binN), GST_STATE_NULL);
printf("add and link next recBin to main\n");
gst_bin_add(GST_BIN(data->main), binN);
gst_element_link(data->srcQueue, binN);
gst_element_set_state(GST_BIN(binN), GST_STATE_PLAYING);
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn blockActiveQueuePad(GstPad* pad, GstPadProbeInfo *probeInfo, gpointer userData){
GstBuffer *buffer;
GstPad* tp;
CustomData *data = userData;
buffer = GST_PAD_PROBE_INFO_BUFFER (probeInfo);
if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)){
gst_pad_remove_probe(pad, probeInfo->id);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(data->main), GST_DEBUG_GRAPH_SHOW_ALL, "main_blockActiveQueuePad");
tp = gst_element_get_static_pad(data->srcQueue, "src");
gst_pad_add_probe(tp, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, (GstPadProbeCallback) preparePipeline, data, NULL);
gst_object_unref (GST_OBJECT (tp));
return GST_PAD_PROBE_DROP;
}else{
printf("no keyframe yet");
return GST_PAD_PROBE_OK;
}
}
static int blockOnNextKeyframe(CustomData *data){
GstPad *tp;
tp = gst_element_get_static_pad(data->srcQueue, "sink");
gst_element_send_event(data->main, gst_video_event_new_upstream_force_key_unit(GST_CLOCK_TIME_NONE, TRUE, data->cnt));
gst_pad_add_probe(tp, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) blockActiveQueuePad, data, NULL);
gst_object_unref (GST_OBJECT (tp));
return 1;
}
/* This function is called when the PLAY button is clicked */
static void play_cb (GtkButton *button, CustomData *data) {
gst_element_set_state (data->main, GST_STATE_PLAYING);
}
/* This function is called when the PAUSE button is clicked */
static void pause_cb (GtkButton *button, CustomData *data) {
blockOnNextKeyframe(data);
}
/* This function is called when the STOP button is clicked */
static void stop_cb (GtkButton *button, CustomData *data) {
gst_element_set_state (data->main, GST_STATE_READY);
}
/* This function is called when the main window is closed */
static void delete_event_cb (GtkWidget *widget, GdkEvent *event, CustomData *data) {
stop_cb (NULL, data);
gtk_main_quit();
}
static void createUI(CustomData *data){
GtkWidget *main_window; /* The uppermost window, containing all other windows */
GtkWidget *video_window; /* The drawing area where the video will be shown */
GtkWidget *main_box; /* VBox to hold main_hbox and the controls */
GtkWidget *main_hbox; /* HBox to hold the video_window and the stream info text widget */
GtkWidget *controls; /* HBox to hold the buttons and the slider */
GtkWidget *play_button, *pause_button, *stop_button; /* Buttons */
main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (G_OBJECT (main_window), "delete-event", G_CALLBACK (delete_event_cb), data);
video_window = gtk_drawing_area_new ();
gtk_widget_set_double_buffered (video_window, FALSE);
play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), data);
pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), data);
stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), data);
controls = gtk_box_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (controls), play_button, FALSE, FALSE, 2);
gtk_box_pack_start (GTK_BOX (controls), pause_button, FALSE, FALSE, 2);
gtk_box_pack_start (GTK_BOX (controls), stop_button, FALSE, FALSE, 2);
main_hbox = gtk_box_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (main_hbox), video_window, TRUE, TRUE, 0);
main_box = gtk_box_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (main_box), main_hbox, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (main_box), controls, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (main_window), main_box);
gtk_window_set_default_size (GTK_WINDOW (main_window), 640, 480);
gtk_widget_show_all (main_window);
}
int main(int argc, char *argv[]) {
CustomData data;
GstStateChangeReturn ret;
GstBus *bus;
/* Initialize GTK */
gtk_init (&argc, &argv);
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Initialize our data structure */
memset (&data, 0, sizeof (data));
/* create and link pipeline */
printf("createElements\n");
createElements(&data);
printf("linkMainPipeline\n");
linkMainPipeline(&data);
printf("linkRecBin1\n");
linkRecBin1(&data);
printf("linkRecBin2\n");
linkRecBin2(&data);
printf("linkRecBin1ToMain\n");
linkRecBin1ToMain(&data);
printf("setProperties\n");
setProperties(&data);
printf("setCaps\n");
setCaps(&data);
/* set GTK up */
printf("create ui\n");
createUI(&data);
/* Start playing */
printf("start playing\n");
ret = gst_element_set_state (data.main, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.main);
return -1;
}
/* Start the GTK main loop. We will not regain control until gtk_main_quit is called. */
gtk_main ();
/* Free resources */
gst_element_set_state (data.main, GST_STATE_NULL);
gst_object_unref (data.main);
return 0;
}
@groakat
Copy link
Author

groakat commented Aug 7, 2013

Complementary file to Gstreamer bug #705531
https://bugzilla.gnome.org/show_bug.cgi?id=705531

This is a showcase of a pipeline swap that is supposed to work either way, but has problems with both.

Method 1:
Either I block the srcQueue src pad and then exchange between recBin1 and two, while recycling these bins (set #define TEST_RECYCLE).

Result:
As soon as a bin gets recycled the new file location is created, but no data is streamed into it. (I think the h264parse blocks). No memory leak as far as I can tell.

Method 2:
Or each time re-make the new bin always with the element factories.

Result:
New files are created and data is streamed into them. But a memory leak appears (at least in the python version). I still have to test the C version for memory leaks. If there is a leak, I again think that the h264parse might be the trouble maker.

Please read top of the file:

/* TODOS FOR TESTERS OF BUG */

/* SET MODE OF RECYCLING OF RECBINS

  • define to recycle the recBins, comment out to
  • replace the recBins with fresh factory made ones
    */
    #define TEST_RECYCLE

/* SET LOCATION OF FILESINK

  • go to line approx. 185 in function updateFilesinkLocation() and replace
  • the file location
    */

/* TO SWAP TO NEXT FILE LOCATION CLICK PAUSE BUTTON */

gst-launch pipeline equivalent:

gst-launch-1.0 -e uvch264src device=/dev/video1 name=src auto-start=true
src.vfsrc ! queue name=prevQueue !
video/x-raw,width=320,height=240,framerate=30/1 ! autovideosink
name=previewsink src.vidsrc ! queue name=vidQueue !
video/x-h264,width=1920,height=1080,framerate=30/1,profile=constrained-baseline
! tee name=t ! queue ! h264parse ! avdec_h264 ! autovideosink t. ! queue
name=srcQueue ! myBin

Template of bins I swap around:
myBin: queue name=fileQueue1 ! h264parse name=ph264_1 ! mp4mux name=mux_1 !
filesink name=filesink1 location="test.mp4"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment