Esempio n. 1
0
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;
  }
}
Esempio n. 3
0
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;
  }
}