Example #1
0
/*
 * helper_find_peek:
 * @data: helper data struct
 * @off: stream offset
 * @size: block size
 *
 * Get data pointer within a stream. Keeps a cache of read buffers (partly
 * for performance reasons, but mostly because pointers returned by us need
 * to stay valid until typefinding has finished)
 *
 * Returns: address of the data or %NULL if buffer does not cover the
 * requested range.
 */
static guint8 *
helper_find_peek (gpointer data, gint64 offset, guint size)
{
  GstTypeFindHelper *helper;
  GstBuffer *buffer;
  GstFlowReturn ret;
  GSList *insert_pos = NULL;
  guint buf_size;
  guint64 buf_offset;
  GstCaps *caps;

  helper = (GstTypeFindHelper *) data;

  GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT
      ", %u)", GST_PLUGIN_FEATURE_NAME (helper->factory), offset, size);

  if (size == 0)
    return NULL;

  if (offset < 0) {
    if (helper->size == -1 || helper->size < -offset)
      return NULL;

    offset += helper->size;
  }

  /* see if we have a matching buffer already in our list */
  if (size > 0 && offset <= helper->last_offset) {
    GSList *walk;

    for (walk = helper->buffers; walk; walk = walk->next) {
      GstBuffer *buf = GST_BUFFER_CAST (walk->data);
      guint64 buf_offset = GST_BUFFER_OFFSET (buf);
      guint buf_size = GST_BUFFER_SIZE (buf);

      /* buffers are kept sorted by end offset (highest first) in the list, so
       * at this point we save the current position and stop searching if 
       * we're after the searched end offset */
      if (buf_offset <= offset) {
        if ((offset + size) < (buf_offset + buf_size)) {
          return GST_BUFFER_DATA (buf) + (offset - buf_offset);
        }
      } else if (offset + size >= buf_offset + buf_size) {
        insert_pos = walk;
        break;
      }
    }
  }

  buffer = NULL;
  /* some typefinders go in 1 byte steps over 1k of data and request
   * small buffers. It is really inefficient to pull each time, and pulling
   * a larger chunk is almost free. Trying to pull a larger chunk at the end
   * of the file is also not a problem here, we'll just get a truncated buffer
   * in that case (and we'll have to double-check the size we actually get
   * anyway, see below) */
  ret = helper->func (helper->obj, offset, MAX (size, 4096), &buffer);

  if (ret != GST_FLOW_OK)
    goto error;

  caps = GST_BUFFER_CAPS (buffer);

  if (caps && !gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) {
    GST_DEBUG ("buffer has caps %" GST_PTR_FORMAT ", suggest max probability",
        caps);

    gst_caps_replace (&helper->caps, caps);
    helper->best_probability = GST_TYPE_FIND_MAXIMUM;

    gst_buffer_unref (buffer);
    return NULL;
  }

  /* getrange might silently return shortened buffers at the end of a file,
   * we must, however, always return either the full requested data or NULL */
  buf_offset = GST_BUFFER_OFFSET (buffer);
  buf_size = GST_BUFFER_SIZE (buffer);

  if ((buf_offset != -1 && buf_offset != offset) || buf_size < size) {
    GST_DEBUG ("dropping short buffer: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT
        " instead of %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
        buf_offset, buf_offset + buf_size - 1, offset, offset + size - 1);
    gst_buffer_unref (buffer);
    return NULL;
  }

  if (insert_pos) {
    helper->buffers =
        g_slist_insert_before (helper->buffers, insert_pos, buffer);
  } else {
    /* if insert_pos is not set, our offset is bigger than the largest offset
     * we have so far; since we keep the list sorted with highest offsets
     * first, we need to prepend the buffer to the list */
    helper->last_offset = GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer);
    helper->buffers = g_slist_prepend (helper->buffers, buffer);
  }
  return GST_BUFFER_DATA (buffer);

error:
  {
    GST_INFO ("typefind function returned: %s", gst_flow_get_name (ret));
    return NULL;
  }
}
Example #2
0
/*
 * helper_find_peek:
 * @data: helper data struct
 * @off: stream offset
 * @size: block size
 *
 * Get data pointer within a stream. Keeps a cache of read buffers (partly
 * for performance reasons, but mostly because pointers returned by us need
 * to stay valid until typefinding has finished)
 *
 * Returns: (nullable): address of the data or %NULL if buffer does not cover
 * the requested range.
 */
static const guint8 *
helper_find_peek (gpointer data, gint64 offset, guint size)
{
  GstTypeFindHelper *helper;
  GstBuffer *buffer;
  GSList *insert_pos = NULL;
  gsize buf_size;
  guint64 buf_offset;
  GstMappedBuffer *bmap;
#if 0
  GstCaps *caps;
#endif

  helper = (GstTypeFindHelper *) data;

  GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT
      ", %u)", GST_OBJECT_NAME (helper->factory), offset, size);

  if (size == 0)
    return NULL;

  if (offset < 0) {
    if (helper->size == -1 || helper->size < -offset)
      return NULL;

    offset += helper->size;
  }

  /* see if we have a matching buffer already in our list */
  if (size > 0 && offset <= helper->last_offset) {
    GSList *walk;

    for (walk = helper->buffers; walk; walk = walk->next) {
      GstMappedBuffer *bmp = (GstMappedBuffer *) walk->data;
      GstBuffer *buf = GST_BUFFER_CAST (bmp->buffer);

      buf_offset = GST_BUFFER_OFFSET (buf);
      buf_size = bmp->map.size;

      /* buffers are kept sorted by end offset (highest first) in the list, so
       * at this point we save the current position and stop searching if
       * we're after the searched end offset */
      if (buf_offset <= offset) {
        if ((offset + size) < (buf_offset + buf_size)) {
          /* must already have been mapped before */
          return (guint8 *) bmp->map.data + (offset - buf_offset);
        }
      } else if (offset + size >= buf_offset + buf_size) {
        insert_pos = walk;
        break;
      }
    }
  }

  buffer = NULL;
  /* some typefinders go in 1 byte steps over 1k of data and request
   * small buffers. It is really inefficient to pull each time, and pulling
   * a larger chunk is almost free. Trying to pull a larger chunk at the end
   * of the file is also not a problem here, we'll just get a truncated buffer
   * in that case (and we'll have to double-check the size we actually get
   * anyway, see below) */
  helper->flow_ret =
      helper->func (helper->obj, helper->parent, offset, MAX (size, 4096),
      &buffer);

  if (helper->flow_ret != GST_FLOW_OK)
    goto error;

#if 0
  caps = GST_BUFFER_CAPS (buffer);

  if (caps && !gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) {
    GST_DEBUG ("buffer has caps %" GST_PTR_FORMAT ", suggest max probability",
        caps);

    gst_caps_replace (&helper->caps, caps);
    helper->best_probability = GST_TYPE_FIND_MAXIMUM;

    gst_buffer_unref (buffer);
    return NULL;
  }
#endif

  /* getrange might silently return shortened buffers at the end of a file,
   * we must, however, always return either the full requested data or %NULL */
  buf_offset = GST_BUFFER_OFFSET (buffer);
  buf_size = gst_buffer_get_size (buffer);

  if (buf_size < size) {
    GST_DEBUG ("dropping short buffer of size %" G_GSIZE_FORMAT ","
        "requested size was %u", buf_size, size);
    gst_buffer_unref (buffer);
    return NULL;
  }

  if (buf_offset != -1 && buf_offset != offset) {
    GST_DEBUG ("dropping buffer with unexpected offset %" G_GUINT64_FORMAT ", "
        "expected offset was %" G_GUINT64_FORMAT, buf_offset, offset);
    gst_buffer_unref (buffer);
    return NULL;
  }

  bmap = g_slice_new0 (GstMappedBuffer);

  if (!gst_buffer_map (buffer, &bmap->map, GST_MAP_READ))
    goto map_failed;

  bmap->buffer = buffer;

  if (insert_pos) {
    helper->buffers = g_slist_insert_before (helper->buffers, insert_pos, bmap);
  } else {
    /* if insert_pos is not set, our offset is bigger than the largest offset
     * we have so far; since we keep the list sorted with highest offsets
     * first, we need to prepend the buffer to the list */
    helper->last_offset = GST_BUFFER_OFFSET (buffer) + buf_size;
    helper->buffers = g_slist_prepend (helper->buffers, bmap);
  }

  return bmap->map.data;

error:
  {
    GST_INFO ("typefind function returned: %s",
        gst_flow_get_name (helper->flow_ret));
    return NULL;
  }
map_failed:
  {
    GST_ERROR ("map failed");
    gst_buffer_unref (buffer);
    g_slice_free (GstMappedBuffer, bmap);
    return NULL;
  }
}