static GstFlowReturn
gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf)
{
  GstFlowReturn ret;
  GstRtpSsrcDemux *demux;
  guint32 ssrc;
  GstRtpSsrcDemuxPad *dpad;

  demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));

  if (!gst_rtp_buffer_validate (buf))
    goto invalid_payload;

  ssrc = gst_rtp_buffer_get_ssrc (buf);

  GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc);

  GST_PAD_LOCK (demux);
  dpad = find_demux_pad_for_ssrc (demux, ssrc);
  if (dpad == NULL) {
    if (!(dpad =
            create_demux_pad_for_ssrc (demux, ssrc,
                GST_BUFFER_TIMESTAMP (buf))))
      goto create_failed;
  }
  GST_PAD_UNLOCK (demux);

  /* push to srcpad */
  ret = gst_pad_push (dpad->rtp_pad, buf);

  return ret;

  /* ERRORS */
invalid_payload:
  {
    /* this is fatal and should be filtered earlier */
    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
        ("Dropping invalid RTP payload"));
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
create_failed:
  {
    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
        ("Could not create new pad"));
    GST_PAD_UNLOCK (demux);
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
}
static void
compare_rtp_packets (GstBuffer * a, GstBuffer * b)
{
  GstRTPBuffer rtp_a = GST_RTP_BUFFER_INIT;
  GstRTPBuffer rtp_b = GST_RTP_BUFFER_INIT;

  gst_rtp_buffer_map (a, GST_MAP_READ, &rtp_a);
  gst_rtp_buffer_map (b, GST_MAP_READ, &rtp_b);

  fail_unless_equals_int (gst_rtp_buffer_get_header_len (&rtp_a),
      gst_rtp_buffer_get_header_len (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_version (&rtp_a),
      gst_rtp_buffer_get_version (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp_a),
      gst_rtp_buffer_get_ssrc (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp_a),
      gst_rtp_buffer_get_seq (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp_a),
      gst_rtp_buffer_get_csrc_count (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_marker (&rtp_a),
      gst_rtp_buffer_get_marker (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp_a),
      gst_rtp_buffer_get_payload_type (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp_a),
      gst_rtp_buffer_get_timestamp (&rtp_b));
  fail_unless_equals_int (gst_rtp_buffer_get_extension (&rtp_a),
      gst_rtp_buffer_get_extension (&rtp_b));

  fail_unless_equals_int (gst_rtp_buffer_get_payload_len (&rtp_a),
      gst_rtp_buffer_get_payload_len (&rtp_b));
  fail_unless_equals_int (memcmp (gst_rtp_buffer_get_payload (&rtp_a),
          gst_rtp_buffer_get_payload (&rtp_b),
          gst_rtp_buffer_get_payload_len (&rtp_a)), 0);

  gst_rtp_buffer_unmap (&rtp_a);
  gst_rtp_buffer_unmap (&rtp_b);
}
static GstFlowReturn gst_scream_queue_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer)
{
    GstScreamQueue *self = GST_SCREAM_QUEUE(parent);
    GstRTPBuffer rtp_buffer = GST_RTP_BUFFER_INIT;
    GstFlowReturn flow_ret = GST_FLOW_OK;
    GstScreamDataQueueRtpItem *rtp_item;

    if (GST_PAD_IS_FLUSHING(pad)) {
        flow_ret = GST_FLOW_FLUSHING;
        goto end;
    }

    if (!gst_rtp_buffer_map(buffer, GST_MAP_READ, &rtp_buffer)) {
        flow_ret = GST_FLOW_ERROR;
        goto end;
    }

    rtp_item = g_slice_new(GstScreamDataQueueRtpItem);
    ((GstDataQueueItem *)rtp_item)->object = GST_MINI_OBJECT(buffer);
    ((GstDataQueueItem *)rtp_item)->size = gst_buffer_get_size(buffer);
    ((GstDataQueueItem *)rtp_item)->visible = TRUE;
    ((GstDataQueueItem *)rtp_item)->duration = GST_BUFFER_DURATION(buffer);
    ((GstDataQueueItem *)rtp_item)->destroy = (GDestroyNotify) gst_scream_data_queue_rtp_item_free;

    ((GstScreamDataQueueItem *)rtp_item)->type = GST_SCREAM_DATA_QUEUE_ITEM_TYPE_RTP;
    ((GstScreamDataQueueItem *)rtp_item)->rtp_ssrc = gst_rtp_buffer_get_ssrc(&rtp_buffer);
    rtp_item->rtp_pt = gst_rtp_buffer_get_payload_type(&rtp_buffer);
    rtp_item->gst_ts = GST_BUFFER_PTS(buffer);
    rtp_item->rtp_seq = gst_rtp_buffer_get_seq(&rtp_buffer);
    rtp_item->rtp_ts = gst_rtp_buffer_get_timestamp(&rtp_buffer);
    rtp_item->rtp_marker = gst_rtp_buffer_get_marker(&rtp_buffer);
    rtp_item->rtp_payload_size = gst_rtp_buffer_get_payload_len(&rtp_buffer);
    rtp_item->enqueued_time = get_gst_time_us(self);
    gst_rtp_buffer_unmap(&rtp_buffer);

    if (self->pass_through) {
        rtp_item->adapted = FALSE;
        GST_LOG_OBJECT(self, "passing through: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through);
        gst_data_queue_push(self->approved_packets, (GstDataQueueItem *)rtp_item);
        goto end;
    }

    GST_LOG_OBJECT(self, "queuing: pt = %u, seq: %u, pass: %u", rtp_item->rtp_pt, rtp_item->rtp_seq, self->pass_through);
    g_async_queue_push(self->incoming_packets, (gpointer)rtp_item);

end:
    return flow_ret;
}
Exemple #4
0
static GstFlowReturn
gst_rtp_ssrc_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstFlowReturn ret;
  GstRtpSsrcDemux *demux;
  guint32 ssrc;
  GstRTPBuffer rtp = { NULL };
  GstPad *srcpad;

  demux = GST_RTP_SSRC_DEMUX (parent);

  if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp))
    goto invalid_payload;

  ssrc = gst_rtp_buffer_get_ssrc (&rtp);
  gst_rtp_buffer_unmap (&rtp);

  GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc);

  srcpad = find_or_create_demux_pad_for_ssrc (demux, ssrc, RTP_PAD);
  if (srcpad == NULL)
    goto create_failed;

  /* push to srcpad */
  ret = gst_pad_push (srcpad, buf);

  gst_object_unref (srcpad);

  return ret;

  /* ERRORS */
invalid_payload:
  {
    /* this is fatal and should be filtered earlier */
    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
        ("Dropping invalid RTP payload"));
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
create_failed:
  {
    GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
        ("Could not create new pad"));
    gst_buffer_unref (buf);
    return GST_FLOW_ERROR;
  }
}
static void
packet_recovered_cb (GObject * internal_storage, GstBuffer * buffer,
    GList * infos)
{
  gboolean found = FALSE;
  GstRTPBuffer rtp = { NULL };

  fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));

  for (GList * it = infos; it; it = it->next) {
    RecoveredPacketInfo *info = it->data;
    if (gst_rtp_buffer_get_seq (&rtp) == info->seq) {
      fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), info->pt);
      fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), info->ssrc);
      found = TRUE;
      break;
    }
  }

  gst_rtp_buffer_unmap (&rtp);
  fail_unless (found);
}
static void
add_rtp_source_meta (GstBuffer * outbuf, GstBuffer * rtpbuf)
{
  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
  GstRTPSourceMeta *meta;
  guint32 ssrc;

  if (!gst_rtp_buffer_map (rtpbuf, GST_MAP_READ, &rtp))
    return;

  ssrc = gst_rtp_buffer_get_ssrc (&rtp);
  meta = gst_buffer_add_rtp_source_meta (outbuf, &ssrc, NULL, 0);
  if (meta != NULL) {
    gint csrc_count = gst_rtp_buffer_get_csrc_count (&rtp);
    for (gint i = 0; i < csrc_count; i++) {
      guint32 csrc = gst_rtp_buffer_get_csrc (&rtp, i);
      gst_rtp_source_meta_append_csrc (meta, &csrc, 1);
    }
  }

  gst_rtp_buffer_unmap (&rtp);
}
Exemple #7
0
static void
change_ssrc_handler (GstPad *pad, GstBuffer *buf, gpointer user_data)
{
  guint sess_ssrc;
  guint buf_ssrc;

  ts_fail_unless (gst_rtp_buffer_validate (buf));

  buf_ssrc = gst_rtp_buffer_get_ssrc (buf);

  g_object_get (dat->session, "ssrc", &sess_ssrc, NULL);

  if (buf_ssrc == 12345)
  {
    /* Step two, set it to 6789 */
    ts_fail_unless (buf_ssrc == sess_ssrc || sess_ssrc == 6789);

    g_object_set (dat->session, "ssrc", 6789, NULL);
  }
  else if (buf_ssrc == 6789)
  {
    /* Step three, quit */
    ts_fail_unless (buf_ssrc == sess_ssrc);

    g_main_loop_quit (loop);
  }
  else
  {
    ts_fail_unless (checked || buf_ssrc == sess_ssrc);
    checked = TRUE;

    /* Step one, set the ssrc to 12345 */
    if (sess_ssrc != 12345)
      g_object_set (dat->session, "ssrc", 12345, NULL);
  }
}
Exemple #8
0
/* Return a stream structure for a given buffer
 */
static GstSrtpDecSsrcStream *
validate_buffer (GstSrtpDec * filter, GstBuffer * buf, guint32 * ssrc,
    gboolean * is_rtcp)
{
  GstSrtpDecSsrcStream *stream = NULL;
  GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;

  if (gst_rtp_buffer_map (buf,
          GST_MAP_READ | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &rtpbuf)) {
    if (gst_rtp_buffer_get_payload_type (&rtpbuf) < 64
        || gst_rtp_buffer_get_payload_type (&rtpbuf) > 80) {
      *ssrc = gst_rtp_buffer_get_ssrc (&rtpbuf);

      gst_rtp_buffer_unmap (&rtpbuf);
      *is_rtcp = FALSE;
      goto have_ssrc;
    }
    gst_rtp_buffer_unmap (&rtpbuf);
  }

  if (rtcp_buffer_get_ssrc (buf, ssrc)) {
    *is_rtcp = TRUE;
  } else {
    GST_WARNING_OBJECT (filter, "No SSRC found in buffer");
    return NULL;
  }

have_ssrc:

  stream = find_stream_by_ssrc (filter, *ssrc);

  if (stream)
    return stream;

  return request_key_with_signal (filter, *ssrc, SIGNAL_REQUEST_KEY);
}
static GstPadProbeReturn
rtpsession_sinkpad_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer user_data)
{
  GstPadProbeReturn ret = GST_PAD_PROBE_OK;

  if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
    GstBuffer *buffer = GST_BUFFER (info->data);
    GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
    GstBuffer *rtcp_buffer = 0;
    guint ssrc = 0;

    /* retrieve current ssrc */
    gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
    ssrc = gst_rtp_buffer_get_ssrc (&rtp);
    gst_rtp_buffer_unmap (&rtp);

    /* if not first buffer, check that our ssrc has changed */
    if (ssrc_prev != -1 && ssrc != ssrc_prev)
      ++nb_ssrc_changes;

    /* update prev ssrc */
    ssrc_prev = ssrc;

    /* feint a collision on recv_rtcp_sink pad of gstrtpsession
     * (note that after being marked as collied the rtpsession ignores
     * all non bye packets)
     */
    rtcp_buffer = create_rtcp_app (ssrc, nb_ssrc_changes);

    /* push collied packet on recv_rtcp_sink */
    gst_pad_push (srcpad, rtcp_buffer);
  }

  return ret;
}
Exemple #10
0
static void
lock_check_cb (GstPad * pad, int i)
{
  GstBuffer *inbuf;

  if (i % 2) {
    fail_unless (buffers == NULL);
  } else {
    GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;

    fail_unless (buffers && g_list_length (buffers) == 1);
    gst_rtp_buffer_map (buffers->data, GST_MAP_READ, &rtpbuffer);
    fail_unless_equals_int (66, gst_rtp_buffer_get_ssrc (&rtpbuffer));
    fail_unless_equals_int64 (200 - 57 + 1000 + i,
        gst_rtp_buffer_get_timestamp (&rtpbuffer));
    fail_unless_equals_int (100 + 1 + i, gst_rtp_buffer_get_seq (&rtpbuffer));
    gst_rtp_buffer_unmap (&rtpbuffer);

    inbuf = gst_rtp_buffer_new_allocate (10, 0, 0);
    GST_BUFFER_PTS (inbuf) = i * 1000 + 500;
    GST_BUFFER_DURATION (inbuf) = 1000;
    gst_rtp_buffer_map (inbuf, GST_MAP_WRITE, &rtpbuffer);
    gst_rtp_buffer_set_version (&rtpbuffer, 2);
    gst_rtp_buffer_set_payload_type (&rtpbuffer, 98);
    gst_rtp_buffer_set_ssrc (&rtpbuffer, 44);
    gst_rtp_buffer_set_timestamp (&rtpbuffer, 200 + i);
    gst_rtp_buffer_set_seq (&rtpbuffer, 2000 + i);
    gst_rtp_buffer_unmap (&rtpbuffer);
    fail_unless (gst_pad_push (pad, inbuf) == GST_FLOW_OK);


    g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
    g_list_free (buffers);
    buffers = NULL;
  }
}
static GstFlowReturn
gst_rtp_dec_chain_rtp (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
    GstFlowReturn res;
    GstRTPDec *rtpdec;
    GstRTPDecSession *session;
    guint32 ssrc;
    guint8 pt;
    GstRTPBuffer rtp = { NULL, };

    rtpdec = GST_RTP_DEC (parent);

    GST_DEBUG_OBJECT (rtpdec, "got rtp packet");

    if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
        goto bad_packet;

    ssrc = gst_rtp_buffer_get_ssrc (&rtp);
    pt = gst_rtp_buffer_get_payload_type (&rtp);
    gst_rtp_buffer_unmap (&rtp);

    GST_DEBUG_OBJECT (rtpdec, "SSRC %08x, PT %d", ssrc, pt);

    /* find session */
    session = gst_pad_get_element_private (pad);

    /* see if we have the pad */
    if (!session->active) {
        GstPadTemplate *templ;
        GstElementClass *klass;
        gchar *name;
        GstCaps *caps;
        GValue ret = { 0 };
        GValue args[3] = { {0}
            , {0}
            , {0}
        };

        GST_DEBUG_OBJECT (rtpdec, "creating stream");

        session->ssrc = ssrc;
        session->pt = pt;

        /* get pt map */
        g_value_init (&args[0], GST_TYPE_ELEMENT);
        g_value_set_object (&args[0], rtpdec);
        g_value_init (&args[1], G_TYPE_UINT);
        g_value_set_uint (&args[1], session->id);
        g_value_init (&args[2], G_TYPE_UINT);
        g_value_set_uint (&args[2], pt);

        g_value_init (&ret, GST_TYPE_CAPS);
        g_value_set_boxed (&ret, NULL);

        g_signal_emitv (args, gst_rtp_dec_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);

        caps = (GstCaps *) g_value_get_boxed (&ret);

        name = g_strdup_printf ("recv_rtp_src_%u_%u_%u", session->id, ssrc, pt);
        klass = GST_ELEMENT_GET_CLASS (rtpdec);
        templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%u_%u_%u");
        session->recv_rtp_src = gst_pad_new_from_template (templ, name);
        g_free (name);

        gst_pad_set_caps (session->recv_rtp_src, caps);

        gst_pad_set_element_private (session->recv_rtp_src, session);
        gst_pad_set_query_function (session->recv_rtp_src, gst_rtp_dec_query_src);
        gst_pad_set_active (session->recv_rtp_src, TRUE);
        gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtp_src);

        session->active = TRUE;
    }

    res = gst_pad_push (session->recv_rtp_src, buffer);

    return res;

bad_packet:
    {
        GST_ELEMENT_WARNING (rtpdec, STREAM, DECODE, (NULL),
                             ("RTP packet did not validate, dropping"));
        gst_buffer_unref (buffer);
        return GST_FLOW_OK;
    }
}
gboolean
kms_rtp_synchronizer_process_rtp_buffer_mapped (KmsRtpSynchronizer * self,
    GstRTPBuffer * rtp_buffer, GError ** error)
{
  GstBuffer *buffer = rtp_buffer->buffer;
  guint64 pts_orig, ext_ts, last_sr_ext_ts, last_sr_ntp_ns_time;
  guint64 diff_ntp_ns_time;
  guint8 pt;
  guint32 ssrc, ts;
  gint32 clock_rate;
  gboolean ret = TRUE;

  ssrc = gst_rtp_buffer_get_ssrc (rtp_buffer);

  KMS_RTP_SYNCHRONIZER_LOCK (self);

  if (self->priv->ssrc == 0) {
    self->priv->ssrc = ssrc;
  } else if (ssrc != self->priv->ssrc) {
    gchar *msg = g_strdup_printf ("Invalid SSRC (%u), not matching with %u",
        ssrc, self->priv->ssrc);

    GST_ERROR_OBJECT (self, "%s", msg);
    g_set_error_literal (error, KMS_RTP_SYNC_ERROR, KMS_RTP_SYNC_INVALID_DATA,
        msg);
    g_free (msg);

    KMS_RTP_SYNCHRONIZER_UNLOCK (self);

    return FALSE;
  }

  pt = gst_rtp_buffer_get_payload_type (rtp_buffer);
  if (pt != self->priv->pt || self->priv->clock_rate <= 0) {
    gchar *msg =
        g_strdup_printf ("Invalid clock-rate %d for PT %u, not changing PTS",
        self->priv->clock_rate, pt);

    GST_ERROR_OBJECT (self, "%s", msg);
    g_set_error_literal (error, KMS_RTP_SYNC_ERROR, KMS_RTP_SYNC_INVALID_DATA,
        msg);
    g_free (msg);

    KMS_RTP_SYNCHRONIZER_UNLOCK (self);

    return FALSE;
  }

  pts_orig = GST_BUFFER_PTS (buffer);
  ts = gst_rtp_buffer_get_timestamp (rtp_buffer);
  gst_rtp_buffer_ext_timestamp (&self->priv->ext_ts, ts);

  if (self->priv->feeded_sorted) {
    if (self->priv->fs_last_ext_ts != -1
        && self->priv->ext_ts < self->priv->fs_last_ext_ts) {
      guint16 seq = gst_rtp_buffer_get_seq (rtp_buffer);
      gchar *msg =
          g_strdup_printf
          ("Received an unsorted RTP buffer when expecting sorted (ssrc: %"
          G_GUINT32_FORMAT ", seq: %" G_GUINT16_FORMAT ", ts: %"
          G_GUINT32_FORMAT ", ext_ts: %" G_GUINT64_FORMAT
          "). Moving to unsorted mode",
          ssrc, seq, ts, self->priv->ext_ts);

      GST_ERROR_OBJECT (self, "%s", msg);
      g_set_error_literal (error, KMS_RTP_SYNC_ERROR, KMS_RTP_SYNC_INVALID_DATA,
          msg);
      g_free (msg);

      self->priv->feeded_sorted = FALSE;
      ret = FALSE;
    } else if (self->priv->ext_ts == self->priv->fs_last_ext_ts) {
      GST_BUFFER_PTS (buffer) = self->priv->fs_last_pts;
      goto end;
    }
  }

  if (!self->priv->base_initiated) {
    GST_DEBUG_OBJECT (self,
        "Do not sync data for SSRC %u and PT %u, interpolating PTS", ssrc, pt);

    if (!self->priv->base_interpolate_initiated) {
      self->priv->base_interpolate_ext_ts = self->priv->ext_ts;
      self->priv->base_interpolate_time = GST_BUFFER_PTS (buffer);
      self->priv->base_interpolate_initiated = TRUE;
    } else {
      buffer = gst_buffer_make_writable (buffer);
      GST_BUFFER_PTS (buffer) = self->priv->base_interpolate_time;
      kms_rtp_synchronizer_rtp_diff (self, rtp_buffer, self->priv->clock_rate,
          self->priv->base_interpolate_ext_ts);
    }
  } else {
    gboolean wrapped_down, wrapped_up;

    wrapped_down = wrapped_up = FALSE;

    buffer = gst_buffer_make_writable (buffer);
    GST_BUFFER_PTS (buffer) = self->priv->base_sync_time;

    if (self->priv->last_sr_ntp_ns_time > self->priv->base_ntp_ns_time) {
      diff_ntp_ns_time =
          self->priv->last_sr_ntp_ns_time - self->priv->base_ntp_ns_time;
      wrapped_up = diff_ntp_ns_time > (G_MAXUINT64 - GST_BUFFER_PTS (buffer));
      GST_BUFFER_PTS (buffer) += diff_ntp_ns_time;
    } else if (self->priv->last_sr_ntp_ns_time < self->priv->base_ntp_ns_time) {
      diff_ntp_ns_time =
          self->priv->base_ntp_ns_time - self->priv->last_sr_ntp_ns_time;
      wrapped_down = GST_BUFFER_PTS (buffer) < diff_ntp_ns_time;
      GST_BUFFER_PTS (buffer) -= diff_ntp_ns_time;
    }
    /* if equals do nothing */

    kms_rtp_synchronizer_rtp_diff_full (self, rtp_buffer,
        self->priv->clock_rate, self->priv->last_sr_ext_ts, wrapped_down,
        wrapped_up);
  }

  if (self->priv->feeded_sorted) {
    if (GST_BUFFER_PTS (buffer) < self->priv->fs_last_pts) {
      guint16 seq = gst_rtp_buffer_get_seq (rtp_buffer);

      GST_WARNING_OBJECT (self,
          "Non monotonic PTS assignment in sorted mode (ssrc: %"
          G_GUINT32_FORMAT ", seq: %" G_GUINT16_FORMAT ", ts: %"
          G_GUINT32_FORMAT ", ext_ts: %" G_GUINT64_FORMAT
          "). Forcing monotonic", ssrc, seq, ts, self->priv->ext_ts);

      GST_BUFFER_PTS (buffer) = self->priv->fs_last_pts;
    }

    self->priv->fs_last_ext_ts = self->priv->ext_ts;
    self->priv->fs_last_pts = GST_BUFFER_PTS (buffer);
  }

end:
  clock_rate = self->priv->clock_rate;
  ext_ts = self->priv->ext_ts;
  last_sr_ext_ts = self->priv->last_sr_ext_ts;
  last_sr_ntp_ns_time = self->priv->last_sr_ntp_ns_time;

  KMS_RTP_SYNCHRONIZER_UNLOCK (self);

  kms_rtp_sync_context_write_stats (self->priv->context, ssrc, clock_rate,
      pts_orig, GST_BUFFER_PTS (buffer), GST_BUFFER_DTS (buffer), ext_ts,
      last_sr_ntp_ns_time, last_sr_ext_ts);

  return ret;
}
static GstFlowReturn
gst_rtp_rtx_receive_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
  GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (parent);
  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
  GstFlowReturn ret = GST_FLOW_OK;
  GstBuffer *new_buffer = NULL;
  guint32 ssrc = 0;
  gpointer ssrc1 = 0;
  guint32 ssrc2 = 0;
  guint16 seqnum = 0;
  guint16 orign_seqnum = 0;
  guint8 payload_type = 0;
  guint8 origin_payload_type = 0;
  gboolean is_rtx;
  gboolean drop = FALSE;

  /* map current rtp packet to parse its header */
  gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
  ssrc = gst_rtp_buffer_get_ssrc (&rtp);
  seqnum = gst_rtp_buffer_get_seq (&rtp);
  payload_type = gst_rtp_buffer_get_payload_type (&rtp);

  /* check if we have a retransmission packet (this information comes from SDP) */
  GST_OBJECT_LOCK (rtx);

  rtx->last_time = GST_BUFFER_PTS (buffer);

  if (g_hash_table_size (rtx->seqnum_ssrc1_map) > 0) {
    GHashTableIter iter;
    gpointer key, value;

    g_hash_table_iter_init (&iter, rtx->seqnum_ssrc1_map);
    while (g_hash_table_iter_next (&iter, &key, &value)) {
      SsrcAssoc *assoc = value;

      /* remove association request if it is too old */
      if (GST_CLOCK_TIME_IS_VALID (rtx->last_time) &&
          GST_CLOCK_TIME_IS_VALID (assoc->time) &&
          assoc->time + ASSOC_TIMEOUT < rtx->last_time) {
        g_hash_table_iter_remove (&iter);
      }
    }
  }

  is_rtx =
      g_hash_table_lookup_extended (rtx->rtx_pt_map,
      GUINT_TO_POINTER (payload_type), NULL, NULL);

  /* if the current packet is from a retransmission stream */
  if (is_rtx) {
    /* increase our statistic */
    ++rtx->num_rtx_packets;

    /* read OSN in the rtx payload */
    orign_seqnum = GST_READ_UINT16_BE (gst_rtp_buffer_get_payload (&rtp));
    origin_payload_type =
        GPOINTER_TO_UINT (g_hash_table_lookup (rtx->rtx_pt_map,
            GUINT_TO_POINTER (payload_type)));

    /* first we check if we already have associated this retransmission stream
     * to a master stream */
    if (g_hash_table_lookup_extended (rtx->ssrc2_ssrc1_map,
            GUINT_TO_POINTER (ssrc), NULL, &ssrc1)) {
      GST_DEBUG_OBJECT (rtx,
          "packet is from retransmission stream %" G_GUINT32_FORMAT
          " already associated to master stream %" G_GUINT32_FORMAT, ssrc,
          GPOINTER_TO_UINT (ssrc1));
      ssrc2 = ssrc;
    } else {
      SsrcAssoc *assoc;

      /* the current retransmitted packet has its rtx stream not already
       * associated to a master stream, so retrieve it from our request
       * history */
      if (g_hash_table_lookup_extended (rtx->seqnum_ssrc1_map,
              GUINT_TO_POINTER (orign_seqnum), NULL, (gpointer *) & assoc)) {
        GST_DEBUG_OBJECT (rtx,
            "associate retransmitted stream %" G_GUINT32_FORMAT
            " to master stream %" G_GUINT32_FORMAT " thanks to packet %"
            G_GUINT16_FORMAT "", ssrc, assoc->ssrc, orign_seqnum);
        ssrc1 = GUINT_TO_POINTER (assoc->ssrc);
        ssrc2 = ssrc;

        /* just put a guard */
        if (GPOINTER_TO_UINT (ssrc1) == ssrc2)
          GST_WARNING_OBJECT (rtx, "RTX receiver ssrc2_ssrc1_map bad state, "
              "ssrc %" G_GUINT32_FORMAT " are the same\n", ssrc);

        /* free the spot so that this seqnum can be used to do another
         * association */
        g_hash_table_remove (rtx->seqnum_ssrc1_map,
            GUINT_TO_POINTER (orign_seqnum));

        /* actually do the association between rtx stream and master stream */
        g_hash_table_insert (rtx->ssrc2_ssrc1_map, GUINT_TO_POINTER (ssrc2),
            ssrc1);

        /* also do the association between master stream and rtx stream
         * every ssrc are unique so we can use the same hash table
         * for both retrieving the ssrc1 from ssrc2 and also ssrc2 from ssrc1
         */
        g_hash_table_insert (rtx->ssrc2_ssrc1_map, ssrc1,
            GUINT_TO_POINTER (ssrc2));

      } else {
        /* we are not able to associate this rtx packet with a master stream */
        GST_DEBUG_OBJECT (rtx,
            "drop rtx packet because its orign_seqnum %" G_GUINT16_FORMAT
            " is not in pending retransmission requests", orign_seqnum);
        drop = TRUE;
      }
    }
  }

  /* if not dropped the packet was successfully associated */
  if (is_rtx && !drop)
    ++rtx->num_rtx_assoc_packets;

  GST_OBJECT_UNLOCK (rtx);

  /* just drop the packet if the association could not have been made */
  if (drop) {
    gst_rtp_buffer_unmap (&rtp);
    gst_buffer_unref (buffer);
    return GST_FLOW_OK;
  }

  /* create the retransmission packet */
  if (is_rtx)
    new_buffer =
        _gst_rtp_buffer_new_from_rtx (&rtp, GPOINTER_TO_UINT (ssrc1),
        orign_seqnum, origin_payload_type);

  gst_rtp_buffer_unmap (&rtp);

  /* push the packet */
  if (is_rtx) {
    gst_buffer_unref (buffer);
    GST_LOG_OBJECT (rtx, "push packet seqnum:%" G_GUINT16_FORMAT
        " from a restransmission stream ssrc2:%" G_GUINT32_FORMAT " (src %"
        G_GUINT32_FORMAT ")", orign_seqnum, ssrc2, GPOINTER_TO_UINT (ssrc1));
    ret = gst_pad_push (rtx->srcpad, new_buffer);
  } else {
    GST_LOG_OBJECT (rtx, "push packet seqnum:%" G_GUINT16_FORMAT
        " from a master stream ssrc: %" G_GUINT32_FORMAT, seqnum, ssrc);
    ret = gst_pad_push (rtx->srcpad, buffer);
  }

  return ret;
}
/* takes ownership of the input buffer */
static GstFlowReturn
gst_rtp_base_depayload_handle_buffer (GstRTPBaseDepayload * filter,
    GstRTPBaseDepayloadClass * bclass, GstBuffer * in)
{
  GstBuffer *(*process_rtp_packet_func) (GstRTPBaseDepayload * base,
      GstRTPBuffer * rtp_buffer);
  GstBuffer *(*process_func) (GstRTPBaseDepayload * base, GstBuffer * in);
  GstRTPBaseDepayloadPrivate *priv;
  GstFlowReturn ret = GST_FLOW_OK;
  GstBuffer *out_buf;
  guint32 ssrc;
  guint16 seqnum;
  guint32 rtptime;
  gboolean discont, buf_discont;
  gint gap;
  GstRTPBuffer rtp = { NULL };

  priv = filter->priv;

  process_func = bclass->process;
  process_rtp_packet_func = bclass->process_rtp_packet;

  /* we must have a setcaps first */
  if (G_UNLIKELY (!priv->negotiated))
    goto not_negotiated;

  if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
    goto invalid_buffer;

  buf_discont = GST_BUFFER_IS_DISCONT (in);

  priv->pts = GST_BUFFER_PTS (in);
  priv->dts = GST_BUFFER_DTS (in);
  priv->duration = GST_BUFFER_DURATION (in);

  ssrc = gst_rtp_buffer_get_ssrc (&rtp);
  seqnum = gst_rtp_buffer_get_seq (&rtp);
  rtptime = gst_rtp_buffer_get_timestamp (&rtp);

  priv->last_seqnum = seqnum;
  priv->last_rtptime = rtptime;

  discont = buf_discont;

  GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, pts %"
      GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, buf_discont, seqnum, rtptime,
      GST_TIME_ARGS (priv->pts), GST_TIME_ARGS (priv->dts));

  /* Check seqnum. This is a very simple check that makes sure that the seqnums
   * are strictly increasing, dropping anything that is out of the ordinary. We
   * can only do this when the next_seqnum is known. */
  if (G_LIKELY (priv->next_seqnum != -1)) {
    if (ssrc != priv->last_ssrc) {
      GST_LOG_OBJECT (filter,
          "New ssrc %u (current ssrc %u), sender restarted",
          ssrc, priv->last_ssrc);
      discont = TRUE;
    } else {
      gap = gst_rtp_buffer_compare_seqnum (seqnum, priv->next_seqnum);

      /* if we have no gap, all is fine */
      if (G_UNLIKELY (gap != 0)) {
        GST_LOG_OBJECT (filter, "got packet %u, expected %u, gap %d", seqnum,
            priv->next_seqnum, gap);
        if (gap < 0) {
          /* seqnum > next_seqnum, we are missing some packets, this is always a
           * DISCONT. */
          GST_LOG_OBJECT (filter, "%d missing packets", gap);
          discont = TRUE;
        } else {
          /* seqnum < next_seqnum, we have seen this packet before, have a
           * reordered packet or the sender could be restarted. If the packet
           * is not too old, we throw it away as a duplicate. Otherwise we
           * mark discont and continue assuming the sender has restarted. See
           * also RFC 4737. */
          GST_WARNING ("gap %d <= priv->max_reorder %d -> dropping %d",
              gap, priv->max_reorder, gap <= priv->max_reorder);
          if (gap <= priv->max_reorder)
            goto dropping;

          GST_LOG_OBJECT (filter,
              "%d > %d, packet too old, sender likely restarted", gap,
              priv->max_reorder);
          discont = TRUE;
        }
      }
    }
  }
  priv->next_seqnum = (seqnum + 1) & 0xffff;
  priv->last_ssrc = ssrc;

  if (G_UNLIKELY (discont)) {
    priv->discont = TRUE;
    if (!buf_discont) {
      gpointer old_inbuf = in;

      /* we detected a seqnum discont but the buffer was not flagged with a discont,
       * set the discont flag so that the subclass can throw away old data. */
      GST_LOG_OBJECT (filter, "mark DISCONT on input buffer");
      in = gst_buffer_make_writable (in);
      GST_BUFFER_FLAG_SET (in, GST_BUFFER_FLAG_DISCONT);
      /* depayloaders will check flag on rtpbuffer->buffer, so if the input
       * buffer was not writable already we need to remap to make our
       * newly-flagged buffer current on the rtpbuffer */
      if (in != old_inbuf) {
        gst_rtp_buffer_unmap (&rtp);
        if (G_UNLIKELY (!gst_rtp_buffer_map (in, GST_MAP_READ, &rtp)))
          goto invalid_buffer;
      }
    }
  }

  /* prepare segment event if needed */
  if (filter->need_newsegment) {
    priv->segment_event = create_segment_event (filter, rtptime,
        GST_BUFFER_PTS (in));
    filter->need_newsegment = FALSE;
  }

  priv->input_buffer = in;

  if (process_rtp_packet_func != NULL) {
    out_buf = process_rtp_packet_func (filter, &rtp);
    gst_rtp_buffer_unmap (&rtp);
  } else if (process_func != NULL) {
    gst_rtp_buffer_unmap (&rtp);
    out_buf = process_func (filter, in);
  } else {
    goto no_process;
  }

  /* let's send it out to processing */
  if (out_buf) {
    ret = gst_rtp_base_depayload_push (filter, out_buf);
  }

  gst_buffer_unref (in);
  priv->input_buffer = NULL;

  return ret;

  /* ERRORS */
not_negotiated:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION,
        ("No RTP format was negotiated."),
        ("Input buffers need to have RTP caps set on them. This is usually "
            "achieved by setting the 'caps' property of the upstream source "
            "element (often udpsrc or appsrc), or by putting a capsfilter "
            "element before the depayloader and setting the 'caps' property "
            "on that. Also see http://cgit.freedesktop.org/gstreamer/"
            "gst-plugins-good/tree/gst/rtp/README"));
    gst_buffer_unref (in);
    return GST_FLOW_NOT_NEGOTIATED;
  }
invalid_buffer:
  {
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL),
        ("Received invalid RTP payload, dropping"));
    gst_buffer_unref (in);
    return GST_FLOW_OK;
  }
dropping:
  {
    gst_rtp_buffer_unmap (&rtp);
    GST_WARNING_OBJECT (filter, "%d <= 100, dropping old packet", gap);
    gst_buffer_unref (in);
    return GST_FLOW_OK;
  }
no_process:
  {
    gst_rtp_buffer_unmap (&rtp);
    /* this is not fatal but should be filtered earlier */
    GST_ELEMENT_ERROR (filter, STREAM, NOT_IMPLEMENTED, (NULL),
        ("The subclass does not have a process or process_rtp_packet method"));
    gst_buffer_unref (in);
    return GST_FLOW_ERROR;
  }
}