examples/switch/switcher.c (loop, my_bus_callback, switch_timer, last_message_receive...
authorZaheer Abbas Merali <zaheerabbas@merali.org>
Thu, 3 May 2007 16:49:05 +0000 (16:49 +0000)
committerZaheer Abbas Merali <zaheerabbas@merali.org>
Thu, 3 May 2007 16:49:05 +0000 (16:49 +0000)
Original commit message from CVS:
* configure.ac:
* examples/Makefile.am:
* examples/switch/switcher.c (loop, my_bus_callback, switch_timer,
last_message_received, main):
* gst/switch/gstswitch.c (GST_CAT_DEFAULT, gst_switch_details,
gst_switch_src_factory, parent_class, gst_switch_release_pad,
gst_switch_request_new_pad, gst_switch_chain, gst_switch_event,
gst_switch_set_property, gst_switch_get_property,
gst_switch_get_linked_pad, gst_switch_getcaps,
gst_switch_bufferalloc, gst_switch_get_linked_pads,
gst_switch_dispose, gst_switch_init, gst_switch_base_init,
gst_switch_class_init):
* gst/switch/gstswitch.h (GstSwitch, GstSwitchClass, _GstSwitch,
element, active_sinkpad, srcpad, nb_sinkpads, newsegment_events,
need_to_send_newsegment):
Port switch element and example program to 0.10.

ChangeLog
configure.ac
examples/Makefile.am
examples/switch/switcher.c
gst/switch/gstswitch.c
gst/switch/gstswitch.h

index 5c71a4580fe59aee4d3b0b74f9bbaf909c799117..52246ba517b45316d5569c90b3028053a80c51bf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2007-05-03  Zaheer Abbas Merali  <<zaheerabbas at merali dot org>>
+
+       * configure.ac:
+       * examples/Makefile.am:
+       * examples/switch/switcher.c (loop, my_bus_callback, switch_timer,
+         last_message_received, main):
+       * gst/switch/gstswitch.c (GST_CAT_DEFAULT, gst_switch_details,
+         gst_switch_src_factory, parent_class, gst_switch_release_pad,
+         gst_switch_request_new_pad, gst_switch_chain, gst_switch_event,
+         gst_switch_set_property, gst_switch_get_property,
+         gst_switch_get_linked_pad, gst_switch_getcaps,
+         gst_switch_bufferalloc, gst_switch_get_linked_pads,
+         gst_switch_dispose, gst_switch_init, gst_switch_base_init,
+         gst_switch_class_init):
+       * gst/switch/gstswitch.h (GstSwitch, GstSwitchClass, _GstSwitch,
+         element, active_sinkpad, srcpad, nb_sinkpads, newsegment_events,
+         need_to_send_newsegment):
+       Port switch element and example program to 0.10.
+
 2007-05-02  Sebastian Dröge  <slomo@circular-chaos.org>
 
        * ext/wavpack/gstwavpack.c: (plugin_init):
index 782fe87e2fc74258d3d0f6019013d3c478b880de..e5f4aa99d1ac58ffcbceb7eec8dbf252cec39e30 100644 (file)
@@ -99,6 +99,7 @@ GST_PLUGINS_ALL="\
   rtpmanager \
   spectrum \
   speed \
+  switch \
   qtdemux \
   tta \
   videocrop \
@@ -1098,6 +1099,7 @@ gst/replaygain/Makefile
 gst/rtpmanager/Makefile
 gst/spectrum/Makefile
 gst/speed/Makefile
+gst/switch/Makefile
 gst/qtdemux/Makefile
 gst/tta/Makefile
 gst/videocrop/Makefile
@@ -1120,6 +1122,7 @@ sys/osxvideo/Makefile
 examples/Makefile
 examples/app/Makefile
 examples/directfb/Makefile
+examples/switch/Makefile
 ext/amrwb/Makefile
 ext/alsaspdif/Makefile
 ext/bz2/Makefile
index 8b069d81ed16f61a7986ecc9e5897493ef85991d..972bc6a623b063ae73084d9b1089aeef14a08434 100644 (file)
@@ -5,5 +5,5 @@ else
 DIRECTFB_DIR=
 endif
 
-SUBDIRS= $(DIRECTFB_DIR) app
-DIST_SUBDIRS= directfb app
+SUBDIRS= $(DIRECTFB_DIR) app switch
+DIST_SUBDIRS= directfb app switch
index 92bf501ec9b0c60b6f37f37d6f8d9dc37981dada..29f1d1a4b2e4f7ed65413287400b19cad7c30cad 100644 (file)
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+#include <string.h>
 
 #include <gst/gst.h>
 
 static GMainLoop *loop = NULL;
 
-
-
-static void
-got_eos (GstElement * pipeline)
-{
-  g_main_loop_quit (loop);
-}
-
 static gboolean
-idle_iterate (GstElement * pipeline)
+my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
 {
-  gst_bin_iterate (GST_BIN (pipeline));
-  return (GST_STATE (GST_ELEMENT (pipeline)) == GST_STATE_PLAYING);
+  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_timer (GstElement * video_switch)
 {
-  gint nb_sources, active_source;
+  gint nb_sources;
+  gchar *active_pad;
 
-  g_object_get (G_OBJECT (video_switch), "nb_sources", &nb_sources, NULL);
-  g_object_get (G_OBJECT (video_switch), "active_source", &active_source, NULL);
+  g_object_get (G_OBJECT (video_switch), "num-sources", &nb_sources, NULL);
+  g_object_get (G_OBJECT (video_switch), "active-pad", &active_pad, NULL);
 
-  active_source++;
+  if (strcmp (active_pad, "sink0") == 0) {
 
-  if (active_source > nb_sources - 1)
-    active_source = 0;
+    g_object_set (G_OBJECT (video_switch), "active-pad", "sink1", NULL);
+  } else {
+    g_object_set (G_OBJECT (video_switch), "active-pad", "sink0", NULL);
+  }
+  g_message ("current number of sources : %d, active source %s",
+      nb_sources, active_pad);
 
-  g_object_set (G_OBJECT (video_switch), "active_source", active_source, NULL);
+  return (GST_STATE (GST_ELEMENT (video_switch)) == GST_STATE_PLAYING);
+}
 
-  g_message ("current number of sources : %d, active source %d",
-      nb_sources, active_source);
+static void
+last_message_received (GObject * segment)
+{
+  gchar *last_message;
 
-  return (GST_STATE (GST_ELEMENT (video_switch)) == GST_STATE_PLAYING);
+  g_object_get (segment, "last_message", &last_message, NULL);
+  g_print ("last-message: %s\n", last_message);
+  g_free (last_message);
 }
 
 int
 main (int argc, char *argv[])
 {
-  GstElement *pipeline, *src1, *src2, *video_switch, *video_sink;
+  GstElement *pipeline, *src1, *src2, *video_switch, *video_sink, *segment;
+  GstElement *sink1_sync, *sink2_sync;
+  GstBus *bus;
 
   /* Initing GStreamer library */
   gst_init (&argc, &argv);
@@ -77,20 +111,30 @@ main (int argc, char *argv[])
   src2 = gst_element_factory_make ("videotestsrc", "src2");
   g_object_set (G_OBJECT (src2), "pattern", 1, NULL);
   video_switch = gst_element_factory_make ("switch", "video_switch");
-  video_sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "video_sink");
-
-  gst_bin_add_many (GST_BIN (pipeline), src1, src2, video_switch,
-      video_sink, NULL);
-
-  gst_element_link (src1, video_switch);
-  gst_element_link (src2, video_switch);
-  gst_element_link (video_switch, video_sink);
-
-  g_signal_connect (G_OBJECT (pipeline), "eos", G_CALLBACK (got_eos), NULL);
-
+  segment = gst_element_factory_make ("identity", "identity-segment");
+  g_signal_connect (G_OBJECT (segment), "notify::last-message",
+      G_CALLBACK (last_message_received), segment);
+  g_object_set (G_OBJECT (segment), "single-segment", TRUE, NULL);
+  video_sink = gst_element_factory_make ("ximagesink", "video_sink");
+  //g_object_set (G_OBJECT (video_sink), "sync", FALSE, NULL);
+  sink1_sync = gst_element_factory_make ("identity", "sink0_sync");
+  g_object_set (G_OBJECT (sink1_sync), "sync", TRUE, NULL);
+  sink2_sync = gst_element_factory_make ("identity", "sink1_sync");
+  g_object_set (G_OBJECT (sink2_sync), "sync", TRUE, NULL);
+  gst_bin_add_many (GST_BIN (pipeline), src1, src2, segment, video_switch,
+      video_sink, sink1_sync, sink2_sync, NULL);
+  gst_element_link (src1, sink1_sync);
+  gst_element_link (sink1_sync, video_switch);
+  gst_element_link (src2, sink2_sync);
+  gst_element_link (sink2_sync, video_switch);
+  gst_element_link (video_switch, segment);
+  gst_element_link (segment, video_sink);
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+  gst_bus_add_watch (bus, my_bus_callback, NULL);
+  gst_object_unref (bus);
   gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
 
-  g_idle_add ((GSourceFunc) idle_iterate, pipeline);
   g_timeout_add (2000, (GSourceFunc) switch_timer, video_switch);
 
   g_main_loop_run (loop);
index 57efa55224d9c729205bb1060462ae88999fe8f2..c832e32f2fa08f42769ab40ea3dc278751ce9aa8 100644 (file)
 
 /* Object header */
 #include "gstswitch.h"
+#include <gst/gst.h>
 
+#include <string.h>
+
+/**
+ * This element allows runtime switching between many sources. It outputs a
+ * new segment every time it switches. The input sources are expected to be
+ * rate controlled/live or synced to the clock using identity sync=true upstream
+ * of this element. If they are not, your cpu usage will hike up.
+ *
+ * Example pipelines:
+ * videotestsrc pattern=0 ! identity sync=true \
+ *                                              switch ! ximagesink
+ * videotestsrc pattern=1 ! identity sync=true /
+ *
+ * videotestsrc pattern=0 ! identity sync=true \
+ *                                              switch ! 
+ *                                               identity single-segment=true !
+ *                                               theoraenc ! oggmux ! filesink
+ * videotestsrc pattern=1 ! identity sync=true /
+ */
 enum
 {
   ARG_0,
@@ -32,13 +52,14 @@ enum
 };
 
 GST_DEBUG_CATEGORY_STATIC (switch_debug);
-
+#define GST_CAT_DEFAULT switch_debug
 /* ElementFactory information */
 static const GstElementDetails gst_switch_details =
 GST_ELEMENT_DETAILS ("Switch",
     "Generic",
     "N-to-1 input switching",
-    "Julien Moutte <julien@moutte.net>");
+    "Julien Moutte <julien@moutte.net>\n"
+    "Zaheer Merali <zaheerabbas at merali dot org>");
 
 static GstStaticPadTemplate gst_switch_sink_factory =
 GST_STATIC_PAD_TEMPLATE ("sink%d",
@@ -46,7 +67,19 @@ GST_STATIC_PAD_TEMPLATE ("sink%d",
     GST_PAD_REQUEST,
     GST_STATIC_CAPS_ANY);
 
+static GstStaticPadTemplate gst_switch_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
 static GstElementClass *parent_class = NULL;
+static GstCaps *gst_switch_getcaps (GstPad * pad);
+static GList *gst_switch_get_linked_pads (GstPad * pad);
+static GstFlowReturn gst_switch_bufferalloc (GstPad * pad, guint64 offset,
+    guint size, GstCaps * caps, GstBuffer ** buf);
+static GstFlowReturn gst_switch_chain (GstPad * pad, GstBuffer * buf);
+static gboolean gst_switch_event (GstPad * pad, GstEvent * event);
 
 /* ============================================================= */
 /*                                                               */
@@ -54,12 +87,11 @@ static GstElementClass *parent_class = NULL;
 /*                                                               */
 /* ============================================================= */
 
+
 static void
 gst_switch_release_pad (GstElement * element, GstPad * pad)
 {
-  GList *sinkpads = NULL;
   GstSwitch *gstswitch = NULL;
-  GstSwitchPad *switchpad = NULL;
 
   g_return_if_fail (GST_IS_SWITCH (element));
 
@@ -67,45 +99,36 @@ gst_switch_release_pad (GstElement * element, GstPad * pad)
 
   GST_LOG_OBJECT (gstswitch, "releasing requested pad %p", pad);
 
-  sinkpads = gstswitch->sinkpads;
-
-  /* Walking through our pad list searching for the pad we want to release */
-  while (sinkpads) {
-    switchpad = sinkpads->data;
-
-    if (switchpad && switchpad->sinkpad == pad)
-      break;
-    else
-      switchpad = NULL;
-
-    sinkpads = g_list_next (sinkpads);
-  }
-
-  /* Releasing the found pad */
-  if (switchpad) {
-    /* We unref the data of that pad to loose our reference */
-    gst_data_unref (switchpad->data);
-    /* If data has not been forwarded we have to destroy it */
-    if (!switchpad->forwarded && switchpad->data) {
-      gst_data_unref (switchpad->data);
+  gst_element_remove_pad (element, pad);
+  GST_OBJECT_LOCK (gstswitch);
+  gstswitch->nb_sinkpads--;
+  if (gstswitch->active_sinkpad == pad) {
+    gst_object_unref (gstswitch->active_sinkpad);
+    gstswitch->active_sinkpad = NULL;
+    if (gstswitch->nb_sinkpads == 0) {
+      GstIterator *iter =
+          gst_element_iterate_sink_pads (GST_ELEMENT (gstswitch));
+      gpointer active_sinkpad_store = (gpointer) gstswitch->active_sinkpad;
+
+      if (gst_iterator_next (iter, &active_sinkpad_store) == GST_ITERATOR_DONE) {
+        GST_LOG_OBJECT (gstswitch, "active pad now %p",
+            gstswitch->active_sinkpad);
+      } else {
+        GST_LOG_OBJECT (gstswitch, "could not get first sinkpad");
+      }
+      gst_iterator_free (iter);
     }
-    gst_element_remove_pad (element, pad);
-    gstswitch->sinkpads = g_list_remove (gstswitch->sinkpads, switchpad);
-    gstswitch->nb_sinkpads--;
-    if (gstswitch->active_sinkpad >= gstswitch->nb_sinkpads)
-      gstswitch->active_sinkpad = 0;
-    g_free (switchpad);
   }
+  GST_OBJECT_UNLOCK (gstswitch);
 }
 
 static GstPad *
 gst_switch_request_new_pad (GstElement * element,
     GstPadTemplate * templ, const gchar * unused)
 {
-  char *name = NULL;
+  gchar *name = NULL;
   GstPad *sinkpad = NULL;
   GstSwitch *gstswitch = NULL;
-  GstSwitchPad *switchpad = NULL;
 
   g_return_val_if_fail (GST_IS_SWITCH (element), NULL);
 
@@ -117,6 +140,7 @@ gst_switch_request_new_pad (GstElement * element,
     return NULL;
   }
 
+  GST_OBJECT_LOCK (gstswitch);
   name = g_strdup_printf ("sink%d", gstswitch->nb_sinkpads);
 
   sinkpad = gst_pad_new_from_template (templ, name);
@@ -124,185 +148,109 @@ gst_switch_request_new_pad (GstElement * element,
   if (name)
     g_free (name);
 
-  /* That pad will proxy caps and link */
-  gst_pad_set_link_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
+  if (gstswitch->active_sinkpad == NULL)
+    gstswitch->active_sinkpad = gst_object_ref (sinkpad);
+  GST_OBJECT_UNLOCK (gstswitch);
+
   gst_pad_set_getcaps_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+      GST_DEBUG_FUNCPTR (gst_switch_getcaps));
+  gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_switch_chain));
+  gst_pad_set_internal_link_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_switch_get_linked_pads));
+  gst_pad_set_bufferalloc_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_switch_bufferalloc));
+  gst_pad_set_event_function (sinkpad, GST_DEBUG_FUNCPTR (gst_switch_event));
+  gst_pad_set_active (sinkpad, TRUE);
 
   gst_element_add_pad (GST_ELEMENT (gstswitch), sinkpad);
 
-  switchpad = g_new0 (GstSwitchPad, 1);
-  if (!switchpad)
-    return NULL;
-
-  switchpad->sinkpad = sinkpad;
-  switchpad->data = NULL;
-  switchpad->forwarded = FALSE;
-  switchpad->eos = FALSE;
-
-  gstswitch->sinkpads = g_list_insert (gstswitch->sinkpads, switchpad,
-      gstswitch->nb_sinkpads);
   gstswitch->nb_sinkpads++;
 
-  if (GST_PAD_CAPS (gstswitch->srcpad)) {
-    gst_pad_try_set_caps (sinkpad, GST_PAD_CAPS (gstswitch->srcpad));
-  }
-
   return sinkpad;
 }
 
-static gboolean
-gst_switch_poll_sinkpads (GstSwitch * gstswitch)
+static GstFlowReturn
+gst_switch_chain (GstPad * pad, GstBuffer * buf)
 {
-  GList *pads;
-
-  g_return_val_if_fail (gstswitch != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_SWITCH (gstswitch), FALSE);
-
-  pads = gstswitch->sinkpads;
-
-  while (pads) {
-    GstSwitchPad *switchpad = pads->data;
-
-    /* We only pull from usable pads and non EOS pads */
-    if (GST_PAD_IS_USABLE (switchpad->sinkpad) && !switchpad->eos) {
-
-      GST_LOG_OBJECT (gstswitch, "polling pad %p", switchpad->sinkpad);
-
-      /* We loose the reference to the data we stored */
-      if (switchpad->data) {
-        gst_data_unref (switchpad->data);
-      }
-
-      /* If that data was not forwarded we unref it another time to destroy it */
-      if (!switchpad->forwarded && switchpad->data) {
-        gst_data_unref (switchpad->data);
-      }
-
-      switchpad->data = NULL;
-      switchpad->data = gst_pad_pull (switchpad->sinkpad);
+  GstSwitch *gstswitch = GST_SWITCH (gst_pad_get_parent (pad));
+  GstFlowReturn res;
+  GstPad *active_sinkpad;
+
+  GST_OBJECT_LOCK (gstswitch);
+  active_sinkpad = gstswitch->active_sinkpad;
+  GST_OBJECT_UNLOCK (gstswitch);
+
+  /* Ignore buffers from pads except the selected one */
+  if (pad != active_sinkpad) {
+    GST_DEBUG_OBJECT (gstswitch, "Ignoring buffer %p from pad %s:%s",
+        buf, GST_DEBUG_PAD_NAME (pad));
+
+    gst_object_unref (gstswitch);
+    gst_buffer_unref (buf);
+    return GST_FLOW_OK;
+  }
 
-      if (!switchpad->data) {
-        GST_LOG_OBJECT (gstswitch,
-            "received NULL data from pad %p", switchpad->sinkpad);
-      } else {
-        gst_data_ref (switchpad->data);
-        switchpad->forwarded = FALSE;
-
-        /* If the buffer is an EOS event we tag the pad as being in EOS. That 
-           means we won't try to pull more data from that pad */
-        if (GST_IS_EVENT (switchpad->data) &&
-            (GST_EVENT_TYPE (GST_EVENT (switchpad->data)) == GST_EVENT_EOS)) {
-          GST_LOG_OBJECT (gstswitch,
-              "received EOS event on pad %p", switchpad->sinkpad);
-          switchpad->eos = TRUE;
-        }
-      }
-    } else {
-      GST_LOG_OBJECT (gstswitch,
-          "not pulling from pad %s (eos is %d)",
-          gst_pad_get_name (switchpad->sinkpad), switchpad->eos);
+  /* check if we need to send a new segment event */
+  GST_OBJECT_LOCK (gstswitch);
+  if (gstswitch->need_to_send_newsegment) {
+    /* retrieve event from hash table */
+    GstEvent *event =
+        (GstEvent *) g_hash_table_lookup (gstswitch->newsegment_events, pad);
+    if (event) {
+      /* create a copy of this event so we can change start to match
+       * the start time of this buffer */
+      gboolean update;
+      gdouble rate, applied_rate;
+      GstFormat format;
+      gint64 start, stop, position;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+          &format, &start, &stop, &position);
+      gst_pad_push_event (gstswitch->srcpad,
+          gst_event_new_new_segment_full (update, rate, applied_rate, format,
+              GST_BUFFER_TIMESTAMP (buf), stop, position));
+      gstswitch->need_to_send_newsegment = FALSE;
+      GST_DEBUG_OBJECT (gstswitch,
+          "Sending new segment with start of %" G_GINT64_FORMAT,
+          GST_BUFFER_TIMESTAMP (buf));
     }
-    pads = g_list_next (pads);
   }
+  GST_OBJECT_UNLOCK (gstswitch);
+  /* forward */
+  GST_DEBUG_OBJECT (gstswitch, "Forwarding buffer %p from pad %s:%s",
+      buf, GST_DEBUG_PAD_NAME (pad));
+  res = gst_pad_push (gstswitch->srcpad, buf);
 
-  return TRUE;
-}
-
-static void
-gst_switch_loop (GstElement * element)
-{
-  GstSwitch *gstswitch = NULL;
-  GstSwitchPad *switchpad = NULL;
-
-  g_return_if_fail (element != NULL);
-  g_return_if_fail (GST_IS_SWITCH (element));
-
-  gstswitch = GST_SWITCH (element);
+  gst_object_unref (gstswitch);
 
-  /* We poll all our sinkpads */
-  gst_switch_poll_sinkpads (gstswitch);
-
-  /* We get the active sinkpad */
-  switchpad = g_list_nth_data (gstswitch->sinkpads, gstswitch->active_sinkpad);
-
-  if (switchpad && switchpad->data) {
-    GstData *data = switchpad->data;
-
-    /* Loose our reference to that data */
-    gst_data_unref (switchpad->data);
-    switchpad->data = NULL;
-
-    GST_LOG_OBJECT (gstswitch,
-        "using data from active pad %p", switchpad->sinkpad);
-
-    if (GST_IS_EVENT (data)) {
-      GstEvent *event = GST_EVENT (data);
-
-      GST_LOG_OBJECT (gstswitch,
-          "handling event from active pad %p", switchpad->sinkpad);
-      /* Handling event */
-      gst_pad_event_default (switchpad->sinkpad, event);
-    } else {
-      /* Pushing active sinkpad data to srcpad */
-      GST_LOG_OBJECT (gstswitch,
-          "pushing data from active pad %p to %p",
-          switchpad->sinkpad, gstswitch->srcpad);
-      gst_pad_push (gstswitch->srcpad, data);
-    }
-
-    /* Mark this data as forwarded so that it won't get unrefed on next poll */
-    switchpad->forwarded = TRUE;
-  }
+  return res;
 }
 
-static GstStateChangeReturn
-gst_switch_change_state (GstElement * element, GstStateChange transition)
+static gboolean
+gst_switch_event (GstPad * pad, GstEvent * event)
 {
-  GstSwitch *gstswitch;
-
-  gstswitch = GST_SWITCH (element);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      break;
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-    {
-      GList *sinkpads = NULL;
-
-      sinkpads = gstswitch->sinkpads;
-
-      while (sinkpads) {
-        GstSwitchPad *switchpad = sinkpads->data;
-
-        /* If a data is still stored in our structure we unref it */
-        if (switchpad->data) {
-          gst_data_unref (switchpad->data);
-          switchpad->data = NULL;
-        }
-
-        switchpad->forwarded = FALSE;
-        switchpad->eos = FALSE;
-
-        sinkpads = g_list_next (sinkpads);
+  GstSwitch *gstswitch = GST_SWITCH (gst_pad_get_parent (pad));
+  gboolean ret = TRUE;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+      GST_OBJECT_LOCK (gstswitch);
+      /* need to put in or replace what's in hash table */
+      g_hash_table_replace (gstswitch->newsegment_events, pad, event);
+      if (pad == gstswitch->active_sinkpad) {
+        /* want to ref event because we have kept it */
+        gst_event_ref (event);
+        /* need to send it across if we are active pad */
+        ret = gst_pad_push_event (gstswitch->srcpad, event);
       }
-    }
+      GST_OBJECT_UNLOCK (gstswitch);
       break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
+    default:
+      ret = gst_pad_event_default (pad, event);
       break;
   }
-
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-  else
-    return GST_STATE_CHANGE_SUCCESS;
+  gst_object_unref (gstswitch);
+  return ret;
 }
 
 /* =========================================== */
@@ -316,6 +264,9 @@ gst_switch_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
   GstSwitch *gstswitch = NULL;
+  const gchar *pad_name;
+  GstPad *pad = NULL;
+  GstPad **active_pad_p;
 
   g_return_if_fail (GST_IS_SWITCH (object));
 
@@ -323,7 +274,27 @@ gst_switch_set_property (GObject * object, guint prop_id,
 
   switch (prop_id) {
     case ARG_ACTIVE_SOURCE:
-      gstswitch->active_sinkpad = g_value_get_int (value);
+      pad_name = g_value_get_string (value);
+      if (strcmp (pad_name, "") != 0) {
+        pad = gst_element_get_pad (GST_ELEMENT (object), pad_name);
+      }
+
+      GST_OBJECT_LOCK (object);
+      if (pad == gstswitch->active_sinkpad) {
+        GST_OBJECT_UNLOCK (object);
+        if (pad)
+          gst_object_unref (pad);
+        break;
+      }
+      active_pad_p = &gstswitch->active_sinkpad;
+      gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
+      if (pad)
+        gst_object_unref (pad);
+
+      GST_DEBUG_OBJECT (gstswitch, "New active pad is %" GST_PTR_FORMAT,
+          gstswitch->active_sinkpad);
+      gstswitch->need_to_send_newsegment = TRUE;
+      GST_OBJECT_UNLOCK (object);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -343,10 +314,17 @@ gst_switch_get_property (GObject * object, guint prop_id,
 
   switch (prop_id) {
     case ARG_ACTIVE_SOURCE:
-      g_value_set_int (value, gstswitch->active_sinkpad);
+      GST_OBJECT_LOCK (object);
+      if (gstswitch->active_sinkpad != NULL) {
+        g_value_take_string (value,
+            gst_pad_get_name (gstswitch->active_sinkpad));
+      } else {
+        g_value_set_string (value, "");
+      }
+      GST_OBJECT_UNLOCK (object);
       break;
     case ARG_NB_SOURCES:
-      g_value_set_int (value, gstswitch->nb_sinkpads);
+      g_value_set_uint (value, gstswitch->nb_sinkpads);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -354,6 +332,105 @@ gst_switch_get_property (GObject * object, guint prop_id,
   }
 }
 
+static GstPad *
+gst_switch_get_linked_pad (GstPad * pad, gboolean strict)
+{
+  GstSwitch *gstswitch = GST_SWITCH (gst_pad_get_parent (pad));
+  GstPad *otherpad = NULL;
+
+  if (pad == gstswitch->srcpad)
+    otherpad = gstswitch->active_sinkpad;
+  else if (pad == gstswitch->active_sinkpad || !strict)
+    otherpad = gstswitch->srcpad;
+
+  gst_object_unref (gstswitch);
+
+  return otherpad;
+}
+
+static GstCaps *
+gst_switch_getcaps (GstPad * pad)
+{
+  GstPad *otherpad = gst_switch_get_linked_pad (pad, FALSE);
+  GstObject *parent;
+  GstCaps *caps;
+
+  parent = gst_object_get_parent (GST_OBJECT (pad));
+  if (!otherpad) {
+    GST_DEBUG_OBJECT (parent,
+        "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
+
+    gst_object_unref (parent);
+    return gst_caps_new_any ();
+  }
+
+  GST_DEBUG_OBJECT (parent,
+      "Pad %s:%s is linked (to %s:%s), returning allowed-caps",
+      GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
+
+  gst_object_unref (parent);
+
+  caps = gst_pad_peer_get_caps (otherpad);
+  if (caps == NULL) {
+    caps = gst_caps_new_any ();
+  }
+  return caps;
+}
+
+static GstFlowReturn
+gst_switch_bufferalloc (GstPad * pad, guint64 offset,
+    guint size, GstCaps * caps, GstBuffer ** buf)
+{
+  GstSwitch *gstswitch = GST_SWITCH (gst_pad_get_parent (pad));
+  GstFlowReturn result;
+  GstPad *active_sinkpad;
+
+  GST_OBJECT_LOCK (gstswitch);
+  active_sinkpad = gstswitch->active_sinkpad;
+  GST_OBJECT_UNLOCK (gstswitch);
+
+  /* Fallback allocation for buffers from pads except the selected one */
+  if (pad != active_sinkpad) {
+    GST_DEBUG_OBJECT (gstswitch,
+        "Pad %s:%s is not selected. Performing fallback allocation",
+        GST_DEBUG_PAD_NAME (pad));
+
+    *buf = NULL;
+    result = GST_FLOW_OK;
+  } else {
+    result = gst_pad_alloc_buffer (gstswitch->srcpad, offset, size, caps, buf);
+
+    /* FIXME: HACK. If buffer alloc returns not-linked, perform a fallback
+     * allocation.  This should NOT be necessary, because playbin should
+     * properly block the source pad from running until it's finished hooking 
+     * everything up, but playbin needs refactoring first. */
+    if (result == GST_FLOW_NOT_LINKED) {
+      GST_DEBUG_OBJECT (gstswitch,
+          "No peer pad yet - performing fallback allocation for pad %s:%s",
+          GST_DEBUG_PAD_NAME (pad));
+
+      *buf = NULL;
+      result = GST_FLOW_OK;
+    }
+  }
+
+  gst_object_unref (gstswitch);
+
+  return result;
+}
+
+static GList *
+gst_switch_get_linked_pads (GstPad * pad)
+{
+  GstPad *otherpad = gst_switch_get_linked_pad (pad, TRUE);
+
+  if (!otherpad)
+    return NULL;
+
+  return g_list_append (NULL, otherpad);
+}
+
+
 /* =========================================== */
 /*                                             */
 /*              Init & Class init              */
@@ -364,33 +441,16 @@ static void
 gst_switch_dispose (GObject * object)
 {
   GstSwitch *gstswitch = NULL;
-  GList *sinkpads = NULL;
 
   gstswitch = GST_SWITCH (object);
 
-  sinkpads = gstswitch->sinkpads;
-
-  while (sinkpads) {
-    GstSwitchPad *switchpad = sinkpads->data;
-
-    /* If a data is still stored in our structure we unref it */
-    if (switchpad->data) {
-      gst_data_unref (switchpad->data);
-      switchpad->data = NULL;
-    }
-
-    /* Freeing our structure */
-    g_free (switchpad);
-
-    sinkpads = g_list_next (sinkpads);
+  if (gstswitch->active_sinkpad) {
+    gst_object_unref (gstswitch->active_sinkpad);
+    gstswitch->active_sinkpad = NULL;
   }
-
-  /* Freeing the list correctly */
-  if (gstswitch->sinkpads) {
-    g_list_free (gstswitch->sinkpads);
-    gstswitch->sinkpads = NULL;
+  if (gstswitch->newsegment_events) {
+    g_hash_table_destroy (gstswitch->newsegment_events);
   }
-
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -398,16 +458,17 @@ static void
 gst_switch_init (GstSwitch * gstswitch)
 {
   gstswitch->srcpad = gst_pad_new ("src", GST_PAD_SRC);
-  gst_element_add_pad (GST_ELEMENT (gstswitch), gstswitch->srcpad);
-  gst_pad_set_link_function (gstswitch->srcpad,
-      GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link));
+  gst_pad_set_internal_link_function (gstswitch->srcpad,
+      GST_DEBUG_FUNCPTR (gst_switch_get_linked_pads));
   gst_pad_set_getcaps_function (gstswitch->srcpad,
-      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
-  gst_element_set_loop_function (GST_ELEMENT (gstswitch), gst_switch_loop);
+      GST_DEBUG_FUNCPTR (gst_switch_getcaps));
+  gst_element_add_pad (GST_ELEMENT (gstswitch), gstswitch->srcpad);
 
-  gstswitch->sinkpads = NULL;
-  gstswitch->active_sinkpad = 0;
+  gstswitch->active_sinkpad = NULL;
   gstswitch->nb_sinkpads = 0;
+  gstswitch->newsegment_events = g_hash_table_new_full (g_direct_hash,
+      g_direct_equal, NULL, (GDestroyNotify) gst_mini_object_unref);
+  gstswitch->need_to_send_newsegment = FALSE;
 }
 
 static void
@@ -419,6 +480,9 @@ gst_switch_base_init (gpointer g_class)
 
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&gst_switch_sink_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_switch_src_factory));
+
 }
 
 static void
@@ -431,23 +495,22 @@ gst_switch_class_init (GstSwitchClass * klass)
   gstelement_class = (GstElementClass *) klass;
 
   parent_class = g_type_class_peek_parent (klass);
+  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_switch_set_property);
+  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_switch_get_property);
 
   g_object_class_install_property (gobject_class,
       ARG_NB_SOURCES,
-      g_param_spec_int ("nb_sources",
+      g_param_spec_uint ("num-sources",
           "number of sources",
-          "number of sources", G_MININT, G_MAXINT, 0, G_PARAM_READABLE));
+          "number of sources", 0, G_MAXUINT, 0, G_PARAM_READABLE));
   g_object_class_install_property (gobject_class,
       ARG_ACTIVE_SOURCE,
-      g_param_spec_int ("active_source",
-          "active source",
-          "active source", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
+      g_param_spec_string ("active-pad",
+          "Active Pad",
+          "Name of the currently active sink pad", NULL, G_PARAM_READWRITE));
 
   gobject_class->dispose = gst_switch_dispose;
-  gobject_class->set_property = gst_switch_set_property;
-  gobject_class->get_property = gst_switch_get_property;
 
-  gstelement_class->change_state = gst_switch_change_state;
   gstelement_class->request_new_pad = gst_switch_request_new_pad;
   gstelement_class->release_pad = gst_switch_release_pad;
 }
index ed6b2f2717825e2964386d3369183ce114c063d3..b546ba2963eec58feb7765bc33eb981b53d0f216 100644 (file)
@@ -35,26 +35,24 @@ G_BEGIN_DECLS
 #define GST_IS_SWITCH_CLASS(klass) \
   (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SWITCH))
 
-typedef struct _GstSwitchPad GstSwitchPad;
-
 typedef struct _GstSwitch GstSwitch;
 typedef struct _GstSwitchClass GstSwitchClass;
 
-struct _GstSwitchPad {
-  GstPad *sinkpad;
-  GstData *data;
-  gboolean forwarded;
-  gboolean eos;
-};
-
 struct _GstSwitch {
   GstElement element;
   
-  GList *sinkpads;
+  GstPad *active_sinkpad;
   GstPad *srcpad;
   
   guint nb_sinkpads;
-  guint active_sinkpad;
+  /* this hash table stores for key of the pad pointer
+   * the last new segment event received for this pad
+   * so when switching we can send new segment events
+   */
+  GHashTable *newsegment_events;
+  /* flag to decide whether we need to send a new segment event
+   * before we receive the next buffer */
+  gboolean need_to_send_newsegment;
 };
 
 struct _GstSwitchClass {