├── .gitignore ├── COPYING ├── Makefile ├── app.c ├── dynamic-filter.c ├── dynamic-tee-vsink.c └── qt5-eventloop ├── gst-qt-example.cc ├── gst-qt-example.h └── gst-qt-example.pro /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*.sw? 3 | 4 | app 5 | dynamic-filter 6 | dynamic-tee-vsink 7 | http-launch 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 Sebastian Dröge 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -O2 -g -Wall 2 | LDFLAGS ?= 3 | 4 | GST_CFLAGS = $(shell pkg-config --cflags gstreamer-1.0) 5 | GST_LDFLAGS = $(shell pkg-config --libs gstreamer-1.0) 6 | 7 | GIO_CFLAGS = $(shell pkg-config --cflags gio-2.0) 8 | GIO_LDFLAGS = $(shell pkg-config --libs gio-2.0) 9 | 10 | all: app dynamic-filter dynamic-tee-vsink 11 | 12 | clean: 13 | rm -f app dynamic-filter dynamic-tee-vsink 14 | 15 | app: app.c 16 | $(CC) -o $@ $^ $(CFLAGS) $(GST_CFLAGS) $(LDFLAGS) $(GST_LDFLAGS) 17 | 18 | dynamic-filter: dynamic-filter.c 19 | $(CC) -o $@ $^ $(CFLAGS) $(GST_CFLAGS) $(LDFLAGS) $(GST_LDFLAGS) 20 | 21 | dynamic-tee-vsink: dynamic-tee-vsink.c 22 | $(CC) -o $@ $^ $(CFLAGS) $(GST_CFLAGS) $(LDFLAGS) $(GST_LDFLAGS) 23 | -------------------------------------------------------------------------------- /app.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple GStreamer application template 3 | * 4 | * Copyright (c) 2014 Sebastian Dröge 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | 28 | static GMainLoop *loop; 29 | static GstElement *pipeline; 30 | static GstElement *src, *conv, *sink; 31 | 32 | static gboolean 33 | message_cb (GstBus * bus, GstMessage * message, gpointer user_data) 34 | { 35 | switch (GST_MESSAGE_TYPE (message)) { 36 | case GST_MESSAGE_ERROR:{ 37 | GError *err = NULL; 38 | gchar *name, *debug = NULL; 39 | 40 | name = gst_object_get_path_string (message->src); 41 | gst_message_parse_error (message, &err, &debug); 42 | 43 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 44 | if (debug != NULL) 45 | g_printerr ("Additional debug info:\n%s\n", debug); 46 | 47 | g_error_free (err); 48 | g_free (debug); 49 | g_free (name); 50 | 51 | g_main_loop_quit (loop); 52 | break; 53 | } 54 | case GST_MESSAGE_WARNING:{ 55 | GError *err = NULL; 56 | gchar *name, *debug = NULL; 57 | 58 | name = gst_object_get_path_string (message->src); 59 | gst_message_parse_warning (message, &err, &debug); 60 | 61 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 62 | if (debug != NULL) 63 | g_printerr ("Additional debug info:\n%s\n", debug); 64 | 65 | g_error_free (err); 66 | g_free (debug); 67 | g_free (name); 68 | break; 69 | } 70 | case GST_MESSAGE_EOS: 71 | g_print ("Got EOS\n"); 72 | g_main_loop_quit (loop); 73 | break; 74 | default: 75 | break; 76 | } 77 | 78 | return TRUE; 79 | } 80 | 81 | int 82 | main (int argc, char **argv) 83 | { 84 | GstBus *bus; 85 | 86 | gst_init (&argc, &argv); 87 | 88 | pipeline = gst_pipeline_new (NULL); 89 | src = gst_element_factory_make ("videotestsrc", NULL); 90 | conv = gst_element_factory_make ("videoconvert", NULL); 91 | sink = gst_element_factory_make ("autovideosink", NULL); 92 | 93 | if (!pipeline || !src || !conv || !sink) { 94 | g_error ("Failed to create elements"); 95 | return -1; 96 | } 97 | 98 | gst_bin_add_many (GST_BIN (pipeline), src, conv, sink, NULL); 99 | if (!gst_element_link_many (src, conv, sink, NULL)) { 100 | g_error ("Failed to link elements"); 101 | return -2; 102 | } 103 | 104 | loop = g_main_loop_new (NULL, FALSE); 105 | 106 | bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 107 | gst_bus_add_signal_watch (bus); 108 | g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (message_cb), NULL); 109 | gst_object_unref (GST_OBJECT (bus)); 110 | 111 | if (gst_element_set_state (pipeline, 112 | GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { 113 | g_error ("Failed to go into PLAYING state"); 114 | return -3; 115 | } 116 | 117 | g_main_loop_run (loop); 118 | 119 | gst_element_set_state (pipeline, GST_STATE_NULL); 120 | 121 | g_main_loop_unref (loop); 122 | gst_object_unref (pipeline); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /dynamic-filter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Dynamic pipelines example, uridecodebin with a switching video filter 3 | * 4 | * Copyright (c) 2014 Sebastian Dröge 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | static GMainLoop *loop; 30 | static GstElement *pipeline; 31 | static GstElement *src, *dbin, *conv, *scale, *navseek, *queue, *sink; 32 | static GstElement *conv2, *filter; 33 | static GstPad *dbin_srcpad; 34 | static gboolean linked = FALSE; 35 | 36 | static gboolean 37 | message_cb (GstBus * bus, GstMessage * message, gpointer user_data) 38 | { 39 | switch (GST_MESSAGE_TYPE (message)) { 40 | case GST_MESSAGE_ERROR:{ 41 | GError *err = NULL; 42 | gchar *name, *debug = NULL; 43 | 44 | name = gst_object_get_path_string (message->src); 45 | gst_message_parse_error (message, &err, &debug); 46 | 47 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 48 | if (debug != NULL) 49 | g_printerr ("Additional debug info:\n%s\n", debug); 50 | 51 | g_error_free (err); 52 | g_free (debug); 53 | g_free (name); 54 | 55 | g_main_loop_quit (loop); 56 | break; 57 | } 58 | case GST_MESSAGE_WARNING:{ 59 | GError *err = NULL; 60 | gchar *name, *debug = NULL; 61 | 62 | name = gst_object_get_path_string (message->src); 63 | gst_message_parse_warning (message, &err, &debug); 64 | 65 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 66 | if (debug != NULL) 67 | g_printerr ("Additional debug info:\n%s\n", debug); 68 | 69 | g_error_free (err); 70 | g_free (debug); 71 | g_free (name); 72 | break; 73 | } 74 | case GST_MESSAGE_EOS: 75 | g_print ("Got EOS\n"); 76 | g_main_loop_quit (loop); 77 | break; 78 | default: 79 | break; 80 | } 81 | 82 | return TRUE; 83 | } 84 | 85 | static gint in_idle_probe = FALSE; 86 | 87 | static GstPadProbeReturn 88 | pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) 89 | { 90 | GstPad *sinkpad, *srcpad; 91 | 92 | if (!g_atomic_int_compare_and_exchange (&in_idle_probe, FALSE, TRUE)) 93 | return GST_PAD_PROBE_OK; 94 | 95 | /* Insert or remove filter? */ 96 | if (conv2) { 97 | sinkpad = gst_element_get_static_pad (conv2, "sink"); 98 | gst_pad_unlink (dbin_srcpad, sinkpad); 99 | gst_object_unref (sinkpad); 100 | 101 | sinkpad = gst_element_get_static_pad (conv, "sink"); 102 | srcpad = gst_element_get_static_pad (filter, "src"); 103 | gst_pad_unlink (srcpad, sinkpad); 104 | gst_object_unref (srcpad); 105 | 106 | gst_bin_remove (GST_BIN (pipeline), filter); 107 | gst_bin_remove (GST_BIN (pipeline), conv2); 108 | 109 | gst_element_set_state (filter, GST_STATE_NULL); 110 | gst_element_set_state (conv2, GST_STATE_NULL); 111 | 112 | gst_object_unref (filter); 113 | gst_object_unref (conv2); 114 | filter = NULL; 115 | conv2 = NULL; 116 | 117 | if (gst_pad_link (dbin_srcpad, sinkpad) != GST_PAD_LINK_OK) { 118 | g_printerr ("Failed to link src with conv\n"); 119 | gst_object_unref (sinkpad); 120 | g_main_loop_quit (loop); 121 | } 122 | gst_object_unref (sinkpad); 123 | } else { 124 | conv2 = gst_element_factory_make ("videoconvert", NULL); 125 | filter = gst_element_factory_make ("agingtv", NULL); 126 | 127 | gst_object_ref (conv2); 128 | gst_object_ref (filter); 129 | gst_bin_add_many (GST_BIN (pipeline), conv2, filter, NULL); 130 | 131 | gst_element_sync_state_with_parent (conv2); 132 | gst_element_sync_state_with_parent (filter); 133 | 134 | if (!gst_element_link (conv2, filter)) { 135 | g_printerr ("Failed to link conv2 with filter\n"); 136 | g_main_loop_quit (loop); 137 | } 138 | 139 | sinkpad = gst_element_get_static_pad (conv, "sink"); 140 | gst_pad_unlink (dbin_srcpad, sinkpad); 141 | gst_object_unref (sinkpad); 142 | 143 | sinkpad = gst_element_get_static_pad (conv2, "sink"); 144 | if (gst_pad_link (dbin_srcpad, sinkpad) != GST_PAD_LINK_OK) { 145 | g_printerr ("Failed to link src with conv2\n"); 146 | gst_object_unref (sinkpad); 147 | g_main_loop_quit (loop); 148 | } 149 | gst_object_unref (sinkpad); 150 | 151 | srcpad = gst_element_get_static_pad (filter, "src"); 152 | sinkpad = gst_element_get_static_pad (conv, "sink"); 153 | if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK) { 154 | g_printerr ("Failed to link filter with conv\n"); 155 | gst_object_unref (srcpad); 156 | gst_object_unref (sinkpad); 157 | g_main_loop_quit (loop); 158 | } 159 | gst_object_unref (srcpad); 160 | gst_object_unref (sinkpad); 161 | } 162 | 163 | return GST_PAD_PROBE_REMOVE; 164 | } 165 | 166 | static gboolean 167 | timeout_cb (gpointer user_data) 168 | { 169 | in_idle_probe = FALSE; 170 | gst_pad_add_probe (dbin_srcpad, GST_PAD_PROBE_TYPE_IDLE, pad_probe_cb, NULL, 171 | NULL); 172 | 173 | return TRUE; 174 | } 175 | 176 | static void 177 | pad_added_cb (GstElement * element, GstPad * pad, gpointer user_data) 178 | { 179 | GstCaps *caps; 180 | GstStructure *s; 181 | const gchar *name; 182 | 183 | if (linked) 184 | return; 185 | 186 | caps = gst_pad_get_current_caps (pad); 187 | s = gst_caps_get_structure (caps, 0); 188 | name = gst_structure_get_name (s); 189 | 190 | if (strcmp (name, "video/x-raw") == 0) { 191 | GstPad *sinkpad; 192 | 193 | sinkpad = gst_element_get_static_pad (conv, "sink"); 194 | if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) { 195 | g_printerr ("Failed to link dbin with conv\n"); 196 | g_main_loop_quit (loop); 197 | } 198 | gst_object_unref (sinkpad); 199 | 200 | dbin_srcpad = gst_object_ref (pad); 201 | 202 | g_timeout_add_seconds (5, timeout_cb, NULL); 203 | linked = TRUE; 204 | } 205 | 206 | gst_caps_unref (caps); 207 | } 208 | 209 | int 210 | main (int argc, char **argv) 211 | { 212 | GstBus *bus; 213 | 214 | gst_init (&argc, &argv); 215 | 216 | if (argc != 2) { 217 | g_error ("Usage: %s filename", argv[0]); 218 | return -1; 219 | } 220 | 221 | pipeline = gst_pipeline_new (NULL); 222 | src = gst_element_factory_make ("filesrc", NULL); 223 | dbin = gst_element_factory_make ("decodebin", NULL); 224 | conv = gst_element_factory_make ("videoconvert", NULL); 225 | scale = gst_element_factory_make ("videoscale", NULL); 226 | navseek = gst_element_factory_make ("navseek", NULL); 227 | queue = gst_element_factory_make ("queue", NULL); 228 | sink = gst_element_factory_make ("autovideosink", NULL); 229 | 230 | if (!pipeline || !src || !dbin || !conv || !scale || !navseek || !queue 231 | || !sink) { 232 | g_error ("Failed to create elements"); 233 | return -2; 234 | } 235 | 236 | g_object_set (src, "location", argv[1], NULL); 237 | 238 | gst_bin_add_many (GST_BIN (pipeline), src, dbin, conv, scale, navseek, queue, 239 | sink, NULL); 240 | if (!gst_element_link_many (src, dbin, NULL) 241 | || !gst_element_link_many (conv, scale, navseek, queue, sink, NULL)) { 242 | g_error ("Failed to link elements"); 243 | return -3; 244 | } 245 | 246 | g_signal_connect (dbin, "pad-added", G_CALLBACK (pad_added_cb), NULL); 247 | 248 | loop = g_main_loop_new (NULL, FALSE); 249 | 250 | bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 251 | gst_bus_add_signal_watch (bus); 252 | g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (message_cb), NULL); 253 | gst_object_unref (GST_OBJECT (bus)); 254 | 255 | if (gst_element_set_state (pipeline, 256 | GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { 257 | g_error ("Failed to go into PLAYING state"); 258 | return -4; 259 | } 260 | 261 | g_main_loop_run (loop); 262 | 263 | gst_element_set_state (pipeline, GST_STATE_NULL); 264 | 265 | g_main_loop_unref (loop); 266 | 267 | if (dbin_srcpad) 268 | gst_object_unref (dbin_srcpad); 269 | gst_object_unref (pipeline); 270 | 271 | return 0; 272 | } 273 | -------------------------------------------------------------------------------- /dynamic-tee-vsink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Dynamic pipelines example, uridecodebin with sinks added and removed 3 | * 4 | * Copyright (c) 2014 Sebastian Dröge 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | static GMainLoop *loop; 30 | static GstElement *pipeline; 31 | static GstElement *src, *dbin, *conv, *tee; 32 | static gboolean linked = FALSE; 33 | static GList *sinks; 34 | 35 | typedef struct 36 | { 37 | GstPad *teepad; 38 | GstElement *queue; 39 | GstElement *conv; 40 | GstElement *sink; 41 | gboolean removing; 42 | } Sink; 43 | 44 | static gboolean 45 | message_cb (GstBus * bus, GstMessage * message, gpointer user_data) 46 | { 47 | switch (GST_MESSAGE_TYPE (message)) { 48 | case GST_MESSAGE_ERROR:{ 49 | GError *err = NULL; 50 | gchar *name, *debug = NULL; 51 | 52 | name = gst_object_get_path_string (message->src); 53 | gst_message_parse_error (message, &err, &debug); 54 | 55 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 56 | if (debug != NULL) 57 | g_printerr ("Additional debug info:\n%s\n", debug); 58 | 59 | g_error_free (err); 60 | g_free (debug); 61 | g_free (name); 62 | 63 | g_main_loop_quit (loop); 64 | break; 65 | } 66 | case GST_MESSAGE_WARNING:{ 67 | GError *err = NULL; 68 | gchar *name, *debug = NULL; 69 | 70 | name = gst_object_get_path_string (message->src); 71 | gst_message_parse_warning (message, &err, &debug); 72 | 73 | g_printerr ("ERROR: from element %s: %s\n", name, err->message); 74 | if (debug != NULL) 75 | g_printerr ("Additional debug info:\n%s\n", debug); 76 | 77 | g_error_free (err); 78 | g_free (debug); 79 | g_free (name); 80 | break; 81 | } 82 | case GST_MESSAGE_EOS: 83 | g_print ("Got EOS\n"); 84 | g_main_loop_quit (loop); 85 | break; 86 | default: 87 | break; 88 | } 89 | 90 | return TRUE; 91 | } 92 | 93 | static GstPadProbeReturn 94 | unlink_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) 95 | { 96 | Sink *sink = user_data; 97 | GstPad *sinkpad; 98 | 99 | if (!g_atomic_int_compare_and_exchange (&sink->removing, FALSE, TRUE)) 100 | return GST_PAD_PROBE_OK; 101 | 102 | sinkpad = gst_element_get_static_pad (sink->queue, "sink"); 103 | gst_pad_unlink (sink->teepad, sinkpad); 104 | gst_object_unref (sinkpad); 105 | 106 | gst_bin_remove (GST_BIN (pipeline), sink->queue); 107 | gst_bin_remove (GST_BIN (pipeline), sink->conv); 108 | gst_bin_remove (GST_BIN (pipeline), sink->sink); 109 | 110 | gst_element_set_state (sink->sink, GST_STATE_NULL); 111 | gst_element_set_state (sink->conv, GST_STATE_NULL); 112 | gst_element_set_state (sink->queue, GST_STATE_NULL); 113 | 114 | gst_object_unref (sink->queue); 115 | gst_object_unref (sink->conv); 116 | gst_object_unref (sink->sink); 117 | 118 | gst_element_release_request_pad (tee, sink->teepad); 119 | gst_object_unref (sink->teepad); 120 | 121 | g_print ("removed\n"); 122 | 123 | return GST_PAD_PROBE_REMOVE; 124 | } 125 | 126 | static gboolean 127 | tick_cb (gpointer data) 128 | { 129 | if (!sinks || g_random_int () % 2 == 0) { 130 | Sink *sink = g_new0 (Sink, 1); 131 | GstPad *sinkpad; 132 | GstPadTemplate *templ; 133 | 134 | templ = 135 | gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), 136 | "src_%u"); 137 | 138 | g_print ("add\n"); 139 | 140 | sink->teepad = gst_element_request_pad (tee, templ, NULL, NULL); 141 | 142 | sink->queue = gst_element_factory_make ("queue", NULL); 143 | sink->conv = gst_element_factory_make ("videoconvert", NULL); 144 | sink->sink = gst_element_factory_make ("autovideosink", NULL); 145 | sink->removing = FALSE; 146 | 147 | gst_bin_add_many (GST_BIN (pipeline), gst_object_ref (sink->queue), 148 | gst_object_ref (sink->conv), gst_object_ref (sink->sink), NULL); 149 | gst_element_link_many (sink->queue, sink->conv, sink->sink, NULL); 150 | 151 | gst_element_sync_state_with_parent (sink->queue); 152 | gst_element_sync_state_with_parent (sink->conv); 153 | gst_element_sync_state_with_parent (sink->sink); 154 | 155 | sinkpad = gst_element_get_static_pad (sink->queue, "sink"); 156 | gst_pad_link (sink->teepad, sinkpad); 157 | gst_object_unref (sinkpad); 158 | 159 | g_print ("added\n"); 160 | 161 | sinks = g_list_append (sinks, sink); 162 | } else { 163 | Sink *sink; 164 | 165 | g_print ("remove\n"); 166 | 167 | sink = sinks->data; 168 | sinks = g_list_delete_link (sinks, sinks); 169 | gst_pad_add_probe (sink->teepad, GST_PAD_PROBE_TYPE_IDLE, unlink_cb, sink, 170 | (GDestroyNotify) g_free); 171 | } 172 | 173 | return TRUE; 174 | } 175 | 176 | static void 177 | pad_added_cb (GstElement * element, GstPad * pad, gpointer user_data) 178 | { 179 | GstCaps *caps; 180 | GstStructure *s; 181 | const gchar *name; 182 | 183 | if (linked) 184 | return; 185 | 186 | caps = gst_pad_get_current_caps (pad); 187 | s = gst_caps_get_structure (caps, 0); 188 | name = gst_structure_get_name (s); 189 | 190 | if (strcmp (name, "video/x-raw") == 0) { 191 | GstPad *sinkpad, *teepad; 192 | GstElement *queue, *sink; 193 | GstPadTemplate *templ; 194 | 195 | sinkpad = gst_element_get_static_pad (conv, "sink"); 196 | if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) { 197 | g_printerr ("Failed to link dbin with conv\n"); 198 | gst_object_unref (sinkpad); 199 | g_main_loop_quit (loop); 200 | return; 201 | } 202 | gst_object_unref (sinkpad); 203 | 204 | templ = 205 | gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), 206 | "src_%u"); 207 | teepad = gst_element_request_pad (tee, templ, NULL, NULL); 208 | queue = gst_element_factory_make ("queue", NULL); 209 | sink = gst_element_factory_make ("fakesink", NULL); 210 | 211 | g_object_set (sink, "sync", TRUE, NULL); 212 | 213 | gst_bin_add_many (GST_BIN (pipeline), queue, sink, NULL); 214 | 215 | gst_element_sync_state_with_parent (sink); 216 | gst_element_sync_state_with_parent (queue); 217 | 218 | gst_element_link_many (queue, sink, NULL); 219 | 220 | sinkpad = gst_element_get_static_pad (queue, "sink"); 221 | gst_pad_link (teepad, sinkpad); 222 | gst_object_unref (sinkpad); 223 | 224 | g_timeout_add_seconds (3, tick_cb, NULL); 225 | linked = TRUE; 226 | } 227 | 228 | gst_caps_unref (caps); 229 | } 230 | 231 | int 232 | main (int argc, char **argv) 233 | { 234 | GstBus *bus; 235 | 236 | if (argc != 2) { 237 | g_error ("Usage: %s filename", argv[0]); 238 | return 0; 239 | } 240 | 241 | gst_init (&argc, &argv); 242 | 243 | pipeline = gst_pipeline_new (NULL); 244 | src = gst_element_factory_make ("filesrc", NULL); 245 | dbin = gst_element_factory_make ("decodebin", NULL); 246 | conv = gst_element_factory_make ("videoconvert", NULL); 247 | tee = gst_element_factory_make ("tee", NULL); 248 | 249 | if (!pipeline || !src || !dbin || !conv || !tee) { 250 | g_error ("Failed to create elements"); 251 | return -1; 252 | } 253 | 254 | g_object_set (src, "location", argv[1], NULL); 255 | 256 | gst_bin_add_many (GST_BIN (pipeline), src, dbin, conv, tee, NULL); 257 | if (!gst_element_link_many (src, dbin, NULL) || 258 | !gst_element_link_many (conv, tee, NULL)) { 259 | g_error ("Failed to link elements"); 260 | return -2; 261 | } 262 | 263 | g_signal_connect (dbin, "pad-added", G_CALLBACK (pad_added_cb), NULL); 264 | 265 | loop = g_main_loop_new (NULL, FALSE); 266 | 267 | bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 268 | gst_bus_add_signal_watch (bus); 269 | g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (message_cb), NULL); 270 | gst_object_unref (GST_OBJECT (bus)); 271 | 272 | gst_element_set_state (pipeline, GST_STATE_PLAYING); 273 | 274 | g_main_loop_run (loop); 275 | 276 | gst_element_set_state (pipeline, GST_STATE_NULL); 277 | 278 | g_main_loop_unref (loop); 279 | 280 | gst_object_unref (pipeline); 281 | 282 | return 0; 283 | } 284 | -------------------------------------------------------------------------------- /qt5-eventloop/gst-qt-example.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "gst-qt-example.h" 7 | 8 | Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr); 9 | Q_DECLARE_METATYPE(std::shared_ptr); 10 | 11 | template 12 | std::shared_ptr 13 | takeGstObject(T *o) 14 | { 15 | std::shared_ptr ptr(o, [] (T *d) { 16 | gst_object_unref(reinterpret_cast(d)); 17 | }); 18 | 19 | return ptr; 20 | } 21 | 22 | template 23 | std::shared_ptr 24 | takeGstMiniObject(T *o) 25 | { 26 | std::shared_ptr ptr(o, [] (T *d) { 27 | gst_mini_object_unref(reinterpret_cast(d)); 28 | }); 29 | 30 | return ptr; 31 | } 32 | 33 | GstQtExample::GstQtExample() 34 | { 35 | pipeline = takeGstObject(gst_parse_launch("audiotestsrc num-buffers=100 ! autoaudiosink", nullptr)); 36 | bus = takeGstObject(gst_element_get_bus(pipeline.get())); 37 | gst_bus_set_sync_handler(bus.get(), busMessageDispatcher, this, nullptr); 38 | } 39 | 40 | GstQtExample::~GstQtExample() 41 | { 42 | gst_bus_set_sync_handler(bus.get(), nullptr, nullptr, nullptr); 43 | } 44 | 45 | void 46 | GstQtExample::busMessage(std::shared_ptr message) 47 | { 48 | switch (GST_MESSAGE_TYPE(message.get())) { 49 | case GST_MESSAGE_STATE_CHANGED: 50 | if (GST_MESSAGE_SRC(message.get()) == reinterpret_cast(pipeline.get())) { 51 | GstState newState; 52 | 53 | gst_message_parse_state_changed(message.get(), nullptr, &newState, nullptr); 54 | if (newState == GST_STATE_PLAYING) { 55 | g_print("reached PLAYING\n"); 56 | } 57 | } 58 | break; 59 | case GST_MESSAGE_EOS: 60 | g_print("reached EOS\n"); 61 | gst_element_set_state(pipeline.get(), GST_STATE_NULL); 62 | QCoreApplication::exit(); 63 | break; 64 | default: 65 | break; 66 | } 67 | } 68 | 69 | GstBusSyncReply 70 | GstQtExample::busMessageDispatcher(GstBus *bus, GstMessage *message, gpointer userData) 71 | { 72 | auto example = static_cast(userData); 73 | 74 | Q_UNUSED(bus); 75 | 76 | auto messagePtr = takeGstMiniObject(message); 77 | QMetaObject::invokeMethod(example, "busMessage", Qt::QueuedConnection, Q_ARG(std::shared_ptr, messagePtr)); 78 | 79 | return GST_BUS_DROP; 80 | } 81 | 82 | void 83 | GstQtExample::start(void) 84 | { 85 | gst_element_set_state(pipeline.get(), GST_STATE_PLAYING); 86 | } 87 | 88 | int 89 | main(int argc, char **argv) 90 | { 91 | gst_init(&argc, &argv); 92 | QCoreApplication app(argc, argv); 93 | 94 | // Register our shared pointer type 95 | QMetaTypeId>::qt_metatype_id(); 96 | 97 | GstQtExample example; 98 | example.start(); 99 | 100 | return app.exec(); 101 | } 102 | -------------------------------------------------------------------------------- /qt5-eventloop/gst-qt-example.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #ifndef __GST_QT_EXAMPLE_H__ 7 | #define __GST_QT_EXAMPLE_H__ 8 | 9 | class GstQtExample : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | GstQtExample(); 15 | virtual ~GstQtExample(); 16 | 17 | void start(void); 18 | 19 | private slots: 20 | void busMessage(std::shared_ptr message); 21 | 22 | private: 23 | static GstBusSyncReply busMessageDispatcher(GstBus *bus, GstMessage *message, gpointer user_data); 24 | 25 | std::shared_ptr bus; 26 | std::shared_ptr pipeline; 27 | }; 28 | 29 | #endif /*__GST_QT_EXAMPLE_H__ */ 30 | -------------------------------------------------------------------------------- /qt5-eventloop/gst-qt-example.pro: -------------------------------------------------------------------------------- 1 | CONFIG += c++11 2 | 3 | CONFIG += link_pkgconfig 4 | PKGCONFIG += gstreamer-1.0 5 | 6 | TARGET = gst-qt-example 7 | SOURCES += gst-qt-example.cc 8 | HEADERS += gst-qt-example.h 9 | 10 | --------------------------------------------------------------------------------