static gboolean
gst_rdt_manager_src_activate_mode (GstPad * pad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean result;
  GstRDTManager *rdtmanager;
  GstRDTManagerSession *session;

  session = gst_pad_get_element_private (pad);
  rdtmanager = session->dec;

  switch (mode) {
    case GST_PAD_MODE_PUSH:
      if (active) {
        /* allow data processing */
        JBUF_LOCK (session);
        GST_DEBUG_OBJECT (rdtmanager, "Enabling pop on queue");
        /* Mark as non flushing */
        session->srcresult = GST_FLOW_OK;
        gst_segment_init (&session->segment, GST_FORMAT_TIME);
        session->last_popped_seqnum = -1;
        session->last_out_time = -1;
        session->next_seqnum = -1;
        session->eos = FALSE;
        JBUF_UNLOCK (session);

        /* start pushing out buffers */
        GST_DEBUG_OBJECT (rdtmanager, "Starting task on srcpad");
        result =
            gst_pad_start_task (pad, (GstTaskFunction) gst_rdt_manager_loop,
            pad, NULL);
      } else {
        /* make sure all data processing stops ASAP */
        JBUF_LOCK (session);
        /* mark ourselves as flushing */
        session->srcresult = GST_FLOW_FLUSHING;
        GST_DEBUG_OBJECT (rdtmanager, "Disabling pop on queue");
        /* this unblocks any waiting pops on the src pad task */
        JBUF_SIGNAL (session);
        /* unlock clock, we just unschedule, the entry will be released by
         * the locking streaming thread. */
        if (session->clock_id)
          gst_clock_id_unschedule (session->clock_id);
        JBUF_UNLOCK (session);

        /* NOTE this will hardlock if the state change is called from the src pad
         * task thread because we will _join() the thread. */
        GST_DEBUG_OBJECT (rdtmanager, "Stopping task on srcpad");
        result = gst_pad_stop_task (pad);
      }
      break;
    default:
      result = FALSE;
      break;
  }
  return result;
}
static GstFlowReturn
gst_rdt_manager_handle_data_packet (GstRDTManagerSession * session,
    GstClockTime timestamp, GstRDTPacket * packet)
{
  GstRDTManager *rdtmanager;
  guint16 seqnum;
  gboolean tail;
  GstFlowReturn res;
  GstBuffer *buffer;

  rdtmanager = session->dec;

  res = GST_FLOW_OK;

  seqnum = 0;
  GST_DEBUG_OBJECT (rdtmanager,
      "Received packet #%d at time %" GST_TIME_FORMAT, seqnum,
      GST_TIME_ARGS (timestamp));

  buffer = gst_rdt_packet_to_buffer (packet);

  JBUF_LOCK_CHECK (session, out_flushing);

  /* insert the packet into the queue now, FIXME, use seqnum */
  if (!rdt_jitter_buffer_insert (session->jbuf, buffer, timestamp,
          session->clock_rate, &tail))
    goto duplicate;

  /* signal addition of new buffer when the _loop is waiting. */
  if (session->waiting)
    JBUF_SIGNAL (session);

finished:
  JBUF_UNLOCK (session);

  return res;

  /* ERRORS */
out_flushing:
  {
    res = session->srcresult;
    GST_DEBUG_OBJECT (rdtmanager, "flushing %s", gst_flow_get_name (res));
    gst_buffer_unref (buffer);
    goto finished;
  }
duplicate:
  {
    GST_WARNING_OBJECT (rdtmanager, "Duplicate packet #%d detected, dropping",
        seqnum);
    session->num_duplicates++;
    gst_buffer_unref (buffer);
    goto finished;
  }
}
Esempio n. 3
0
static void
gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer)
{
  GstRtpJitterBufferPrivate *priv;

  priv = jitterbuffer->priv;

  JBUF_LOCK (priv);
  /* mark ourselves as flushing */
  priv->srcresult = GST_FLOW_WRONG_STATE;
  GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue");
  /* this unblocks any waiting pops on the src pad task */
  JBUF_SIGNAL (priv);
  /* unlock clock, we just unschedule, the entry will be released by the 
   * locking streaming thread. */
  if (priv->clock_id)
    gst_clock_id_unschedule (priv->clock_id);
  JBUF_UNLOCK (priv);
}
Esempio n. 4
0
static GstFlowReturn
gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer)
{
  GstRtpJitterBuffer *jitterbuffer;
  GstRtpJitterBufferPrivate *priv;
  guint16 seqnum;
  GstFlowReturn ret = GST_FLOW_OK;
  GstClockTime timestamp;
  guint64 latency_ts;
  gboolean tail;

  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));

  if (!gst_rtp_buffer_validate (buffer))
    goto invalid_buffer;

  priv = jitterbuffer->priv;

  if (priv->last_pt != gst_rtp_buffer_get_payload_type (buffer)) {
    GstCaps *caps;

    priv->last_pt = gst_rtp_buffer_get_payload_type (buffer);
    /* reset clock-rate so that we get a new one */
    priv->clock_rate = -1;
    /* Try to get the clock-rate from the caps first if we can. If there are no
     * caps we must fire the signal to get the clock-rate. */
    if ((caps = GST_BUFFER_CAPS (buffer))) {
      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
    }
  }

  if (priv->clock_rate == -1) {
    guint8 pt;

    /* no clock rate given on the caps, try to get one with the signal */
    pt = gst_rtp_buffer_get_payload_type (buffer);

    gst_rtp_jitter_buffer_get_clock_rate (jitterbuffer, pt);
    if (priv->clock_rate == -1)
      goto not_negotiated;
  }

  /* take the timestamp of the buffer. This is the time when the packet was
   * received and is used to calculate jitter and clock skew. We will adjust
   * this timestamp with the smoothed value after processing it in the
   * jitterbuffer. */
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  /* bring to running time */
  timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME,
      timestamp);

  seqnum = gst_rtp_buffer_get_seq (buffer);
  GST_DEBUG_OBJECT (jitterbuffer,
      "Received packet #%d at time %" GST_TIME_FORMAT, seqnum,
      GST_TIME_ARGS (timestamp));

  JBUF_LOCK_CHECK (priv, out_flushing);
  /* don't accept more data on EOS */
  if (priv->eos)
    goto have_eos;

  /* let's check if this buffer is too late, we can only accept packets with
   * bigger seqnum than the one we last pushed. */
  if (priv->last_popped_seqnum != -1) {
    gint gap;

    gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum);

    if (gap <= 0) {
      /* priv->last_popped_seqnum >= seqnum, this packet is too late or the
       * sender might have been restarted with different seqnum. */
      if (gap < -100) {
        GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d", gap);
        priv->last_popped_seqnum = -1;
        priv->next_seqnum = -1;
      } else {
        goto too_late;
      }
    } else {
      /* priv->last_popped_seqnum < seqnum, this is a new packet */
      if (gap > 3000) {
        GST_DEBUG_OBJECT (jitterbuffer, "reset: too many dropped packets %d",
            gap);
        priv->last_popped_seqnum = -1;
        priv->next_seqnum = -1;
      }
    }
  }

  /* let's drop oldest packet if the queue is already full and drop-on-latency
   * is set. We can only do this when there actually is a latency. When no
   * latency is set, we just pump it in the queue and let the other end push it
   * out as fast as possible. */
  if (priv->latency_ms && priv->drop_on_latency) {

    latency_ts =
        gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000);

    if (rtp_jitter_buffer_get_ts_diff (priv->jbuf) >= latency_ts) {
      GstBuffer *old_buf;

      GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet #%d",
          seqnum);

      old_buf = rtp_jitter_buffer_pop (priv->jbuf);
      gst_buffer_unref (old_buf);
    }
  }

  /* now insert the packet into the queue in sorted order. This function returns
   * FALSE if a packet with the same seqnum was already in the queue, meaning we
   * have a duplicate. */
  if (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp,
          priv->clock_rate, &tail))
    goto duplicate;

  /* signal addition of new buffer when the _loop is waiting. */
  if (priv->waiting)
    JBUF_SIGNAL (priv);

  /* let's unschedule and unblock any waiting buffers. We only want to do this
   * when the tail buffer changed */
  if (priv->clock_id && tail) {
    GST_DEBUG_OBJECT (jitterbuffer,
        "Unscheduling waiting buffer, new tail buffer");
    gst_clock_id_unschedule (priv->clock_id);
  }

  GST_DEBUG_OBJECT (jitterbuffer, "Pushed packet #%d, now %d packets",
      seqnum, rtp_jitter_buffer_num_packets (priv->jbuf));

finished:
  JBUF_UNLOCK (priv);

  gst_object_unref (jitterbuffer);

  return ret;

  /* ERRORS */
invalid_buffer:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
        ("Received invalid RTP payload, dropping"));
    gst_buffer_unref (buffer);
    gst_object_unref (jitterbuffer);
    return GST_FLOW_OK;
  }
not_negotiated:
  {
    GST_WARNING_OBJECT (jitterbuffer, "No clock-rate in caps!");
    gst_buffer_unref (buffer);
    gst_object_unref (jitterbuffer);
    return GST_FLOW_OK;
  }
out_flushing:
  {
    ret = priv->srcresult;
    GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret));
    gst_buffer_unref (buffer);
    goto finished;
  }
have_eos:
  {
    ret = GST_FLOW_UNEXPECTED;
    GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer");
    gst_buffer_unref (buffer);
    goto finished;
  }
too_late:
  {
    GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
        " popped, dropping", seqnum, priv->last_popped_seqnum);
    priv->num_late++;
    gst_buffer_unref (buffer);
    goto finished;
  }
duplicate:
  {
    GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
        seqnum);
    priv->num_duplicates++;
    gst_buffer_unref (buffer);
    goto finished;
  }
}
Esempio n. 5
0
static gboolean
gst_rtp_jitter_buffer_sink_event (GstPad * pad, GstEvent * event)
{
  gboolean ret = TRUE;
  GstRtpJitterBuffer *jitterbuffer;
  GstRtpJitterBufferPrivate *priv;

  jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
  priv = jitterbuffer->priv;

  GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_NEWSEGMENT:
    {
      GstFormat format;
      gdouble rate, arate;
      gint64 start, stop, time;
      gboolean update;

      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
          &start, &stop, &time);

      /* we need time for now */
      if (format != GST_FORMAT_TIME)
        goto newseg_wrong_format;

      GST_DEBUG_OBJECT (jitterbuffer,
          "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
          ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
          update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
          GST_TIME_ARGS (time));

      /* now configure the values, we need these to time the release of the
       * buffers on the srcpad. */
      gst_segment_set_newsegment_full (&priv->segment, update,
          rate, arate, format, start, stop, time);

      /* FIXME, push SEGMENT in the queue. Sorting order might be difficult. */
      ret = gst_pad_push_event (priv->srcpad, event);
      break;
    }
    case GST_EVENT_FLUSH_START:
      gst_rtp_jitter_buffer_flush_start (jitterbuffer);
      ret = gst_pad_push_event (priv->srcpad, event);
      break;
    case GST_EVENT_FLUSH_STOP:
      ret = gst_pad_push_event (priv->srcpad, event);
      ret = gst_rtp_jitter_buffer_src_activate_push (priv->srcpad, TRUE);
      break;
    case GST_EVENT_EOS:
    {
      /* push EOS in queue. We always push it at the head */
      JBUF_LOCK (priv);
      /* check for flushing, we need to discard the event and return FALSE when
       * we are flushing */
      ret = priv->srcresult == GST_FLOW_OK;
      if (ret && !priv->eos) {
        GST_DEBUG_OBJECT (jitterbuffer, "queuing EOS");
        priv->eos = TRUE;
        JBUF_SIGNAL (priv);
      } else if (priv->eos) {
        GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, we are already EOS");
      } else {
        GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, reason %s",
            gst_flow_get_name (priv->srcresult));
      }
      JBUF_UNLOCK (priv);
      gst_event_unref (event);
      break;
    }
    default:
      ret = gst_pad_push_event (priv->srcpad, event);
      break;
  }

done:
  gst_object_unref (jitterbuffer);

  return ret;

  /* ERRORS */
newseg_wrong_format:
  {
    GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment");
    ret = FALSE;
    goto done;
  }
}
Esempio n. 6
0
static GstStateChangeReturn
gst_rtp_jitter_buffer_change_state (GstElement * element,
    GstStateChange transition)
{
  GstRtpJitterBuffer *jitterbuffer;
  GstRtpJitterBufferPrivate *priv;
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;

  jitterbuffer = GST_RTP_JITTER_BUFFER (element);
  priv = jitterbuffer->priv;

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      JBUF_LOCK (priv);
      /* reset negotiated values */
      priv->clock_rate = -1;
      priv->clock_base = -1;
      priv->peer_latency = 0;
      priv->last_pt = -1;
      /* block until we go to PLAYING */
      priv->blocked = TRUE;
      /* reset skew detection initialy */
      rtp_jitter_buffer_reset_skew (priv->jbuf);
      JBUF_UNLOCK (priv);
      break;
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      JBUF_LOCK (priv);
      /* unblock to allow streaming in PLAYING */
      priv->blocked = FALSE;
      JBUF_SIGNAL (priv);
      JBUF_UNLOCK (priv);
      break;
    default:
      break;
  }

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

  switch (transition) {
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      /* we are a live element because we sync to the clock, which we can only
       * do in the PLAYING state */
      if (ret != GST_STATE_CHANGE_FAILURE)
        ret = GST_STATE_CHANGE_NO_PREROLL;
      break;
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      JBUF_LOCK (priv);
      /* block to stop streaming when PAUSED */
      priv->blocked = TRUE;
      JBUF_UNLOCK (priv);
      if (ret != GST_STATE_CHANGE_FAILURE)
        ret = GST_STATE_CHANGE_NO_PREROLL;
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      break;
    default:
      break;
  }

  return ret;
}