Last active
December 20, 2015 18:49
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 TEST_RECYCLE
/* SET LOCATION OF FILESINK
*/
/* 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"