]> git.sesse.net Git - vlc/blobdiff - modules/stream_out/rtp.c
Trailing ;
[vlc] / modules / stream_out / rtp.c
index 91b29a17d2c4659b10c90c7f111c627b644f3e69..ede24afa63c0bbf7db6d5f9c3a482a65da004c72 100644 (file)
@@ -2,8 +2,7 @@
  * rtp.c: rtp stream output module
  *****************************************************************************
  * Copyright (C) 2003-2004 the VideoLAN team
- * Copyright © 2007 Rémi Denis-Courmont
- * $Id$
+ * Copyright © 2007-2008 Rémi Denis-Courmont
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *
@@ -30,7 +29,8 @@
 # include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
+#include <vlc_plugin.h>
 #include <vlc_sout.h>
 #include <vlc_block.h>
 
@@ -39,6 +39,8 @@
 #include <vlc_network.h>
 #include <vlc_charset.h>
 #include <vlc_strings.h>
+#include <vlc_rand.h>
+#include <srtp.h>
 
 #include "rtp.h"
 
@@ -60,6 +62,8 @@
 
 #include <errno.h>
 
+#include <assert.h>
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
 #define TTL_TEXT N_("Hop limit (TTL)")
 #define TTL_LONGTEXT N_( \
     "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
-    "the multicast packets sent by the stream output (0 = use operating " \
+    "the multicast packets sent by the stream output (-1 = use operating " \
     "system built-in default).")
 
 #define RTCP_MUX_TEXT N_("RTP/RTCP multiplexing")
 #define PROTO_LONGTEXT N_( \
     "This selects which transport protocol to use for RTP." )
 
+#define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
+#define SRTP_KEY_LONGTEXT N_( \
+    "RTP packets will be integrity-protected and ciphered "\
+    "with this Secure RTP master shared secret key.")
+
+#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
+#define SRTP_SALT_LONGTEXT N_( \
+    "Secure RTP requires a (non-secret) master salt value.")
+
 static const char *const ppsz_protos[] = {
     "dccp", "sctp", "tcp", "udp", "udplite",
 };
@@ -145,63 +158,67 @@ static void Close( vlc_object_t * );
 #define SOUT_CFG_PREFIX "sout-rtp-"
 #define MAX_EMPTY_BLOCKS 200
 
-vlc_module_begin();
-    set_shortname( _("RTP"));
-    set_description( _("RTP stream output") );
-    set_capability( "sout stream", 0 );
-    add_shortcut( "rtp" );
-    set_category( CAT_SOUT );
-    set_subcategory( SUBCAT_SOUT_STREAM );
+vlc_module_begin ()
+    set_shortname( N_("RTP"))
+    set_description( N_("RTP stream output") )
+    set_capability( "sout stream", 0 )
+    add_shortcut( "rtp" )
+    set_category( CAT_SOUT )
+    set_subcategory( SUBCAT_SOUT_STREAM )
 
     add_string( SOUT_CFG_PREFIX "dst", "", NULL, DEST_TEXT,
-                DEST_LONGTEXT, true );
-        change_unsafe();
+                DEST_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "sdp", "", NULL, SDP_TEXT,
-                SDP_LONGTEXT, true );
+                SDP_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
-                MUX_LONGTEXT, true );
+                MUX_LONGTEXT, true )
     add_bool( SOUT_CFG_PREFIX "sap", false, NULL, SAP_TEXT, SAP_LONGTEXT,
-              true );
+              true )
 
     add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT,
-                NAME_LONGTEXT, true );
+                NAME_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "description", "", NULL, DESC_TEXT,
-                DESC_LONGTEXT, true );
+                DESC_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
-                URL_LONGTEXT, true );
+                URL_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "email", "", NULL, EMAIL_TEXT,
-                EMAIL_LONGTEXT, true );
+                EMAIL_LONGTEXT, true )
     add_string( SOUT_CFG_PREFIX "phone", "", NULL, PHONE_TEXT,
-                PHONE_LONGTEXT, true );
+                PHONE_LONGTEXT, true )
 
     add_string( SOUT_CFG_PREFIX "proto", "udp", NULL, PROTO_TEXT,
-                PROTO_LONGTEXT, false );
+                PROTO_LONGTEXT, false )
         change_string_list( ppsz_protos, ppsz_protocols, NULL );
-    add_integer( SOUT_CFG_PREFIX "port", 50004, NULL, PORT_TEXT,
-                 PORT_LONGTEXT, true );
-    add_integer( SOUT_CFG_PREFIX "port-audio", 50000, NULL, PORT_AUDIO_TEXT,
-                 PORT_AUDIO_LONGTEXT, true );
-    add_integer( SOUT_CFG_PREFIX "port-video", 50002, NULL, PORT_VIDEO_TEXT,
-                 PORT_VIDEO_LONGTEXT, true );
-
-    add_integer( SOUT_CFG_PREFIX "ttl", 0, NULL, TTL_TEXT,
-                 TTL_LONGTEXT, true );
+    add_integer( SOUT_CFG_PREFIX "port", 5004, NULL, PORT_TEXT,
+                 PORT_LONGTEXT, true )
+    add_integer( SOUT_CFG_PREFIX "port-audio", 0, NULL, PORT_AUDIO_TEXT,
+                 PORT_AUDIO_LONGTEXT, true )
+    add_integer( SOUT_CFG_PREFIX "port-video", 0, NULL, PORT_VIDEO_TEXT,
+                 PORT_VIDEO_LONGTEXT, true )
+
+    add_integer( SOUT_CFG_PREFIX "ttl", -1, NULL, TTL_TEXT,
+                 TTL_LONGTEXT, true )
     add_bool( SOUT_CFG_PREFIX "rtcp-mux", false, NULL,
-              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false );
+              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false )
+
+    add_string( SOUT_CFG_PREFIX "key", "", NULL,
+                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false )
+    add_string( SOUT_CFG_PREFIX "salt", "", NULL,
+                SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false )
 
     add_bool( SOUT_CFG_PREFIX "mp4a-latm", 0, NULL, RFC3016_TEXT,
-                 RFC3016_LONGTEXT, false );
+                 RFC3016_LONGTEXT, false )
 
-    set_callbacks( Open, Close );
-vlc_module_end();
+    set_callbacks( Open, Close )
+vlc_module_end ()
 
 /*****************************************************************************
  * Exported prototypes
  *****************************************************************************/
-static const char *ppsz_sout_options[] = {
+static const char *const ppsz_sout_options[] = {
     "dst", "name", "port", "port-audio", "port-video", "*sdp", "ttl", "mux",
     "sap", "description", "url", "email", "phone",
-    "proto", "rtcp-mux",
+    "proto", "rtcp-mux", "key", "salt",
     "mp4a-latm", NULL
 };
 
@@ -215,9 +232,9 @@ static int               MuxSend( sout_stream_t *, sout_stream_id_t *,
                                   block_t* );
 
 static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
-static void ThreadSend( vlc_object_t *p_this );
+static void* ThreadSend( vlc_object_t *p_this );
 
-static void SDPHandleUrl( sout_stream_t *, char * );
+static void SDPHandleUrl( sout_stream_t *, const char * );
 
 static int SapSetup( sout_stream_t *p_stream );
 static int FileSetup( sout_stream_t *p_stream );
@@ -246,16 +263,14 @@ struct sout_stream_sys_t
 
     /* */
     char     *psz_destination;
-    uint8_t   proto;
-    uint8_t   i_ttl;
+    uint32_t  payload_bitmap;
     uint16_t  i_port;
     uint16_t  i_port_audio;
     uint16_t  i_port_video;
-    bool b_latm;
-    bool rtcp_mux;
-
-    /* when need to use a private one or when using muxer */
-    int i_payload_type;
+    uint8_t   proto;
+    bool      rtcp_mux;
+    int       i_ttl:9;
+    bool      b_latm;
 
     /* in case we do TS/PS over rtp */
     sout_mux_t        *p_mux;
@@ -268,8 +283,7 @@ struct sout_stream_sys_t
     sout_stream_id_t **es;
 };
 
-typedef int (*pf_rtp_packetizer_t)( sout_stream_t *, sout_stream_id_t *,
-                                    block_t * );
+typedef int (*pf_rtp_packetizer_t)( sout_stream_id_t *, block_t * );
 
 typedef struct rtp_sink_t
 {
@@ -297,8 +311,9 @@ struct sout_stream_id_t
     int          i_bitrate;
 
     /* Packetizer specific fields */
+    int                 i_mtu;
+    srtp_session_t     *srtp;
     pf_rtp_packetizer_t pf_packetize;
-    int          i_mtu;
 
     /* Packets sinks */
     vlc_mutex_t       lock_sink;
@@ -335,15 +350,16 @@ static int Open( vlc_object_t *p_this )
     p_sys->i_port       = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port" );
     p_sys->i_port_audio = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-audio" );
     p_sys->i_port_video = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-video" );
-    p_sys->rtcp_mux   = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
+    p_sys->rtcp_mux     = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
 
     p_sys->psz_sdp_file = NULL;
 
-    if( p_sys->i_port_audio == p_sys->i_port_video )
+    if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio )
     {
-        msg_Err( p_stream, "audio and video port cannot be the same" );
-        p_sys->i_port_audio = 0;
-        p_sys->i_port_video = 0;
+        msg_Err( p_stream, "audio and video RTP port must be distinct" );
+        free( p_sys->psz_destination );
+        free( p_sys );
+        return VLC_EGENERIC;
     }
 
     for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
@@ -412,7 +428,7 @@ static int Open( vlc_object_t *p_this )
     }
 
     p_sys->i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
-    if( p_sys->i_ttl == 0 )
+    if( p_sys->i_ttl == -1 )
     {
         /* Normally, we should let the default hop limit up to the core,
          * but we have to know it to build our SDP properly, which is why
@@ -423,7 +439,7 @@ static int Open( vlc_object_t *p_this )
 
     p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );
 
-    p_sys->i_payload_type = 96;
+    p_sys->payload_bitmap = 0;
     p_sys->i_es = 0;
     p_sys->es   = NULL;
     p_sys->rtsp = NULL;
@@ -438,8 +454,8 @@ static int Open( vlc_object_t *p_this )
 
     p_stream->p_sys     = p_sys;
 
-    vlc_mutex_init( p_stream, &p_sys->lock_sdp );
-    vlc_mutex_init( p_stream, &p_sys->lock_es );
+    vlc_mutex_init( &p_sys->lock_sdp );
+    vlc_mutex_init( &p_sys->lock_es );
 
     psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
     if( psz != NULL )
@@ -455,6 +471,7 @@ static int Open( vlc_object_t *p_this )
             free( psz );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
         }
@@ -469,6 +486,7 @@ static int Open( vlc_object_t *p_this )
             sout_AccessOutDelete( p_sys->p_grab );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
         }
@@ -480,6 +498,7 @@ static int Open( vlc_object_t *p_this )
             sout_AccessOutDelete( p_sys->p_grab );
             vlc_mutex_destroy( &p_sys->lock_sdp );
             vlc_mutex_destroy( &p_sys->lock_es );
+            free( p_sys->psz_destination );
             free( p_sys );
             return VLC_EGENERIC;
         }
@@ -590,7 +609,7 @@ static void Close( vlc_object_t * p_this )
 /*****************************************************************************
  * SDPHandleUrl:
  *****************************************************************************/
-static void SDPHandleUrl( sout_stream_t *p_stream, char *psz_url )
+static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
     vlc_url_t url;
@@ -620,10 +639,8 @@ static void SDPHandleUrl( sout_stream_t *p_stream, char *psz_url )
         /* FIXME test if destination is multicast or no destination at all */
         p_sys->rtsp = RtspSetup( p_stream, &url );
         if( p_sys->rtsp == NULL )
-        {
             msg_Err( p_stream, "cannot export SDP as RTSP" );
-        }
-
+        else
         if( p_sys->p_mux != NULL )
         {
             sout_stream_id_t *id = p_sys->es[0];
@@ -771,6 +788,9 @@ char *SDPGenerate( const sout_stream_t *p_stream, const char *rtsp_url )
                       id->psz_enc, id->i_clock_rate, id->i_channels,
                       id->psz_fmtp);
 
+        if( !p_sys->rtcp_mux && (id->i_port & 1) ) /* cf RFC4566 §5.14 */
+            sdp_AddAttribute ( &psz_sdp, "rtcp", "%u", id->i_port + 1 );
+
         if( rtsp_url != NULL )
         {
             assert( strlen( rtsp_url ) > 0 );
@@ -784,7 +804,7 @@ char *SDPGenerate( const sout_stream_t *p_stream, const char *rtsp_url )
             if( id->listen_fd != NULL )
                 sdp_AddAttribute( &psz_sdp, "setup", "passive" );
             if( p_sys->proto == IPPROTO_DCCP )
-                sdp_AddAttribute( &psz_sdp, "dccp-service-code", 
+                sdp_AddAttribute( &psz_sdp, "dccp-service-code",
                                   "SC:RTP%c", toupper( mime_major[0] ) );
         }
     }
@@ -809,6 +829,22 @@ static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
     s[2*i_data] = '\0';
 }
 
+/**
+ * Shrink the MTU down to a fixed packetization time (for audio).
+ */
+static void
+rtp_set_ptime (sout_stream_id_t *id, unsigned ptime_ms, size_t bytes)
+{
+    /* Samples per second */
+    size_t spl = (id->i_clock_rate - 1) * ptime_ms / 1000 + 1;
+    bytes *= id->i_channels;
+    spl *= bytes;
+
+    if (spl < rtp_mtu (id)) /* MTU is big enough for ptime */
+        id->i_mtu = 12 + spl;
+    else /* MTU is too small for ptime, align to a sample boundary */
+        id->i_mtu = 12 + (((id->i_mtu - 12) / bytes) * bytes);
+}
 
 /** Add an ES as a new RTP stream */
 static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
@@ -819,6 +855,14 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
     sout_stream_id_t  *id;
     int               i_port, cscov = -1;
     char              *psz_sdp;
+    int               i_port_audio_option = var_GetInteger( p_stream, "port-audio" );
+    int               i_port_video_option = var_GetInteger( p_stream, "port-video" );
+
+    if (0xffffffff == p_sys->payload_bitmap)
+    {
+        msg_Err (p_stream, "too many RTP elementary streams");
+        return NULL;
+    }
 
     id = vlc_object_create( p_stream, sizeof( sout_stream_id_t ) );
     if( id == NULL )
@@ -844,24 +888,24 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
 
     while( i_port == 0 )
     {
-        if( p_sys->i_port != p_sys->i_port_audio
-         && p_sys->i_port != p_sys->i_port_video )
+        if( p_sys->i_port != i_port_audio_option
+         && p_sys->i_port != i_port_video_option )
         {
             i_port = p_sys->i_port;
-            p_sys->i_port += 2;
-            break;
         }
         p_sys->i_port += 2;
     }
 
     id->p_stream   = p_stream;
 
-    id->i_sequence = rand()&0xffff;
-    id->i_payload_type = p_sys->i_payload_type;
-    id->ssrc[0] = rand()&0xff;
-    id->ssrc[1] = rand()&0xff;
-    id->ssrc[2] = rand()&0xff;
-    id->ssrc[3] = rand()&0xff;
+    /* Look for free dymanic payload type */
+    id->i_payload_type = 96;
+    while (p_sys->payload_bitmap & (1 << (id->i_payload_type - 96)))
+        id->i_payload_type++;
+    assert (id->i_payload_type < 128);
+
+    vlc_rand_bytes (&id->i_sequence, sizeof (id->i_sequence));
+    vlc_rand_bytes (id->ssrc, sizeof (id->ssrc));
 
     id->psz_enc    = NULL;
     id->psz_fmtp   = NULL;
@@ -884,14 +928,38 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         id->i_bitrate = 0;
     }
 
-    id->pf_packetize = NULL;
     id->i_mtu = config_GetInt( p_stream, "mtu" );
     if( id->i_mtu <= 12 + 16 )
         id->i_mtu = 576 - 20 - 8; /* pessimistic */
-
     msg_Dbg( p_stream, "maximum RTP packet size: %d bytes", id->i_mtu );
 
-    vlc_mutex_init( p_stream, &id->lock_sink );
+    id->srtp = NULL;
+    id->pf_packetize = NULL;
+
+    char *key = var_CreateGetNonEmptyString (p_stream, SOUT_CFG_PREFIX"key");
+    if (key)
+    {
+        id->srtp = srtp_create (SRTP_ENCR_AES_CM, SRTP_AUTH_HMAC_SHA1, 10,
+                                   SRTP_PRF_AES_CM, SRTP_RCC_MODE1);
+        if (id->srtp == NULL)
+        {
+            free (key);
+            goto error;
+        }
+
+        char *salt = var_CreateGetNonEmptyString (p_stream, SOUT_CFG_PREFIX"salt");
+        errno = srtp_setkeystring (id->srtp, key, salt ? salt : "");
+        free (salt);
+        free (key);
+        if (errno)
+        {
+            msg_Err (p_stream, "bad SRTP key/salt combination (%m)");
+            goto error;
+        }
+        id->i_sequence = 0; /* FIXME: awful hack for libvlc_srtp */
+    }
+
+    vlc_mutex_init( &id->lock_sink );
     id->sinkc = 0;
     id->sinkv = NULL;
     id->rtsp_id = NULL;
@@ -911,7 +979,7 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
                 {
                     case VIDEO_ES: code = "RTPV";     break;
                     case AUDIO_ES: code = "RTPARTPV"; break;
-                    case SPU_ES:   code = "RTPTRPTV"; break;
+                    case SPU_ES:   code = "RTPTRTPV"; break;
                     default:       code = "RTPORTPV"; break;
                 }
                 var_SetString (p_stream, "dccp-service", code);
@@ -929,7 +997,7 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
 
             default:
             {
-                int ttl = (p_sys->i_ttl > 0) ? p_sys->i_ttl : -1;
+                int ttl = (p_sys->i_ttl >= 0) ? p_sys->i_ttl : -1;
                 int fd = net_ConnectDgram( p_stream, p_sys->psz_destination,
                                            i_port, ttl, p_sys->proto );
                 if( fd == -1 )
@@ -957,6 +1025,7 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
         {
             id->psz_enc = "MP2P";
         }
+        free( psz );
     }
     else
     switch( p_fmt->i_codec )
@@ -965,13 +1034,15 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
                 id->i_payload_type = 0;
             id->psz_enc = "PCMU";
-            id->pf_packetize = rtp_packetize_l8;
+            id->pf_packetize = rtp_packetize_split;
+            rtp_set_ptime (id, 20, 1);
             break;
         case VLC_FOURCC( 'a', 'l', 'a', 'w' ):
             if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
                 id->i_payload_type = 8;
             id->psz_enc = "PCMA";
-            id->pf_packetize = rtp_packetize_l8;
+            id->pf_packetize = rtp_packetize_split;
+            rtp_set_ptime (id, 20, 1);
             break;
         case VLC_FOURCC( 's', '1', '6', 'b' ):
             if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
@@ -984,16 +1055,19 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
                 id->i_payload_type = 10;
             }
             id->psz_enc = "L16";
-            id->pf_packetize = rtp_packetize_l16;
+            id->pf_packetize = rtp_packetize_split;
+            rtp_set_ptime (id, 20, 2);
             break;
         case VLC_FOURCC( 'u', '8', ' ', ' ' ):
             id->psz_enc = "L8";
-            id->pf_packetize = rtp_packetize_l8;
+            id->pf_packetize = rtp_packetize_split;
+            rtp_set_ptime (id, 20, 1);
             break;
         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
         case VLC_FOURCC( 'm', 'p', '3', ' ' ):
             id->i_payload_type = 14;
             id->psz_enc = "MPA";
+            id->i_clock_rate = 90000; /* not 44100 */
             id->pf_packetize = rtp_packetize_mpa;
             break;
         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
@@ -1001,6 +1075,28 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             id->psz_enc = "MPV";
             id->pf_packetize = rtp_packetize_mpv;
             break;
+        case VLC_FOURCC( 'G', '7', '2', '6' ):
+        case VLC_FOURCC( 'g', '7', '2', '6' ):
+            switch( p_fmt->i_bitrate / 1000 )
+            {
+            case 16:
+                id->psz_enc = "G726-16";
+                id->pf_packetize = rtp_packetize_g726_16;
+                break;
+            case 24:
+                id->psz_enc = "G726-24";
+                id->pf_packetize = rtp_packetize_g726_24;
+                break;
+            case 32:
+                id->psz_enc = "G726-32";
+                id->pf_packetize = rtp_packetize_g726_32;
+                break;
+            case 40:
+                id->psz_enc = "G726-40";
+                id->pf_packetize = rtp_packetize_g726_40;
+                break;
+            }
+            break;
         case VLC_FOURCC( 'a', '5', '2', ' ' ):
             id->psz_enc = "ac3";
             id->pf_packetize = rtp_packetize_ac3;
@@ -1139,7 +1235,6 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
             id->pf_packetize = rtp_packetize_amr;
             break;
         case VLC_FOURCC( 's', 'p', 'x', ' ' ):
-            id->i_payload_type = p_sys->i_payload_type++;
             id->psz_enc = "SPEEX";
             id->pf_packetize = rtp_packetize_spx;
             break;
@@ -1154,14 +1249,16 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
                      "codec:%4.4s)", (char*)&p_fmt->i_codec );
             goto error;
     }
+    if (id->i_payload_type >= 96)
+        /* Mark dynamic payload type in use */
+        p_sys->payload_bitmap |= 1 << (id->i_payload_type - 96);
 
+#if 0 /* No payload formats sets this at the moment */
     if( cscov != -1 )
         cscov += 8 /* UDP */ + 12 /* RTP */;
     if( id->sinkc > 0 )
         net_SetCSCov( id->sinkv[0].rtp_fd, cscov, -1 );
-
-    if( id->i_payload_type == p_sys->i_payload_type )
-        p_sys->i_payload_type++;
+#endif
 
     if( p_sys->rtsp != NULL )
         id->rtsp_id = RtspAddId( p_sys->rtsp, id, p_sys->i_es,
@@ -1171,7 +1268,7 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
 
     id->p_fifo = block_FifoNew();
     if( vlc_thread_create( id, "RTP send thread", ThreadSend,
-                           VLC_THREAD_PRIORITY_HIGHEST, false ) )
+                           VLC_THREAD_PRIORITY_HIGHEST ) )
         goto error;
 
     /* Update p_sys context */
@@ -1206,7 +1303,6 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
     if( id->p_fifo != NULL )
     {
         vlc_object_kill( id );
-        block_FifoWake( id->p_fifo );
         vlc_thread_join( id );
         block_FifoRelease( id->p_fifo );
     }
@@ -1220,6 +1316,9 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
         p_sys->i_port_audio = id->i_port;
     if( id->i_port == var_GetInteger( p_stream, "port-video" ) )
         p_sys->i_port_video = id->i_port;
+    /* Release dynamic payload type */
+    if (id->i_payload_type >= 96)
+        p_sys->payload_bitmap &= ~(1 << (id->i_payload_type - 96));
 
     free( id->psz_fmtp );
 
@@ -1229,6 +1328,8 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
         rtp_del_sink( id, id->sinkv[0].rtp_fd ); /* sink for explicit dst= */
     if( id->listen_fd != NULL )
         net_ListenClose( id->listen_fd );
+    if( id->srtp != NULL )
+        srtp_destroy( id->srtp );
 
     vlc_mutex_destroy( &id->lock_sink );
 
@@ -1247,11 +1348,12 @@ static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
     block_t *p_next;
 
     assert( p_stream->p_sys->p_mux == NULL );
+    (void)p_stream;
 
     while( p_buffer != NULL )
     {
         p_next = p_buffer->p_next;
-        if( id->pf_packetize( p_stream, id, p_buffer ) )
+        if( id->pf_packetize( id, p_buffer ) )
             break;
 
         block_Release( p_buffer );
@@ -1364,21 +1466,44 @@ static int  HttpCallback( httpd_file_sys_t *p_args,
 /****************************************************************************
  * RTP send
  ****************************************************************************/
-static void ThreadSend( vlc_object_t *p_this )
+static void* ThreadSend( vlc_object_t *p_this )
 {
     sout_stream_id_t *id = (sout_stream_id_t *)p_this;
     unsigned i_caching = id->i_caching;
 
-    while( !id->b_die )
+    for (;;)
     {
         block_t *out = block_FifoGet( id->p_fifo );
-        if( out == NULL )
-            continue; /* Forced wakeup */
+        block_cleanup_push (out);
+
+        if( id->srtp )
+        {   /* FIXME: this is awfully inefficient */
+            size_t len = out->i_buffer;
+            out = block_Realloc( out, 0, len + 10 );
+            out->i_buffer = len;
+
+            int canc = vlc_savecancel ();
+            int val = srtp_send( id->srtp, out->p_buffer, &len, len + 10 );
+            vlc_restorecancel (canc);
+            if( val )
+            {
+                errno = val;
+                msg_Dbg( id, "SRTP sending error: %m" );
+                block_Release( out );
+                out = NULL;
+            }
+            else
+                out->i_buffer = len;
+        }
 
-        mtime_t  i_date = out->i_dts + i_caching;
-        ssize_t  len = out->i_buffer;
+        if (out)
+            mwait (out->i_dts + i_caching);
+        vlc_cleanup_pop ();
+        if (out == NULL)
+            continue;
 
-        mwait( i_date );
+        ssize_t len = out->i_buffer;
+        int canc = vlc_savecancel ();
 
         vlc_mutex_lock( &id->lock_sink );
         unsigned deadc = 0; /* How many dead sockets? */
@@ -1386,7 +1511,8 @@ static void ThreadSend( vlc_object_t *p_this )
 
         for( int i = 0; i < id->sinkc; i++ )
         {
-            SendRTCP( id->sinkv[i].rtcp, out );
+            if( !id->srtp ) /* FIXME: SRTCP support */
+                SendRTCP( id->sinkv[i].rtcp, out );
 
             if( send( id->sinkv[i].rtp_fd, out->p_buffer, len, 0 ) >= 0 )
                 continue;
@@ -1414,7 +1540,9 @@ static void ThreadSend( vlc_object_t *p_this )
             msg_Dbg( id, "adding socket %d", fd );
             rtp_add_sink( id, fd, true );
         }
+        vlc_restorecancel (canc);
     }
+    return NULL;
 }
 
 int rtp_add_sink( sout_stream_id_t *id, int fd, bool rtcp_mux )
@@ -1512,16 +1640,6 @@ size_t rtp_mtu (const sout_stream_id_t *id)
     return id->i_mtu - 12;
 }
 
-/**
- * @return number of audio samples to include for a given packetization time
- * (this really only makes sense for audio formats).
- */
-size_t rtp_plen (const sout_stream_id_t * id, unsigned ptime_ms)
-{
-    return id->i_channels * (((id->i_clock_rate - 1) * ptime_ms / 1000) + 1);
-}
-
-
 /*****************************************************************************
  * Non-RTP mux
  *****************************************************************************/
@@ -1566,8 +1684,8 @@ static int MuxDel( sout_stream_t *p_stream, sout_stream_id_t *id )
 }
 
 
-static int AccessOutGrabberWriteBuffer( sout_stream_t *p_stream,
-                                        const block_t *p_buffer )
+static ssize_t AccessOutGrabberWriteBuffer( sout_stream_t *p_stream,
+                                            const block_t *p_buffer )
 {
     sout_stream_sys_t *p_sys = p_stream->p_sys;
     sout_stream_id_t *id = p_sys->es[0];
@@ -1575,14 +1693,14 @@ static int AccessOutGrabberWriteBuffer( sout_stream_t *p_stream,
     int64_t  i_dts = p_buffer->i_dts;
 
     uint8_t         *p_data = p_buffer->p_buffer;
-    unsigned int    i_data  = p_buffer->i_buffer;
-    unsigned int    i_max   = id->i_mtu - 12;
+    size_t          i_data  = p_buffer->i_buffer;
+    size_t          i_max   = id->i_mtu - 12;
 
-    unsigned i_packet = ( p_buffer->i_buffer + i_max - 1 ) / i_max;
+    size_t i_packet = ( p_buffer->i_buffer + i_max - 1 ) / i_max;
 
     while( i_data > 0 )
     {
-        unsigned int i_size;
+        size_t i_size;
 
         /* output complete packet */
         if( p_sys->packet &&
@@ -1617,8 +1735,8 @@ static int AccessOutGrabberWriteBuffer( sout_stream_t *p_stream,
 }
 
 
-static int AccessOutGrabberWrite( sout_access_out_t *p_access,
-                                  block_t *p_buffer )
+static ssize_t AccessOutGrabberWrite( sout_access_out_t *p_access,
+                                      block_t *p_buffer )
 {
     sout_stream_t *p_stream = (sout_stream_t*)p_access->p_sys;
 
@@ -1646,7 +1764,6 @@ static sout_access_out_t *GrabberCreate( sout_stream_t *p_stream )
         return NULL;
 
     p_grab->p_module    = NULL;
-    p_grab->p_sout      = p_stream->p_sout;
     p_grab->psz_access  = strdup( "grab" );
     p_grab->p_cfg       = NULL;
     p_grab->psz_path    = strdup( "" );