| #include <gst/gst.h> |
| |
| #define SWITCH_TIMEOUT 1 |
| #define NUM_VIDEO_BUFFERS 500 |
| |
| static GMainLoop *loop; |
| |
| /* Output selector src pads */ |
| static GstPad *osel_src1 = NULL; |
| static GstPad *osel_src2 = NULL; |
| |
| static gboolean |
| my_bus_callback (GstBus * bus, GstMessage * message, gpointer data) |
| { |
| g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message)); |
| |
| switch (GST_MESSAGE_TYPE (message)) { |
| case GST_MESSAGE_ERROR:{ |
| GError *err; |
| gchar *debug; |
| |
| gst_message_parse_error (message, &err, &debug); |
| g_print ("Error: %s\n", err->message); |
| g_error_free (err); |
| g_free (debug); |
| |
| g_main_loop_quit (loop); |
| break; |
| } |
| case GST_MESSAGE_EOS: |
| /* end-of-stream */ |
| g_main_loop_quit (loop); |
| break; |
| default: |
| /* unhandled message */ |
| break; |
| } |
| /* we want to be notified again the next time there is a message |
| * on the bus, so returning TRUE (FALSE means we want to stop watching |
| * for messages on the bus and our callback should not be called again) |
| */ |
| return TRUE; |
| } |
| |
| static gboolean |
| switch_cb (gpointer user_data) |
| { |
| GstElement *sel = GST_ELEMENT (user_data); |
| GstPad *old_pad, *new_pad = NULL; |
| |
| g_object_get (G_OBJECT (sel), "active-pad", &old_pad, NULL); |
| |
| if (old_pad == osel_src1) |
| new_pad = osel_src2; |
| else |
| new_pad = osel_src1; |
| |
| g_object_set (G_OBJECT (sel), "active-pad", new_pad, NULL); |
| |
| g_print ("switched from %s:%s to %s:%s\n", GST_DEBUG_PAD_NAME (old_pad), |
| GST_DEBUG_PAD_NAME (new_pad)); |
| |
| gst_object_unref (old_pad); |
| |
| return TRUE; |
| |
| } |
| |
| static void |
| on_bin_element_added (GstBin * bin, GstElement * element, gpointer user_data) |
| { |
| g_object_set (G_OBJECT (element), "sync", FALSE, "async", FALSE, NULL); |
| } |
| |
| gint |
| main (gint argc, gchar * argv[]) |
| { |
| GstElement *pipeline, *src, *toverlay, *osel, *sink1, *sink2, *c1, *c2, *c0; |
| GstPad *sinkpad; |
| GstBus *bus; |
| |
| /* init GStreamer */ |
| gst_init (&argc, &argv); |
| loop = g_main_loop_new (NULL, FALSE); |
| |
| /* create elements */ |
| pipeline = gst_element_factory_make ("pipeline", "pipeline"); |
| src = gst_element_factory_make ("videotestsrc", "src"); |
| c0 = gst_element_factory_make ("videoconvert", NULL); |
| toverlay = gst_element_factory_make ("timeoverlay", "timeoverlay"); |
| osel = gst_element_factory_make ("output-selector", "osel"); |
| c1 = gst_element_factory_make ("videoconvert", NULL); |
| c2 = gst_element_factory_make ("videoconvert", NULL); |
| sink1 = gst_element_factory_make ("autovideosink", "sink1"); |
| sink2 = gst_element_factory_make ("autovideosink", "sink2"); |
| |
| if (!pipeline || !src || !c0 || !toverlay || !osel || !c1 || !c2 || !sink1 || |
| !sink2) { |
| g_print ("missing element\n"); |
| return -1; |
| } |
| |
| /* add them to bin */ |
| gst_bin_add_many (GST_BIN (pipeline), src, c0, toverlay, osel, c1, sink1, c2, |
| sink2, NULL); |
| |
| /* set properties */ |
| g_object_set (G_OBJECT (src), "is-live", TRUE, NULL); |
| g_object_set (G_OBJECT (src), "do-timestamp", TRUE, NULL); |
| g_object_set (G_OBJECT (src), "num-buffers", NUM_VIDEO_BUFFERS, NULL); |
| g_object_set (G_OBJECT (osel), "resend-latest", TRUE, NULL); |
| |
| /* handle deferred properties */ |
| g_signal_connect (G_OBJECT (sink1), "element-added", |
| G_CALLBACK (on_bin_element_added), NULL); |
| g_signal_connect (G_OBJECT (sink2), "element-added", |
| G_CALLBACK (on_bin_element_added), NULL); |
| |
| /* link src ! timeoverlay ! osel */ |
| if (!gst_element_link_many (src, c0, toverlay, osel, NULL)) { |
| g_print ("linking failed\n"); |
| return -1; |
| } |
| |
| /* link output 1 */ |
| sinkpad = gst_element_get_static_pad (c1, "sink"); |
| osel_src1 = gst_element_get_request_pad (osel, "src_%u"); |
| if (gst_pad_link (osel_src1, sinkpad) != GST_PAD_LINK_OK) { |
| g_print ("linking output 1 converter failed\n"); |
| return -1; |
| } |
| gst_object_unref (sinkpad); |
| |
| if (!gst_element_link (c1, sink1)) { |
| g_print ("linking output 1 failed\n"); |
| return -1; |
| } |
| |
| /* link output 2 */ |
| sinkpad = gst_element_get_static_pad (c2, "sink"); |
| osel_src2 = gst_element_get_request_pad (osel, "src_%u"); |
| if (gst_pad_link (osel_src2, sinkpad) != GST_PAD_LINK_OK) { |
| g_print ("linking output 2 converter failed\n"); |
| return -1; |
| } |
| gst_object_unref (sinkpad); |
| |
| if (!gst_element_link (c2, sink2)) { |
| g_print ("linking output 2 failed\n"); |
| return -1; |
| } |
| |
| /* add switch callback */ |
| g_timeout_add_seconds (SWITCH_TIMEOUT, switch_cb, osel); |
| |
| /* change to playing */ |
| bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); |
| gst_bus_add_watch (bus, my_bus_callback, loop); |
| gst_object_unref (bus); |
| |
| gst_element_set_state (pipeline, GST_STATE_PLAYING); |
| |
| /* now run */ |
| g_main_loop_run (loop); |
| |
| /* also clean up */ |
| gst_element_set_state (pipeline, GST_STATE_NULL); |
| gst_element_release_request_pad (osel, osel_src1); |
| gst_element_release_request_pad (osel, osel_src2); |
| gst_object_unref (GST_OBJECT (pipeline)); |
| |
| return 0; |
| } |