static gboolean
gst_output_selector_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
  gboolean res = TRUE;
  GstOutputSelector *sel;
  GstPad *active = NULL;

  sel = GST_OUTPUT_SELECTOR (parent);

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CAPS:
    {
      switch (sel->pad_negotiation_mode) {
        case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL:
          /* Send caps to all src pads */
          res = gst_pad_proxy_query_caps (pad, query);
          break;
        case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE:
          res = FALSE;
          break;
        default:
          active = gst_output_selector_get_active (sel);
          if (active) {
            res = gst_pad_peer_query (active, query);
            gst_object_unref (active);
          } else {
            res = FALSE;
          }
          break;
      }
      break;
    }
    default:
      res = gst_pad_query_default (pad, parent, query);
      break;
  }
  return res;
}
static void
gst_output_selector_release_pad (GstElement * element, GstPad * pad)
{
  GstOutputSelector *osel;

  osel = GST_OUTPUT_SELECTOR (element);

  GST_DEBUG_OBJECT (osel, "releasing pad");

  /* Disable active pad if it's the to be removed pad */
  GST_OBJECT_LOCK (osel);
  if (osel->active_srcpad == pad) {
    osel->active_srcpad = NULL;
    GST_OBJECT_UNLOCK (osel);
    g_object_notify (G_OBJECT (osel), "active-pad");
  } else {
    GST_OBJECT_UNLOCK (osel);
  }

  gst_pad_set_active (pad, FALSE);

  gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
}
static gboolean
gst_output_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  gboolean res = TRUE;
  GstOutputSelector *sel;
  GstPad *active = NULL;

  sel = GST_OUTPUT_SELECTOR (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_EOS:
    {
      res = gst_output_selector_forward_event (sel, event);
      break;
    }
    case GST_EVENT_SEGMENT:
    {
      gst_event_copy_segment (event, &sel->segment);
      GST_DEBUG_OBJECT (sel, "configured SEGMENT %" GST_SEGMENT_FORMAT,
          &sel->segment);
      /* fall through */
    }
    default:
    {
      active = gst_output_selector_get_active (sel);
      if (active) {
        res = gst_pad_push_event (active, event);
        gst_object_unref (active);
      } else {
        gst_event_unref (event);
      }
      break;
    }
  }

  return res;
}
static GstPad *
gst_output_selector_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
{
  gchar *padname;
  GstPad *srcpad;
  GstOutputSelector *osel;

  osel = GST_OUTPUT_SELECTOR (element);

  GST_DEBUG_OBJECT (osel, "requesting pad");

  GST_OBJECT_LOCK (osel);
  padname = g_strdup_printf ("src_%u", osel->nb_srcpads++);
  srcpad = gst_pad_new_from_template (templ, padname);
  GST_OBJECT_UNLOCK (osel);

  gst_pad_set_active (srcpad, TRUE);

  /* Forward sticky events to the new srcpad */
  gst_pad_sticky_events_foreach (osel->sinkpad, forward_sticky_events, srcpad);

  gst_element_add_pad (GST_ELEMENT (osel), srcpad);

  /* Set the first requested src pad as active by default */
  GST_OBJECT_LOCK (osel);
  if (osel->active_srcpad == NULL) {
    osel->active_srcpad = srcpad;
    GST_OBJECT_UNLOCK (osel);
    g_object_notify (G_OBJECT (osel), "active-pad");
  } else {
    GST_OBJECT_UNLOCK (osel);
  }
  g_free (padname);

  return srcpad;
}
static GstFlowReturn
gst_output_selector_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstFlowReturn res;
  GstOutputSelector *osel;
  GstClockTime position, duration;
  GstPad *active_srcpad;

  osel = GST_OUTPUT_SELECTOR (parent);

  /*
   * The _switch function might push a buffer if 'resend-latest' is true.
   *
   * Elements/Applications (e.g. camerabin) might use pad probes to
   * switch output-selector's active pad. If we simply switch and don't
   * recheck any pending pad switch the following codepath could end
   * up pushing a buffer on a non-active pad. This is bad.
   *
   * So we always should check the pending_srcpad before going further down
   * the chain and pushing the new buffer
   */
  while (osel->pending_srcpad) {
    /* Do the switch */
    gst_output_selector_switch (osel);
  }

  active_srcpad = gst_output_selector_get_active (osel);
  if (!active_srcpad) {
    GST_DEBUG_OBJECT (osel, "No active srcpad");
    gst_buffer_unref (buf);
    return GST_FLOW_OK;
  }

  GST_OBJECT_LOCK (osel);
  if (osel->latest_buffer) {
    gst_buffer_unref (osel->latest_buffer);
    osel->latest_buffer = NULL;
  }

  if (osel->resend_latest) {
    /* Keep reference to latest buffer to resend it after switch */
    osel->latest_buffer = gst_buffer_ref (buf);
  }
  GST_OBJECT_UNLOCK (osel);

  /* Keep track of last stop and use it in SEGMENT start after
     switching to a new src pad */
  position = GST_BUFFER_TIMESTAMP (buf);
  if (GST_CLOCK_TIME_IS_VALID (position)) {
    duration = GST_BUFFER_DURATION (buf);
    if (GST_CLOCK_TIME_IS_VALID (duration)) {
      position += duration;
    }
    GST_LOG_OBJECT (osel, "setting last stop %" GST_TIME_FORMAT,
        GST_TIME_ARGS (position));
    osel->segment.position = position;
  }

  GST_LOG_OBJECT (osel, "pushing buffer to %" GST_PTR_FORMAT, active_srcpad);
  res = gst_pad_push (active_srcpad, buf);

  gst_object_unref (active_srcpad);

  return res;
}
static gboolean
gst_output_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  gboolean res = TRUE;
  GstOutputSelector *sel;
  GstPad *active = NULL;

  sel = GST_OUTPUT_SELECTOR (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      switch (sel->pad_negotiation_mode) {
        case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL:
          /* Send caps to all src pads */
          res = gst_pad_event_default (pad, parent, event);
          break;
        case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE:
          gst_event_unref (event);
          break;
        default:
          active = gst_output_selector_get_active (sel);
          if (active) {
            res = gst_pad_push_event (active, event);
            gst_object_unref (active);
          } else {
            gst_event_unref (event);
          }
          break;
      }
      break;
    }
    case GST_EVENT_SEGMENT:
    {
      gst_event_copy_segment (event, &sel->segment);

      GST_DEBUG_OBJECT (sel, "configured SEGMENT %" GST_SEGMENT_FORMAT,
          &sel->segment);

      /* Send newsegment to all src pads */
      res = gst_pad_event_default (pad, parent, event);
      break;
    }
    case GST_EVENT_EOS:
      /* Send eos to all src pads */
      res = gst_pad_event_default (pad, parent, event);
      break;
    default:
    {
      /* Send other events to pending or active src pad */
      active = gst_output_selector_get_active (sel);
      if (active) {
        res = gst_pad_push_event (active, event);
        gst_object_unref (active);
      } else {
        gst_event_unref (event);
      }
      break;
    }
  }

  return res;
}