[vlc-devel] [PATCH] rtp: implement raw video packetization (RFC 4175)

Tristan Matthews tmatth at videolan.org
Tue Jan 27 22:49:31 CET 2015


Supports RGB24 and I420, should be trivial to add other formats.
---
 modules/stream_out/rtp.c    |  19 +++++
 modules/stream_out/rtp.h    |   4 +
 modules/stream_out/rtpfmt.c | 196 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 219 insertions(+)

diff --git a/modules/stream_out/rtp.c b/modules/stream_out/rtp.c
index 98c5fb4..4762d47 100644
--- a/modules/stream_out/rtp.c
+++ b/modules/stream_out/rtp.c
@@ -361,6 +361,9 @@ struct sout_stream_id_sys_t
     uint32_t    i_ts_offset;
     uint8_t     ssrc[4];
 
+    /* For RFC 4175, seqnum is extended to 32-bits */
+    uint16_t    i_extended_sequence;
+
     /* for rtsp */
     uint16_t    i_seq_sent_next;
 
@@ -984,6 +987,7 @@ static sout_stream_id_sys_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         (int64_t)1000 * var_GetInteger( p_stream, SOUT_CFG_PREFIX "caching");
 
     vlc_rand_bytes (&id->i_sequence, sizeof (id->i_sequence));
+    vlc_rand_bytes (&id->i_extended_sequence, sizeof (id->i_extended_sequence));
     vlc_rand_bytes (id->ssrc, sizeof (id->ssrc));
 
     bool format = false;
@@ -1002,6 +1006,7 @@ static sout_stream_id_sys_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
              * initialized inside vod_init_id() to avoid race
              * conditions. */
             id->i_sequence = id->i_seq_sent_next;
+            id->i_extended_sequence = 0;
         }
         /* vod_init_id() may fail either because the ES wasn't found in
          * the VoD media, or because the RTSP session is gone. In the
@@ -1045,6 +1050,7 @@ static sout_stream_id_sys_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             goto error;
         }
         id->i_sequence = 0; /* FIXME: awful hack for libvlc_srtp */
+        id->i_extended_sequence = 0;
     }
 #endif
 
@@ -1650,6 +1656,14 @@ void rtp_packetize_common( sout_stream_id_sys_t *id, block_t *out,
     memcpy( out->p_buffer + 8, id->ssrc, 4 );
 
     id->i_sequence++;
+    /* Wrap-around to 0 just occured */
+    if (!id->i_sequence)
+        id->i_extended_sequence++;
+}
+
+uint16_t rtp_get_extended_sequence( sout_stream_id_sys_t *id )
+{
+    return id->i_extended_sequence;
 }
 
 void rtp_packetize_send( sout_stream_id_sys_t *id, block_t *out )
@@ -1797,3 +1811,8 @@ static sout_access_out_t *GrabberCreate( sout_stream_t *p_stream )
     p_grab->pf_write    = AccessOutGrabberWrite;
     return p_grab;
 }
+
+void rtp_get_video_geometry( sout_stream_id_sys_t *id, int *width, int *height )
+{
+    sscanf( id->rtp_fmt.fmtp, "%*s width=%d; height=%d; ", width, height );
+}
diff --git a/modules/stream_out/rtp.h b/modules/stream_out/rtp.h
index ff26c69..01d6b61 100644
--- a/modules/stream_out/rtp.h
+++ b/modules/stream_out/rtp.h
@@ -89,6 +89,10 @@ typedef struct rtp_format_t
 int rtp_get_fmt( vlc_object_t *obj, es_format_t *p_fmt, const char *mux,
                  rtp_format_t *p_rtp_fmt );
 
+/* Only used by rtp_packetize_rawvideo */
+void rtp_get_video_geometry( sout_stream_id_sys_t *id, int *width, int *height );
+uint16_t rtp_get_extended_sequence( sout_stream_id_sys_t *id );
+
 /* VoD */
 int  OpenVoD ( vlc_object_t * );
 void CloseVoD( vlc_object_t * );
diff --git a/modules/stream_out/rtpfmt.c b/modules/stream_out/rtpfmt.c
index d8c8621..1759c6a 100644
--- a/modules/stream_out/rtpfmt.c
+++ b/modules/stream_out/rtpfmt.c
@@ -6,6 +6,8 @@
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ * RFC 4175 support based on gstrtpvrawpay.c (LGPL 2) by:
+ * Wim Taymans <wim.taymans at gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -57,6 +59,8 @@ static int rtp_packetize_g726_40 (sout_stream_id_sys_t *, block_t *);
 static int rtp_packetize_xiph (sout_stream_id_sys_t *, block_t *);
 static int rtp_packetize_vp8 (sout_stream_id_sys_t *, block_t *);
 static int rtp_packetize_jpeg (sout_stream_id_sys_t *, block_t *);
+static int rtp_packetize_i420 (sout_stream_id_sys_t *, block_t *);
+static int rtp_packetize_rgb24 (sout_stream_id_sys_t *, block_t *);
 
 #define XIPH_IDENT (0)
 
@@ -524,6 +528,31 @@ int rtp_get_fmt( vlc_object_t *obj, es_format_t *p_fmt, const char *mux,
             rtp_fmt->ptname = "VP8";
             rtp_fmt->pf_packetize = rtp_packetize_vp8;
             break;
+        case VLC_CODEC_I420:
+            rtp_fmt->ptname = "RAW";
+            rtp_fmt->pf_packetize = rtp_packetize_i420;
+            if( asprintf( &rtp_fmt->fmtp,
+                    "sampling=YCbCr-4:2:0; width=%d; height=%d; "
+                    "depth=8; colorimetry=BT%s",
+                    p_fmt->video.i_width, p_fmt->video.i_height,
+                    p_fmt->video.i_height > 576 ? "709-2" : "601-5") == -1 )
+            {
+                rtp_fmt->fmtp = NULL;
+                return VLC_ENOMEM;
+            }
+            break;
+        case VLC_CODEC_RGB24:
+            rtp_fmt->ptname = "RAW";
+            rtp_fmt->pf_packetize = rtp_packetize_rgb24;
+            if( asprintf( &rtp_fmt->fmtp,
+                    "sampling=RGB; width=%d; height=%d; "
+                    "depth=8; colorimetry=SMPTE240M",
+                    p_fmt->video.i_width, p_fmt->video.i_height) == -1 )
+            {
+                rtp_fmt->fmtp = NULL;
+                return VLC_ENOMEM;
+            }
+            break;
         case VLC_CODEC_MJPG:
         case VLC_CODEC_JPEG:
             rtp_fmt->ptname = "JPEG";
@@ -1502,6 +1531,173 @@ static int rtp_packetize_vp8( sout_stream_id_sys_t *id, block_t *in )
     return VLC_SUCCESS;
 }
 
+
+/* See RFC 4175 */
+static int rtp_packetize_rawvideo( sout_stream_id_sys_t *id, block_t *in, vlc_fourcc_t i_format  )
+{
+    int i_width, i_height;
+    rtp_get_video_geometry( id, &i_width, &i_height );
+    int i_pgroup; /* Size of a group of pixels */
+    int i_xdec, i_ydec; /* sub-sampling factor in x and y */
+    switch( i_format )
+    {
+        case VLC_CODEC_RGB24:
+            i_pgroup = 3;
+            i_xdec = i_ydec = 1;
+            break;
+        case VLC_CODEC_I420:
+            i_pgroup = 6;
+            i_xdec = i_ydec = 2;
+            break;
+        default:
+            assert(0);
+    }
+
+    static const int RTP_HEADER_LEN = 12;
+    /* each partial or complete line needs a 6 byte header */
+    const int i_line_header_size = 6;
+    const int i_min_line_size = i_line_header_size + i_pgroup;
+    uint8_t *p_data = in->p_buffer;
+
+    for( uint16_t i_line_number = 0, i_column = 0; i_line_number < i_height; )
+    {
+        /* Allocate a packet */
+        int i_payload = (int)(rtp_mtu (id) - RTP_HEADER_LEN);
+        if( i_payload <= 0 )
+        {
+            block_Release( in );
+            return VLC_EGENERIC;
+        }
+
+        block_t *out = block_Alloc( RTP_HEADER_LEN + i_payload );
+        if( unlikely( out == NULL ) )
+        {
+            block_Release( in );
+            return VLC_ENOMEM;
+        }
+
+        /* Do headers first... */
+
+        /* Write extended seqnum */
+        uint8_t *p_outdata = out->p_buffer + RTP_HEADER_LEN;
+        SetWBE( p_outdata, rtp_get_extended_sequence( id ) );
+        p_outdata += 2;
+        i_payload -= 2;
+
+        uint8_t *p_headers = p_outdata;
+
+        for( uint8_t i_cont = 0x80; i_cont && i_payload > i_min_line_size; )
+        {
+            i_payload -= i_line_header_size;
+
+            int i_pixels = i_width - i_column;
+            int i_length = (i_pixels * i_pgroup) / i_xdec;
+
+            const bool b_next_line = i_payload >= i_length;
+            if( !b_next_line )
+            {
+                i_pixels = (i_payload / i_pgroup) * i_xdec;
+                i_length = (i_pixels * i_pgroup) / i_xdec;
+            }
+
+            i_payload -= i_length;
+
+            /* write length */
+            SetWBE( p_outdata, i_length );
+            p_outdata += 2;
+
+            /* write line number */
+            /* TODO: support interlaced */
+            const uint8_t i_field = 0;
+            SetWBE( p_outdata, i_line_number );
+            *p_outdata |= i_field << 7;
+            p_outdata += 2;
+
+            /* continue if there's still room in the packet and we have more lines */
+            i_cont = (i_payload > i_min_line_size && i_line_number < (i_height - i_ydec)) ? 0x80 : 0x00;
+
+            /* write offset and continuation marker */
+            SetWBE( p_outdata, i_column );
+            *p_outdata |= i_cont;
+            p_outdata += 2;
+
+            if( b_next_line )
+            {
+                i_column = 0;
+                i_line_number += i_ydec;
+            }
+            else
+            {
+                i_column += i_pixels;
+            }
+        }
+
+        /* write the actual video data here */
+        for( uint8_t i_cont = 0x80; i_cont; p_headers += i_line_header_size )
+        {
+            const uint16_t i_length = GetWBE( p_headers );
+            const uint16_t i_lin = GetWBE( p_headers + 2 ) & 0x7fff;
+            uint16_t i_offs = GetWBE( p_headers + 4 ) & 0x7fff;
+            i_cont = p_headers[4] & 0x80;
+
+            if( i_format == VLC_CODEC_RGB24 )
+            {
+                const int i_ystride = i_width * i_pgroup;
+                i_offs /= i_xdec;
+                memcpy( p_outdata, p_data + (i_lin * i_ystride) + (i_offs * i_pgroup), i_length );
+                p_outdata += i_length;
+            }
+            else if( i_format == VLC_CODEC_I420 )
+            {
+                const int i_ystride = i_width;
+                const uint8_t *p_u = p_data + i_width * i_height;
+                const uint8_t *p_v = p_u + (i_width * i_height) / (i_xdec * i_ydec);
+                const int i_uvstride = i_width / i_xdec;
+
+                const uint8_t *p_yd1 = p_data + (i_lin * i_ystride) + i_offs;
+                const uint8_t *p_yd2 = p_yd1 + i_ystride;
+                const unsigned i_uvoff = (i_lin / i_ydec * i_uvstride) + (i_offs / i_xdec);
+                const uint8_t *p_ud = p_u + i_uvoff;
+                const uint8_t *p_vd = p_v + i_uvoff;
+
+                unsigned i_pixels = i_length / i_pgroup;
+                while( i_pixels-- )
+                {
+                    *p_outdata++ = *p_yd1++;
+                    *p_outdata++ = *p_yd1++;
+                    *p_outdata++ = *p_yd2++;
+                    *p_outdata++ = *p_yd2++;
+                    *p_outdata++ = *p_ud++;
+                    *p_outdata++ = *p_vd++;
+                }
+            }
+            else assert(0);
+        }
+
+        /* rtp common header */
+        rtp_packetize_common( id, out, i_line_number >= i_height,
+                (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
+
+        out->i_dts    = in->i_dts;
+        out->i_length = in->i_length;
+
+        rtp_packetize_send( id, out );
+    }
+
+    block_Release( in );
+    return VLC_SUCCESS;
+}
+
+static int rtp_packetize_i420( sout_stream_id_sys_t *id, block_t *in )
+{
+    return rtp_packetize_rawvideo( id, in, VLC_CODEC_I420 );
+}
+
+static int rtp_packetize_rgb24( sout_stream_id_sys_t *id, block_t *in )
+{
+    return rtp_packetize_rawvideo( id, in, VLC_CODEC_RGB24 );
+}
+
 static int rtp_packetize_jpeg( sout_stream_id_sys_t *id, block_t *in )
 {
     uint8_t *p_data = in->p_buffer;
-- 
2.1.0




More information about the vlc-devel mailing list