rtp.c 54.7 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
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 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
27

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
34 35
#include <vlc_sout.h>
#include <vlc_block.h>
36

Clément Stenac's avatar
Clément Stenac committed
37 38 39 40
#include <vlc_httpd.h>
#include <vlc_url.h>
#include <vlc_network.h>
#include <vlc_charset.h>
41
#include <vlc_strings.h>
42
#include <srtp.h>
43

44 45
#include "rtp.h"

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
46
#ifdef HAVE_UNISTD_H
47
#   include <sys/types.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
48
#   include <unistd.h>
49 50
#   include <fcntl.h>
#   include <sys/stat.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
#endif
52 53 54 55 56 57 58 59 60
#ifdef HAVE_LINUX_DCCP_H
#   include <linux/dccp.h>
#endif
#ifndef IPPROTO_DCCP
# define IPPROTO_DCCP 33
#endif
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
61 62 63

#include <errno.h>

64 65
#include <assert.h>

66 67 68
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
69

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
70 71
#define DEST_TEXT N_("Destination")
#define DEST_LONGTEXT N_( \
72
    "This is the output URL that will be used." )
73 74
#define SDP_TEXT N_("SDP")
#define SDP_LONGTEXT N_( \
75 76 77 78
    "This allows you to specify how the SDP (Session Descriptor) for this RTP "\
    "session will be made available. You must use an url: http://location to " \
    "access the SDP via HTTP, rtsp://location for RTSP access, and sap:// " \
    "for the SDP to be announced via SAP." )
79 80
#define SAP_TEXT N_("SAP announcing")
#define SAP_LONGTEXT N_("Announce this session with SAP.")
81 82
#define MUX_TEXT N_("Muxer")
#define MUX_LONGTEXT N_( \
83 84
    "This allows you to specify the muxer used for the streaming output. " \
    "Default is to use no muxer (standard RTP stream)." )
85 86 87

#define NAME_TEXT N_("Session name")
#define NAME_LONGTEXT N_( \
88 89
    "This is the name of the session that will be announced in the SDP " \
    "(Session Descriptor)." )
90
#define DESC_TEXT N_("Session description")
91
#define DESC_LONGTEXT N_( \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
92 93
    "This allows you to give a short description with details about the stream, " \
    "that will be announced in the SDP (Session Descriptor)." )
94 95
#define URL_TEXT N_("Session URL")
#define URL_LONGTEXT N_( \
96 97 98
    "This allows you to give an URL with more details about the stream " \
    "(often the website of the streaming organization), that will " \
    "be announced in the SDP (Session Descriptor)." )
99 100
#define EMAIL_TEXT N_("Session email")
#define EMAIL_LONGTEXT N_( \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
101 102 103 104 105 106 107
    "This allows you to give a contact mail address for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )
#define PHONE_TEXT N_("Session phone number")
#define PHONE_LONGTEXT N_( \
    "This allows you to give a contact telephone number for the stream, that will " \
    "be announced in the SDP (Session Descriptor)." )

108 109
#define PORT_TEXT N_("Port")
#define PORT_LONGTEXT N_( \
110
    "This allows you to specify the base port for the RTP streaming." )
111 112
#define PORT_AUDIO_TEXT N_("Audio port")
#define PORT_AUDIO_LONGTEXT N_( \
113
    "This allows you to specify the default audio port for the RTP streaming." )
114 115
#define PORT_VIDEO_TEXT N_("Video port")
#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 " \
Sebastien Escudier's avatar
Sebastien Escudier committed
121
    "the multicast packets sent by the stream output (-1 = use operating " \
122
    "system built-in default).")
123

124 125 126 127 128
#define RTCP_MUX_TEXT N_("RTP/RTCP multiplexing")
#define RTCP_MUX_LONGTEXT N_( \
    "This sends and receives RTCP packet multiplexed over the same port " \
    "as RTP packets." )

129 130 131 132
#define PROTO_TEXT N_("Transport protocol")
#define PROTO_LONGTEXT N_( \
    "This selects which transport protocol to use for RTP." )

133 134 135 136 137 138 139 140 141
#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.")

142 143 144 145 146 147 148
static const char *const ppsz_protos[] = {
    "dccp", "sctp", "tcp", "udp", "udplite",
};

static const char *const ppsz_protocols[] = {
    "DCCP", "SCTP", "TCP", "UDP", "UDP-Lite",
};
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 155 156
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

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 164
    set_capability( "sout stream", 0 );
    add_shortcut( "rtp" );
Clément Stenac's avatar
Clément Stenac committed
165 166
    set_category( CAT_SOUT );
    set_subcategory( SUBCAT_SOUT_STREAM );
167

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
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 175
    add_bool( SOUT_CFG_PREFIX "sap", false, NULL, SAP_TEXT, SAP_LONGTEXT,
              true );
176

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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

Sebastien Escudier's avatar
Sebastien Escudier committed
198
    add_integer( SOUT_CFG_PREFIX "ttl", -1, NULL, TTL_TEXT,
199 200 201
                 TTL_LONGTEXT, true );
    add_bool( SOUT_CFG_PREFIX "rtcp-mux", false, NULL,
              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false );
202

203 204 205 206 207
    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 );

208
    add_bool( SOUT_CFG_PREFIX "mp4a-latm", 0, NULL, RFC3016_TEXT,
209
                 RFC3016_LONGTEXT, false );
210

211 212 213 214 215 216
    set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
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 * );
Gildas Bazin's avatar
 
Gildas Bazin committed
226
static int               Send( sout_stream_t *, sout_stream_id_t *,
227
                               block_t* );
228 229 230 231
static sout_stream_id_t *MuxAdd ( sout_stream_t *, es_format_t * );
static int               MuxDel ( sout_stream_t *, sout_stream_id_t * );
static int               MuxSend( sout_stream_t *, sout_stream_id_t *,
                                  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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
240
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );
241

242 243
struct sout_stream_sys_t
{
244
    /* SDP */
245 246 247
    char    *psz_sdp;
    vlc_mutex_t  lock_sdp;

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 255
    session_descriptor_t *p_session;

256
    /* SDP via HTTP */
257 258 259
    httpd_host_t *p_httpd_host;
    httpd_file_t *p_httpd_file;

260
    /* RTSP */
261 262 263
    rtsp_stream_t *rtsp;

    /* */
264
    char     *psz_destination;
265
    uint32_t  payload_bitmap;
266 267 268
    uint16_t  i_port;
    uint16_t  i_port_audio;
    uint16_t  i_port_video;
269 270 271 272
    uint8_t   proto;
    bool      rtcp_mux;
    int       i_ttl:9;
    bool      b_latm;
273 274 275 276 277 278 279 280 281 282 283 284

    /* in case we do TS/PS over rtp */
    sout_mux_t        *p_mux;
    sout_access_out_t *p_grab;
    block_t           *packet;

    /* */
    vlc_mutex_t      lock_es;
    int              i_es;
    sout_stream_id_t **es;
};

285
typedef int (*pf_rtp_packetizer_t)( sout_stream_id_t *, block_t * );
286

287 288 289
typedef struct rtp_sink_t
{
    int rtp_fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
290
    rtcp_sender_t *rtcp;
291 292
} rtp_sink_t;

293 294
struct sout_stream_id_t
{
295 296
    VLC_COMMON_MEMBERS

297 298
    sout_stream_t *p_stream;
    /* rtp field */
299 300
    uint16_t    i_sequence;
    uint8_t     i_payload_type;
301 302 303
    uint8_t     ssrc[4];

    /* for sdp */
304
    const char  *psz_enc;
305
    char        *psz_fmtp;
306 307 308
    int          i_clock_rate;
    int          i_port;
    int          i_cat;
309
    int          i_channels;
310
    int          i_bitrate;
311 312

    /* Packetizer specific fields */
313 314
    int                 i_mtu;
    srtp_session_t     *srtp;
315 316
    pf_rtp_packetizer_t pf_packetize;

317
    /* Packets sinks */
318
    vlc_mutex_t       lock_sink;
319 320
    int               sinkc;
    rtp_sink_t       *sinkv;
321
    rtsp_stream_id_t *rtsp_id;
322
    int              *listen_fd;
323 324 325

    block_fifo_t     *p_fifo;
    int64_t           i_caching;
326 327
};

328 329 330 331 332 333
/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    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 341
    config_ChainParse( p_stream, SOUT_CFG_PREFIX,
                       ppsz_sout_options, p_stream->p_cfg );
342 343

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344 345
    if( p_sys == NULL )
        return VLC_ENOMEM;
346

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
347
    p_sys->psz_destination = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "dst" );
348

349 350 351
    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" );
352
    p_sys->rtcp_mux   = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
353

354 355
    p_sys->psz_sdp_file = NULL;

356 357 358 359 360 361
    if( p_sys->i_port_audio == p_sys->i_port_video )
    {
        msg_Err( p_stream, "audio and video port cannot be the same" );
        p_sys->i_port_audio = 0;
        p_sys->i_port_video = 0;
    }
362

363
    for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
364
    {
365 366 367
        if( !strcmp( p_cfg->psz_name, "sdp" )
         && ( p_cfg->psz_value != NULL )
         && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
368
        {
369
            b_rtsp = true;
370
            break;
371
        }
372 373 374
    }
    if( !b_rtsp )
    {
375 376 377
        psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
        if( psz != NULL )
        {
378
            if( !strncasecmp( psz, "rtsp:", 5 ) )
379
                b_rtsp = true;
380 381
            free( psz );
        }
382
    }
383

384 385
    /* Transport protocol */
    p_sys->proto = IPPROTO_UDP;
386
    psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");
387

388 389 390 391
    if ((psz == NULL) || !strcasecmp (psz, "udp"))
        (void)0; /* default */
    else
    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 404
#endif
#if 0
405
    else
406 407 408
    if (!strcasecmp (psz, "tcp"))
    {
        p_sys->proto = IPPROTO_TCP;
409
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
410
    }
411
#endif
412 413
    else
    if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
414
        p_sys->proto = IPPROTO_UDPLITE;
415 416 417 418
    else
        msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
                  psz);
    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 425 426
        free( p_sys );
        return VLC_EGENERIC;
    }
427

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
428
    p_sys->i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
Sebastien Escudier's avatar
Sebastien Escudier committed
429
    if( p_sys->i_ttl == -1 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
430 431 432 433 434
    {
        /* 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
         * we ask the core. FIXME: broken when neither sout-rtp-ttl nor
         * ttl are set. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435
        p_sys->i_ttl = config_GetInt( p_stream, "ttl" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
436 437
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
438
    p_sys->b_latm = var_GetBool( p_stream, SOUT_CFG_PREFIX "mp4a-latm" );
439

440
    p_sys->payload_bitmap = 0;
441 442
    p_sys->i_es = 0;
    p_sys->es   = NULL;
443
    p_sys->rtsp = NULL;
444 445
    p_sys->psz_sdp = NULL;

446 447
    p_sys->b_export_sap = false;
    p_sys->b_export_sdp_file = false;
448 449
    p_sys->p_session = NULL;

450 451
    p_sys->p_httpd_host = NULL;
    p_sys->p_httpd_file = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
452 453

    p_stream->p_sys     = p_sys;
454

455 456
    vlc_mutex_init( &p_sys->lock_sdp );
    vlc_mutex_init( &p_sys->lock_es );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
457

458 459
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
    if( psz != NULL )
460
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
461
        sout_stream_id_t *id;
462 463

        /* Check muxer type */
464 465 466
        if( strncasecmp( psz, "ps", 2 )
         && strncasecmp( psz, "mpeg1", 5 )
         && strncasecmp( psz, "ts", 2 ) )
467
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
468
            msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
469
            free( psz );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
470 471
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
472
            free( p_sys );
473 474 475
            return VLC_EGENERIC;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
476 477 478
        p_sys->p_grab = GrabberCreate( p_stream );
        p_sys->p_mux = sout_MuxNew( p_sout, psz, p_sys->p_grab );
        free( psz );
479

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
480
        if( p_sys->p_mux == NULL )
481
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
482 483 484 485 486 487
            msg_Err( p_stream, "cannot create muxer" );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
            free( p_sys );
            return VLC_EGENERIC;
488
        }
489

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
490 491
        id = Add( p_stream, NULL );
        if( id == NULL )
492
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
493
            sout_MuxDelete( p_sys->p_mux );
494
            sout_AccessOutDelete( p_sys->p_grab );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
495 496
            vlc_mutex_destroy( &p_sys->lock_sdp );
            vlc_mutex_destroy( &p_sys->lock_es );
497 498 499 500 501
            free( p_sys );
            return VLC_EGENERIC;
        }

        p_sys->packet = NULL;
502

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
503 504 505
        p_stream->pf_add  = MuxAdd;
        p_stream->pf_del  = MuxDel;
        p_stream->pf_send = MuxSend;
506 507 508
    }
    else
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
509 510 511
        p_sys->p_mux    = NULL;
        p_sys->p_grab   = NULL;

512 513 514
        p_stream->pf_add    = Add;
        p_stream->pf_del    = Del;
        p_stream->pf_send   = Send;
515
    }
516

517 518 519
    if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
        SDPHandleUrl( p_stream, "sap" );

520 521
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
    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 )
Laurent Aimar's avatar
Laurent Aimar committed
528
        {
529
            if( !strcmp( p_cfg->psz_name, "sdp" ) )
530
            {
531 532 533
                if( p_cfg->psz_value == NULL || *p_cfg->psz_value == '\0' )
                    continue;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
534
                /* needed both :sout-rtp-sdp= and rtp{sdp=} can be used */
535
                if( !strcmp( p_cfg->psz_value, psz ) )
536 537 538
                    continue;

                SDPHandleUrl( p_stream, p_cfg->psz_value );
539 540
            }
        }
541
        free( psz );
542 543
    }

544 545 546
    /* update p_sout->i_out_pace_nocontrol */
    p_stream->p_sout->i_out_pace_nocontrol++;

547 548 549 550 551 552 553 554 555 556 557
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    sout_stream_t     *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t *p_sys = p_stream->p_sys;

558 559 560
    /* update p_sout->i_out_pace_nocontrol */
    p_stream->p_sout->i_out_pace_nocontrol--;

561 562
    if( p_sys->p_mux )
    {
563 564 565
        assert( p_sys->i_es == 1 );
        Del( p_stream, p_sys->es[0] );

566 567 568 569
        sout_MuxDelete( p_sys->p_mux );
        sout_AccessOutDelete( p_sys->p_grab );
        if( p_sys->packet )
        {
570
            block_Release( p_sys->packet );
571
        }
572
        if( p_sys->b_export_sap )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
573
        {
574 575 576
            p_sys->p_mux = NULL;
            SapSetup( p_stream );
        }
577 578
    }

579 580
    if( p_sys->rtsp != NULL )
        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 )
Laurent Aimar's avatar
Laurent Aimar committed
586
        httpd_FileDelete( p_sys->p_httpd_file );
Laurent Aimar's avatar
Laurent Aimar committed
587

588
    if( p_sys->p_httpd_host )
Laurent Aimar's avatar
Laurent Aimar committed
589
        httpd_HostDelete( p_sys->p_httpd_host );
Laurent Aimar's avatar
Laurent Aimar committed
590

591
    free( p_sys->psz_sdp );
Laurent Aimar's avatar
Laurent Aimar committed
592

Sam Hocevar's avatar
Sam Hocevar committed
593 594 595 596 597 598 599
    if( p_sys->b_export_sdp_file )
    {
#ifdef HAVE_UNISTD_H
        unlink( p_sys->psz_sdp_file );
#endif
        free( p_sys->psz_sdp_file );
    }
600
    free( p_sys->psz_destination );
601 602 603
    free( p_sys );
}

604 605 606
/*****************************************************************************
 * SDPHandleUrl:
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
607
static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
608 609 610 611 612 613 614 615 616
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    vlc_url_t url;

    vlc_UrlParse( &url, psz_url, 0 );
    if( url.psz_protocol && !strcasecmp( url.psz_protocol, "http" ) )
    {
        if( p_sys->p_httpd_file )
        {
617
            msg_Err( p_stream, "you can use sdp=http:// only once" );
618
            goto out;
619 620 621 622
        }

        if( HttpSetup( p_stream, &url ) )
        {
623
            msg_Err( p_stream, "cannot export SDP as HTTP" );
624 625 626 627
        }
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "rtsp" ) )
    {
628
        if( p_sys->rtsp != NULL )
629
        {
630
            msg_Err( p_stream, "you can use sdp=rtsp:// only once" );
631
            goto out;
632 633
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
634
        /* FIXME test if destination is multicast or no destination at all */
635 636
        p_sys->rtsp = RtspSetup( p_stream, &url );
        if( p_sys->rtsp == NULL )
637
        {
638
            msg_Err( p_stream, "cannot export SDP as RTSP" );
639
        }
640 641 642 643

        if( p_sys->p_mux != NULL )
        {
            sout_stream_id_t *id = p_sys->es[0];
644
            id->rtsp_id = RtspAddId( p_sys->rtsp, id, 0, GetDWBE( id->ssrc ),
645 646 647
                                     p_sys->psz_destination, p_sys->i_ttl,
                                     id->i_port, id->i_port + 1 );
        }
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 654 655 656 657 658
        SapSetup( p_stream );
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "file" ) )
    {
        if( p_sys->b_export_sdp_file )
        {
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 664 665 666 667 668 669 670 671 672
        psz_url = &psz_url[5];
        if( psz_url[0] == '/' && psz_url[1] == '/' )
            psz_url += 2;
        p_sys->psz_sdp_file = strdup( psz_url );
    }
    else
    {
        msg_Warn( p_stream, "unknown protocol for SDP (%s)",
                  url.psz_protocol );
    }
673 674

out:
675 676 677
    vlc_UrlClean( &url );
}

678
/*****************************************************************************
679
 * SDPGenerate
680
 *****************************************************************************/
681 682
/*static*/
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;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
685 686 687
    char *psz_sdp;
    struct sockaddr_storage dst;
    socklen_t dstlen;
688
    int i;
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    /*
     * When we have a fixed destination (typically when we do multicast),
     * we need to put the actual port numbers in the SDP.
     * When there is no fixed destination, we only support RTSP unicast
     * on-demand setup, so we should rather let the clients decide which ports
     * to use.
     * When there is both a fixed destination and RTSP unicast, we need to
     * put port numbers used by the fixed destination, otherwise the SDP would
     * become totally incorrect for multicast use. It should be noted that
     * port numbers from SDP with RTSP are only "recommendation" from the
     * server to the clients (per RFC2326), so only broken clients will fail
     * to handle this properly. There is no solution but to use two differents
     * output chain with two different RTSP URLs if you need to handle this
     * scenario.
     */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
704 705 706
    int inclport;

    if( p_sys->psz_destination != NULL )
707
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
708
        inclport = 1;
709

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
710 711
        /* Oh boy, this is really ugly! (+ race condition on lock_es) */
        dstlen = sizeof( dst );
712 713 714 715 716 717
        if( p_sys->es[0]->listen_fd != NULL )
            getsockname( p_sys->es[0]->listen_fd[0],
                         (struct sockaddr *)&dst, &dstlen );
        else
            getpeername( p_sys->es[0]->sinkv[0].rtp_fd,
                         (struct sockaddr *)&dst, &dstlen );
718
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
719
    else
720
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
721 722 723 724 725 726 727 728 729
        inclport = 0;

        /* Dummy destination address for RTSP */
        memset (&dst, 0, sizeof( struct sockaddr_in ) );
        dst.ss_family = AF_INET;
#ifdef HAVE_SA_LEN
        dst.ss_len =
#endif
        dstlen = sizeof( struct sockaddr_in );
730
    }
731

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
732 733
    psz_sdp = vlc_sdp_Start( VLC_OBJECT( p_stream ), SOUT_CFG_PREFIX,
                             NULL, 0, (struct sockaddr *)&dst, dstlen );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
734 735 736 737
    if( psz_sdp == NULL )
        return NULL;

    /* TODO: a=source-filter */
738 739
    if( p_sys->rtcp_mux )
        sdp_AddAttribute( &psz_sdp, "rtcp-mux", NULL );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
740

741 742 743
    if( rtsp_url != NULL )
        sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
744
    /* FIXME: locking?! */
745 746 747
    for( i = 0; i < p_sys->i_es; i++ )
    {
        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 752 753 754 755 756 757 758 759 760 761 762
        switch( id->i_cat )
        {
            case VIDEO_ES:
                mime_major = "video";
                break;
            case AUDIO_ES:
                mime_major = "audio";
                break;
            case SPU_ES:
                mime_major = "text";
                break;
            default:
763
                continue;
764
        }
765

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
        if( rtsp_url == NULL )
        {
            switch( p_sys->proto )
            {
                case IPPROTO_UDP:
                    break;
                case IPPROTO_TCP:
                    proto = "TCP/RTP/AVP";
                    break;
                case IPPROTO_DCCP:
                    proto = "DCCP/RTP/AVP";
                    break;
                case IPPROTO_UDPLITE:
                    continue;
            }
        }

        sdp_AddMedia( &psz_sdp, mime_major, proto, inclport * id->i_port,
784
                      id->i_payload_type, false, id->i_bitrate,
785 786
                      id->psz_enc, id->i_clock_rate, id->i_channels,
                      id->psz_fmtp);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
787

788
        if( rtsp_url != NULL )
789 790
        {
            assert( strlen( rtsp_url ) > 0 );
791
            bool addslash = ( rtsp_url[strlen( rtsp_url ) - 1] != '/' );
792 793
            sdp_AddAttribute ( &psz_sdp, "control",
                               addslash ? "%s/trackID=%u" : "%strackID=%u",
794
                               rtsp_url, i );
795
        }
796 797 798 799 800
        else
        {
            if( id->listen_fd != NULL )
                sdp_AddAttribute( &psz_sdp, "setup", "passive" );
            if( p_sys->proto == IPPROTO_DCCP )
Sebastien Escudier's avatar
Sebastien Escudier committed
801
                sdp_AddAttribute( &psz_sdp, "dccp-service-code",
802
                                  "SC:RTP%c", toupper( mime_major[0] ) );
803
        }
804 805
    }

Laurent Aimar's avatar
Laurent Aimar committed
806
    return psz_sdp;
807 808 809
}

/*****************************************************************************
810
 * RTP mux
811 812 813 814
 *****************************************************************************/

static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
{
815
    static const char hex[16] = "0123456789abcdef";
816 817 818 819
    int i;

    for( i = 0; i < i_data; i++ )
    {
820
        s[2*i+0] = hex[(p_data[i]>>4)&0xf];
821 822 823 824
        s[2*i+1] = hex[(p_data[i]   )&0xf];
    }
    s[2*i_data] = '\0';
}
825