static gboolean
gst_output_selector_forward_event (GstOutputSelector * sel, GstEvent * event)
{
  gboolean res = TRUE;
  GstPad *active;

  switch (sel->pad_negotiation_mode) {
    case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL:
      /* Send to all src pads */
      res = gst_pad_event_default (sel->sinkpad, GST_OBJECT_CAST (sel), 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;
  }

  return res;
}
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;
    }
    case GST_QUERY_DRAIN:
      if (sel->latest_buffer) {
        gst_buffer_unref (sel->latest_buffer);
        sel->latest_buffer = NULL;
      }
      /* fall through */
    default:
      res = gst_pad_query_default (pad, parent, query);
      break;
  }

  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_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 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;
}