root/modules/stream_out/rtp.c

Revision 64db8eea70ae1dda0031babb45e2e226a6f4ee5a, 54.7 kB (checked in by Rémi Denis-Courmont <rdenis@simphalempin.com>, 2 months ago)

RTP: fix TTL

Signed-off-by: Rémi Denis-Courmont <rdenis@simphalempin.com>

  • Property mode set to 100644
Line 
1 /*****************************************************************************
2  * rtp.c: rtp stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * Copyright © 2007-2008 Rémi Denis-Courmont
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_sout.h>
35 #include <vlc_block.h>
36
37 #include <vlc_httpd.h>
38 #include <vlc_url.h>
39 #include <vlc_network.h>
40 #include <vlc_charset.h>
41 #include <vlc_strings.h>
42 #include <srtp.h>
43
44 #include "rtp.h"
45
46 #ifdef HAVE_UNISTD_H
47 #   include <sys/types.h>
48 #   include <unistd.h>
49 #   include <fcntl.h>
50 #   include <sys/stat.h>
51 #endif
52 #ifdef HAVE_LINUX_DCCP_H
53 #   include <linux/dccp.h>
54 #endif
55 #ifndef IPPROTO_DCCP
56 # define IPPROTO_DCCP 33
57 #endif
58 #ifndef IPPROTO_UDPLITE
59 # define IPPROTO_UDPLITE 136
60 #endif
61
62 #include <errno.h>
63
64 #include <assert.h>
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69
70 #define DEST_TEXT N_("Destination")
71 #define DEST_LONGTEXT N_( \
72     "This is the output URL that will be used." )
73 #define SDP_TEXT N_("SDP")
74 #define SDP_LONGTEXT N_( \
75     "This allows you to specify how the SDP (Session Descriptor) for this RTP "\
76     "session will be made available. You must use an url: http://location to " \
77     "access the SDP via HTTP, rtsp://location for RTSP access, and sap:// " \
78     "for the SDP to be announced via SAP." )
79 #define SAP_TEXT N_("SAP announcing")
80 #define SAP_LONGTEXT N_("Announce this session with SAP.")
81 #define MUX_TEXT N_("Muxer")
82 #define MUX_LONGTEXT N_( \
83     "This allows you to specify the muxer used for the streaming output. " \
84     "Default is to use no muxer (standard RTP stream)." )
85
86 #define NAME_TEXT N_("Session name")
87 #define NAME_LONGTEXT N_( \
88     "This is the name of the session that will be announced in the SDP " \
89     "(Session Descriptor)." )
90 #define DESC_TEXT N_("Session description")
91 #define DESC_LONGTEXT N_( \
92     "This allows you to give a short description with details about the stream, " \
93     "that will be announced in the SDP (Session Descriptor)." )
94 #define URL_TEXT N_("Session URL")
95 #define URL_LONGTEXT N_( \
96     "This allows you to give an URL with more details about the stream " \
97     "(often the website of the streaming organization), that will " \
98     "be announced in the SDP (Session Descriptor)." )
99 #define EMAIL_TEXT N_("Session email")
100 #define EMAIL_LONGTEXT N_( \
101     "This allows you to give a contact mail address for the stream, that will " \
102     "be announced in the SDP (Session Descriptor)." )
103 #define PHONE_TEXT N_("Session phone number")
104 #define PHONE_LONGTEXT N_( \
105     "This allows you to give a contact telephone number for the stream, that will " \
106     "be announced in the SDP (Session Descriptor)." )
107
108 #define PORT_TEXT N_("Port")
109 #define PORT_LONGTEXT N_( \
110     "This allows you to specify the base port for the RTP streaming." )
111 #define PORT_AUDIO_TEXT N_("Audio port")
112 #define PORT_AUDIO_LONGTEXT N_( \
113     "This allows you to specify the default audio port for the RTP streaming." )
114 #define PORT_VIDEO_TEXT N_("Video port")
115 #define PORT_VIDEO_LONGTEXT N_( \
116     "This allows you to specify the default video port for the RTP streaming." )
117
118 #define TTL_TEXT N_("Hop limit (TTL)")
119 #define TTL_LONGTEXT N_( \
120     "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
121     "the multicast packets sent by the stream output (-1 = use operating " \
122     "system built-in default).")
123
124 #define RTCP_MUX_TEXT N_("RTP/RTCP multiplexing")
125 #define RTCP_MUX_LONGTEXT N_( \
126     "This sends and receives RTCP packet multiplexed over the same port " \
127     "as RTP packets." )
128
129 #define PROTO_TEXT N_("Transport protocol")
130 #define PROTO_LONGTEXT N_( \
131     "This selects which transport protocol to use for RTP." )
132
133 #define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
134 #define SRTP_KEY_LONGTEXT N_( \
135     "RTP packets will be integrity-protected and ciphered "\
136     "with this Secure RTP master shared secret key.")
137
138 #define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
139 #define SRTP_SALT_LONGTEXT N_( \
140     "Secure RTP requires a (non-secret) master salt value.")
141
142 static const char *const ppsz_protos[] = {
143     "dccp", "sctp", "tcp", "udp", "udplite",
144 };
145
146 static const char *const ppsz_protocols[] = {
147     "DCCP", "SCTP", "TCP", "UDP", "UDP-Lite",
148 };
149
150 #define RFC3016_TEXT N_("MP4A LATM")
151 #define RFC3016_LONGTEXT N_( \
152     "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )
153
154 static int  Open ( vlc_object_t * );
155 static void Close( vlc_object_t * );
156
157 #define SOUT_CFG_PREFIX "sout-rtp-"
158 #define MAX_EMPTY_BLOCKS 200
159
160 vlc_module_begin();
161     set_shortname( N_("RTP"));
162     set_description( N_("RTP stream output") );
163     set_capability( "sout stream", 0 );
164     add_shortcut( "rtp" );
165     set_category( CAT_SOUT );
166     set_subcategory( SUBCAT_SOUT_STREAM );
167
168     add_string( SOUT_CFG_PREFIX "dst", "", NULL, DEST_TEXT,
169                 DEST_LONGTEXT, true );
170     add_string( SOUT_CFG_PREFIX "sdp", "", NULL, SDP_TEXT,
171                 SDP_LONGTEXT, true );
172     add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
173                 MUX_LONGTEXT, true );
174     add_bool( SOUT_CFG_PREFIX "sap", false, NULL, SAP_TEXT, SAP_LONGTEXT,
175               true );
176
177     add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT,
178                 NAME_LONGTEXT, true );
179     add_string( SOUT_CFG_PREFIX "description", "", NULL, DESC_TEXT,
180                 DESC_LONGTEXT, true );
181     add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
182                 URL_LONGTEXT, true );
183     add_string( SOUT_CFG_PREFIX "email", "", NULL, EMAIL_TEXT,
184                 EMAIL_LONGTEXT, true );
185     add_string( SOUT_CFG_PREFIX "phone", "", NULL, PHONE_TEXT,
186                 PHONE_LONGTEXT, true );
187
188     add_string( SOUT_CFG_PREFIX "proto", "udp", NULL, PROTO_TEXT,
189                 PROTO_LONGTEXT, false );
190         change_string_list( ppsz_protos, ppsz_protocols, NULL );
191     add_integer( SOUT_CFG_PREFIX "port", 50004, NULL, PORT_TEXT,
192                  PORT_LONGTEXT, true );
193     add_integer( SOUT_CFG_PREFIX "port-audio", 50000, NULL, PORT_AUDIO_TEXT,
194                  PORT_AUDIO_LONGTEXT, true );
195     add_integer( SOUT_CFG_PREFIX "port-video", 50002, NULL, PORT_VIDEO_TEXT,
196                  PORT_VIDEO_LONGTEXT, true );
197
198     add_integer( SOUT_CFG_PREFIX "ttl", -1, NULL, TTL_TEXT,
199                  TTL_LONGTEXT, true );
200     add_bool( SOUT_CFG_PREFIX "rtcp-mux", false, NULL,
201               RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false );
202
203     add_string( SOUT_CFG_PREFIX "key", "", NULL,
204                 SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false );
205     add_string( SOUT_CFG_PREFIX "salt", "", NULL,
206                 SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false );
207
208     add_bool( SOUT_CFG_PREFIX "mp4a-latm", 0, NULL, RFC3016_TEXT,
209                  RFC3016_LONGTEXT, false );
210
211     set_callbacks( Open, Close );
212 vlc_module_end();
213
214 /*****************************************************************************
215  * Exported prototypes
216  *****************************************************************************/
217 static const char *const ppsz_sout_options[] = {
218     "dst", "name", "port", "port-audio", "port-video", "*sdp", "ttl", "mux",
219     "sap", "description", "url", "email", "phone",
220     "proto", "rtcp-mux", "key", "salt",
221     "mp4a-latm", NULL
222 };
223
224 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
225 static int               Del ( sout_stream_t *, sout_stream_id_t * );
226 static int               Send( sout_stream_t *, sout_stream_id_t *,
227                                block_t* );
228 static sout_stream_id_t *MuxAdd ( sout_stream_t *, es_format_t * );
229 static int               MuxDel ( sout_stream_t *, sout_stream_id_t * );
230 static int               MuxSend( sout_stream_t *, sout_stream_id_t *,
231                                   block_t* );
232
233 static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
234 static void* ThreadSend( vlc_object_t *p_this );
235
236 static void SDPHandleUrl( sout_stream_t *, const char * );
237
238 static int SapSetup( sout_stream_t *p_stream );
239 static int FileSetup( sout_stream_t *p_stream );
240 static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );
241
242 struct sout_stream_sys_t
243 {
244     /* SDP */
245     char    *psz_sdp;
246     vlc_mutex_t  lock_sdp;
247
248     /* SDP to disk */
249     bool b_export_sdp_file;
250     char *psz_sdp_file;
251
252     /* SDP via SAP */
253     bool b_export_sap;
254     session_descriptor_t *p_session;
255
256     /* SDP via HTTP */
257     httpd_host_t *p_httpd_host;
258     httpd_file_t *p_httpd_file;
259
260     /* RTSP */
261     rtsp_stream_t *rtsp;
262
263     /* */
264     char     *psz_destination;
265     uint32_t  payload_bitmap;
266     uint16_t  i_port;
267     uint16_t  i_port_audio;
268     uint16_t  i_port_video;
269     uint8_t   proto;
270     bool      rtcp_mux;
271     int       i_ttl:9;
272     bool      b_latm;
273
274     /* in case we do TS/PS over rtp */
275     sout_mux_t        *p_mux;
276     sout_access_out_t *p_grab;
277     block_t           *packet;
278
279     /* */
280     vlc_mutex_t      lock_es;
281     int              i_es;
282     sout_stream_id_t **es;
283 };
284
285 typedef int (*pf_rtp_packetizer_t)( sout_stream_id_t *, block_t * );
286
287 typedef struct rtp_sink_t
288 {
289     int rtp_fd;
290     rtcp_sender_t *rtcp;
291 } rtp_sink_t;
292
293 struct sout_stream_id_t
294 {
295     VLC_COMMON_MEMBERS
296
297     sout_stream_t *p_stream;
298     /* rtp field */
299     uint16_t    i_sequence;
300     uint8_t     i_payload_type;
301     uint8_t     ssrc[4];
302
303     /* for sdp */
304     const char  *psz_enc;
305     char        *psz_fmtp;
306     int          i_clock_rate;
307     int          i_port;
308     int          i_cat;
309     int          i_channels;
310     int          i_bitrate;
311
312     /* Packetizer specific fields */
313     int                 i_mtu;
314     srtp_session_t     *srtp;
315     pf_rtp_packetizer_t pf_packetize;
316
317     /* Packets sinks */
318     vlc_mutex_t       lock_sink;
319     int               sinkc;
320     rtp_sink_t       *sinkv;
321     rtsp_stream_id_t *rtsp_id;
322     int              *listen_fd;
323
324     block_fifo_t     *p_fifo;
325     int64_t           i_caching;
326 };
327
328 /*****************************************************************************
329  * Open:
330  *****************************************************************************/
331 static int Open( vlc_object_t *p_this )
332 {
333     sout_stream_t       *p_stream = (sout_stream_t*)p_this;
334     sout_instance_t     *p_sout = p_stream->p_sout;
335     sout_stream_sys_t   *p_sys = NULL;
336     config_chain_t      *p_cfg = NULL;
337     char                *psz;
338     bool          b_rtsp = false;
339
340     config_ChainParse( p_stream, SOUT_CFG_PREFIX,
341                        ppsz_sout_options, p_stream->p_cfg );
342
343     p_sys = malloc( sizeof( sout_stream_sys_t ) );
344     if( p_sys == NULL )
345         return VLC_ENOMEM;
346
347     p_sys->psz_destination = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst" );
348
349     p_sys->i_port       = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port" );
350     p_sys->i_port_audio = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-audio" );
351     p_sys->i_port_video = var_GetInteger( p_stream, SOUT_CFG_PREFIX "port-video" );
352     p_sys->rtcp_mux   = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
353
354     p_sys->psz_sdp_file = NULL;
355
356     if( p_sys->i_port_audio == p_sys->i_port_video )
357     {
358         msg_Err( p_stream, "audio and video port cannot be the same" );
359         p_sys->i_port_audio = 0;
360         p_sys->i_port_video = 0;
361     }
362
363     for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
364     {
365         if( !strcmp( p_cfg->psz_name, "sdp" )
366          && ( p_cfg->psz_value != NULL )
367          && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
368         {
369             b_rtsp = true;
370             break;
371         }
372     }
373     if( !b_rtsp )
374     {
375         psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
376         if( psz != NULL )
377         {
378             if( !strncasecmp( psz, "rtsp:", 5 ) )
379                 b_rtsp = true;
380             free( psz );
381         }
382     }
383
384     /* Transport protocol */
385     p_sys->proto = IPPROTO_UDP;
386     psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");
387
388     if ((psz == NULL) || !strcasecmp (psz, "udp"))
389         (void)0; /* default */
390     else
391     if (!strcasecmp (psz, "dccp"))
392     {
393         p_sys->proto = IPPROTO_DCCP;
394         p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
395     }
396 #if 0
397     else
398     if (!strcasecmp (psz, "sctp"))
399     {
400         p_sys->proto = IPPROTO_TCP;
401         p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
402     }
403 #endif
404 #if 0
405     else
406     if (!strcasecmp (psz, "tcp"))
407     {
408         p_sys->proto = IPPROTO_TCP;
409         p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
410     }
411 #endif
412     else
413     if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
414         p_sys->proto = IPPROTO_UDPLITE;
415     else
416         msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
417                   psz);
418     free (psz);
419     var_Create (p_this, "dccp-service", VLC_VAR_STRING);
420
421     if( ( p_sys->psz_destination == NULL ) && !b_rtsp )
422     {
423         msg_Err( p_stream, "missing destination and not in RTSP mode" );
424         free( p_sys );
425         return VLC_EGENERIC;
426     }
427
428     p_sys->i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
429     if( p_sys->i_ttl == -1 )
430     {
431         /* Normally, we should let the default hop limit up to the core,
432          * but we have to know it to build our SDP properly, which is why
433          * we ask the core. FIXME: broken when neither sout-rtp-ttl nor
434          * ttl are set. */
435         p_sys->i_ttl = config_GetInt( p_stream, "ttl" );
436     }
437
438     p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );
439
440     p_sys->payload_bitmap = 0;
441     p_sys->i_es = 0;
442     p_sys->es   = NULL;
443     p_sys->rtsp = NULL;
444     p_sys->psz_sdp = NULL;
445
446     p_sys->b_export_sap = false;
447     p_sys->b_export_sdp_file = false;
448     p_sys->p_session = NULL;
449
450     p_sys->p_httpd_host = NULL;
451     p_sys->p_httpd_file = NULL;
452
453     p_stream->p_sys     = p_sys;
454
455     vlc_mutex_init( &p_sys->lock_sdp );
456     vlc_mutex_init( &p_sys->lock_es );
457
458     psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
459     if( psz != NULL )
460     {
461         sout_stream_id_t *id;
462
463         /* Check muxer type */
464         if( strncasecmp( psz, "ps", 2 )
465          && strncasecmp( psz, "mpeg1", 5 )
466          && strncasecmp( psz, "ts", 2 ) )
467         {
468             msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
469             free( psz );
470             vlc_mutex_destroy( &p_sys->lock_sdp );
471             vlc_mutex_destroy( &p_sys->lock_es );
472             free( p_sys );
473             return VLC_EGENERIC;
474         }
475
476         p_sys->p_grab = GrabberCreate( p_stream );
477         p_sys->p_mux = sout_MuxNew( p_sout, psz, p_sys->p_grab );
478         free( psz );
479
480         if( p_sys->p_mux == NULL )
481         {
482             msg_Err( p_stream, "cannot create muxer" );
483             sout_AccessOutDelete( p_sys->p_grab );
484             vlc_mutex_destroy( &p_sys->lock_sdp );
485             vlc_mutex_destroy( &p_sys->lock_es );
486             free( p_sys );
487             return VLC_EGENERIC;
488         }
489
490         id = Add( p_stream, NULL );
491         if( id == NULL )
492         {
493             sout_MuxDelete( p_sys->p_mux );
494             sout_AccessOutDelete( p_sys->p_grab );
495             vlc_mutex_destroy( &p_sys->lock_sdp );
496             vlc_mutex_destroy( &p_sys->lock_es );
497             free( p_sys );
498             return VLC_EGENERIC;
499         }
500
501         p_sys->packet = NULL;
502
503         p_stream->pf_add  = MuxAdd;
504         p_stream->pf_del  = MuxDel;
505         p_stream->pf_send = MuxSend;
506     }
507     else
508     {
509         p_sys->p_mux    = NULL;
510         p_sys->p_grab   = NULL;
511
512         p_stream->pf_add    = Add;
513         p_stream->pf_del    = Del;
514         p_stream->pf_send   = Send;
515     }
516
517     if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
518         SDPHandleUrl( p_stream, "sap" );
519
520     psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
521     if( psz != NULL )
522     {
523         config_chain_t *p_cfg;
524
525         SDPHandleUrl( p_stream, psz );
526
527         for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
528         {
529             if( !strcmp( p_cfg->psz_name, "sdp" ) )
530             {
531                 if( p_cfg->psz_value == NULL || *p_cfg->psz_value == '\0' )
532                     continue;
533
534                 /* needed both :sout-rtp-sdp= and rtp{sdp=} can be used */
535                 if( !strcmp( p_cfg->psz_value, psz ) )
536                     continue;
537
538                 SDPHandleUrl( p_stream, p_cfg->psz_value );
539             }
540         }
541         free( psz );
542     }
543
544     /* update p_sout->i_out_pace_nocontrol */
545     p_stream->p_sout->i_out_pace_nocontrol++;
546
547     return VLC_SUCCESS;
548 }
549
550 /*****************************************************************************
551  * Close:
552  *****************************************************************************/
553 static void Close( vlc_object_t * p_this )
554 {
555     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
556     sout_stream_sys_t *p_sys = p_stream->p_sys;
557
558     /* update p_sout->i_out_pace_nocontrol */
559     p_stream->p_sout->i_out_pace_nocontrol--;
560
561     if( p_sys->p_mux )
562     {
563         assert( p_sys->i_es == 1 );
564         Del( p_stream, p_sys->es[0] );
565
566         sout_MuxDelete( p_sys->p_mux );
567         sout_AccessOutDelete( p_sys->p_grab );
568         if( p_sys->packet )
569         {
570             block_Release( p_sys->packet );
571         }
572         if( p_sys->b_export_sap )
573         {
574             p_sys->p_mux = NULL;
575             SapSetup( p_stream );
576         }
577     }
578
579     if( p_sys->rtsp != NULL )
580         RtspUnsetup( p_sys->rtsp );
581
582     vlc_mutex_destroy( &p_sys->lock_sdp );
583     vlc_mutex_destroy( &p_sys->lock_es );
584
585     if( p_sys->p_httpd_file )
586         httpd_FileDelete( p_sys->p_httpd_file );
587
588     if( p_sys->p_httpd_host )
589         httpd_HostDelete( p_sys->p_httpd_host );
590
591     free( p_sys->psz_sdp );
592
593     if( p_sys->b_export_sdp_file )
594     {
595 #ifdef HAVE_UNISTD_H
596         unlink( p_sys->psz_sdp_file );
597 #endif
598         free( p_sys->psz_sdp_file );
599     }
600     free( p_sys->psz_destination );
601     free( p_sys );
602 }
603
604 /*****************************************************************************
605  * SDPHandleUrl:
606  *****************************************************************************/
607 static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
608 {
609     sout_stream_sys_t *p_sys = p_stream->p_sys;
610     vlc_url_t url;
611
612     vlc_UrlParse( &url, psz_url, 0 );
613     if( url.psz_protocol && !strcasecmp( url.psz_protocol, "http" ) )
614     {
615         if( p_sys->p_httpd_file )
616         {
617             msg_Err( p_stream, "you can use sdp=http:// only once" );
618             goto out;
619         }
620
621         if( HttpSetup( p_stream, &url ) )
622         {
623             msg_Err( p_stream, "cannot export SDP as HTTP" );
624         }
625     }
626     else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "rtsp" ) )
627     {
628         if( p_sys->rtsp != NULL )
629         {
630             msg_Err( p_stream, "you can use sdp=rtsp:// only once" );
631             goto out;
632         }
633
634         /* FIXME test if destination is multicast or no destination at all */
635         p_sys->rtsp = RtspSetup( p_stream, &url );
636         if( p_sys->rtsp == NULL )
637         {
638             msg_Err( p_stream, "cannot export SDP as RTSP" );
639         }
640
641         if( p_sys->p_mux != NULL )
642         {
643             sout_stream_id_t *id = p_sys->es[0];
644             id->rtsp_id = RtspAddId( p_sys->rtsp, id, 0, GetDWBE( id->ssrc ),
645                                      p_sys->psz_destination, p_sys->i_ttl,
646                                      id->i_port, id->i_port + 1 );
647         }
648     }
649     else if( ( url.psz_protocol && !strcasecmp( url.psz_protocol, "sap" ) ) ||
650              ( url.psz_host && !strcasecmp( url.psz_host, "sap" ) ) )
651     {
652         p_sys->b_export_sap = true;
653         SapSetup( p_stream );
654     }
655     else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "file" ) )
656     {
657         if( p_sys->b_export_sdp_file )
658         {
659             msg_Err( p_stream, "you can use sdp=file:// only once" );
660             goto out;
661         }
662         p_sys->b_export_sdp_file = true;
663         psz_url = &psz_url[5];
664         if( psz_url[0] == '/' && psz_url[1] == '/' )
665             psz_url += 2;
666         p_sys->psz_sdp_file = strdup( psz_url );
667     }
668     else
669     {
670         msg_Warn( p_stream, "unknown protocol for SDP (%s)",
671                   url.psz_protocol );
672     }
673
674 out:
675     vlc_UrlClean( &url );
676 }
677
678 /*****************************************************************************
679  * SDPGenerate
680  *****************************************************************************/
681 /*static*/
682 char *SDPGenerate( const sout_stream_t *p_stream, const char *rtsp_url )
683 {
684     const sout_stream_sys_t *p_sys = p_stream->p_sys;
685     char *psz_sdp;
686     struct sockaddr_storage dst;
687     socklen_t dstlen;
688     int i;
689     /*
690      * When we have a fixed destination (typically when we do multicast),
691      * we need to put the actual port numbers in the SDP.
692      * When there is no fixed destination, we only support RTSP unicast
693      * on-demand setup, so we should rather let the clients decide which ports
694      * to use.
695      * When there is both a fixed destination and RTSP unicast, we need to
696      * put port numbers used by the fixed destination, otherwise the SDP would
697      * become totally incorrect for multicast use. It should be noted that
698      * port numbers from SDP with RTSP are only "recommendation" from the
699      * server to the clients (per RFC2326), so only broken clients will fail
700      * to handle this properly. There is no solution but to use two differents
701      * output chain with two different RTSP URLs if you need to handle this
702      * scenario.
703      */
704     int inclport;
705
706     if( p_sys->psz_destination != NULL )
707     {
708         inclport = 1;
709
710         /* Oh boy, this is really ugly! (+ race condition on lock_es) */
711         dstlen = sizeof( dst );
712         if( p_sys->es[0]->listen_fd != NULL )
713             getsockname( p_sys->es[0]->listen_fd[0],
714                          (struct sockaddr *)&dst, &dstlen );
715         else
716             getpeername( p_sys->es[0]->sinkv[0].rtp_fd,
717                          (struct sockaddr *)&dst, &dstlen );
718     }
719     else
720     {
721         inclport = 0;
722
723         /* Dummy destination address for RTSP */
724         memset (&dst, 0, sizeof( struct sockaddr_in ) );
725         dst.ss_family = AF_INET;
726 #ifdef HAVE_SA_LEN
727         dst.ss_len =
728 #endif
729         dstlen = sizeof( struct sockaddr_in );
730     }
731
732     psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_stream ), SOUT_CFG_PREFIX,
733                              NULL, 0, (struct sockaddr *)&dst, dstlen );
734     if( psz_sdp == NULL )
735         return NULL;
736
737     /* TODO: a=source-filter */
738     if( p_sys->rtcp_mux )
739         sdp_AddAttribute( &psz_sdp, "rtcp-mux", NULL );
740
741     if( rtsp_url != NULL )
742         sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );
743
744     /* FIXME: locking?! */
745     for( i = 0; i < p_sys->i_es; i++ )
746     {
747         sout_stream_id_t *id = p_sys->es[i];
748         const char *mime_major; /* major MIME type */
749         const char *proto = "RTP/AVP"; /* protocol */
750
751         switch( id->i_cat )
752         {
753             case VIDEO_ES:
754                 mime_major = "video";
755                 break;
756             case AUDIO_ES:
757                 mime_major = "audio";
758                 break;
759             case SPU_ES:
760                 mime_major = "text";
761                 break;
762             default:
763                 continue;
764         }
765
766         if( rtsp_url == NULL )
767         {
768             switch( p_sys->proto )
769             {
770                 case IPPROTO_UDP:
771                     break;
772                 case IPPROTO_TCP:
773                     proto = "TCP/RTP/AVP";
774                     break;
775                 case IPPROTO_DCCP:
776                     proto = "DCCP/RTP/AVP";
777                     break;
778                 case IPPROTO_UDPLITE:
779                     continue;
780             }
781         }
782
783         sdp_AddMedia( &psz_sdp, mime_major, proto, inclport * id->i_port,
784                       id->i_payload_type, false, id->i_bitrate,
785                       id->psz_enc, id->i_clock_rate, id->i_channels,
786                       id->psz_fmtp);
787
788         if( rtsp_url != NULL )
789         {
790             assert( strlen( rtsp_url ) > 0 );
791             bool addslash = ( rtsp_url[strlen( rtsp_url ) - 1] != '/' );
792             sdp_AddAttribute ( &psz_sdp, "control",
793                                addslash ? "%s/trackID=%u" : "%strackID=%u",
794                                rtsp_url, i );
795         }
796         else
797         {
798             if( id->listen_fd != NULL )
799                 sdp_AddAttribute( &psz_sdp, "setup", "passive" );
800             if( p_sys->proto == IPPROTO_DCCP )
801                 sdp_AddAttribute( &psz_sdp, "dccp-service-code",
802                                   "SC:RTP%c", toupper( mime_major[0] ) );
803         }
804     }
805
806     return psz_sdp;
807 }
808
809 /*****************************************************************************
810  * RTP mux
811  *****************************************************************************/
812
813 static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
814 {
815     static const char hex[16] = "0123456789abcdef";
816     int i;
817
818     for( i = 0; i < i_data; i++ )
819     {
820         s[2*i+0] = hex[(p_data[i]>>4)&0xf];
821         s[2*i+1] = hex[(p_data[i]   )&0xf];
822     }
823     s[2*i_data] = '\0';
824 }
825
826 /**
827  * Shrink the MTU down to a fixed packetization time (for audio).
828  */
829 static void
830 rtp_set_ptime (sout_stream_id_t *id, unsigned ptime_ms, size_t bytes)
831 {
832     /* Samples per second */
833     size_t spl = (id->i_clock_rate - 1) * ptime_ms / 1000 + 1;
834     bytes *= id->i_channels;
835     spl *= bytes;
836
837     if (spl < rtp_mtu (id)) /* MTU is big enough for ptime */
838         id->i_mtu = 12 + spl;
839     else /* MTU is too small for ptime, align to a sample boundary */
840         id->i_mtu = 12 + (((id->i_mtu - 12) / bytes) * bytes);
841 }
842
843 /** Add an ES as a new RTP stream */
844 static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
845 {
846     /* NOTE: As a special case, if we use a non-RTP
847      * mux (TS/PS), then p_fmt is NULL. */
848     sout_stream_sys_t *p_sys = p_stream->p_sys;
849     sout_stream_id_t  *id;
850     int               i_port, cscov = -1;
851     char              *psz_sdp;
852
853     if (0xffffffff == p_sys->payload_bitmap)
854     {
855         msg_Err (p_stream, "too many RTP elementary streams");
856         return NULL;
857     }
858
859     id = vlc_object_create( p_stream, sizeof( sout_stream_id_t ) );
860     if( id == NULL )
861         return NULL;
862     vlc_object_attach( id, p_stream );
863
864     /* Choose the port */
865     i_port = 0;
866     if( p_fmt == NULL )
867         ;
868     else
869     if( p_fmt->i_cat == AUDIO_ES && p_sys->i_port_audio > 0 )
870     {
871         i_port = p_sys->i_port_audio;
872         p_sys->i_port_audio = 0;
873     }
874     else
875     if( p_fmt->i_cat == VIDEO_ES && p_sys->i_port_video > 0 )
876     {
877         i_port = p_sys->i_port_video;
878         p_sys->i_port_video = 0;
879     }
880
881     while( i_port == 0 )
882     {
883         if( p_sys->i_port != p_sys->i_port_audio
884          && p_sys->i_port != p_sys->i_port_video )
885         {
886             i_port = p_sys->i_port;
887             p_sys->i_port += 2;
888             break;
889         }
890         p_sys->i_port += 2;
891     }
892
893     id->p_stream   = p_stream;
894
895     id->i_sequence = rand()&0x