static GstStateChangeReturn
gst_type_find_element_change_state (GstElement * element,
    GstStateChange transition)
{
  GstStateChangeReturn ret;
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (element);


  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
    case GST_STATE_CHANGE_READY_TO_NULL:
      GST_OBJECT_LOCK (typefind);
      gst_caps_replace (&typefind->caps, NULL);

      g_list_foreach (typefind->cached_events,
          (GFunc) gst_mini_object_unref, NULL);
      g_list_free (typefind->cached_events);
      typefind->cached_events = NULL;
      typefind->mode = MODE_TYPEFIND;
      GST_OBJECT_UNLOCK (typefind);
      break;
    default:
      break;
  }

  return ret;
}
static gboolean
gst_type_find_element_activate_sink_mode (GstPad * pad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean res;
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  switch (mode) {
    case GST_PAD_MODE_PULL:
      if (active) {
        gst_segment_init (&typefind->segment, GST_FORMAT_BYTES);
        typefind->need_segment = TRUE;
        typefind->need_stream_start = TRUE;
        typefind->offset = 0;
        res = TRUE;
      } else {
        res = gst_pad_stop_task (pad);
      }
      break;
    case GST_PAD_MODE_PUSH:
      if (active)
        start_typefinding (typefind);
      else
        stop_typefinding (typefind);

      res = TRUE;
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}
Пример #3
0
static GstFlowReturn
gst_type_find_element_chain (GstPad * pad, GstBuffer * buffer)
{
  GstTypeFindElement *typefind;
  GstFlowReturn res = GST_FLOW_OK;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  switch (typefind->mode) {
    case MODE_ERROR:
      /* we should already have called GST_ELEMENT_ERROR */
      return GST_FLOW_ERROR;
    case MODE_NORMAL:
      gst_buffer_set_caps (buffer, typefind->caps);
      return gst_pad_push (typefind->src, buffer);
    case MODE_TYPEFIND:{
      if (typefind->store)
        typefind->store = gst_buffer_join (typefind->store, buffer);
      else
        typefind->store = buffer;

      res = gst_type_find_element_chain_do_typefinding (typefind);

      if (typefind->mode == MODE_ERROR)
        res = GST_FLOW_ERROR;

      break;
    }
    default:
      g_assert_not_reached ();
      return GST_FLOW_ERROR;
  }

  return res;
}
Пример #4
0
static gboolean
gst_type_find_element_setcaps (GstPad * pad, GstCaps * caps)
{
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  /* don't operate on ANY caps */
  if (gst_caps_is_any (caps))
    return TRUE;

  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
      GST_TYPE_FIND_MAXIMUM, caps);

  /* Shortcircuit typefinding if we get caps */
  if (typefind->mode == MODE_TYPEFIND) {
    GST_DEBUG_OBJECT (typefind, "Skipping typefinding, using caps from "
        "upstream buffer: %" GST_PTR_FORMAT, caps);
    typefind->mode = MODE_NORMAL;

    gst_type_find_element_send_cached_events (typefind);
    if (typefind->store) {
      GST_DEBUG_OBJECT (typefind, "Pushing store: %d",
          GST_BUFFER_SIZE (typefind->store));
      gst_buffer_set_caps (typefind->store, typefind->caps);
      gst_pad_push (typefind->src, typefind->store);
      typefind->store = NULL;
    }
  }

  return TRUE;
}
static void
gst_type_find_element_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (object);

  switch (prop_id) {
    case PROP_CAPS:
      GST_OBJECT_LOCK (typefind);
      g_value_set_boxed (value, typefind->caps);
      GST_OBJECT_UNLOCK (typefind);
      break;
    case PROP_MINIMUM:
      g_value_set_uint (value, typefind->min_probability);
      break;
    case PROP_FORCE_CAPS:
      GST_OBJECT_LOCK (typefind);
      g_value_set_boxed (value, typefind->force_caps);
      GST_OBJECT_UNLOCK (typefind);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
static gboolean
gst_type_find_element_activate_src_mode (GstPad * pad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean res;
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  switch (mode) {
    case GST_PAD_MODE_PULL:
      /* make sure our task stops pushing, we can't call _stop here because this
       * activation might happen from the streaming thread. */
      gst_pad_pause_task (typefind->sink);
      res = gst_pad_activate_mode (typefind->sink, mode, active);
      if (active && res && typefind->caps) {
        GstCaps *caps;
        GST_OBJECT_LOCK (typefind);
        caps = gst_caps_ref (typefind->caps);
        GST_OBJECT_UNLOCK (typefind);
        res = gst_pad_set_caps (typefind->src, caps);
        gst_caps_unref (caps);
      }
      break;
    default:
      res = TRUE;
      break;
  }
  return res;
}
Пример #7
0
static gboolean
gst_type_find_element_activate_src_pull (GstPad * pad, gboolean active)
{
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));

  return gst_pad_activate_pull (typefind->sink, active);
}
Пример #8
0
static gboolean
gst_type_find_element_checkgetrange (GstPad * srcpad)
{
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));

  return gst_pad_check_pull_range (typefind->sink);
}
Пример #9
0
static gboolean
gst_type_find_element_src_event (GstPad * pad, GstEvent * event)
{
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  if (typefind->mode != MODE_NORMAL) {
    /* need to do more? */
    gst_mini_object_unref (GST_MINI_OBJECT (event));
    return FALSE;
  }
  return gst_pad_push_event (typefind->sink, event);
}
static GstFlowReturn
gst_type_find_element_getrange (GstPad * srcpad, GstObject * parent,
    guint64 offset, guint length, GstBuffer ** buffer)
{
  GstTypeFindElement *typefind;
  GstFlowReturn ret;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  ret = gst_pad_pull_range (typefind->sink, offset, length, buffer);

  return ret;
}
Пример #11
0
static gboolean
gst_type_find_handle_src_query (GstPad * pad, GstQuery * query)
{
  GstTypeFindElement *typefind;
  gboolean res = FALSE;
  GstPad *peer;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  peer = gst_pad_get_peer (typefind->sink);
  if (peer == NULL)
    return FALSE;

  res = gst_pad_query (peer, query);
  if (!res)
    goto out;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
      gint64 peer_pos;
      GstFormat format;

      if (typefind->store == NULL)
        goto out;

      gst_query_parse_position (query, &format, &peer_pos);

      /* FIXME: this code assumes that there's no discont in the queue */
      switch (format) {
        case GST_FORMAT_BYTES:
          peer_pos -= GST_BUFFER_SIZE (typefind->store);
          break;
        default:
          /* FIXME */
          break;
      }
      gst_query_set_position (query, format, peer_pos);
      break;
    }
    default:
      break;
  }

out:
  gst_object_unref (peer);
  return res;
}
Пример #12
0
static GstFlowReturn
gst_type_find_element_getrange (GstPad * srcpad,
    guint64 offset, guint length, GstBuffer ** buffer)
{
  GstTypeFindElement *typefind;
  GstFlowReturn ret;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (srcpad));

  ret = gst_pad_pull_range (typefind->sink, offset, length, buffer);

  if (ret == GST_FLOW_OK && buffer && *buffer)
    gst_buffer_set_caps (*buffer, typefind->caps);

  return ret;
}
Пример #13
0
static void
gst_type_find_element_dispose (GObject * object)
{
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (object);

  if (typefind->store) {
    gst_buffer_unref (typefind->store);
    typefind->store = NULL;
  }
  if (typefind->force_caps) {
    gst_caps_unref (typefind->force_caps);
    typefind->force_caps = NULL;
  }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
gst_type_find_element_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (parent);

  if (typefind->mode != MODE_NORMAL) {
    /* need to do more? */
    gst_mini_object_unref (GST_MINI_OBJECT_CAST (event));
    return FALSE;
  }

  /* Only handle seeks here if driving the pipeline */
  if (typefind->segment.format != GST_FORMAT_UNDEFINED &&
      GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
    return gst_type_find_element_seek (typefind, event);
  } else {
    return gst_pad_push_event (typefind->sink, event);
  }
}
static GstFlowReturn
gst_type_find_element_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buffer)
{
  GstTypeFindElement *typefind;
  GstFlowReturn res = GST_FLOW_OK;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  GST_LOG_OBJECT (typefind, "handling buffer in mode %d", typefind->mode);

  switch (typefind->mode) {
    case MODE_ERROR:
      /* we should already have called GST_ELEMENT_ERROR */
      return GST_FLOW_ERROR;
    case MODE_NORMAL:
      /* don't take object lock as typefind->caps should not change anymore */
      return gst_pad_push (typefind->src, buffer);
    case MODE_TYPEFIND:
    {
      GST_OBJECT_LOCK (typefind);
      if (typefind->initial_offset == GST_BUFFER_OFFSET_NONE)
        typefind->initial_offset = GST_BUFFER_OFFSET (buffer);
      gst_adapter_push (typefind->adapter, buffer);
      GST_OBJECT_UNLOCK (typefind);

      res = gst_type_find_element_chain_do_typefinding (typefind, TRUE, FALSE);

      if (typefind->mode == MODE_ERROR)
        res = GST_FLOW_ERROR;

      break;
    }
    default:
      g_assert_not_reached ();
      return GST_FLOW_ERROR;
  }

  return res;
}
static gboolean
gst_type_find_element_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean res = FALSE;
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (parent);

  GST_DEBUG_OBJECT (typefind, "got %s event in mode %d",
      GST_EVENT_TYPE_NAME (event), typefind->mode);

  switch (typefind->mode) {
    case MODE_TYPEFIND:
      switch (GST_EVENT_TYPE (event)) {
        case GST_EVENT_CAPS:
        {
          GstCaps *caps;

          /* Parse and push out our caps and data */
          gst_event_parse_caps (event, &caps);
          res = gst_type_find_element_setcaps (typefind, caps);

          gst_event_unref (event);
          break;
        }
        case GST_EVENT_GAP:
        {
          GST_FIXME_OBJECT (typefind,
              "GAP events during typefinding not handled properly");

          /* FIXME: These would need to be inserted in the stream at
           * the right position between buffers, but we combine all
           * buffers with a GstAdapter. Drop the GAP event for now,
           * which will only cause an implicit GAP between buffers.
           */
          gst_event_unref (event);
          res = TRUE;
          break;
        }
        case GST_EVENT_EOS:
        {
          GST_INFO_OBJECT (typefind, "Got EOS and no type found yet");
          gst_type_find_element_chain_do_typefinding (typefind, FALSE, TRUE);

          res = gst_pad_push_event (typefind->src, event);
          break;
        }
        case GST_EVENT_FLUSH_STOP:{
          GList *l;

          GST_OBJECT_LOCK (typefind);

          for (l = typefind->cached_events; l; l = l->next) {
            if (GST_EVENT_IS_STICKY (l->data) &&
                GST_EVENT_TYPE (l->data) != GST_EVENT_SEGMENT &&
                GST_EVENT_TYPE (l->data) != GST_EVENT_EOS) {
              gst_pad_store_sticky_event (typefind->src, l->data);
            }
            gst_event_unref (l->data);
          }

          g_list_free (typefind->cached_events);
          typefind->cached_events = NULL;
          gst_adapter_clear (typefind->adapter);
          GST_OBJECT_UNLOCK (typefind);
          /* fall through */
        }
        case GST_EVENT_FLUSH_START:
          res = gst_pad_push_event (typefind->src, event);
          break;
        default:
          /* Forward events that would happen before the caps event
           * directly instead of storing them. There's no reason not
           * to send them directly and we should only store events
           * for later sending that would need to come after the caps
           * event */
          if (GST_EVENT_TYPE (event) < GST_EVENT_CAPS) {
            res = gst_pad_push_event (typefind->src, event);
          } else {
            GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
                GST_EVENT_TYPE_NAME (event));
            GST_OBJECT_LOCK (typefind);
            typefind->cached_events =
                g_list_append (typefind->cached_events, event);
            GST_OBJECT_UNLOCK (typefind);
            res = TRUE;
          }
          break;
      }
      break;
    case MODE_NORMAL:
      res = gst_pad_push_event (typefind->src, event);
      break;
    case MODE_ERROR:
      break;
    default:
      g_assert_not_reached ();
  }
  return res;
}
static gboolean
gst_type_find_element_activate_sink (GstPad * pad, GstObject * parent)
{
  GstTypeFindElement *typefind;
  GstQuery *query;
  gboolean pull_mode;
  GstCaps *found_caps = NULL;
  GstTypeFindProbability probability = GST_TYPE_FIND_NONE;
  GstSchedulingFlags sched_flags;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  /* if we have force caps, use those */
  GST_OBJECT_LOCK (typefind);
  if (typefind->force_caps) {
    found_caps = gst_caps_ref (typefind->force_caps);
    probability = GST_TYPE_FIND_MAXIMUM;
    GST_OBJECT_UNLOCK (typefind);

    GST_DEBUG ("Emiting found caps %" GST_PTR_FORMAT, found_caps);
    g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
        0, probability, found_caps);
    typefind->mode = MODE_NORMAL;
    /* the signal above could have made a downstream element activate
     * the pad in pull mode, we check if the pad is already active now and if
     * so, we are done */
    if (gst_pad_is_active (pad))
      return TRUE;

    goto typefind_push;
  }
  GST_OBJECT_UNLOCK (typefind);

  query = gst_query_new_scheduling ();

  if (!gst_pad_peer_query (pad, query)) {
    gst_query_unref (query);
    goto typefind_push;
  }

  gst_query_parse_scheduling (query, &sched_flags, NULL, NULL, NULL);

  pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL)
      && ((sched_flags & GST_SCHEDULING_FLAG_SEEKABLE) != 0);

  gst_query_unref (query);

  if (!pull_mode)
    goto typefind_push;

  if (!gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE))
    goto typefind_push;

  /* only start our task if we ourselves decide to start in pull mode */
  return gst_pad_start_task (pad, (GstTaskFunction) gst_type_find_element_loop,
      pad, NULL);

typefind_push:
  {
    return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
  }
}
static gboolean
gst_type_find_handle_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query)
{
  GstTypeFindElement *typefind;
  gboolean res = FALSE;

  typefind = GST_TYPE_FIND_ELEMENT (parent);
  GST_DEBUG_OBJECT (typefind, "Handling src query %s",
      GST_QUERY_TYPE_NAME (query));

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_SCHEDULING:
      /* FIXME, filter out the scheduling modes that we understand */
      res = gst_pad_peer_query (typefind->sink, query);
      break;
    case GST_QUERY_CAPS:
    {
      GST_DEBUG_OBJECT (typefind,
          "Got caps query, our caps are %" GST_PTR_FORMAT, typefind->caps);

      /* We can hijack caps query if we typefind already */
      if (typefind->caps) {
        gst_query_set_caps_result (query, typefind->caps);
        res = TRUE;
      } else {
        res = gst_pad_peer_query (typefind->sink, query);
      }
      break;
    }
    case GST_QUERY_POSITION:
    {
      gint64 peer_pos;
      GstFormat format;

      if (!(res = gst_pad_peer_query (typefind->sink, query)))
        goto out;

      gst_query_parse_position (query, &format, &peer_pos);

      GST_OBJECT_LOCK (typefind);
      /* FIXME: this code assumes that there's no discont in the queue */
      switch (format) {
        case GST_FORMAT_BYTES:
          peer_pos -= gst_adapter_available (typefind->adapter);
          break;
        default:
          /* FIXME */
          break;
      }
      GST_OBJECT_UNLOCK (typefind);
      gst_query_set_position (query, format, peer_pos);
      break;
    }
    default:
      res = gst_pad_query_default (pad, parent, query);
      break;
  }
out:
  return res;
}
Пример #19
0
static gboolean
gst_type_find_element_handle_event (GstPad * pad, GstEvent * event)
{
  gboolean res = FALSE;
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  GST_DEBUG_OBJECT (typefind, "got %s event in mode %d",
      GST_EVENT_TYPE_NAME (event), typefind->mode);

  switch (typefind->mode) {
    case MODE_TYPEFIND:
      switch (GST_EVENT_TYPE (event)) {
        case GST_EVENT_EOS:{
          GstTypeFindProbability prob = 0;
          GstCaps *caps = NULL;

          GST_INFO_OBJECT (typefind, "Got EOS and no type found yet");

          /* we might not have started typefinding yet because there was not
           * enough data so far; just give it a shot now and see what we get */
          if (typefind->store) {
            caps = gst_type_find_helper_for_buffer (GST_OBJECT (typefind),
                typefind->store, &prob);

            if (caps && prob >= typefind->min_probability) {
              g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
                  0, prob, caps);
            } else {
              GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
                  (NULL), (NULL));
            }
            gst_caps_replace (&caps, NULL);
          } else {
            /* keep message in sync with the one in the pad activate function */
            GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
                (_("Stream contains no data.")),
                ("Can't typefind empty stream"));
          }

          stop_typefinding (typefind);
          res = gst_pad_push_event (typefind->src, event);
          break;
        }
        case GST_EVENT_FLUSH_STOP:
          g_list_foreach (typefind->cached_events,
              (GFunc) gst_mini_object_unref, NULL);
          g_list_free (typefind->cached_events);
          typefind->cached_events = NULL;
          gst_buffer_replace (&typefind->store, NULL);
          /* fall through */
        case GST_EVENT_FLUSH_START:
          res = gst_pad_push_event (typefind->src, event);
          break;
        default:
          GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
              GST_EVENT_TYPE_NAME (event));
          typefind->cached_events =
              g_list_append (typefind->cached_events, event);
          res = TRUE;
          break;
      }
      break;
    case MODE_NORMAL:
      res = gst_pad_push_event (typefind->src, event);
      break;
    case MODE_ERROR:
      break;
    default:
      g_assert_not_reached ();
  }
  return res;
}
Пример #20
0
static gboolean
gst_type_find_element_activate (GstPad * pad)
{
  GstTypeFindProbability probability = 0;
  GstCaps *found_caps = NULL;
  GstTypeFindElement *typefind;

  typefind = GST_TYPE_FIND_ELEMENT (GST_OBJECT_PARENT (pad));

  /* if we have force caps, use those */
  if (typefind->force_caps) {
    found_caps = gst_caps_ref (typefind->force_caps);
    probability = GST_TYPE_FIND_MAXIMUM;
    goto done;
  }

  /* 1. try to activate in pull mode. if not, switch to push and succeed.
     2. try to pull type find.
     3. deactivate pull mode.
     4. src pad might have been activated push by the state change. deactivate.
     5. if we didn't find any caps, try getting the uri extension by doing an uri
     query.
     6. if we didn't find any caps, fail.
     7. emit have-type; maybe the app connected the source pad to something.
     8. if the sink pad is activated, we are in pull mode. succeed.
     otherwise activate both pads in push mode and succeed.
   */

  /* 1 */
  if (!gst_pad_check_pull_range (pad) || !gst_pad_activate_pull (pad, TRUE)) {
    start_typefinding (typefind);
    return gst_pad_activate_push (pad, TRUE);
  }

  /* 2 */
  {
    GstPad *peer;

    peer = gst_pad_get_peer (pad);
    if (peer) {
      gint64 size;
      GstFormat format = GST_FORMAT_BYTES;

      if (!gst_pad_query_duration (peer, &format, &size)) {
        GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
        gst_object_unref (peer);
        return FALSE;
      }

      /* the size if 0, we cannot continue */
      if (size == 0) {
        /* keep message in sync with message in sink event handler */
        GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
            (_("Stream contains no data.")), ("Can't typefind empty stream"));
        gst_object_unref (peer);
        return FALSE;
      }

      found_caps = gst_type_find_helper_get_range (GST_OBJECT_CAST (peer),
          (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
          (guint64) size, &probability);

      gst_object_unref (peer);
    }
  }

  /* 3 */
  gst_pad_activate_pull (pad, FALSE);

  /* 4 */
  gst_pad_activate_push (typefind->src, FALSE);

  /* 5 */
  if (!found_caps || probability < typefind->min_probability) {
    found_caps = gst_type_find_guess_by_extension (typefind, pad, &probability);
  }

  /* 6 */
  if (!found_caps || probability < typefind->min_probability) {
    GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
    gst_caps_replace (&found_caps, NULL);
    return FALSE;
  }

done:
  /* 7 */
  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
      0, probability, found_caps);
  gst_caps_unref (found_caps);
  typefind->mode = MODE_NORMAL;

  /* 8 */
  if (gst_pad_is_active (pad))
    return TRUE;
  else {
    gboolean ret;

    ret = gst_pad_activate_push (typefind->src, TRUE);
    ret &= gst_pad_activate_push (pad, TRUE);
    return ret;
  }
}
static void
gst_type_find_element_loop (GstPad * pad)
{
  GstTypeFindElement *typefind;
  GstFlowReturn ret = GST_FLOW_OK;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  if (typefind->need_stream_start) {
    gchar *stream_id;
    GstEvent *event;

    stream_id = gst_pad_create_stream_id (typefind->src,
        GST_ELEMENT_CAST (typefind), NULL);

    GST_DEBUG_OBJECT (typefind, "Pushing STREAM_START");
    event = gst_event_new_stream_start (stream_id);
    gst_event_set_group_id (event, gst_util_group_id_next ());
    gst_pad_push_event (typefind->src, event);

    typefind->need_stream_start = FALSE;
    g_free (stream_id);
  }

  if (typefind->mode == MODE_TYPEFIND) {
    GstPad *peer = NULL;
    GstCaps *found_caps = NULL;
    GstTypeFindProbability probability = GST_TYPE_FIND_NONE;

    GST_DEBUG_OBJECT (typefind, "find type in pull mode");

    GST_OBJECT_LOCK (typefind);
    if (typefind->force_caps) {
      found_caps = gst_caps_ref (typefind->force_caps);
      probability = GST_TYPE_FIND_MAXIMUM;
    }
    GST_OBJECT_UNLOCK (typefind);

    if (!found_caps) {
      peer = gst_pad_get_peer (pad);
      if (peer) {
        gint64 size;
        gchar *ext;

        if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &size)) {
          GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
          gst_object_unref (peer);

          ret = GST_FLOW_ERROR;
          goto pause;
        }

        /* the size if 0, we cannot continue */
        if (size == 0) {
          /* keep message in sync with message in sink event handler */
          GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
              (_("Stream contains no data.")), ("Can't typefind empty stream"));
          gst_object_unref (peer);
          ret = GST_FLOW_ERROR;
          goto pause;
        }
        ext = gst_type_find_get_extension (typefind, pad);

        found_caps =
            gst_type_find_helper_get_range (GST_OBJECT_CAST (peer),
            GST_OBJECT_PARENT (peer),
            (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
            (guint64) size, ext, &probability);
        g_free (ext);

        GST_DEBUG ("Found caps %" GST_PTR_FORMAT, found_caps);

        gst_object_unref (peer);
      }
    }

    if (!found_caps || probability < typefind->min_probability) {
      GST_DEBUG ("Trying to guess using extension");
      gst_caps_replace (&found_caps, NULL);
      found_caps =
          gst_type_find_guess_by_extension (typefind, pad, &probability);
    }

    if (!found_caps || probability < typefind->min_probability) {
      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
      gst_caps_replace (&found_caps, NULL);
      ret = GST_FLOW_ERROR;
      goto pause;
    }

    GST_DEBUG ("Emiting found caps %" GST_PTR_FORMAT, found_caps);
    gst_type_find_element_emit_have_type (typefind, probability, found_caps);
    typefind->mode = MODE_NORMAL;
    gst_caps_unref (found_caps);
  } else if (typefind->mode == MODE_NORMAL) {
    GstBuffer *outbuf = NULL;

    if (typefind->need_segment) {
      typefind->need_segment = FALSE;
      gst_pad_push_event (typefind->src,
          gst_event_new_segment (&typefind->segment));
    }

    /* Pull 4k blocks and send downstream */
    ret = gst_pad_pull_range (typefind->sink, typefind->offset, 4096, &outbuf);
    if (ret != GST_FLOW_OK)
      goto pause;

    typefind->offset += gst_buffer_get_size (outbuf);

    ret = gst_pad_push (typefind->src, outbuf);
    if (ret != GST_FLOW_OK)
      goto pause;
  } else {
    /* Error out */
    ret = GST_FLOW_ERROR;
    goto pause;
  }

  return;

pause:
  {
    const gchar *reason = gst_flow_get_name (ret);
    gboolean push_eos = FALSE;

    GST_LOG_OBJECT (typefind, "pausing task, reason %s", reason);
    gst_pad_pause_task (typefind->sink);

    if (ret == GST_FLOW_EOS) {
      /* perform EOS logic */

      if (typefind->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
        gint64 stop;

        /* for segment playback we need to post when (in stream time)
         * we stopped, this is either stop (when set) or the duration. */
        if ((stop = typefind->segment.stop) == -1)
          stop = typefind->offset;

        GST_LOG_OBJECT (typefind, "Sending segment done, at end of segment");
        gst_element_post_message (GST_ELEMENT (typefind),
            gst_message_new_segment_done (GST_OBJECT (typefind),
                GST_FORMAT_BYTES, stop));
        gst_pad_push_event (typefind->src,
            gst_event_new_segment_done (GST_FORMAT_BYTES, stop));
      } else {
        push_eos = TRUE;
      }
    } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
      /* for fatal errors we post an error message */
      GST_ELEMENT_ERROR (typefind, STREAM, FAILED, (NULL),
          ("stream stopped, reason %s", reason));
      push_eos = TRUE;
    }
    if (push_eos) {
      /* send EOS, and prevent hanging if no streams yet */
      GST_LOG_OBJECT (typefind, "Sending EOS, at end of stream");
      gst_pad_push_event (typefind->src, gst_event_new_eos ());
    }
    return;
  }
}
static gboolean
gst_type_find_element_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  gboolean res = FALSE;
  GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (parent);

  GST_DEBUG_OBJECT (typefind, "got %s event in mode %d",
      GST_EVENT_TYPE_NAME (event), typefind->mode);

  switch (typefind->mode) {
    case MODE_TYPEFIND:
      switch (GST_EVENT_TYPE (event)) {
        case GST_EVENT_CAPS:
        {
          GstCaps *caps;

          /* first pass the caps event downstream */
          res = gst_pad_push_event (typefind->src, gst_event_ref (event));

          /* then parse and push out our data */
          gst_event_parse_caps (event, &caps);
          res = gst_type_find_element_setcaps (typefind, caps);

          gst_event_unref (event);
          break;
        }
        case GST_EVENT_EOS:
        {
          GST_INFO_OBJECT (typefind, "Got EOS and no type found yet");
          gst_type_find_element_chain_do_typefinding (typefind, FALSE);

          res = gst_pad_push_event (typefind->src, event);
          break;
        }
        case GST_EVENT_FLUSH_STOP:
          GST_OBJECT_LOCK (typefind);
          g_list_foreach (typefind->cached_events,
              (GFunc) gst_mini_object_unref, NULL);
          g_list_free (typefind->cached_events);
          typefind->cached_events = NULL;
          gst_adapter_clear (typefind->adapter);
          GST_OBJECT_UNLOCK (typefind);
          /* fall through */
        case GST_EVENT_FLUSH_START:
          res = gst_pad_push_event (typefind->src, event);
          break;
        default:
          GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
              GST_EVENT_TYPE_NAME (event));
          GST_OBJECT_LOCK (typefind);
          typefind->cached_events =
              g_list_append (typefind->cached_events, event);
          GST_OBJECT_UNLOCK (typefind);
          res = TRUE;
          break;
      }
      break;
    case MODE_NORMAL:
      res = gst_pad_push_event (typefind->src, event);
      break;
    case MODE_ERROR:
      break;
    default:
      g_assert_not_reached ();
  }
  return res;
}