/* convert the PacketLost event form a jitterbuffer to a segment update. * subclasses can override this. */ static gboolean gst_base_rtp_depayload_packet_lost (GstBaseRTPDepayload * filter, GstEvent * event) { GstBaseRTPDepayloadPrivate *priv; GstClockTime timestamp, duration, position; GstEvent *sevent; const GstStructure *s; priv = filter->priv; s = gst_event_get_structure (event); /* first start by parsing the timestamp and duration */ timestamp = -1; duration = -1; gst_structure_get_clock_time (s, "timestamp", ×tamp); gst_structure_get_clock_time (s, "duration", &duration); position = timestamp; if (duration != -1) position += duration; /* update the current segment with the elapsed time */ sevent = create_segment_event (filter, TRUE, position); return gst_pad_push_event (filter->srcpad, sevent); }
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_base_rtp_depayload_push_full (GstBaseRTPDepayload * filter, gboolean do_ts, guint32 rtptime, GstBuffer * out_buf) { GstFlowReturn ret; GstCaps *srccaps; GstBaseRTPDepayloadClass *bclass; GstBaseRTPDepayloadPrivate *priv; priv = filter->priv; /* set the caps if any */ srccaps = GST_PAD_CAPS (filter->srcpad); if (G_LIKELY (srccaps)) gst_buffer_set_caps (out_buf, srccaps); bclass = GST_BASE_RTP_DEPAYLOAD_GET_CLASS (filter); /* set the timestamp if we must and can */ if (bclass->set_gst_timestamp && do_ts) bclass->set_gst_timestamp (filter, rtptime, out_buf); /* if this is the first buffer send a NEWSEGMENT */ if (G_UNLIKELY (filter->need_newsegment)) { GstEvent *event; event = create_segment_event (filter, FALSE, 0); gst_pad_push_event (filter->srcpad, event); filter->need_newsegment = FALSE; GST_DEBUG_OBJECT (filter, "Pushed newsegment event on this first buffer"); } if (G_UNLIKELY (priv->discont)) { GST_LOG_OBJECT (filter, "Marking DISCONT on output buffer"); GST_BUFFER_FLAG_SET (out_buf, GST_BUFFER_FLAG_DISCONT); priv->discont = FALSE; } /* push it */ GST_LOG_OBJECT (filter, "Pushing buffer size %d, timestamp %" GST_TIME_FORMAT, GST_BUFFER_SIZE (out_buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out_buf))); ret = gst_pad_push (filter->srcpad, out_buf); return ret; }
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; } if (rdtdepay->discont) { GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); rdtdepay->discont = FALSE; } ret = gst_pad_push (rdtdepay->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; } }