Exemple #1
0
static GstFlowReturn
vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf)
{
    GstFlowReturn ret = GST_FLOW_OK;
    long blocksize;
    ogg_packet packet;

    buf = gst_buffer_make_metadata_writable (buf);

    packet.packet = GST_BUFFER_DATA (buf);
    packet.bytes = GST_BUFFER_SIZE (buf);
    packet.granulepos = GST_BUFFER_OFFSET_END (buf);
    packet.packetno = parse->packetno + parse->buffer_queue->length;
    packet.e_o_s = 0;

    blocksize = vorbis_packet_blocksize (&parse->vi, &packet);

    /* temporarily store the sample count in OFFSET -- we overwrite this later */

    if (parse->prev_blocksize < 0)
        GST_BUFFER_OFFSET (buf) = 0;
    else
        GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4;

    parse->prev_blocksize = blocksize;

    g_queue_push_tail (parse->buffer_queue, buf);

    if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
        ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf));

    return ret;
}
static GstFlowReturn
gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf)
{
    GstFlowReturn ret = GST_FLOW_OK;
    gint64 granpos;

    buf = gst_buffer_make_metadata_writable (buf);

    /* oggdemux stores the granule pos in the offset end */
    granpos = GST_BUFFER_OFFSET_END (buf);
    GST_LOG_OBJECT (parse, "granpos %16" G_GINT64_MODIFIER "x", granpos);
    g_queue_push_tail (parse->buffer_queue, buf);

#if 1
    /* if getting buffers from matroska, we won't have a granpos here... */
    //if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) {
    ret = gst_kate_parse_drain_queue (parse, granpos);
    //}
#else
    if (granpos >= 0) {
        ret = gst_kate_parse_drain_queue (parse, granpos);
    } else {
        GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL),
                           ("Bad granulepos %" G_GINT64_FORMAT, granpos));
        ret = GST_FLOW_ERROR;
    }
#endif

    return ret;
}
Exemple #3
0
static GstFlowReturn
gst_valve_chain (GstPad * pad, GstBuffer * buffer)
{
  GstValve *valve = GST_VALVE (GST_OBJECT_PARENT (pad));
  GstFlowReturn ret = GST_FLOW_OK;

  if (g_atomic_int_get (&valve->drop)) {
    gst_buffer_unref (buffer);
    valve->discont = TRUE;
  } else {
    if (valve->discont) {
      buffer = gst_buffer_make_metadata_writable (buffer);
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
      valve->discont = FALSE;
    }

    ret = gst_pad_push (valve->srcpad, buffer);
  }


  /* Ignore errors if "drop" was changed while the thread was blocked
   * downwards
   */
  if (g_atomic_int_get (&valve->drop))
    ret = GST_FLOW_OK;

  return ret;
}
Exemple #4
0
static GstFlowReturn
gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
{
  GstFlowReturn ret;

  if (rdtdepay->need_newsegment) {
    GstEvent *event;

    event = create_segment_event (rdtdepay, FALSE, 0);
    gst_pad_push_event (rdtdepay->srcpad, event);

    rdtdepay->need_newsegment = FALSE;
  }

  buffer = gst_buffer_make_metadata_writable (buffer);
  gst_buffer_set_caps (buffer, GST_PAD_CAPS (rdtdepay->srcpad));

  if (rdtdepay->discont) {
    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
    rdtdepay->discont = FALSE;
  }
  ret = gst_pad_push (rdtdepay->srcpad, buffer);

  return ret;
}
static GstFlowReturn
gst_inter_sub_src_create (GstBaseSrc * src, guint64 offset, guint size,
    GstBuffer ** buf)
{
  GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (src);
  GstBuffer *buffer;

  GST_DEBUG_OBJECT (intersubsrc, "create");

  buffer = NULL;

  g_mutex_lock (intersubsrc->surface->mutex);
  if (intersubsrc->surface->sub_buffer) {
    buffer = gst_buffer_ref (intersubsrc->surface->sub_buffer);
    //intersubsrc->surface->sub_buffer_count++;
    //if (intersubsrc->surface->sub_buffer_count >= 30) {
    gst_buffer_unref (intersubsrc->surface->sub_buffer);
    intersubsrc->surface->sub_buffer = NULL;
    //}
  }
  g_mutex_unlock (intersubsrc->surface->mutex);

  if (buffer == NULL) {
    guint8 *data;

    buffer = gst_buffer_new_and_alloc (1);

    data = GST_BUFFER_DATA (buffer);
    data[0] = 0;
  }

  buffer = gst_buffer_make_metadata_writable (buffer);

  GST_BUFFER_TIMESTAMP (buffer) =
      gst_util_uint64_scale_int (GST_SECOND, intersubsrc->n_frames,
      intersubsrc->rate);
  GST_DEBUG_OBJECT (intersubsrc, "create ts %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
  GST_BUFFER_DURATION (buffer) =
      gst_util_uint64_scale_int (GST_SECOND, (intersubsrc->n_frames + 1),
      intersubsrc->rate) - GST_BUFFER_TIMESTAMP (buffer);
  GST_BUFFER_OFFSET (buffer) = intersubsrc->n_frames;
  GST_BUFFER_OFFSET_END (buffer) = -1;
  GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
  if (intersubsrc->n_frames == 0) {
    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
  }
  gst_buffer_set_caps (buffer, GST_PAD_CAPS (GST_BASE_SRC_PAD (intersubsrc)));
  intersubsrc->n_frames++;

  *buf = buffer;

  return GST_FLOW_OK;
}
static GstFlowReturn
gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer)
{
  GstTagLibMux *mux = GST_TAG_LIB_MUX (GST_OBJECT_PARENT (pad));

  if (mux->render_tag) {
    GstFlowReturn ret;
    GstBuffer *tag_buffer;

    GST_INFO_OBJECT (mux, "Adding tags to stream");
    tag_buffer = gst_tag_lib_mux_render_tag (mux);
    if (tag_buffer == NULL)
      goto no_tag_buffer;
    ret = gst_pad_push (mux->srcpad, tag_buffer);
    if (ret != GST_FLOW_OK) {
      GST_DEBUG_OBJECT (mux, "flow: %s", gst_flow_get_name (ret));
      gst_buffer_unref (buffer);
      return ret;
    }

    /* Now send the cached newsegment event that we got from upstream */
    if (mux->newsegment_ev) {
      GST_DEBUG_OBJECT (mux, "sending cached newsegment event");
      gst_pad_push_event (mux->srcpad,
          gst_tag_lib_mux_adjust_event_offsets (mux, mux->newsegment_ev));
      gst_event_unref (mux->newsegment_ev);
      mux->newsegment_ev = NULL;
    } else {
      /* upstream sent no newsegment event or only one in a non-BYTE format */
    }

    mux->render_tag = FALSE;
  }

  buffer = gst_buffer_make_metadata_writable (buffer);

  if (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE) {
    GST_LOG_OBJECT (mux, "Adjusting buffer offset from %" G_GINT64_FORMAT
        " to %" G_GINT64_FORMAT, GST_BUFFER_OFFSET (buffer),
        GST_BUFFER_OFFSET (buffer) + mux->tag_size);
    GST_BUFFER_OFFSET (buffer) += mux->tag_size;
  }

  gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->srcpad));
  return gst_pad_push (mux->srcpad, buffer);

/* ERRORS */
no_tag_buffer:
  {
    GST_ELEMENT_ERROR (mux, LIBRARY, ENCODE, (NULL), (NULL));
    return GST_FLOW_ERROR;
  }
}
static GstFlowReturn
gst_fake_h264_decoder_sink_chain (GstPad * pad, GstBuffer * buf)
{
  GstElement *self = GST_ELEMENT (gst_pad_get_parent (pad));
  GstPad *otherpad = gst_element_get_static_pad (self, "src");
  GstFlowReturn ret = GST_FLOW_OK;

  buf = gst_buffer_make_metadata_writable (buf);
  gst_buffer_set_caps (buf, GST_PAD_CAPS (otherpad));

  ret = gst_pad_push (otherpad, buf);

  gst_object_unref (otherpad);
  gst_object_unref (self);

  return ret;
}
static GstFlowReturn
gst_y4m_encode_chain (GstPad * pad, GstBuffer * buf)
{
  GstY4mEncode *filter = GST_Y4M_ENCODE (GST_PAD_PARENT (pad));
  GstBuffer *outbuf;
  GstClockTime timestamp;

  /* check we got some decent info from caps */
  if (filter->width < 0) {
    GST_ELEMENT_ERROR ("filter", CORE, NEGOTIATION, (NULL),
        ("format wasn't negotiated before chain function"));
    gst_buffer_unref (buf);
    return GST_FLOW_NOT_NEGOTIATED;
  }

  timestamp = GST_BUFFER_TIMESTAMP (buf);

  if (G_UNLIKELY (!filter->header)) {
    if (filter->interlaced == TRUE) {
      if (GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_TFF)) {
        filter->top_field_first = TRUE;
      } else {
        filter->top_field_first = FALSE;
      }
    }
    outbuf = gst_y4m_encode_get_stream_header (filter);
    filter->header = TRUE;
    outbuf = gst_buffer_join (outbuf, gst_y4m_encode_get_frame_header (filter));
  } else {
    outbuf = gst_y4m_encode_get_frame_header (filter);
  }
  /* join with data */
  outbuf = gst_buffer_join (outbuf, buf);
  /* decorate */
  gst_buffer_make_metadata_writable (outbuf);
  gst_buffer_set_caps (outbuf, GST_PAD_CAPS (filter->srcpad));

  GST_BUFFER_TIMESTAMP (outbuf) = timestamp;

  return gst_pad_push (filter->srcpad, outbuf);
}
Exemple #9
0
static GstFlowReturn
gst_identity_prepare_output_buffer (GstBaseTransform * trans,
    GstBuffer * in_buf, gint out_size, GstCaps * out_caps, GstBuffer ** out_buf)
{
  GstIdentity *identity = GST_IDENTITY (trans);

  /* only bother if we may have to alter metadata */
  if (identity->datarate > 0 || identity->single_segment) {
    if (gst_buffer_is_metadata_writable (in_buf))
      *out_buf = gst_buffer_ref (in_buf);
    else {
      /* make even less writable */
      gst_buffer_ref (in_buf);
      /* extra ref is dropped going through the official process */
      *out_buf = gst_buffer_make_metadata_writable (in_buf);
    }
  } else
    *out_buf = gst_buffer_ref (in_buf);

  return GST_FLOW_OK;
}
Exemple #10
0
static GstFlowReturn
gst_wavenc_chain (GstPad * pad, GstBuffer * buf)
{
  GstWavEnc *wavenc = GST_WAVENC (GST_PAD_PARENT (pad));
  GstFlowReturn flow = GST_FLOW_OK;

  g_return_val_if_fail (wavenc->channels > 0, GST_FLOW_WRONG_STATE);

  if (!wavenc->sent_header) {
    /* use bogus size initially, we'll write the real
     * header when we get EOS and know the exact length */
    flow = gst_wavenc_push_header (wavenc, 0x7FFF0000);

    if (flow != GST_FLOW_OK)
      return flow;

    GST_DEBUG_OBJECT (wavenc, "wrote dummy header");
    wavenc->sent_header = TRUE;
  }

  wavenc->length += GST_BUFFER_SIZE (buf);

  GST_LOG_OBJECT (wavenc, "pushing %u bytes raw audio, ts=%" GST_TIME_FORMAT,
      GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));

  if (wavenc->width != wavenc->depth) {
    buf = gst_buffer_make_writable (buf);
    gst_wavenc_format_samples (buf, wavenc->width, wavenc->depth);
  } else {
    buf = gst_buffer_make_metadata_writable (buf);
  }

  gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad));
  GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length;
  GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;

  flow = gst_pad_push (wavenc->srcpad, buf);

  return flow;
}
GstBuffer *
rsn_meta_wrapped_unwrap_and_unref (GstBuffer * wrap_buf, RsnMetaWrapped * meta)
{
  GstBuffer *buf;
  gboolean is_readonly;

  g_return_val_if_fail (wrap_buf != NULL, NULL);
  g_return_val_if_fail (meta->wrapped_buffer != NULL, NULL);

  buf = gst_buffer_ref (meta->wrapped_buffer);
  buf = gst_buffer_make_metadata_writable (buf);

  /* Copy changed metadata back to the wrapped buffer from the wrapper,
   * except the the read-only flag and the caps. */
  is_readonly = GST_BUFFER_FLAG_IS_SET (wrap_buf, GST_BUFFER_FLAG_READONLY);
  gst_buffer_copy_metadata (buf, GST_BUFFER (wrap_buf),
      GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
  if (!is_readonly)
    GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_READONLY);

  gst_buffer_unref (wrap_buf);

  return buf;
}
Exemple #12
0
static GstFlowReturn
rsn_parsetter_chain (GstPad * pad, GstBuffer * buf)
{
  RsnParSetter *parset = RSN_PARSETTER (GST_OBJECT_PARENT (pad));

  /* If this is a buffer we wrapped up earlier, unwrap it now */
  if (RSN_IS_WRAPPEDBUFFER (buf)) {
    RsnWrappedBuffer *wrap_buf = RSN_WRAPPEDBUFFER (buf);

    if (wrap_buf->owner == GST_ELEMENT (parset)) {
      buf = rsn_wrappedbuffer_unwrap_and_unref (wrap_buf);
      GST_DEBUG_OBJECT (parset, "Unwrapping %p yields buffer %p with caps %"
          GST_PTR_FORMAT, wrap_buf, buf, GST_BUFFER_CAPS (buf));
    }
  }

  if (parset->outcaps != GST_BUFFER_CAPS (buf)) {
    if (parset->override_outcaps == FALSE &&
        gst_caps_is_equal (parset->outcaps, GST_BUFFER_CAPS (buf))) {
      /* Just update our output caps var */
      gst_caps_replace (&parset->outcaps, GST_BUFFER_CAPS (buf));
      goto out;
    }

    /* Replace the caps on the output buffer */
    buf = gst_buffer_make_metadata_writable (buf);
    gst_buffer_set_caps (buf, parset->outcaps);

    GST_DEBUG_OBJECT (parset,
        "Replacing caps on buffer %p with caps %" GST_PTR_FORMAT,
        buf, parset->outcaps);
  }

out:
  return gst_pad_push (parset->srcpad, buf);
}
Exemple #13
0
EXPORT_C
#endif

GstBuffer *
gst_audio_buffer_clip (GstBuffer * buffer, GstSegment * segment, gint rate,
    gint frame_size)
{
  GstBuffer *ret;
  GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
  guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE;
  guint8 *data;
  guint size;

  gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end =
      TRUE;

  g_return_val_if_fail (segment->format == GST_FORMAT_TIME ||
      segment->format == GST_FORMAT_DEFAULT, buffer);
  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);

  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
    /* No timestamp - assume the buffer is completely in the segment */
    return buffer;

  /* Get copies of the buffer metadata to change later. 
   * Calculate the missing values for the calculations,
   * they won't be changed later though. */

  data = GST_BUFFER_DATA (buffer);
  size = GST_BUFFER_SIZE (buffer);

  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
    duration = GST_BUFFER_DURATION (buffer);
  } else {
    change_duration = FALSE;
    duration = gst_util_uint64_scale (size / frame_size, GST_SECOND, rate);
  }

  if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
    offset = GST_BUFFER_OFFSET (buffer);
  } else {
    change_offset = FALSE;
    offset = 0;
  }

  if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) {
    offset_end = GST_BUFFER_OFFSET_END (buffer);
  } else {
    change_offset_end = FALSE;
    offset_end = offset + size / frame_size;
  }

  if (segment->format == GST_FORMAT_TIME) {
    /* Handle clipping for GST_FORMAT_TIME */

    gint64 start, stop, cstart, cstop, diff;

    start = timestamp;
    stop = timestamp + duration;

    if (gst_segment_clip (segment, GST_FORMAT_TIME,
            start, stop, &cstart, &cstop)) {

      diff = cstart - start;
      if (diff > 0) {
        timestamp = cstart;

        if (change_duration)
          duration -= diff;

        diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
        if (change_offset)
          offset += diff;
        data += diff * frame_size;
        size -= diff * frame_size;
      }

      diff = stop - cstop;
      if (diff > 0) {
        /* duration is always valid if stop is valid */
        duration -= diff;

        diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
        if (change_offset_end)
          offset_end -= diff;
        size -= diff * frame_size;
      }
    } else {
      gst_buffer_unref (buffer);
      return NULL;
    }
  } else {
    /* Handle clipping for GST_FORMAT_DEFAULT */
    gint64 start, stop, cstart, cstop, diff;

    g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer);

    start = offset;
    stop = offset_end;

    if (gst_segment_clip (segment, GST_FORMAT_DEFAULT,
            start, stop, &cstart, &cstop)) {

      diff = cstart - start;
      if (diff > 0) {
        offset = cstart;

        timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate);

        if (change_duration)
          duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);

        data += diff * frame_size;
        size -= diff * frame_size;
      }

      diff = stop - cstop;
      if (diff > 0) {
        offset_end = cstop;

        if (change_duration)
          duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);

        size -= diff * frame_size;
      }
    } else {
      gst_buffer_unref (buffer);
      return NULL;
    }
  }

  /* Get a metadata writable buffer and apply all changes */
  ret = gst_buffer_make_metadata_writable (buffer);

  GST_BUFFER_TIMESTAMP (ret) = timestamp;
  GST_BUFFER_SIZE (ret) = size;
  GST_BUFFER_DATA (ret) = data;

  if (change_duration)
    GST_BUFFER_DURATION (ret) = duration;
  if (change_offset)
    GST_BUFFER_OFFSET (ret) = offset;
  if (change_offset_end)
    GST_BUFFER_OFFSET_END (ret) = offset_end;

  return ret;
}
static GstFlowReturn
webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer)
{
    WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(bsink);
    WebKitVideoSinkPrivate* priv = sink->priv;

    g_mutex_lock(priv->buffer_mutex);

    if (priv->unlocked) {
        g_mutex_unlock(priv->buffer_mutex);
        return GST_FLOW_OK;
    }

    // Ignore buffers if the video is already in fullscreen using
    // another sink.
    if (priv->gstGWorld->isFullscreen()) {
        g_mutex_unlock(priv->buffer_mutex);
        return GST_FLOW_OK;
    }

    priv->buffer = gst_buffer_ref(buffer);

    // For the unlikely case where the buffer has no caps, the caps
    // are implicitely the caps of the pad. This shouldn't happen.
    if (G_UNLIKELY(!GST_BUFFER_CAPS(buffer))) {
        buffer = priv->buffer = gst_buffer_make_metadata_writable(priv->buffer);
        gst_buffer_set_caps(priv->buffer, GST_PAD_CAPS(GST_BASE_SINK_PAD(bsink)));
    }

    GstCaps *caps = GST_BUFFER_CAPS(buffer);
    GstVideoFormat format;
    int width, height;
    if (G_UNLIKELY(!gst_video_format_parse_caps(caps, &format, &width, &height))) {
        gst_buffer_unref(buffer);
        g_mutex_unlock(priv->buffer_mutex);
        return GST_FLOW_ERROR;
    }

    // Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't.
    // Here we convert to Cairo's ARGB.
    if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA) {
        // Because GstBaseSink::render() only owns the buffer reference in the
        // method scope we can't use gst_buffer_make_writable() here. Also
        // The buffer content should not be changed here because the same buffer
        // could be passed multiple times to this method (in theory)
        GstBuffer *newBuffer = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buffer));

        // Check if allocation failed
        if (G_UNLIKELY(!newBuffer)) {
            gst_buffer_unref(buffer);
            g_mutex_unlock(priv->buffer_mutex);
            return GST_FLOW_ERROR;
        }

        gst_buffer_copy_metadata(newBuffer, buffer, (GstBufferCopyFlags) GST_BUFFER_COPY_ALL);

        // We don't use Color::premultipliedARGBFromColor() here because
        // one function call per video pixel is just too expensive:
        // For 720p/PAL for example this means 1280*720*25=23040000
        // function calls per second!
        unsigned short alpha;
        const guint8 *source = GST_BUFFER_DATA(buffer);
        guint8 *destination = GST_BUFFER_DATA(newBuffer);

        for (int x = 0; x < height; x++) {
            for (int y = 0; y < width; y++) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
                alpha = source[3];
                destination[0] = (source[0] * alpha + 128) / 255;
                destination[1] = (source[1] * alpha + 128) / 255;
                destination[2] = (source[2] * alpha + 128) / 255;
                destination[3] = alpha;
#else
                alpha = source[0];
                destination[0] = alpha;
                destination[1] = (source[1] * alpha + 128) / 255;
                destination[2] = (source[2] * alpha + 128) / 255;
                destination[3] = (source[3] * alpha + 128) / 255;
#endif
                source += 4;
                destination += 4;
            }
        }
        gst_buffer_unref(buffer);
        buffer = priv->buffer = newBuffer;
    }

    // This should likely use a lower priority, but glib currently starves
    // lower priority sources.
    // See: https://bugzilla.gnome.org/show_bug.cgi?id=610830.
    priv->timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, 0,
                                          webkit_video_sink_timeout_func,
                                          gst_object_ref(sink),
                                          (GDestroyNotify)gst_object_unref);

    g_cond_wait(priv->data_cond, priv->buffer_mutex);
    g_mutex_unlock(priv->buffer_mutex);
    return GST_FLOW_OK;
}
Exemple #15
0
static GstFlowReturn
fs_funnel_chain (GstPad * pad, GstBuffer * buffer)
{
  GstFlowReturn res;
  FsFunnel *funnel = FS_FUNNEL (gst_pad_get_parent (pad));
  FsFunnelPadPrivate *priv = gst_pad_get_element_private (pad);
  GstEvent *event = NULL;
  GstClockTime newts;
  GstCaps *padcaps;

  GST_DEBUG_OBJECT (funnel, "received buffer %p", buffer);

  GST_OBJECT_LOCK (funnel);
  if (priv->segment.format == GST_FORMAT_UNDEFINED) {
    GST_WARNING_OBJECT (funnel, "Got buffer without segment,"
        " setting segment [0,inf[");
     gst_segment_set_newsegment_full (&priv->segment, FALSE, 1.0, 1.0,
         GST_FORMAT_TIME, 0, -1, 0);
  }

  if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
    gst_segment_set_last_stop (&priv->segment, priv->segment.format,
        GST_BUFFER_TIMESTAMP (buffer));

  newts = gst_segment_to_running_time (&priv->segment,
      priv->segment.format, GST_BUFFER_TIMESTAMP (buffer));
  if (newts != GST_BUFFER_TIMESTAMP (buffer)) {
    buffer = gst_buffer_make_metadata_writable (buffer);
    GST_BUFFER_TIMESTAMP (buffer) = newts;
  }

  if (!funnel->has_segment)
  {
    event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, GST_FORMAT_TIME,
        0, -1, 0);
    funnel->has_segment = TRUE;
  }
  GST_OBJECT_UNLOCK (funnel);

  if (event) {
    if (!gst_pad_push_event (funnel->srcpad, event))
      GST_WARNING_OBJECT (funnel, "Could not push out newsegment event");
  }


  GST_OBJECT_LOCK (pad);
  padcaps = GST_PAD_CAPS (funnel->srcpad);
  GST_OBJECT_UNLOCK (pad);

  if (GST_BUFFER_CAPS (buffer) && GST_BUFFER_CAPS (buffer) != padcaps) {
    if (!gst_pad_set_caps (funnel->srcpad, GST_BUFFER_CAPS (buffer))) {
      res = GST_FLOW_NOT_NEGOTIATED;
      goto out;
    }
  }

  res = gst_pad_push (funnel->srcpad, buffer);

  GST_LOG_OBJECT (funnel, "handled buffer %s", gst_flow_get_name (res));

 out:
  gst_object_unref (funnel);

  return res;
}
Exemple #16
0
static void
vorbis_parse_push_headers (GstVorbisParse * parse)
{
    /* mark and put on caps */
    GstCaps *caps;
    GstBuffer *outbuf, *outbuf1, *outbuf2, *outbuf3;
    ogg_packet packet;

    /* get the headers into the caps, passing them to vorbis as we go */
    caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad));
    vorbis_parse_set_header_on_caps (parse, caps);
    GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
    gst_pad_set_caps (parse->srcpad, caps);
    gst_caps_unref (caps);

    outbuf = GST_BUFFER_CAST (parse->streamheader->data);
    packet.packet = GST_BUFFER_DATA (outbuf);
    packet.bytes = GST_BUFFER_SIZE (outbuf);
    packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
    packet.packetno = 1;
    packet.e_o_s = 0;
    packet.b_o_s = 1;
    vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
    parse->sample_rate = parse->vi.rate;
    outbuf1 = outbuf;

    outbuf = GST_BUFFER_CAST (parse->streamheader->next->data);
    packet.packet = GST_BUFFER_DATA (outbuf);
    packet.bytes = GST_BUFFER_SIZE (outbuf);
    packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
    packet.packetno = 2;
    packet.e_o_s = 0;
    packet.b_o_s = 0;
    vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
    outbuf2 = outbuf;

    outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data);
    packet.packet = GST_BUFFER_DATA (outbuf);
    packet.bytes = GST_BUFFER_SIZE (outbuf);
    packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
    packet.packetno = 3;
    packet.e_o_s = 0;
    packet.b_o_s = 0;
    vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
    outbuf3 = outbuf;

    /* first process queued events */
    vorbis_parse_drain_event_queue (parse);

    /* push out buffers, ignoring return value... */
    outbuf1 = gst_buffer_make_metadata_writable (outbuf1);
    gst_buffer_set_caps (outbuf1, GST_PAD_CAPS (parse->srcpad));
    gst_pad_push (parse->srcpad, outbuf1);
    outbuf2 = gst_buffer_make_metadata_writable (outbuf2);
    gst_buffer_set_caps (outbuf2, GST_PAD_CAPS (parse->srcpad));
    gst_pad_push (parse->srcpad, outbuf2);
    outbuf3 = gst_buffer_make_metadata_writable (outbuf3);
    gst_buffer_set_caps (outbuf3, GST_PAD_CAPS (parse->srcpad));
    gst_pad_push (parse->srcpad, outbuf3);

    g_list_free (parse->streamheader);
    parse->streamheader = NULL;
}
static GstFlowReturn
gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
{
  GstInputSelector *sel;
  GstFlowReturn res;
  GstPad *active_sinkpad;
  GstPad *prev_active_sinkpad;
  GstSelectorPad *selpad;
  GstClockTime start_time;
  GstSegment *seg;
  GstEvent *close_event = NULL, *start_event = NULL;
  GstCaps *caps;

  sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
  selpad = GST_SELECTOR_PAD_CAST (pad);
  seg = &selpad->segment;

  GST_INPUT_SELECTOR_LOCK (sel);
  /* wait or check for flushing */
  if (gst_input_selector_wait (sel, pad))
    goto flushing;

  GST_LOG_OBJECT (pad, "getting active pad");

  prev_active_sinkpad = sel->active_sinkpad;
  active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);

  /* update the segment on the srcpad */
  start_time = GST_BUFFER_TIMESTAMP (buf);
  if (GST_CLOCK_TIME_IS_VALID (start_time)) {
    GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT,
        GST_TIME_ARGS (start_time));
    if (GST_BUFFER_DURATION_IS_VALID (buf))
      GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT,
          GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf)));

    GST_OBJECT_LOCK (pad);
    gst_segment_set_last_stop (seg, seg->format, start_time);
    GST_OBJECT_UNLOCK (pad);
  }

  /* Ignore buffers from pads except the selected one */
  if (pad != active_sinkpad)
    goto ignore;

  if (G_UNLIKELY (sel->pending_close)) {
    GstSegment *cseg = &sel->segment;

    GST_DEBUG_OBJECT (sel,
        "pushing close NEWSEGMENT update %d, rate %lf, applied rate %lf, "
        "format %d, "
        "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
        G_GINT64_FORMAT, TRUE, cseg->rate, cseg->applied_rate, cseg->format,
        cseg->start, cseg->stop, cseg->time);

    /* create update segment */
    close_event = gst_event_new_new_segment_full (TRUE, cseg->rate,
        cseg->applied_rate, cseg->format, cseg->start, cseg->stop, cseg->time);

    sel->pending_close = FALSE;
  }
  /* if we have a pending segment, push it out now */
  if (G_UNLIKELY (selpad->segment_pending)) {
    GST_DEBUG_OBJECT (pad,
        "pushing pending NEWSEGMENT update %d, rate %lf, applied rate %lf, "
        "format %d, "
        "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
        G_GINT64_FORMAT, FALSE, seg->rate, seg->applied_rate, seg->format,
        seg->start, seg->stop, seg->time);

    start_event = gst_event_new_new_segment_full (FALSE, seg->rate,
        seg->applied_rate, seg->format, seg->start, seg->stop, seg->time);

    selpad->segment_pending = FALSE;
  }
  GST_INPUT_SELECTOR_UNLOCK (sel);

  if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad)
    g_object_notify (G_OBJECT (sel), "active-pad");

  if (close_event)
    gst_pad_push_event (sel->srcpad, close_event);

  if (start_event)
    gst_pad_push_event (sel->srcpad, start_event);

  if (selpad->discont) {
    buf = gst_buffer_make_metadata_writable (buf);

    GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf);
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    selpad->discont = FALSE;
  }

  /* forward */
  GST_LOG_OBJECT (pad, "Forwarding buffer %p", buf);

  if ((caps = GST_BUFFER_CAPS (buf))) {
    if (GST_PAD_CAPS (sel->srcpad) != caps)
      gst_pad_set_caps (sel->srcpad, caps);
  }

  res = gst_pad_push (sel->srcpad, buf);

done:
  gst_object_unref (sel);
  return res;

  /* dropped buffers */
ignore:
  {
    GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf);
    /* when we drop a buffer, we're creating a discont on this pad */
    selpad->discont = TRUE;
    GST_INPUT_SELECTOR_UNLOCK (sel);
    gst_buffer_unref (buf);

    /* figure out what to return upstream */
    GST_OBJECT_LOCK (selpad);
    if (selpad->always_ok)
      res = GST_FLOW_OK;
    else
      res = GST_FLOW_NOT_LINKED;
    GST_OBJECT_UNLOCK (selpad);

    goto done;
  }
flushing:
  {
    GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf);
    GST_INPUT_SELECTOR_UNLOCK (sel);
    gst_buffer_unref (buf);
    res = GST_FLOW_WRONG_STATE;
    goto done;
  }
}
Exemple #18
0
/* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits
 * pages to output pad.
 */
static GstFlowReturn
gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer)
{
  GstOggParse *ogg;
  GstFlowReturn result = GST_FLOW_OK;
  gint ret = -1;
  guint32 serialno;
  GstBuffer *pagebuffer;
  GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer);

  ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad));

  GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d",
      GST_BUFFER_SIZE (buffer));

  gst_ogg_parse_submit_buffer (ogg, buffer);

  while (ret != 0 && result == GST_FLOW_OK) {
    ogg_page page;

    /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can
     * track how many bytes the ogg layer discarded (in the case of sync errors,
     * etc.); this allows us to accurately track the current stream offset
     */
    ret = ogg_sync_pageseek (&ogg->sync, &page);
    if (ret == 0) {
      /* need more data, that's fine... */
      break;
    } else if (ret < 0) {
      /* discontinuity; track how many bytes we skipped (-ret) */
      ogg->offset -= ret;
    } else {
      gint64 granule = ogg_page_granulepos (&page);
#ifndef GST_DISABLE_GST_DEBUG
      int bos = ogg_page_bos (&page);
#endif
      guint64 startoffset = ogg->offset;
      GstOggStream *stream;
      gboolean keyframe;

      serialno = ogg_page_serialno (&page);
      stream = gst_ogg_parse_find_stream (ogg, serialno);

      GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT,
          GST_TIME_ARGS (buffertimestamp));

      if (stream) {
        buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream,
            granule);
        if (ogg->video_stream) {
          if (stream == ogg->video_stream) {
            keyframe = gst_ogg_stream_granulepos_is_key_frame (stream, granule);
          } else {
            keyframe = FALSE;
          }
        } else {
          keyframe = TRUE;
        }
      } else {
        buffertimestamp = GST_CLOCK_TIME_NONE;
        keyframe = TRUE;
      }
      pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset,
          buffertimestamp);

      /* We read out 'ret' bytes, so we set the next offset appropriately */
      ogg->offset += ret;

      GST_LOG_OBJECT (ogg,
          "processing ogg page (serial %08x, pageno %ld, "
          "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %"
          G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ") keyframe=%d",
          serialno, ogg_page_pageno (&page),
          granule, bos, startoffset, ogg->offset, keyframe);

      if (ogg_page_bos (&page)) {
        /* If we've seen this serialno before, this is technically an error,
         * we log this case but accept it - this one replaces the previous
         * stream with this serialno. We can do this since we're streaming, and
         * not supporting seeking...
         */
        GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);

        if (stream != NULL) {
          GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u "
              "at offset %" G_GINT64_FORMAT, serialno, ogg->offset);
        }

        if (ogg->last_page_not_bos) {
          GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new "
              "chain starting with serial %u", serialno);
          gst_ogg_parse_delete_all_streams (ogg);
        }

        stream = gst_ogg_parse_new_stream (ogg, &page);

        ogg->last_page_not_bos = FALSE;

        gst_buffer_ref (pagebuffer);
        stream->headers = g_list_append (stream->headers, pagebuffer);

        if (!ogg->in_headers) {
          GST_LOG_OBJECT (ogg,
              "Found start of new chain at offset %" G_GUINT64_FORMAT,
              startoffset);
          ogg->in_headers = 1;
        }

        /* For now, we just keep the header buffer in the stream->headers list;
         * it actually gets output once we've collected the entire set
         */
      } else {
        /* Non-BOS page. Either: we're outside headers, and this isn't a 
         * header (normal data), outside headers and this is (error!), inside
         * headers, this is (append header), or inside headers and this isn't 
         * (we've found the end of headers; flush the lot!)
         *
         * Before that, we flag that the last page seen (this one) was not a 
         * BOS page; that way we know that when we next see a BOS page it's a
         * new chain, and we can flush all existing streams.
         */
        page_type type;
        GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);

        if (!stream) {
          GST_LOG_OBJECT (ogg,
              "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT,
              ogg->offset);
          goto failure;
        }

        ogg->last_page_not_bos = TRUE;

        type = gst_ogg_parse_is_header (ogg, stream, &page);

        if (type == PAGE_PENDING && ogg->in_headers) {
          gst_buffer_ref (pagebuffer);

          stream->unknown_pages = g_list_append (stream->unknown_pages,
              pagebuffer);
        } else if (type == PAGE_HEADER) {
          if (!ogg->in_headers) {
            GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside "
                "headers at offset %" G_GINT64_FORMAT, ogg->offset);
            goto failure;
          } else {
            /* Append the header to the buffer list, after any unknown previous
             * pages
             */
            stream->headers = g_list_concat (stream->headers,
                stream->unknown_pages);
            g_list_free (stream->unknown_pages);
            gst_buffer_ref (pagebuffer);
            stream->headers = g_list_append (stream->headers, pagebuffer);
          }
        } else {                /* PAGE_DATA, or PAGE_PENDING but outside headers */
          if (ogg->in_headers) {
            /* First non-header page... set caps, flush headers.
             *
             * First up, we build a single GValue list of all the pagebuffers
             * we're using for the headers, in order.
             * Then we set this on the caps structure. Then we can start pushing
             * buffers for the headers, and finally we send this non-header
             * page.
             */
            GstCaps *caps;
            GstStructure *structure;
            GValue array = { 0 };
            gint count = 0;
            gboolean found_pending_headers = FALSE;
            GSList *l;

            g_value_init (&array, GST_TYPE_ARRAY);

            for (l = ogg->oggstreams; l != NULL; l = l->next) {
              GstOggStream *stream = (GstOggStream *) l->data;

              if (g_list_length (stream->headers) == 0) {
                GST_LOG_OBJECT (ogg, "No primary header found for stream %08lx",
                    stream->serialno);
                goto failure;
              }

              gst_ogg_parse_append_header (&array,
                  GST_BUFFER (stream->headers->data));
              count++;
            }

            for (l = ogg->oggstreams; l != NULL; l = l->next) {
              GstOggStream *stream = (GstOggStream *) l->data;
              GList *j;

              /* already appended the first header, now do headers 2-N */
              for (j = stream->headers->next; j != NULL; j = j->next) {
                gst_ogg_parse_append_header (&array, GST_BUFFER (j->data));
                count++;
              }
            }

            caps = gst_pad_get_caps (ogg->srcpad);
            caps = gst_caps_make_writable (caps);

            structure = gst_caps_get_structure (caps, 0);
            gst_structure_set_value (structure, "streamheader", &array);

            gst_pad_set_caps (ogg->srcpad, caps);

            g_value_unset (&array);

            if (ogg->caps)
              gst_caps_unref (ogg->caps);
            ogg->caps = caps;

            GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers "
                "(one per page)", count);

            /* Now, we do the same thing, but push buffers... */
            for (l = ogg->oggstreams; l != NULL; l = l->next) {
              GstOggStream *stream = (GstOggStream *) l->data;
              GstBuffer *buf = GST_BUFFER (stream->headers->data);

              buf = gst_buffer_make_metadata_writable (buf);
              gst_buffer_set_caps (buf, caps);

              result = gst_pad_push (ogg->srcpad, buf);
              if (result != GST_FLOW_OK)
                return result;
            }
            for (l = ogg->oggstreams; l != NULL; l = l->next) {
              GstOggStream *stream = (GstOggStream *) l->data;
              GList *j;

              /* pushed the first one for each stream already, now do 2-N */
              for (j = stream->headers->next; j != NULL; j = j->next) {
                GstBuffer *buf = GST_BUFFER (j->data);

                buf = gst_buffer_make_metadata_writable (buf);
                gst_buffer_set_caps (buf, caps);

                result = gst_pad_push (ogg->srcpad, buf);
                if (result != GST_FLOW_OK)
                  return result;
              }
            }

            ogg->in_headers = 0;

            /* And finally the pending data pages */
            for (l = ogg->oggstreams; l != NULL; l = l->next) {
              GstOggStream *stream = (GstOggStream *) l->data;
              GList *k;

              if (stream->unknown_pages == NULL)
                continue;

              if (found_pending_headers) {
                GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at "
                    "approximate offset %" G_GINT64_FORMAT, ogg->offset);
              }
              found_pending_headers = TRUE;

              GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers",
                  g_list_length (stream->unknown_pages) + 1);

              for (k = stream->unknown_pages; k != NULL; k = k->next) {
                GstBuffer *buf;

                buf = gst_buffer_make_metadata_writable (GST_BUFFER (k->data));
                gst_buffer_set_caps (buf, caps);
                result = gst_pad_push (ogg->srcpad, buf);
                if (result != GST_FLOW_OK)
                  return result;
              }
              g_list_foreach (stream->unknown_pages,
                  (GFunc) gst_mini_object_unref, NULL);
              g_list_free (stream->unknown_pages);
              stream->unknown_pages = NULL;
            }
          }

          if (granule == -1) {
            stream->stored_buffers = g_list_append (stream->stored_buffers,
                pagebuffer);
          } else {
            while (stream->stored_buffers) {
              GstBuffer *buf = stream->stored_buffers->data;

              buf = gst_buffer_make_metadata_writable (buf);
              gst_buffer_set_caps (buf, ogg->caps);
              GST_BUFFER_TIMESTAMP (buf) = buffertimestamp;
              if (!keyframe) {
                GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
              } else {
                keyframe = FALSE;
              }

              result = gst_pad_push (ogg->srcpad, buf);
              if (result != GST_FLOW_OK)
                return result;

              stream->stored_buffers =
                  g_list_delete_link (stream->stored_buffers,
                  stream->stored_buffers);
            }

            pagebuffer = gst_buffer_make_metadata_writable (pagebuffer);
            gst_buffer_set_caps (pagebuffer, ogg->caps);
            if (!keyframe) {
              GST_BUFFER_FLAG_SET (pagebuffer, GST_BUFFER_FLAG_DELTA_UNIT);
            } else {
              keyframe = FALSE;
            }

            result = gst_pad_push (ogg->srcpad, pagebuffer);
            if (result != GST_FLOW_OK)
              return result;
          }
        }
      }
    }
  }

  return result;

failure:
  gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ());
  return GST_FLOW_ERROR;
}
Exemple #19
0
static GstFlowReturn
gst_app_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
    GstBuffer ** buf)
{
  GstAppSrc *appsrc = GST_APP_SRC_CAST (bsrc);
  GstAppSrcPrivate *priv = appsrc->priv;
  GstFlowReturn ret;
  GstCaps *caps;

  GST_OBJECT_LOCK (appsrc);
  caps = priv->caps ? gst_caps_ref (priv->caps) : NULL;
  if (G_UNLIKELY (priv->size != bsrc->segment.duration &&
          bsrc->segment.format == GST_FORMAT_BYTES)) {
    GST_DEBUG_OBJECT (appsrc,
        "Size changed from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT,
        bsrc->segment.duration, priv->size);
    gst_segment_set_duration (&bsrc->segment, GST_FORMAT_BYTES, priv->size);
    GST_OBJECT_UNLOCK (appsrc);

    gst_element_post_message (GST_ELEMENT (appsrc),
        gst_message_new_duration (GST_OBJECT (appsrc), GST_FORMAT_BYTES,
            priv->size));
  } else {
    GST_OBJECT_UNLOCK (appsrc);
  }

  g_mutex_lock (priv->mutex);
  /* check flushing first */
  if (G_UNLIKELY (priv->flushing))
    goto flushing;

  if (priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS) {
    /* if we are dealing with a random-access stream, issue a seek if the offset
     * changed. */
    if (G_UNLIKELY (priv->offset != offset)) {
      gboolean res;

      /* do the seek */
      res = gst_app_src_emit_seek (appsrc, offset);

      if (G_UNLIKELY (!res))
        /* failing to seek is fatal */
        goto seek_error;

      priv->offset = offset;
    }
  }

  while (TRUE) {
    /* return data as long as we have some */
    if (!g_queue_is_empty (priv->queue)) {
      guint buf_size;

      *buf = g_queue_pop_head (priv->queue);
      buf_size = GST_BUFFER_SIZE (*buf);

      GST_DEBUG_OBJECT (appsrc, "we have buffer %p of size %u", *buf, buf_size);

      priv->queued_bytes -= buf_size;

      /* only update the offset when in random_access mode */
      if (priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS)
        priv->offset += buf_size;
      if (caps) {
        *buf = gst_buffer_make_metadata_writable (*buf);
        gst_buffer_set_caps (*buf, caps);
      }

      /* signal that we removed an item */
      g_cond_broadcast (priv->cond);

      /* see if we go lower than the empty-percent */
      if (priv->min_percent && priv->max_bytes) {
        if (priv->queued_bytes * 100 / priv->max_bytes <= priv->min_percent)
          /* ignore flushing state, we got a buffer and we will return it now.
           * Errors will be handled in the next round */
          gst_app_src_emit_need_data (appsrc, size);
      }
      ret = GST_FLOW_OK;
      break;
    } else {
      gst_app_src_emit_need_data (appsrc, size);

      /* we can be flushing now because we released the lock above */
      if (G_UNLIKELY (priv->flushing))
        goto flushing;

      /* if we have a buffer now, continue the loop and try to return it. In
       * random-access mode (where a buffer is normally pushed in the above
       * signal) we can still be empty because the pushed buffer got flushed or
       * when the application pushes the requested buffer later, we support both
       * possiblities. */
      if (!g_queue_is_empty (priv->queue))
        continue;

      /* no buffer yet, maybe we are EOS, if not, block for more data. */
    }

    /* check EOS */
    if (G_UNLIKELY (priv->is_eos))
      goto eos;

    /* nothing to return, wait a while for new data or flushing. */
    g_cond_wait (priv->cond, priv->mutex);
  }
  g_mutex_unlock (priv->mutex);
  if (caps)
    gst_caps_unref (caps);
  return ret;

  /* ERRORS */
flushing:
  {
    GST_DEBUG_OBJECT (appsrc, "we are flushing");
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    return GST_FLOW_WRONG_STATE;
  }
eos:
  {
    GST_DEBUG_OBJECT (appsrc, "we are EOS");
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    return GST_FLOW_UNEXPECTED;
  }
seek_error:
  {
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    GST_ELEMENT_ERROR (appsrc, RESOURCE, READ, ("failed to seek"),
        GST_ERROR_SYSTEM);
    return GST_FLOW_ERROR;
  }
}
static GstFlowReturn
gst_dvdlpcmdec_chain_raw (GstPad * pad, GstBuffer * buf)
{
  GstDvdLpcmDec *dvdlpcmdec;
  guint8 *data;
  guint size;
  GstFlowReturn ret;
  guint samples = 0;

  dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad));

  size = GST_BUFFER_SIZE (buf);
  data = GST_BUFFER_DATA (buf);

  GST_LOG_OBJECT (dvdlpcmdec,
      "got buffer %p of size %d with ts %" GST_TIME_FORMAT,
      buf, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));

  if (dvdlpcmdec->rate == 0)
    goto not_negotiated;

  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
    dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf);

  /* We don't currently do anything at all regarding emphasis, mute or
   * dynamic_range - I'm not sure what they're for */
  switch (dvdlpcmdec->width) {
    case 16:
    {
      /* We can just pass 16-bits straight through intact, once we set
       * appropriate things on the buffer */
      samples = size / dvdlpcmdec->channels / 2;
      if (samples < 1)
        goto drop;
      buf = gst_buffer_make_metadata_writable (buf);
      break;
    }
    case 20:
    {
      /* Allocate a new buffer and copy 20-bit width to 24-bit */
      gint64 samples = size * 8 / 20;
      gint64 count = size / 10;
      gint64 i;
      guint8 *src;
      guint8 *dest;
      GstBuffer *outbuf;
      GstCaps *bufcaps = GST_PAD_CAPS (dvdlpcmdec->srcpad);

      if (samples < 1)
        goto drop;

      ret = gst_pad_alloc_buffer_and_set_caps (dvdlpcmdec->srcpad, 0,
          samples * 3, bufcaps, &outbuf);

      if (ret != GST_FLOW_OK)
        goto buffer_alloc_failed;

      gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS);

      /* adjust samples so we can calc the new timestamp */
      samples = samples / dvdlpcmdec->channels;

      src = data;
      dest = GST_BUFFER_DATA (outbuf);

      /* Copy 20-bit LPCM format to 24-bit buffers, with 0x00 in the lowest
       * nibble. Note that the first 2 bytes are already correct */
      for (i = 0; i < count; i++) {
        dest[0] = src[0];
        dest[1] = src[1];
        dest[2] = src[8] & 0xf0;
        dest[3] = src[2];
        dest[4] = src[3];
        dest[5] = (src[8] & 0x0f) << 4;
        dest[6] = src[4];
        dest[7] = src[5];
        dest[8] = src[9] & 0x0f;
        dest[9] = src[6];
        dest[10] = src[7];
        dest[11] = (src[9] & 0x0f) << 4;

        src += 10;
        dest += 12;
      }

      gst_buffer_unref (buf);
      buf = outbuf;
      break;
    }
    case 24:
    {
      /* Rearrange 24-bit LPCM format in-place. Note that the first 2
       * and last byte are already correct */
      guint count = size / 12;
      gint i;
      guint8 *src;

      samples = size / dvdlpcmdec->channels / 3;

      if (samples < 1)
        goto drop;

      /* Ensure our output buffer is writable */
      buf = gst_buffer_make_writable (buf);

      src = GST_BUFFER_DATA (buf);
      for (i = 0; i < count; i++) {
        guint8 tmp;

        tmp = src[10];
        src[10] = src[7];
        src[7] = src[5];
        src[5] = src[9];
        src[9] = src[6];
        src[6] = src[4];
        src[4] = src[3];
        src[3] = src[2];
        src[2] = src[8];
        src[8] = tmp;

        src += 12;
      }
      break;
    }
    default:
      goto invalid_width;
  }

  /* Set appropriate caps on it to pass downstream */
  gst_buffer_set_caps (buf, GST_PAD_CAPS (dvdlpcmdec->srcpad));
  update_timestamps (dvdlpcmdec, buf, samples);

  ret = gst_pad_push (dvdlpcmdec->srcpad, buf);

done:
  gst_object_unref (dvdlpcmdec);

  return ret;

  /* ERRORS */
drop:
  {
    GST_DEBUG_OBJECT (dvdlpcmdec, "Buffer of size %u is too small. Dropping",
        GST_BUFFER_SIZE (buf));
    gst_buffer_unref (buf);
    ret = GST_FLOW_OK;
    goto done;
  }
not_negotiated:
  {
    GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL),
        ("Buffer pushed before negotiation"));
    gst_buffer_unref (buf);
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto done;
  }
buffer_alloc_failed:
  {
    GST_ELEMENT_ERROR (dvdlpcmdec, RESOURCE, FAILED, (NULL),
        ("Buffer allocation failed"));
    gst_buffer_unref (buf);
    goto done;
  }
invalid_width:
  {
    GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, WRONG_TYPE, (NULL),
        ("Invalid sample width configured"));
    gst_buffer_unref (buf);
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto done;
  }
}
Exemple #21
0
static GstFlowReturn webkitVideoSinkRender(GstBaseSink* baseSink, GstBuffer* buffer)
{
    WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(baseSink);
    WebKitVideoSinkPrivate* priv = sink->priv;

    g_mutex_lock(priv->bufferMutex);

    if (priv->unlocked) {
        g_mutex_unlock(priv->bufferMutex);
        return GST_FLOW_OK;
    }

#if USE(NATIVE_FULLSCREEN_VIDEO)
    // Ignore buffers if the video is already in fullscreen using
    // another sink.
    if (priv->gstGWorld->isFullscreen()) {
        g_mutex_unlock(priv->bufferMutex);
        return GST_FLOW_OK;
    }
#endif

    priv->buffer = gst_buffer_ref(buffer);

#ifndef GST_API_VERSION_1
    // For the unlikely case where the buffer has no caps, the caps
    // are implicitely the caps of the pad. This shouldn't happen.
    if (UNLIKELY(!GST_BUFFER_CAPS(buffer))) {
        buffer = priv->buffer = gst_buffer_make_metadata_writable(priv->buffer);
        gst_buffer_set_caps(priv->buffer, GST_PAD_CAPS(GST_BASE_SINK_PAD(baseSink)));
    }

    GRefPtr<GstCaps> caps = GST_BUFFER_CAPS(buffer);
#else
    GRefPtr<GstCaps> caps;
    // The video info structure is valid only if the sink handled an allocation query.
    if (GST_VIDEO_INFO_FORMAT(&priv->info) != GST_VIDEO_FORMAT_UNKNOWN)
        caps = adoptGRef(gst_video_info_to_caps(&priv->info));
    else
        caps = priv->currentCaps;
#endif

    GstVideoFormat format;
    WebCore::IntSize size;
    int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
    if (!getVideoSizeAndFormatFromCaps(caps.get(), size, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride)) {
        gst_buffer_unref(buffer);
        g_mutex_unlock(priv->bufferMutex);
        return GST_FLOW_ERROR;
    }

    // Cairo's ARGB has pre-multiplied alpha while GStreamer's doesn't.
    // Here we convert to Cairo's ARGB.
    if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA) {
        // Because GstBaseSink::render() only owns the buffer reference in the
        // method scope we can't use gst_buffer_make_writable() here. Also
        // The buffer content should not be changed here because the same buffer
        // could be passed multiple times to this method (in theory).

        GstBuffer* newBuffer = createGstBuffer(buffer);

        // Check if allocation failed.
        if (UNLIKELY(!newBuffer)) {
            g_mutex_unlock(priv->bufferMutex);
            return GST_FLOW_ERROR;
        }

        // We don't use Color::premultipliedARGBFromColor() here because
        // one function call per video pixel is just too expensive:
        // For 720p/PAL for example this means 1280*720*25=23040000
        // function calls per second!
#ifndef GST_API_VERSION_1
        const guint8* source = GST_BUFFER_DATA(buffer);
        guint8* destination = GST_BUFFER_DATA(newBuffer);
#else
        GstMapInfo sourceInfo;
        GstMapInfo destinationInfo;
        gst_buffer_map(buffer, &sourceInfo, GST_MAP_READ);
        const guint8* source = const_cast<guint8*>(sourceInfo.data);
        gst_buffer_map(newBuffer, &destinationInfo, GST_MAP_WRITE);
        guint8* destination = static_cast<guint8*>(destinationInfo.data);
#endif

        for (int x = 0; x < size.height(); x++) {
            for (int y = 0; y < size.width(); y++) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
                unsigned short alpha = source[3];
                destination[0] = (source[0] * alpha + 128) / 255;
                destination[1] = (source[1] * alpha + 128) / 255;
                destination[2] = (source[2] * alpha + 128) / 255;
                destination[3] = alpha;
#else
                unsigned short alpha = source[0];
                destination[0] = alpha;
                destination[1] = (source[1] * alpha + 128) / 255;
                destination[2] = (source[2] * alpha + 128) / 255;
                destination[3] = (source[3] * alpha + 128) / 255;
#endif
                source += 4;
                destination += 4;
            }
        }

#ifdef GST_API_VERSION_1
        gst_buffer_unmap(buffer, &sourceInfo);
        gst_buffer_unmap(newBuffer, &destinationInfo);
#endif
        gst_buffer_unref(buffer);
        buffer = priv->buffer = newBuffer;
    }

    // This should likely use a lower priority, but glib currently starves
    // lower priority sources.
    // See: https://bugzilla.gnome.org/show_bug.cgi?id=610830.
    priv->timeoutId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, webkitVideoSinkTimeoutCallback,
                                          gst_object_ref(sink), reinterpret_cast<GDestroyNotify>(gst_object_unref));

    g_cond_wait(priv->dataCondition, priv->bufferMutex);
    g_mutex_unlock(priv->bufferMutex);
    return GST_FLOW_OK;
}
Exemple #22
0
static GstFlowReturn
gst_interlace_chain (GstPad * pad, GstBuffer * buffer)
{
  GstInterlace *interlace = GST_INTERLACE (gst_pad_get_parent (pad));
  GstFlowReturn ret = GST_FLOW_OK;
  gint num_fields = 0;
  int current_fields;
  const PulldownFormat *format;

  GST_DEBUG ("Received buffer at %u:%02u:%02u:%09u",
      (guint) (GST_BUFFER_TIMESTAMP (buffer) / (GST_SECOND * 60 * 60)),
      (guint) ((GST_BUFFER_TIMESTAMP (buffer) / (GST_SECOND * 60)) % 60),
      (guint) ((GST_BUFFER_TIMESTAMP (buffer) / GST_SECOND) % 60),
      (guint) (GST_BUFFER_TIMESTAMP (buffer) % GST_SECOND));

  GST_DEBUG ("duration %" GST_TIME_FORMAT " flags %04x %s %s %s",
      GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
      GST_BUFFER_FLAGS (buffer),
      (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_TFF) ? "tff" : "",
      (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_RFF) ? "rff" : "",
      (GST_BUFFER_FLAGS (buffer) & GST_VIDEO_BUFFER_ONEFIELD) ? "onefield" :
      "");

  if (GST_BUFFER_FLAGS (buffer) & GST_BUFFER_FLAG_DISCONT) {
    GST_DEBUG ("discont");

    if (interlace->stored_frame) {
      gst_buffer_unref (interlace->stored_frame);
    }
    interlace->stored_frame = NULL;
    interlace->stored_fields = 0;

    if (interlace->top_field_first) {
      interlace->field_index = 0;
    } else {
      interlace->field_index = 1;
    }
  }

  if (interlace->timebase == GST_CLOCK_TIME_NONE) {
    /* get the initial ts */
    interlace->timebase = GST_BUFFER_TIMESTAMP (buffer);
  }

  format = &formats[interlace->pattern];

  if (interlace->stored_fields == 0
      && interlace->phase_index == interlace->pattern_offset
      && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer))) {
    interlace->timebase = GST_BUFFER_TIMESTAMP (buffer);
    interlace->fields_since_timebase = 0;
  }

  if (!format->n_fields[interlace->phase_index]) {
    interlace->phase_index = 0;
  }

  current_fields = format->n_fields[interlace->phase_index];
  /* increment the phase index */
  interlace->phase_index++;
  GST_DEBUG ("incoming buffer assigned %d fields", current_fields);

  num_fields = interlace->stored_fields + current_fields;
  while (num_fields >= 2) {
    GstBuffer *output_buffer;
    int n_output_fields;

    GST_DEBUG ("have %d fields, %d current, %d stored",
        num_fields, current_fields, interlace->stored_fields);

    if (interlace->stored_fields > 0) {
      GST_DEBUG ("1 field from stored, 1 from current");

      output_buffer = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buffer));
      /* take the first field from the stored frame */
      copy_field (interlace, output_buffer, interlace->stored_frame,
          interlace->field_index);
      interlace->stored_fields--;
      /* take the second field from the incoming buffer */
      copy_field (interlace, output_buffer, buffer, interlace->field_index ^ 1);
      current_fields--;
      n_output_fields = 2;
    } else {
      output_buffer =
          gst_buffer_make_metadata_writable (gst_buffer_ref (buffer));
      if (num_fields >= 3 && interlace->allow_rff) {
        GST_DEBUG ("3 fields from current");
        /* take both fields from incoming buffer */
        current_fields -= 3;
        n_output_fields = 3;
      } else {
        GST_DEBUG ("2 fields from current");
        /* take both buffers from incoming buffer */
        current_fields -= 2;
        n_output_fields = 2;
      }
    }
    num_fields -= n_output_fields;

    gst_interlace_decorate_buffer (interlace, output_buffer, n_output_fields);
    interlace->fields_since_timebase += n_output_fields;
    interlace->field_index ^= (n_output_fields & 1);

    GST_DEBUG_OBJECT (interlace, "output timestamp %" GST_TIME_FORMAT
        " duration %" GST_TIME_FORMAT " flags %04x %s %s %s",
        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (output_buffer)),
        GST_TIME_ARGS (GST_BUFFER_DURATION (output_buffer)),
        GST_BUFFER_FLAGS (output_buffer),
        (GST_BUFFER_FLAGS (output_buffer) & GST_VIDEO_BUFFER_TFF) ? "tff" : "",
        (GST_BUFFER_FLAGS (output_buffer) & GST_VIDEO_BUFFER_RFF) ? "rff" : "",
        (GST_BUFFER_FLAGS (output_buffer) & GST_VIDEO_BUFFER_ONEFIELD) ?
        "onefield" : "");

    ret = gst_pad_push (interlace->srcpad, output_buffer);
    if (ret != GST_FLOW_OK) {
      GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p", output_buffer);
      break;
    }
  }

  GST_DEBUG ("done.  %d fields remaining", current_fields);

  if (interlace->stored_frame) {
    gst_buffer_unref (interlace->stored_frame);
    interlace->stored_frame = NULL;
    interlace->stored_fields = 0;
  }

  if (current_fields > 0) {
    interlace->stored_frame = buffer;
    interlace->stored_fields = current_fields;
  } else {
    gst_buffer_unref (buffer);
  }

  gst_object_unref (interlace);

  return ret;
}
Exemple #23
0
/**
 * This funcion will push out buffers on the source pad.
 *
 * For each pushed buffer, the seqnum is recorded, if the next buffer B has a
 * different seqnum (missing packets before B), this function will wait for the
 * missing packet to arrive up to the timestamp of buffer B.
 */
static void
gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
{
  GstRtpJitterBufferPrivate *priv;
  GstBuffer *outbuf;
  GstFlowReturn result;
  guint16 seqnum;
  guint32 next_seqnum;
  GstClockTime timestamp, out_time;
  gboolean discont = FALSE;
  gint gap;

  priv = jitterbuffer->priv;

  JBUF_LOCK_CHECK (priv, flushing);
again:
  GST_DEBUG_OBJECT (jitterbuffer, "Peeking item");
  while (TRUE) {
    /* always wait if we are blocked */
    if (!priv->blocked) {
      /* if we have a packet, we can exit the loop and grab it */
      if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0)
        break;
      /* no packets but we are EOS, do eos logic */
      if (priv->eos)
        goto do_eos;
    }
    /* underrun, wait for packets or flushing now */
    priv->waiting = TRUE;
    JBUF_WAIT_CHECK (priv, flushing);
    priv->waiting = FALSE;
  }

  /* peek a buffer, we're just looking at the timestamp and the sequence number.
   * If all is fine, we'll pop and push it. If the sequence number is wrong we
   * wait on the timestamp. In the chain function we will unlock the wait when a
   * new buffer is available. The peeked buffer is valid for as long as we hold
   * the jitterbuffer lock. */
  outbuf = rtp_jitter_buffer_peek (priv->jbuf);

  /* get the seqnum and the next expected seqnum */
  seqnum = gst_rtp_buffer_get_seq (outbuf);
  next_seqnum = priv->next_seqnum;

  /* get the timestamp, this is already corrected for clock skew by the
   * jitterbuffer */
  timestamp = GST_BUFFER_TIMESTAMP (outbuf);

  GST_DEBUG_OBJECT (jitterbuffer,
      "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT
      ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp),
      rtp_jitter_buffer_num_packets (priv->jbuf));

  /* apply our timestamp offset to the incomming buffer, this will be our output
   * timestamp. */
  out_time = apply_offset (jitterbuffer, timestamp);

  /* get the gap between this and the previous packet. If we don't know the
   * previous packet seqnum assume no gap. */
  if (next_seqnum != -1) {
    gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);

    /* if we have a packet that we already pushed or considered dropped, pop it
     * off and get the next packet */
    if (gap < 0) {
      GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
          seqnum, next_seqnum);
      outbuf = rtp_jitter_buffer_pop (priv->jbuf);
      gst_buffer_unref (outbuf);
      goto again;
    }
  } else {
    GST_DEBUG_OBJECT (jitterbuffer, "no next seqnum known, first packet");
    gap = -1;
  }

  /* If we don't know what the next seqnum should be (== -1) we have to wait
   * because it might be possible that we are not receiving this buffer in-order,
   * a buffer with a lower seqnum could arrive later and we want to push that
   * earlier buffer before this buffer then.
   * If we know the expected seqnum, we can compare it to the current seqnum to
   * determine if we have missing a packet. If we have a missing packet (which
   * must be before this packet) we can wait for it until the deadline for this
   * packet expires. */
  if (gap != 0 && out_time != -1) {
    GstClockID id;
    GstClockTime sync_time;
    GstClockReturn ret;
    GstClock *clock;
    GstClockTime duration = GST_CLOCK_TIME_NONE;

    if (gap > 0) {
      /* we have a gap */
      GST_WARNING_OBJECT (jitterbuffer,
          "Sequence number GAP detected: expected %d instead of %d (%d missing)",
          next_seqnum, seqnum, gap);

      if (priv->last_out_time != -1) {
        GST_DEBUG_OBJECT (jitterbuffer,
            "out_time %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
            GST_TIME_ARGS (out_time), GST_TIME_ARGS (priv->last_out_time));
        /* interpolate between the current time and the last time based on
         * number of packets we are missing, this is the estimated duration
         * for the missing packet based on equidistant packet spacing. Also make
         * sure we never go negative. */
        if (out_time > priv->last_out_time)
          duration = (out_time - priv->last_out_time) / (gap + 1);
        else
          goto lost;

        GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
            GST_TIME_ARGS (duration));
        /* add this duration to the timestamp of the last packet we pushed */
        out_time = (priv->last_out_time + duration);
      }
    } else {
      /* we don't know what the next_seqnum should be, wait for the last
       * possible moment to push this buffer, maybe we get an earlier seqnum
       * while we wait */
      GST_DEBUG_OBJECT (jitterbuffer, "First buffer %d, do sync", seqnum);
    }

    GST_OBJECT_LOCK (jitterbuffer);
    clock = GST_ELEMENT_CLOCK (jitterbuffer);
    if (!clock) {
      GST_OBJECT_UNLOCK (jitterbuffer);
      /* let's just push if there is no clock */
      goto push_buffer;
    }

    GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT,
        GST_TIME_ARGS (out_time));

    /* prepare for sync against clock */
    sync_time = out_time + GST_ELEMENT_CAST (jitterbuffer)->base_time;
    /* add latency, this includes our own latency and the peer latency. */
    sync_time += (priv->latency_ms * GST_MSECOND);
    sync_time += priv->peer_latency;

    /* create an entry for the clock */
    id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
    GST_OBJECT_UNLOCK (jitterbuffer);

    /* release the lock so that the other end can push stuff or unlock */
    JBUF_UNLOCK (priv);

    ret = gst_clock_id_wait (id, NULL);

    JBUF_LOCK (priv);
    /* and free the entry */
    gst_clock_id_unref (id);
    priv->clock_id = NULL;

    /* at this point, the clock could have been unlocked by a timeout, a new
     * tail element was added to the queue or because we are shutting down. Check
     * for shutdown first. */
    if (priv->srcresult != GST_FLOW_OK)
      goto flushing;

    /* if we got unscheduled and we are not flushing, it's because a new tail
     * element became available in the queue. Grab it and try to push or sync. */
    if (ret == GST_CLOCK_UNSCHEDULED) {
      GST_DEBUG_OBJECT (jitterbuffer,
          "Wait got unscheduled, will retry to push with new buffer");
      goto again;
    }

  lost:
    /* we now timed out, this means we lost a packet or finished synchronizing
     * on the first buffer. */
    if (gap > 0) {
      GstEvent *event;

      /* we had a gap and thus we lost a packet. Create an event for this.  */
      GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", next_seqnum);
      priv->num_late++;
      discont = TRUE;

      if (priv->do_lost) {
        /* create paket lost event */
        event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
            gst_structure_new ("GstRTPPacketLost",
                "seqnum", G_TYPE_UINT, (guint) next_seqnum,
                "timestamp", G_TYPE_UINT64, out_time,
                "duration", G_TYPE_UINT64, duration, NULL));
        gst_pad_push_event (priv->srcpad, event);
      }

      /* update our expected next packet */
      priv->last_popped_seqnum = next_seqnum;
      priv->last_out_time = out_time;
      priv->next_seqnum = (next_seqnum + 1) & 0xffff;
      /* look for next packet */
      goto again;
    }

    /* there was no known gap,just the first packet, exit the loop and push */
    GST_DEBUG_OBJECT (jitterbuffer, "First packet #%d synced", seqnum);

    /* get new timestamp, latency might have changed */
    out_time = apply_offset (jitterbuffer, timestamp);
  }
push_buffer:

  /* when we get here we are ready to pop and push the buffer */
  outbuf = rtp_jitter_buffer_pop (priv->jbuf);

  if (discont || priv->discont) {
    /* set DISCONT flag when we missed a packet. */
    outbuf = gst_buffer_make_metadata_writable (outbuf);
    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
    priv->discont = FALSE;
  }

  /* apply timestamp with offset to buffer now */
  GST_BUFFER_TIMESTAMP (outbuf) = out_time;

  /* now we are ready to push the buffer. Save the seqnum and release the lock
   * so the other end can push stuff in the queue again. */
  priv->last_popped_seqnum = seqnum;
  priv->last_out_time = out_time;
  priv->next_seqnum = (seqnum + 1) & 0xffff;
  JBUF_UNLOCK (priv);

  /* push buffer */
  GST_DEBUG_OBJECT (jitterbuffer,
      "Pushing buffer %d, timestamp %" GST_TIME_FORMAT, seqnum,
      GST_TIME_ARGS (out_time));
  result = gst_pad_push (priv->srcpad, outbuf);
  if (result != GST_FLOW_OK)
    goto pause;

  return;

  /* ERRORS */
do_eos:
  {
    /* store result, we are flushing now */
    GST_DEBUG_OBJECT (jitterbuffer, "We are EOS, pushing EOS downstream");
    priv->srcresult = GST_FLOW_UNEXPECTED;
    gst_pad_pause_task (priv->srcpad);
    gst_pad_push_event (priv->srcpad, gst_event_new_eos ());
    JBUF_UNLOCK (priv);
    return;
  }
flushing:
  {
    GST_DEBUG_OBJECT (jitterbuffer, "we are flushing");
    gst_pad_pause_task (priv->srcpad);
    JBUF_UNLOCK (priv);
    return;
  }
pause:
  {
    const gchar *reason = gst_flow_get_name (result);

    GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s", reason);

    JBUF_LOCK (priv);
    /* store result */
    priv->srcresult = result;
    /* we don't post errors or anything because upstream will do that for us
     * when we pass the return value upstream. */
    gst_pad_pause_task (priv->srcpad);
    JBUF_UNLOCK (priv);
    return;
  }
}
Exemple #24
0
static GstFlowReturn
gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
{
  GstSpeexEnc *enc;
  GstFlowReturn ret = GST_FLOW_OK;

  enc = GST_SPEEX_ENC (GST_PAD_PARENT (pad));

  if (!enc->setup)
    goto not_setup;

  if (!enc->header_sent) {
    /* Speex streams begin with two headers; the initial header (with
       most of the codec setup parameters) which is mandated by the Ogg
       bitstream spec.  The second header holds any comment fields.
       We merely need to make the headers, then pass them to libspeex 
       one at a time; libspeex handles the additional Ogg bitstream 
       constraints */
    GstBuffer *buf1, *buf2;
    GstCaps *caps;
    guchar *data;
    gint data_len;

    /* create header buffer */
    data = (guint8 *) speex_header_to_packet (&enc->header, &data_len);
    buf1 = gst_speex_enc_buffer_from_data (enc, data, data_len, 0);
    free (data);

    /* create comment buffer */
    buf2 = gst_speex_enc_create_metadata_buffer (enc);

    /* mark and put on caps */
    caps = gst_pad_get_caps (enc->srcpad);
    caps = gst_speex_enc_set_header_on_caps (caps, buf1, buf2);

    gst_caps_set_simple (caps,
        "rate", G_TYPE_INT, enc->rate,
        "channels", G_TYPE_INT, enc->channels, NULL);

    /* negotiate with these caps */
    GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
    gst_pad_set_caps (enc->srcpad, caps);

    gst_buffer_set_caps (buf1, caps);
    gst_buffer_set_caps (buf2, caps);
    gst_caps_unref (caps);

    /* push out buffers */
    ret = gst_speex_enc_push_buffer (enc, buf1);

    if (ret != GST_FLOW_OK) {
      gst_buffer_unref (buf2);
      goto done;
    }

    ret = gst_speex_enc_push_buffer (enc, buf2);

    if (ret != GST_FLOW_OK)
      goto done;

    speex_bits_reset (&enc->bits);

    enc->header_sent = TRUE;
  }

  /* Save the timestamp of the first buffer. This will be later
   * used as offset for all following buffers */
  if (enc->start_ts == GST_CLOCK_TIME_NONE) {
    if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
      enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
      enc->granulepos_offset = gst_util_uint64_scale
          (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
    } else {
      enc->start_ts = 0;
      enc->granulepos_offset = 0;
    }
  }

  /* Check if we have a continous stream, if not drop some samples or the buffer or
   * insert some silence samples */
  if (enc->next_ts != GST_CLOCK_TIME_NONE &&
      GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
    guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
    guint64 diff_bytes;

    GST_WARNING_OBJECT (enc, "Buffer is older than previous "
        "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
        "), cannot handle. Clipping buffer.",
        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
        GST_TIME_ARGS (enc->next_ts));

    diff_bytes = GST_CLOCK_TIME_TO_FRAMES (diff, enc->rate) * enc->channels * 2;
    if (diff_bytes >= GST_BUFFER_SIZE (buf)) {
      gst_buffer_unref (buf);
      return GST_FLOW_OK;
    }
    buf = gst_buffer_make_metadata_writable (buf);
    GST_BUFFER_DATA (buf) += diff_bytes;
    GST_BUFFER_SIZE (buf) -= diff_bytes;

    GST_BUFFER_TIMESTAMP (buf) += diff;
    if (GST_BUFFER_DURATION_IS_VALID (buf))
      GST_BUFFER_DURATION (buf) -= diff;
  }

  if (enc->next_ts != GST_CLOCK_TIME_NONE
      && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
    guint64 max_diff =
        gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->rate);

    if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts &&
        GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) {
      GST_WARNING_OBJECT (enc,
          "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT,
          GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff);

      gst_speex_enc_encode (enc, TRUE);

      enc->frameno_out = 0;
      enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
      enc->granulepos_offset = gst_util_uint64_scale
          (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
    }
  }

  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)
      && GST_BUFFER_DURATION_IS_VALID (buf))
    enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
  else
    enc->next_ts = GST_CLOCK_TIME_NONE;

  GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf));

  /* push buffer to adapter */
  gst_adapter_push (enc->adapter, buf);
  buf = NULL;

  ret = gst_speex_enc_encode (enc, FALSE);

done:

  if (buf)
    gst_buffer_unref (buf);

  return ret;

  /* ERRORS */
not_setup:
  {
    GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL),
        ("encoder not initialized (input is not audio?)"));
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto done;
  }

}
Exemple #25
0
static gboolean
gst_interlace_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  gboolean ret;
  GstInterlace *interlace;

  interlace = GST_INTERLACE (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_FLUSH_START:
      GST_DEBUG_OBJECT (interlace, "handling FLUSH_START");
      ret = gst_pad_push_event (interlace->srcpad, event);
      break;
    case GST_EVENT_FLUSH_STOP:
      GST_DEBUG_OBJECT (interlace, "handling FLUSH_STOP");
      gst_interlace_reset (interlace);
      ret = gst_pad_push_event (interlace->srcpad, event);
      break;
    case GST_EVENT_EOS:
#if 0
      /* FIXME revive this when we output ONEFIELD and RFF buffers */
    {
      gint num_fields;
      const PulldownFormat *format = &formats[interlace->pattern];

      num_fields =
          format->n_fields[interlace->phase_index] -
          interlace->stored_fields_pushed;
      interlace->stored_fields_pushed = 0;

      /* on EOS we want to push as many sane frames as are left */
      while (num_fields > 1) {
        GstBuffer *output_buffer;

        /* make metadata writable before editing it */
        interlace->stored_frame =
            gst_buffer_make_metadata_writable (interlace->stored_frame);
        num_fields -= 2;

        gst_interlace_decorate_buffer (interlace, interlace->stored_frame,
            n_fields, FALSE);

        /* ref output_buffer/stored frame because we want to keep it for now
         * and pushing gives away a ref */
        output_buffer = gst_buffer_ref (interlace->stored_frame);
        if (gst_pad_push (interlace->srcpad, output_buffer)) {
          GST_DEBUG_OBJECT (interlace, "Failed to push buffer %p",
              output_buffer);
          return FALSE;
        }
        output_buffer = NULL;

        if (num_fields <= 1) {
          gst_buffer_unref (interlace->stored_frame);
          interlace->stored_frame = NULL;
          break;
        }
      }

      /* increment the phase index */
      interlace->phase_index++;
      if (!format->n_fields[interlace->phase_index]) {
        interlace->phase_index = 0;
      }
    }
#endif

      ret = gst_pad_push_event (interlace->srcpad, event);
      break;
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = gst_interlace_setcaps (interlace, caps);
      gst_event_unref (event);
      break;
    }
    default:
      ret = gst_pad_push_event (interlace->srcpad, event);
      break;
  }

  return ret;
}
Exemple #26
0
static GstFlowReturn
gst_icydemux_typefind_or_forward (GstICYDemux * icydemux, GstBuffer * buf)
{
  if (icydemux->typefinding) {
    GstBuffer *tf_buf;
    GstCaps *caps = NULL;
    GstTypeFindProbability prob;

    /* If we have a content-type from upstream, let's see if we can shortcut
     * typefinding */
    if (G_UNLIKELY (icydemux->content_type)) {
      if (!g_ascii_strcasecmp (icydemux->content_type, "video/nsv")) {
        GST_DEBUG ("We have a NSV stream");
        caps = gst_caps_new_simple ("video/x-nsv", NULL);
      } else {
        GST_DEBUG ("Upstream Content-Type isn't supported");
        g_free (icydemux->content_type);
        icydemux->content_type = NULL;
      }
    }

    if (icydemux->typefind_buf) {
      icydemux->typefind_buf = gst_buffer_join (icydemux->typefind_buf, buf);
    } else {
      icydemux->typefind_buf = buf;
    }

    /* Only typefind if we haven't already got some caps */
    if (caps == NULL) {
      caps = gst_type_find_helper_for_buffer (GST_OBJECT (icydemux),
          icydemux->typefind_buf, &prob);

      if (caps == NULL) {
        if (GST_BUFFER_SIZE (icydemux->typefind_buf) < ICY_TYPE_FIND_MAX_SIZE) {
          /* Just break for more data */
          return GST_FLOW_OK;
        }

        /* We failed typefind */
        GST_ELEMENT_ERROR (icydemux, STREAM, TYPE_NOT_FOUND, (NULL),
            ("No caps found for contents within an ICY stream"));
        gst_buffer_unref (icydemux->typefind_buf);
        icydemux->typefind_buf = NULL;
        return GST_FLOW_ERROR;
      }
    }

    if (!gst_icydemux_add_srcpad (icydemux, caps)) {
      GST_DEBUG_OBJECT (icydemux, "Failed to add srcpad");
      gst_caps_unref (caps);
      gst_buffer_unref (icydemux->typefind_buf);
      icydemux->typefind_buf = NULL;
      return GST_FLOW_ERROR;
    }
    gst_caps_unref (caps);

    if (icydemux->cached_events) {
      gst_icydemux_send_cached_events (icydemux);
    }

    if (icydemux->cached_tags) {
      gst_icydemux_send_tag_event (icydemux, icydemux->cached_tags);
      icydemux->cached_tags = NULL;
    }

    /* Move onto streaming: call ourselves recursively with the typefind buffer
     * to get that forwarded. */
    icydemux->typefinding = FALSE;

    tf_buf = icydemux->typefind_buf;
    icydemux->typefind_buf = NULL;
    return gst_icydemux_typefind_or_forward (icydemux, tf_buf);
  } else {
    if (G_UNLIKELY (icydemux->srcpad == NULL)) {
      gst_buffer_unref (buf);
      return GST_FLOW_ERROR;
    }

    buf = gst_buffer_make_metadata_writable (buf);
    gst_buffer_set_caps (buf, icydemux->src_caps);

    /* Most things don't care, and it's a pain to track (we should preserve a
     * 0 offset on the first buffer though if it's there, for id3demux etc.) */
    if (GST_BUFFER_OFFSET (buf) != 0) {
      GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
    }

    return gst_pad_push (icydemux->srcpad, buf);
  }
}
Exemple #27
0
static void
gst_frame_store_task (GstPad *pad)
{
  GstFrameStore *fs;
  GstBuffer *buffer;
  GstEvent *event = NULL;

  fs = GST_FRAME_STORE (gst_pad_get_parent (pad));

  GST_DEBUG("task");

  g_mutex_lock (fs->lock);
  while(1) {
    if (fs->stepping == FALSE || (fs->frame_number != fs->pushed_frame_number)) {
      buffer = gst_frame_store_get_frame (fs, fs->frame_number);
    }
    if (buffer) break;
    g_cond_wait (fs->cond, fs->lock);
  }
  if (fs->need_newsegment) {
    GstClock *clock;
    GstClockTime now;
    GstClockTime stream_time;

    clock = GST_ELEMENT_CLOCK (fs);
    if (clock == NULL) {
      now = 0;
      stream_time = 0;
    } else {
      now = gst_clock_get_time (GST_ELEMENT_CLOCK (fs));
      stream_time = now - GST_ELEMENT(fs)->base_time;
    }
    GST_ERROR("now %lld buffer %lld stream_time %lld",
        now, GST_BUFFER_TIMESTAMP(buffer), stream_time);
    stream_time = GST_SECOND*10;
    event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
        GST_BUFFER_TIMESTAMP(buffer), -1, stream_time);
    fs->need_newsegment = FALSE;
  }
  if (fs->stepping) {
    buffer = gst_buffer_make_metadata_writable (buffer);
    GST_BUFFER_TIMESTAMP(buffer) = -1;
    GST_BUFFER_DURATION(buffer) = -1;
  }
  fs->pushed_frame_number = fs->frame_number;
  if (!fs->stepping) {
    fs->frame_number++;
  }
  if (fs->frame_number + 1 >= fs->range_offset + fs->range_size) {
    gst_frame_store_advance (fs);
  }
  g_mutex_unlock (fs->lock);

  if (event) {
    gst_pad_push_event (fs->srcpad, event);
  }

  gst_pad_push (fs->srcpad, buffer);

  GST_DEBUG("task done");

  gst_object_unref (fs);
}
Exemple #28
0
static GstFlowReturn
gst_vcd_parse_chain (GstPad * pad, GstBuffer * buf)
{
  GstVcdParse *vcd = GST_VCD_PARSE (GST_PAD_PARENT (pad));
  GstFlowReturn flow = GST_FLOW_OK;

  gst_adapter_push (vcd->adapter, buf);
  buf = NULL;

  while (gst_adapter_available (vcd->adapter) >= GST_CDXA_SECTOR_SIZE) {
    const guint8 *data;
    guint8 header[4 + 8];
    gint sync_offset;

    /* find sync (we could peek any size though really) */
    data = gst_adapter_peek (vcd->adapter, GST_CDXA_SECTOR_SIZE);
    sync_offset = gst_vcd_parse_sync (data, GST_CDXA_SECTOR_SIZE);
    GST_LOG_OBJECT (vcd, "sync offset = %d", sync_offset);

    if (sync_offset < 0) {
      gst_adapter_flush (vcd->adapter, GST_CDXA_SECTOR_SIZE - 12);
      continue;                 /* try again */
    }

    gst_adapter_flush (vcd->adapter, sync_offset);

    if (gst_adapter_available (vcd->adapter) < GST_CDXA_SECTOR_SIZE) {
      GST_LOG_OBJECT (vcd, "not enough data in adapter, waiting for more");
      break;
    }

    GST_LOG_OBJECT (vcd, "have full sector");

    /* have one sector: a sector is 2352 bytes long and is composed of:
     *
     * +-------------------------------------------------------+
     * !  sync    !  header ! subheader ! data ...   ! edc     !
     * ! 12 bytes ! 4 bytes ! 8 bytes   ! 2324 bytes ! 4 bytes !
     * +-------------------------------------------------------+
     * 
     * We strip the data out of it and send it to the srcpad.
     * 
     * sync       : 00 FF FF FF FF FF FF FF FF FF FF 00
     * header     : hour minute second mode
     * sub-header : track channel sub_mode coding repeat (4 bytes)
     * edc        : checksum
     */

    /* Skip CDXA header and edc footer, only keep data in the middle */
    gst_adapter_copy (vcd->adapter, header, 12, sizeof (header));
    gst_adapter_flush (vcd->adapter, GST_CDXA_HEADER_SIZE);
    buf = gst_adapter_take_buffer (vcd->adapter, GST_CDXA_DATA_SIZE);
    gst_adapter_flush (vcd->adapter, 4);

    /* we could probably do something clever to keep track of buffer offsets */
    buf = gst_buffer_make_metadata_writable (buf);
    GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
    GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
    gst_buffer_set_caps (buf, GST_PAD_CAPS (vcd->srcpad));

    flow = gst_pad_push (vcd->srcpad, buf);
    buf = NULL;

    if (G_UNLIKELY (flow != GST_FLOW_OK)) {
      GST_DEBUG_OBJECT (vcd, "flow: %s", gst_flow_get_name (flow));
      break;
    }
  }

  return flow;
}
static GstFlowReturn
gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf)
{
  GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
  uint32_t sample_count = GST_BUFFER_SIZE (buf) / 4;
  GstFlowReturn ret;

  /* reset the last returns to GST_FLOW_OK. This is only set to something else
   * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
   * so not valid anymore */
  enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;

  GST_DEBUG ("got %u raw samples", sample_count);

  /* check if we already have a valid WavpackContext, otherwise make one */
  if (!enc->wp_context) {
    /* create raw context */
    enc->wp_context =
        WavpackOpenFileOutput (gst_wavpack_enc_push_block, &enc->wv_id,
        (enc->correction_mode > 0) ? &enc->wvc_id : NULL);
    if (!enc->wp_context) {
      GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
          ("error creating Wavpack context"));
      gst_object_unref (enc);
      gst_buffer_unref (buf);
      return GST_FLOW_ERROR;
    }

    /* set the WavpackConfig according to our parameters */
    gst_wavpack_enc_set_wp_config (enc);

    /* set the configuration to the context now that we know everything
     * and initialize the encoder */
    if (!WavpackSetConfiguration (enc->wp_context,
            enc->wp_config, (uint32_t) (-1))
        || !WavpackPackInit (enc->wp_context)) {
      GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL),
          ("error setting up wavpack encoding context"));
      WavpackCloseFile (enc->wp_context);
      gst_object_unref (enc);
      gst_buffer_unref (buf);
      return GST_FLOW_ERROR;
    }
    GST_DEBUG ("setup of encoding context successfull");
  }

  /* Save the timestamp of the first buffer. This will be later
   * used as offset for all following buffers */
  if (enc->timestamp_offset == GST_CLOCK_TIME_NONE) {
    if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
      enc->timestamp_offset = GST_BUFFER_TIMESTAMP (buf);
      enc->next_ts = GST_BUFFER_TIMESTAMP (buf);
    } else {
      enc->timestamp_offset = 0;
      enc->next_ts = 0;
    }
  }

  /* Check if we have a continous stream, if not drop some samples or the buffer or
   * insert some silence samples */
  if (enc->next_ts != GST_CLOCK_TIME_NONE &&
      GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
    guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
    guint64 diff_bytes;

    GST_WARNING_OBJECT (enc, "Buffer is older than previous "
        "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
        "), cannot handle. Clipping buffer.",
        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
        GST_TIME_ARGS (enc->next_ts));

    diff_bytes =
        GST_CLOCK_TIME_TO_FRAMES (diff, enc->samplerate) * enc->channels * 2;
    if (diff_bytes >= GST_BUFFER_SIZE (buf)) {
      gst_buffer_unref (buf);
      return GST_FLOW_OK;
    }
    buf = gst_buffer_make_metadata_writable (buf);
    GST_BUFFER_DATA (buf) += diff_bytes;
    GST_BUFFER_SIZE (buf) -= diff_bytes;

    GST_BUFFER_TIMESTAMP (buf) += diff;
    if (GST_BUFFER_DURATION_IS_VALID (buf))
      GST_BUFFER_DURATION (buf) -= diff;
  }

  /* Allow a diff of at most 5 ms */
  if (enc->next_ts != GST_CLOCK_TIME_NONE
      && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
    if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts &&
        GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > 5 * GST_MSECOND) {
      GST_WARNING_OBJECT (enc,
          "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT,
          GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, 5 * GST_MSECOND);

      WavpackFlushSamples (enc->wp_context);
      enc->timestamp_offset += (GST_BUFFER_TIMESTAMP (buf) - enc->next_ts);
    }
  }

  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)
      && GST_BUFFER_DURATION_IS_VALID (buf))
    enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
  else
    enc->next_ts = GST_CLOCK_TIME_NONE;

  if (enc->need_channel_remap) {
    buf = gst_buffer_make_writable (buf);
    gst_wavpack_enc_fix_channel_order (enc, (gint32 *) GST_BUFFER_DATA (buf),
        sample_count);
  }

  /* if we want to append the MD5 sum to the stream update it here
   * with the current raw samples */
  if (enc->md5) {
    g_checksum_update (enc->md5_context, GST_BUFFER_DATA (buf),
        GST_BUFFER_SIZE (buf));
  }

  /* encode and handle return values from encoding */
  if (WavpackPackSamples (enc->wp_context, (int32_t *) GST_BUFFER_DATA (buf),
          sample_count / enc->channels)) {
    GST_DEBUG ("encoding samples successful");
    ret = GST_FLOW_OK;
  } else {
    if ((enc->srcpad_last_return == GST_FLOW_RESEND) ||
        (enc->wvcsrcpad_last_return == GST_FLOW_RESEND)) {
      ret = GST_FLOW_RESEND;
    } else if ((enc->srcpad_last_return == GST_FLOW_OK) ||
        (enc->wvcsrcpad_last_return == GST_FLOW_OK)) {
      ret = GST_FLOW_OK;
    } else if ((enc->srcpad_last_return == GST_FLOW_NOT_LINKED) &&
        (enc->wvcsrcpad_last_return == GST_FLOW_NOT_LINKED)) {
      ret = GST_FLOW_NOT_LINKED;
    } else if ((enc->srcpad_last_return == GST_FLOW_WRONG_STATE) &&
        (enc->wvcsrcpad_last_return == GST_FLOW_WRONG_STATE)) {
      ret = GST_FLOW_WRONG_STATE;
    } else {
      GST_ELEMENT_ERROR (enc, LIBRARY, ENCODE, (NULL),
          ("encoding samples failed"));
      ret = GST_FLOW_ERROR;
    }
  }

  gst_buffer_unref (buf);
  gst_object_unref (enc);
  return ret;
}
static GstFlowReturn
gst_audio_rate_chain (GstPad * pad, GstBuffer * buf)
{
  GstAudioRate *audiorate;
  GstClockTime in_time, in_duration, in_stop, run_time;
  guint64 in_offset, in_offset_end, in_samples;
  guint in_size;
  GstFlowReturn ret = GST_FLOW_OK;

  audiorate = GST_AUDIO_RATE (gst_pad_get_parent (pad));

  /* need to be negotiated now */
  if (audiorate->bytes_per_sample == 0)
    goto not_negotiated;

  /* we have a new pending segment */
  if (audiorate->next_offset == -1) {
    gint64 pos;

    /* update the TIME segment */
    gst_audio_rate_convert_segments (audiorate);

    /* first buffer, we are negotiated and we have a segment, calculate the
     * current expected offsets based on the segment.start, which is the first
     * media time of the segment and should match the media time of the first
     * buffer in that segment, which is the offset expressed in DEFAULT units.
     */
    /* convert first timestamp of segment to sample position */
    pos = gst_util_uint64_scale_int (audiorate->src_segment.start,
        audiorate->rate, GST_SECOND);

    GST_DEBUG_OBJECT (audiorate, "resync to offset %" G_GINT64_FORMAT, pos);

    audiorate->next_offset = pos;
    audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
        GST_SECOND, audiorate->rate);
  }

  audiorate->in++;

  in_time = GST_BUFFER_TIMESTAMP (buf);
  if (in_time == GST_CLOCK_TIME_NONE) {
    GST_DEBUG_OBJECT (audiorate, "no timestamp, using expected next time");
    in_time = audiorate->next_ts;
  }

  in_size = GST_BUFFER_SIZE (buf);
  in_samples = in_size / audiorate->bytes_per_sample;
  /* get duration from the size because we can and it's more accurate */
  in_duration =
      gst_util_uint64_scale_int (in_samples, GST_SECOND, audiorate->rate);
  in_stop = in_time + in_duration;

  /* Figure out the total accumulated segment time. */
  run_time = in_time + audiorate->src_segment.accum;

  /* calculate the buffer offset */
  in_offset = gst_util_uint64_scale_int (run_time, audiorate->rate, GST_SECOND);
  in_offset_end = in_offset + in_samples;

  GST_LOG_OBJECT (audiorate,
      "in_time:%" GST_TIME_FORMAT ", run_time:%" GST_TIME_FORMAT
      ", in_duration:%" GST_TIME_FORMAT
      ", in_size:%u, in_offset:%lld, in_offset_end:%lld" ", ->next_offset:%lld",
      GST_TIME_ARGS (in_time), GST_TIME_ARGS (run_time),
      GST_TIME_ARGS (in_duration), in_size, in_offset, in_offset_end,
      audiorate->next_offset);

  /* do we need to insert samples */
  if (in_offset > audiorate->next_offset) {
    GstBuffer *fill;
    gint fillsize;
    guint64 fillsamples;

    /* We don't want to allocate a single unreasonably huge buffer - it might
       be hundreds of megabytes. So, limit each output buffer to one second of
       audio */
    fillsamples = in_offset - audiorate->next_offset;

    while (fillsamples > 0) {
      guint64 cursamples = MIN (fillsamples, audiorate->rate);

      fillsamples -= cursamples;
      fillsize = cursamples * audiorate->bytes_per_sample;

      fill = gst_buffer_new_and_alloc (fillsize);
      /* FIXME, 0 might not be the silence byte for the negotiated format. */
      memset (GST_BUFFER_DATA (fill), 0, fillsize);

      GST_DEBUG_OBJECT (audiorate, "inserting %lld samples", cursamples);

      GST_BUFFER_OFFSET (fill) = audiorate->next_offset;
      audiorate->next_offset += cursamples;
      GST_BUFFER_OFFSET_END (fill) = audiorate->next_offset;

      /* Use next timestamp, then calculate following timestamp based on 
       * offset to get duration. Neccesary complexity to get 'perfect' 
       * streams */
      GST_BUFFER_TIMESTAMP (fill) = audiorate->next_ts;
      audiorate->next_ts = gst_util_uint64_scale_int (audiorate->next_offset,
          GST_SECOND, audiorate->rate);
      GST_BUFFER_DURATION (fill) = audiorate->next_ts -
          GST_BUFFER_TIMESTAMP (fill);

      /* we created this buffer to fill a gap */
      GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_GAP);
      /* set discont if it's pending, this is mostly done for the first buffer 
       * and after a flushing seek */
      if (audiorate->discont) {
        GST_BUFFER_FLAG_SET (fill, GST_BUFFER_FLAG_DISCONT);
        audiorate->discont = FALSE;
      }
      gst_buffer_set_caps (fill, GST_PAD_CAPS (audiorate->srcpad));

      ret = gst_pad_push (audiorate->srcpad, fill);
      if (ret != GST_FLOW_OK)
        goto beach;
      audiorate->out++;
      audiorate->add += cursamples;

      if (!audiorate->silent)
        g_object_notify (G_OBJECT (audiorate), "add");
    }

  } else if (in_offset < audiorate->next_offset) {
    /* need to remove samples */
    if (in_offset_end <= audiorate->next_offset) {
      guint64 drop = in_size / audiorate->bytes_per_sample;

      audiorate->drop += drop;

      GST_DEBUG_OBJECT (audiorate, "dropping %lld samples", drop);

      /* we can drop the buffer completely */
      gst_buffer_unref (buf);

      if (!audiorate->silent)
        g_object_notify (G_OBJECT (audiorate), "drop");

      goto beach;
    } else {
      guint64 truncsamples;
      guint truncsize, leftsize;
      GstBuffer *trunc;

      /* truncate buffer */
      truncsamples = audiorate->next_offset - in_offset;
      truncsize = truncsamples * audiorate->bytes_per_sample;
      leftsize = in_size - truncsize;

      trunc = gst_buffer_create_sub (buf, truncsize, leftsize);

      gst_buffer_unref (buf);
      buf = trunc;

      gst_buffer_set_caps (buf, GST_PAD_CAPS (audiorate->srcpad));

      audiorate->drop += truncsamples;
    }
  }

  /* Now calculate parameters for whichever buffer (either the original
   * or truncated one) we're pushing. */
  GST_BUFFER_OFFSET (buf) = audiorate->next_offset;
  GST_BUFFER_OFFSET_END (buf) = in_offset_end;

  GST_BUFFER_TIMESTAMP (buf) = audiorate->next_ts;
  audiorate->next_ts = gst_util_uint64_scale_int (in_offset_end,
      GST_SECOND, audiorate->rate);
  GST_BUFFER_DURATION (buf) = audiorate->next_ts - GST_BUFFER_TIMESTAMP (buf);

  if (audiorate->discont) {
    /* we need to output a discont buffer, do so now */
    GST_DEBUG_OBJECT (audiorate, "marking DISCONT on output buffer");
    buf = gst_buffer_make_metadata_writable (buf);
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    audiorate->discont = FALSE;
  } else if (GST_BUFFER_IS_DISCONT (buf)) {
    /* else we make everything continuous so we can safely remove the DISCONT
     * flag from the buffer if there was one */
    GST_DEBUG_OBJECT (audiorate, "removing DISCONT from buffer");
    buf = gst_buffer_make_metadata_writable (buf);
    GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
  }

  /* set last_stop on segment */
  gst_segment_set_last_stop (&audiorate->src_segment, GST_FORMAT_TIME,
      GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf));

  ret = gst_pad_push (audiorate->srcpad, buf);
  audiorate->out++;

  audiorate->next_offset = in_offset_end;
beach:

  gst_object_unref (audiorate);

  return ret;

  /* ERRORS */
not_negotiated:
  {
    GST_ELEMENT_ERROR (audiorate, STREAM, FORMAT,
        (NULL), ("pipeline error, format was not negotiated"));
    return GST_FLOW_NOT_NEGOTIATED;
  }
}