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; }
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); }
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); } }
/* 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; }
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; } }