static GstBuffer * gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { GstRtpVRawDepay *rtpvrawdepay; guint8 *payload, *data, *yp, *up, *vp, *headers; guint32 timestamp; guint cont, ystride, uvstride, pgroup; rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload); timestamp = gst_rtp_buffer_get_timestamp (buf); if (timestamp != rtpvrawdepay->timestamp || rtpvrawdepay->outbuf == NULL) { GstBuffer *outbuf; GstFlowReturn ret; GST_LOG_OBJECT (depayload, "new frame with timestamp %u", timestamp); /* new timestamp, flush old buffer and create new output buffer */ if (rtpvrawdepay->outbuf) { gst_base_rtp_depayload_push_ts (depayload, rtpvrawdepay->timestamp, rtpvrawdepay->outbuf); rtpvrawdepay->outbuf = NULL; } ret = gst_pad_alloc_buffer (depayload->srcpad, -1, rtpvrawdepay->outsize, GST_PAD_CAPS (depayload->srcpad), &outbuf); if (ret != GST_FLOW_OK) goto alloc_failed; /* clear timestamp from alloc... */ GST_BUFFER_TIMESTAMP (outbuf) = -1; rtpvrawdepay->outbuf = outbuf; rtpvrawdepay->timestamp = timestamp; } data = GST_BUFFER_DATA (rtpvrawdepay->outbuf); /* get pointer and strides of the planes */ yp = data + rtpvrawdepay->yp; up = data + rtpvrawdepay->up; vp = data + rtpvrawdepay->vp; ystride = rtpvrawdepay->ystride; uvstride = rtpvrawdepay->uvstride; pgroup = rtpvrawdepay->pgroup; payload = gst_rtp_buffer_get_payload (buf); /* skip extended seqnum */ payload++; payload++; /* remember header position */ headers = payload; /* find data start */ do { cont = payload[4] & 0x80; payload += 6; } while (cont); while (TRUE) { guint length, line, offs; guint8 *datap; /* read length and cont */ length = (headers[0] << 8) | headers[1]; line = ((headers[2] & 0x7f) << 8) | headers[3]; offs = ((headers[4] & 0x7f) << 8) | headers[5]; cont = headers[4] & 0x80; headers += 6; /* sanity check */ if (line > (rtpvrawdepay->height - rtpvrawdepay->yinc)) continue; if (offs > (rtpvrawdepay->width - rtpvrawdepay->xinc)) continue; GST_LOG_OBJECT (depayload, "writing length %u, line %u, offset %u", length, line, offs); switch (rtpvrawdepay->format) { case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_UYVY: /* samples are packed just like gstreamer packs them */ offs /= rtpvrawdepay->xinc; datap = yp + (line * ystride) + (offs * pgroup); memcpy (datap, payload, length); payload += length; break; case GST_VIDEO_FORMAT_AYUV: { gint i; datap = yp + (line * ystride) + (offs * 4); /* samples are packed in order Cb-Y-Cr for both interlaced and * progressive frames */ for (i = 0; i < length; i += pgroup) { *datap++ = 0; *datap++ = payload[1]; *datap++ = payload[0]; *datap++ = payload[2]; payload += pgroup; } break; } case GST_VIDEO_FORMAT_I420: { gint i; guint uvoff; guint8 *yd1p, *yd2p, *udp, *vdp; yd1p = yp + (line * ystride) + (offs); yd2p = yd1p + ystride; uvoff = (line / rtpvrawdepay->yinc * uvstride) + (offs / rtpvrawdepay->xinc); udp = up + uvoff; vdp = vp + uvoff; /* line 0/1: Y00-Y01-Y10-Y11-Cb00-Cr00 Y02-Y03-Y12-Y13-Cb01-Cr01 ... */ for (i = 0; i < length; i += pgroup) { *yd1p++ = payload[0]; *yd1p++ = payload[1]; *yd2p++ = payload[2]; *yd2p++ = payload[3]; *udp++ = payload[4]; *vdp++ = payload[5]; payload += pgroup; } break; } case GST_VIDEO_FORMAT_Y41B: { gint i; guint uvoff; guint8 *ydp, *udp, *vdp; ydp = yp + (line * ystride) + (offs); uvoff = (line / rtpvrawdepay->yinc * uvstride) + (offs / rtpvrawdepay->xinc); udp = up + uvoff; vdp = vp + uvoff; /* Samples are packed in order Cb0-Y0-Y1-Cr0-Y2-Y3 for both interlaced * and progressive scan lines */ for (i = 0; i < length; i += pgroup) { *udp++ = payload[0]; *ydp++ = payload[1]; *ydp++ = payload[2]; *vdp++ = payload[3]; *ydp++ = payload[4]; *ydp++ = payload[5]; payload += pgroup; } break; } default: goto unknown_sampling; } if (!cont) break; } if (gst_rtp_buffer_get_marker (buf)) { GST_LOG_OBJECT (depayload, "marker, flushing frame"); if (rtpvrawdepay->outbuf) { gst_base_rtp_depayload_push_ts (depayload, timestamp, rtpvrawdepay->outbuf); rtpvrawdepay->outbuf = NULL; } rtpvrawdepay->timestamp = -1; } return NULL; /* ERRORS */ unknown_sampling: { GST_ELEMENT_ERROR (depayload, STREAM, FORMAT, (NULL), ("unimplemented sampling")); return NULL; } alloc_failed: { GST_DEBUG_OBJECT (depayload, "failed to alloc output buffer"); return NULL; } }
static GstFlowReturn gst_base_rtp_depayload_chain (GstPad * pad, GstBuffer * in) { GstBaseRTPDepayload *filter; GstBaseRTPDepayloadPrivate *priv; GstBaseRTPDepayloadClass *bclass; GstFlowReturn ret = GST_FLOW_OK; GstBuffer *out_buf; GstClockTime timestamp; guint16 seqnum; guint32 rtptime; gboolean reset_seq, discont; gint gap; filter = GST_BASE_RTP_DEPAYLOAD (GST_OBJECT_PARENT (pad)); priv = filter->priv; /* we must have a setcaps first */ if (G_UNLIKELY (!priv->negotiated)) goto not_negotiated; /* we must validate, it's possible that this element is plugged right after a * network receiver and we don't want to operate on invalid data */ if (G_UNLIKELY (!gst_rtp_buffer_validate (in))) goto invalid_buffer; priv->discont = GST_BUFFER_IS_DISCONT (in); timestamp = GST_BUFFER_TIMESTAMP (in); /* convert to running_time and save the timestamp, this is the timestamp * we put on outgoing buffers. */ timestamp = gst_segment_to_running_time (&filter->segment, GST_FORMAT_TIME, timestamp); priv->timestamp = timestamp; priv->duration = GST_BUFFER_DURATION (in); seqnum = gst_rtp_buffer_get_seq (in); rtptime = gst_rtp_buffer_get_timestamp (in); reset_seq = TRUE; discont = FALSE; GST_LOG_OBJECT (filter, "discont %d, seqnum %u, rtptime %u, timestamp %" GST_TIME_FORMAT, priv->discont, seqnum, rtptime, GST_TIME_ARGS (timestamp)); /* Check seqnum. This is a very simple check that makes sure that the seqnums * are striclty 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)) { 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 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. 100 misordered * packets is a good threshold. See also RFC 4737. */ if (gap < 100) goto dropping; GST_LOG_OBJECT (filter, "%d > 100, packet too old, sender likely restarted", gap); discont = TRUE; } } } priv->next_seqnum = (seqnum + 1) & 0xffff; if (G_UNLIKELY (discont && !priv->discont)) { GST_LOG_OBJECT (filter, "mark DISCONT on input buffer"); /* 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. */ priv->discont = TRUE; GST_BUFFER_FLAG_SET (in, GST_BUFFER_FLAG_DISCONT); } bclass = GST_BASE_RTP_DEPAYLOAD_GET_CLASS (filter); if (G_UNLIKELY (bclass->process == NULL)) goto no_process; /* let's send it out to processing */ out_buf = bclass->process (filter, in); if (out_buf) { /* we pass rtptime as backward compatibility, in reality, the incomming * buffer timestamp is always applied to the outgoing packet. */ ret = gst_base_rtp_depayload_push_ts (filter, rtptime, out_buf); } gst_buffer_unref (in); return ret; /* ERRORS */ not_negotiated: { /* this is not fatal but should be filtered earlier */ GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL), ("Not RTP format was negotiated")); 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_WARNING_OBJECT (filter, "%d <= 100, dropping old packet", gap); gst_buffer_unref (in); return GST_FLOW_OK; } no_process: { /* this is not fatal but should be filtered earlier */ GST_ELEMENT_ERROR (filter, STREAM, NOT_IMPLEMENTED, (NULL), ("The subclass does not have a process method")); gst_buffer_unref (in); return GST_FLOW_ERROR; } }
static GstBuffer * gst_rtp_vraw_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { GstRtpVRawDepay *rtpvrawdepay; guint8 *payload, *data, *yp, *up, *vp, *headers; guint32 timestamp; guint cont, ystride, uvstride, pgroup, payload_len; gint width, height, xinc, yinc; rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload); timestamp = gst_rtp_buffer_get_timestamp (buf); if (timestamp != rtpvrawdepay->timestamp || rtpvrawdepay->outbuf == NULL) { GstBuffer *outbuf; GstFlowReturn ret; GST_LOG_OBJECT (depayload, "new frame with timestamp %u", timestamp); /* new timestamp, flush old buffer and create new output buffer */ if (rtpvrawdepay->outbuf) { gst_base_rtp_depayload_push_ts (depayload, rtpvrawdepay->timestamp, rtpvrawdepay->outbuf); rtpvrawdepay->outbuf = NULL; } ret = gst_pad_alloc_buffer (depayload->srcpad, -1, rtpvrawdepay->outsize, GST_PAD_CAPS (depayload->srcpad), &outbuf); if (ret != GST_FLOW_OK) goto alloc_failed; /* clear timestamp from alloc... */ GST_BUFFER_TIMESTAMP (outbuf) = -1; rtpvrawdepay->outbuf = outbuf; rtpvrawdepay->timestamp = timestamp; } data = GST_BUFFER_DATA (rtpvrawdepay->outbuf); /* get pointer and strides of the planes */ yp = data + rtpvrawdepay->yp; up = data + rtpvrawdepay->up; vp = data + rtpvrawdepay->vp; ystride = rtpvrawdepay->ystride; uvstride = rtpvrawdepay->uvstride; pgroup = rtpvrawdepay->pgroup; width = rtpvrawdepay->width; height = rtpvrawdepay->height; xinc = rtpvrawdepay->xinc; yinc = rtpvrawdepay->yinc; payload = gst_rtp_buffer_get_payload (buf); payload_len = gst_rtp_buffer_get_payload_len (buf); if (payload_len < 3) goto short_packet; /* skip extended seqnum */ payload += 2; payload_len -= 2; /* remember header position */ headers = payload; /* find data start */ do { if (payload_len < 6) goto short_packet; cont = payload[4] & 0x80; payload += 6; payload_len -= 6; } while (cont); while (TRUE) { guint length, line, offs, plen; guint8 *datap; /* stop when we run out of data */ if (payload_len == 0) break; /* read length and cont. This should work because we iterated the headers * above. */ length = (headers[0] << 8) | headers[1]; line = ((headers[2] & 0x7f) << 8) | headers[3]; offs = ((headers[4] & 0x7f) << 8) | headers[5]; cont = headers[4] & 0x80; headers += 6; /* length must be a multiple of pgroup */ if (length % pgroup != 0) goto wrong_length; if (length > payload_len) length = payload_len; /* sanity check */ if (line > (height - yinc)) { GST_WARNING_OBJECT (depayload, "skipping line %d: out of range", line); goto next; } if (offs > (width - xinc)) { GST_WARNING_OBJECT (depayload, "skipping offset %d: out of range", offs); goto next; } /* calculate the maximim amount of bytes we can use per line */ if (offs + ((length / pgroup) * xinc) > width) { plen = ((width - offs) * pgroup) / xinc; GST_WARNING_OBJECT (depayload, "clipping length %d, offset %d, plen %d", length, offs, plen); } else plen = length; GST_LOG_OBJECT (depayload, "writing length %u/%u, line %u, offset %u, remaining %u", plen, length, line, offs, payload_len); switch (rtpvrawdepay->format) { case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_UYVY: /* samples are packed just like gstreamer packs them */ offs /= xinc; datap = yp + (line * ystride) + (offs * pgroup); memcpy (datap, payload, plen); break; case GST_VIDEO_FORMAT_AYUV: { gint i; guint8 *p; datap = yp + (line * ystride) + (offs * 4); p = payload; /* samples are packed in order Cb-Y-Cr for both interlaced and * progressive frames */ for (i = 0; i < plen; i += pgroup) { *datap++ = 0; *datap++ = p[1]; *datap++ = p[0]; *datap++ = p[2]; p += pgroup; } break; } case GST_VIDEO_FORMAT_I420: { gint i; guint uvoff; guint8 *yd1p, *yd2p, *udp, *vdp, *p; yd1p = yp + (line * ystride) + (offs); yd2p = yd1p + ystride; uvoff = (line / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; p = payload; /* line 0/1: Y00-Y01-Y10-Y11-Cb00-Cr00 Y02-Y03-Y12-Y13-Cb01-Cr01 ... */ for (i = 0; i < plen; i += pgroup) { *yd1p++ = p[0]; *yd1p++ = p[1]; *yd2p++ = p[2]; *yd2p++ = p[3]; *udp++ = p[4]; *vdp++ = p[5]; p += pgroup; } break; } case GST_VIDEO_FORMAT_Y41B: { gint i; guint uvoff; guint8 *ydp, *udp, *vdp, *p; ydp = yp + (line * ystride) + (offs); uvoff = (line / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; p = payload; /* Samples are packed in order Cb0-Y0-Y1-Cr0-Y2-Y3 for both interlaced * and progressive scan lines */ for (i = 0; i < plen; i += pgroup) { *udp++ = p[0]; *ydp++ = p[1]; *ydp++ = p[2]; *vdp++ = p[3]; *ydp++ = p[4]; *ydp++ = p[5]; p += pgroup; } break; } default: goto unknown_sampling; } next: if (!cont) break; payload += length; payload_len -= length; } if (gst_rtp_buffer_get_marker (buf)) { GST_LOG_OBJECT (depayload, "marker, flushing frame"); if (rtpvrawdepay->outbuf) { gst_base_rtp_depayload_push_ts (depayload, timestamp, rtpvrawdepay->outbuf); rtpvrawdepay->outbuf = NULL; } rtpvrawdepay->timestamp = -1; } return NULL; /* ERRORS */ unknown_sampling: { GST_ELEMENT_ERROR (depayload, STREAM, FORMAT, (NULL), ("unimplemented sampling")); return NULL; } alloc_failed: { GST_WARNING_OBJECT (depayload, "failed to alloc output buffer"); return NULL; } wrong_length: { GST_WARNING_OBJECT (depayload, "length not multiple of pgroup"); return NULL; } short_packet: { GST_WARNING_OBJECT (depayload, "short packet"); return NULL; } }