rtp.c 52.4 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * rtp.c: rtp stream output module
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004, 2010 the VideoLAN team
5
 * Copyright © 2007-2008 Rémi Denis-Courmont
6
7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Pierre Ynard
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * 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
dionoea's avatar
dionoea committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
24
25
26
27
 *****************************************************************************/

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

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

33
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
36
37
#include <vlc_sout.h>
#include <vlc_block.h>
38

zorglub's avatar
zorglub committed
39
40
41
#include <vlc_httpd.h>
#include <vlc_url.h>
#include <vlc_network.h>
42
#include <vlc_fs.h>
43
#include <vlc_queue.h>
44
#include <vlc_rand.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
45
#include <vlc_memstream.h>
46
47
#ifdef HAVE_SRTP
# include <srtp.h>
48
49
# include <gcrypt.h>
# include <vlc_gcrypt.h>
50
#endif
51

52
#include "rtp.h"
53
#include "sdp_helper.h"
54

55
56
#include <sys/types.h>
#include <unistd.h>
57
58
59
#ifdef HAVE_ARPA_INET_H
#   include <arpa/inet.h>
#endif
60
61
62
63
64
65
66
67
68
#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
69

70
#include <ctype.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
71
#include <errno.h>
72
73
#include <assert.h>

74
75
76
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
77

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

#define NAME_TEXT N_("Session name")
#define NAME_LONGTEXT N_( \
96
97
    "This is the name of the session that will be announced in the SDP " \
    "(Session Descriptor)." )
98
99
100
101
#define CAT_TEXT N_("Session category")
#define CAT_LONGTEXT N_( \
  "This allows you to specify a category for the session, " \
  "that will be announced if you choose to use SAP." )
102
#define DESC_TEXT N_("Session description")
hartman's avatar
hartman committed
103
#define DESC_LONGTEXT N_( \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
104
105
    "This allows you to give a short description with details about the stream, " \
    "that will be announced in the SDP (Session Descriptor)." )
106
107
#define PORT_TEXT N_("Port")
#define PORT_LONGTEXT N_( \
108
    "This allows you to specify the base port for the RTP streaming." )
109
110
#define PORT_AUDIO_TEXT N_("Audio port")
#define PORT_AUDIO_LONGTEXT N_( \
111
    "This allows you to specify the default audio port for the RTP streaming." )
112
113
#define PORT_VIDEO_TEXT N_("Video port")
#define PORT_VIDEO_LONGTEXT N_( \
114
    "This allows you to specify the default video port for the RTP streaming." )
115

116
#define TTL_TEXT N_("Hop limit (TTL)")
117
#define TTL_LONGTEXT N_( \
118
    "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
sebastien's avatar
sebastien committed
119
    "the multicast packets sent by the stream output (-1 = use operating " \
120
    "system built-in default).")
121

122
123
124
125
126
#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." )

127
128
129
130
131
#define CACHING_TEXT N_("Caching value (ms)")
#define CACHING_LONGTEXT N_( \
    "Default caching value for outbound RTP streams. This " \
    "value should be set in milliseconds." )

132
133
134
135
#define PROTO_TEXT N_("Transport protocol")
#define PROTO_LONGTEXT N_( \
    "This selects which transport protocol to use for RTP." )

136
137
138
#define SRTP_KEY_TEXT N_("SRTP key (hexadecimal)")
#define SRTP_KEY_LONGTEXT N_( \
    "RTP packets will be integrity-protected and ciphered "\
139
140
    "with this Secure RTP master shared secret key. "\
    "This must be a 32-character-long hexadecimal string.")
141
142
143

#define SRTP_SALT_TEXT N_("SRTP salt (hexadecimal)")
#define SRTP_SALT_LONGTEXT N_( \
144
145
    "Secure RTP requires a (non-secret) master salt value. " \
    "This must be a 28-character-long hexadecimal string.")
146

147
148
149
150
151
152
153
static const char *const ppsz_protos[] = {
    "dccp", "sctp", "tcp", "udp", "udplite",
};

static const char *const ppsz_protocols[] = {
    "DCCP", "SCTP", "TCP", "UDP", "UDP-Lite",
};
154

155
#define RFC3016_TEXT N_("MP4A LATM")
156
#define RFC3016_LONGTEXT N_( \
157
    "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )
158

159
160
161
162
163
164
#define RTSP_TIMEOUT_TEXT N_( "RTSP session timeout (s)" )
#define RTSP_TIMEOUT_LONGTEXT N_( "RTSP sessions will be closed after " \
    "not receiving any RTSP request for this long. Setting it to a " \
    "negative value or zero disables timeouts. The default is 60 (one " \
    "minute)." )

Pierre Ynard's avatar
Pierre Ynard committed
165
#define RTSP_USER_TEXT N_("Username")
166
#define RTSP_USER_LONGTEXT N_("Username that will be " \
Pierre Ynard's avatar
Pierre Ynard committed
167
168
169
170
171
                              "requested to access the stream." )
#define RTSP_PASS_TEXT N_("Password")
#define RTSP_PASS_LONGTEXT N_("Password that will be " \
                              "requested to access the stream." )

172
173
174
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

175
#define SOUT_CFG_PREFIX "sout-rtp-"
176
#define MAX_EMPTY_BLOCKS 200
177

178
179
180
vlc_module_begin ()
    set_shortname( N_("RTP"))
    set_description( N_("RTP stream output") )
181
    set_capability( "sout output", 0 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
182
    add_shortcut( "rtp" )
183
    set_subcategory( SUBCAT_SOUT_STREAM )
184

185
    add_string( SOUT_CFG_PREFIX "dst", "", DEST_TEXT,
186
                DEST_LONGTEXT )
187
    add_string( SOUT_CFG_PREFIX "sdp", "", SDP_TEXT,
188
                SDP_LONGTEXT )
189
    add_string( SOUT_CFG_PREFIX "mux", "", MUX_TEXT,
190
191
                MUX_LONGTEXT )
    add_bool( SOUT_CFG_PREFIX "sap", false, SAP_TEXT, SAP_LONGTEXT )
192

193
    add_string( SOUT_CFG_PREFIX "name", "", NAME_TEXT,
194
195
                NAME_LONGTEXT )
    add_string( SOUT_CFG_PREFIX "cat", "", CAT_TEXT, CAT_LONGTEXT )
196
    add_string( SOUT_CFG_PREFIX "description", "", DESC_TEXT,
197
                DESC_LONGTEXT )
198
199
    add_obsolete_string( SOUT_CFG_PREFIX "url" ) /* since 4.0.0 */
    add_obsolete_string( SOUT_CFG_PREFIX "email" ) /* since 4.0.0 */
200
    add_obsolete_string( SOUT_CFG_PREFIX "phone" ) /* since 3.0.0 */
201

202
    add_string( SOUT_CFG_PREFIX "proto", "udp", PROTO_TEXT,
203
                PROTO_LONGTEXT )
204
        change_string_list( ppsz_protos, ppsz_protocols )
205
    add_integer( SOUT_CFG_PREFIX "port", 5004, PORT_TEXT,
206
                 PORT_LONGTEXT )
207
    add_integer( SOUT_CFG_PREFIX "port-audio", 0, PORT_AUDIO_TEXT,
208
                 PORT_AUDIO_LONGTEXT )
209
    add_integer( SOUT_CFG_PREFIX "port-video", 0, PORT_VIDEO_TEXT,
210
                 PORT_VIDEO_LONGTEXT )
211

212
    add_integer( SOUT_CFG_PREFIX "ttl", -1, TTL_TEXT,
213
                 TTL_LONGTEXT )
214
    add_bool( SOUT_CFG_PREFIX "rtcp-mux", false,
215
              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT )
216
    add_integer( SOUT_CFG_PREFIX "caching", MS_FROM_VLC_TICK(DEFAULT_PTS_DELAY),
217
                 CACHING_TEXT, CACHING_LONGTEXT )
218
    add_integer( "rtsp-timeout", 60, RTSP_TIMEOUT_TEXT,
219
                 RTSP_TIMEOUT_LONGTEXT )
220
    add_string( "sout-rtsp-user", "",
221
                RTSP_USER_TEXT, RTSP_USER_LONGTEXT )
222
    add_password("sout-rtsp-pwd", "", RTSP_PASS_TEXT, RTSP_PASS_LONGTEXT)
223

224
#ifdef HAVE_SRTP
225
    add_string( SOUT_CFG_PREFIX "key", "",
226
                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT )
227
    add_string( SOUT_CFG_PREFIX "salt", "",
228
                SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT )
229
#endif
230

231
    add_bool( SOUT_CFG_PREFIX "mp4a-latm", false, RFC3016_TEXT,
232
                 RFC3016_LONGTEXT )
233

234
235
    set_callbacks( Open, Close )
vlc_module_end ()
236
237
238
239

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
240
static const char *const ppsz_sout_options[] = {
241
    "dst", "name", "cat", "port", "port-audio", "port-video", "*sdp", "ttl",
242
    "mux", "sap", "description", "proto", "rtcp-mux", "caching",
243
244
245
#ifdef HAVE_SRTP
    "key", "salt",
#endif
246
    "mp4a-latm", NULL
247
248
};

249
250
251
252
253
254
255
static void *Add( sout_stream_t *, const es_format_t * );
static void  Del( sout_stream_t *, void * );
static int   Send( sout_stream_t *, void *, block_t * );

static void *MuxAdd( sout_stream_t *, const es_format_t * );
static void  MuxDel( sout_stream_t *, void * );
static int   MuxSend( sout_stream_t *, void *, block_t * );
256

257
static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
258
static void* ThreadSend( void * );
259
static void *rtp_listen_thread( void * );
260

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
261
static void SDPHandleUrl( sout_stream_t *, const char * );
262

zorglub's avatar
zorglub committed
263
static int SapSetup( sout_stream_t *p_stream );
264
static int FileSetup( sout_stream_t *p_stream );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
265
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );
266

267
typedef struct
268
{
269
    /* SDP */
270
271
272
    char    *psz_sdp;
    vlc_mutex_t  lock_sdp;

273
    /* SDP to disk */
274
    char *psz_sdp_file;
275
276

    /* SDP via SAP */
277
    bool b_export_sap;
278
279
    session_descriptor_t *p_session;

280
    /* SDP via HTTP */
281
282
283
    httpd_host_t *p_httpd_host;
    httpd_file_t *p_httpd_file;

284
    /* RTSP */
285
286
    rtsp_stream_t *rtsp;

287
    /* RTSP NPT and timestamp computations */
Steve Lhomme's avatar
Steve Lhomme committed
288
289
290
    vlc_tick_t   i_npt_zero;    /* when NPT=0 packet is sent */
    vlc_tick_t   i_pts_zero;    /* predicts PTS of NPT=0 packet */
    vlc_tick_t   i_pts_offset;  /* matches actual PTS to prediction */
291
292
    vlc_mutex_t  lock_ts;

293
    /* */
294
295
296
297
    char     *psz_destination;
    uint16_t  i_port;
    uint16_t  i_port_audio;
    uint16_t  i_port_video;
298
299
300
    uint8_t   proto;
    bool      rtcp_mux;
    bool      b_latm;
301
302
303
304
305
306
307
308
309

    /* 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;
310
    sout_stream_id_sys_t **es;
311
} sout_stream_sys_t;
312

313
314
315
typedef struct rtp_sink_t
{
    int rtp_fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
316
    rtcp_sender_t *rtcp;
317
318
} rtp_sink_t;

319
struct sout_stream_id_sys_t
320
321
322
{
    sout_stream_t *p_stream;
    /* rtp field */
323
324
    /* For RFC 4175, seqnum is extended to 32-bits */
    uint32_t    i_sequence;
Pierre Ynard's avatar
Pierre Ynard committed
325
    bool        b_first_packet;
326
327
    bool        b_ts_init;
    uint32_t    i_ts_offset;
328
329
    uint8_t     ssrc[4];

330
331
332
    /* for rtsp */
    uint16_t    i_seq_sent_next;

333
    /* for sdp */
334
    rtp_format_t rtp_fmt;
335
    int          i_port;
336
337

    /* Packetizer specific fields */
338
    int                 i_mtu;
339
#ifdef HAVE_SRTP
340
    srtp_session_t     *srtp;
341
#endif
342

343
    /* Packets sinks */
344
    vlc_thread_t      thread;
345
    vlc_mutex_t       lock_sink;
346
347
    vlc_queue_t       queue;
    bool              dead;
348
349
    int               sinkc;
    rtp_sink_t       *sinkv;
350
    rtsp_stream_id_t *rtsp_id;
351
352
353
354
    struct {
        int          *fd;
        vlc_thread_t  thread;
    } listen;
355

Steve Lhomme's avatar
Steve Lhomme committed
356
    vlc_tick_t        i_caching;
357
358
};

359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
static int Control(sout_stream_t *stream, int query, va_list args)
{
    (void) stream;

    switch (query)
    {
        case SOUT_STREAM_IS_SYNCHRONOUS:
            *va_arg(args, bool *) = true;
            break;

        default:
            return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}

376
static const struct sout_stream_operations stream_ops = {
377
    Add, Del, Send, Control, NULL,
378
379
380
};

static const struct sout_stream_operations mux_ops = {
381
    MuxAdd, MuxDel, MuxSend, Control, NULL,
382
383
};

384
385
386
387
388
389
/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
390
    sout_stream_sys_t   *p_sys = NULL;
391
    char                *psz;
392
    bool          b_rtsp = false;
393

394
395
    config_ChainParse( p_stream, SOUT_CFG_PREFIX,
                       ppsz_sout_options, p_stream->p_cfg );
396
397

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
398
399
    if( p_sys == NULL )
        return VLC_ENOMEM;
400

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

403
404
405
    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" );
406
    p_sys->rtcp_mux     = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
407

408
    if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio )
409
    {
410
        msg_Err( p_stream, "audio and video RTP port must be distinct" );
411
412
        free( p_sys->psz_destination );
        free( p_sys );
413
        return VLC_EGENERIC;
414
    }
415

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
416
    for( config_chain_t *p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
417
    {
418
419
420
        if( !strcmp( p_cfg->psz_name, "sdp" )
         && ( p_cfg->psz_value != NULL )
         && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
421
        {
422
            b_rtsp = true;
423
            break;
424
        }
425
426
427
    }
    if( !b_rtsp )
    {
428
429
430
        psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
        if( psz != NULL )
        {
431
            if( !strncasecmp( psz, "rtsp:", 5 ) )
432
                b_rtsp = true;
433
434
            free( psz );
        }
435
    }
436

437
438
    /* Transport protocol */
    p_sys->proto = IPPROTO_UDP;
439
    psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");
440

441
442
443
444
    if ((psz == NULL) || !strcasecmp (psz, "udp"))
        (void)0; /* default */
    else
    if (!strcasecmp (psz, "dccp"))
445
    {
446
        p_sys->proto = IPPROTO_DCCP;
447
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
448
    }
449
#if 0
450
    else
451
    if (!strcasecmp (psz, "sctp"))
452
453
    {
        p_sys->proto = IPPROTO_TCP;
454
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
455
    }
456
457
#endif
#if 0
458
    else
459
460
461
    if (!strcasecmp (psz, "tcp"))
    {
        p_sys->proto = IPPROTO_TCP;
462
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
463
    }
464
#endif
465
466
    else
    if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
467
        p_sys->proto = IPPROTO_UDPLITE;
468
469
470
471
    else
        msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
                  psz);
    free (psz);
472
    var_Create (p_this, "dccp-service", VLC_VAR_STRING);
473

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
474
    if( p_sys->psz_destination == NULL && !b_rtsp )
475
    {
476
        msg_Err( p_stream, "missing destination and not in RTSP mode" );
477
478
479
        free( p_sys );
        return VLC_EGENERIC;
    }
480

481
482
    int i_ttl = var_GetInteger( p_stream, SOUT_CFG_PREFIX "ttl" );
    if( i_ttl != -1 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
483
    {
484
485
        var_Create( p_stream, "ttl", VLC_VAR_INTEGER );
        var_SetInteger( p_stream, "ttl", i_ttl );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
486
487
    }

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

490
491
    /* NPT=0 time will be determined when we packetize the first packet
     * (of any ES). But we want to be able to report rtptime in RTSP
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
492
     * without waiting. So until then,
493
494
     * we use an arbitrary reference PTS for timestamp computations, and
     * then actual PTS will catch up using offsets. */
495
    p_sys->i_npt_zero = VLC_TICK_INVALID;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
496
    p_sys->i_pts_zero = vlc_tick_now();
497
498
    p_sys->i_es = 0;
    p_sys->es   = NULL;
499
    p_sys->rtsp = NULL;
500
501
    p_sys->psz_sdp = NULL;

502
    p_sys->b_export_sap = false;
zorglub's avatar
zorglub committed
503
    p_sys->p_session = NULL;
504
    p_sys->psz_sdp_file = NULL;
zorglub's avatar
zorglub committed
505

506
507
    p_sys->p_httpd_host = NULL;
    p_sys->p_httpd_file = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
508
509

    p_stream->p_sys     = p_sys;
510

511
    vlc_mutex_init( &p_sys->lock_sdp );
512
    vlc_mutex_init( &p_sys->lock_ts );
513
    vlc_mutex_init( &p_sys->lock_es );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
514

515
516
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
    if( psz != NULL )
517
518
    {
        /* Check muxer type */
519
520
521
        if( strncasecmp( psz, "ps", 2 )
         && strncasecmp( psz, "mpeg1", 5 )
         && strncasecmp( psz, "ts", 2 ) )
522
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
523
            msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
524
            free( psz );
525
            free( p_sys->psz_destination );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
526
            free( p_sys );
527
528
529
            return VLC_EGENERIC;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
530
        p_sys->p_grab = GrabberCreate( p_stream );
531
        p_sys->p_mux = sout_MuxNew( p_sys->p_grab, psz );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
532
        free( psz );
533

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
534
        if( p_sys->p_mux == NULL )
535
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
536
537
            msg_Err( p_stream, "cannot create muxer" );
            sout_AccessOutDelete( p_sys->p_grab );
538
            free( p_sys->psz_destination );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
539
540
            free( p_sys );
            return VLC_EGENERIC;
541
        }
542

543
        p_sys->packet = NULL;
544
        p_stream->ops = &mux_ops;
545
546
547
    }
    else
    {
548
549
550
        p_sys->p_mux = NULL;
        p_sys->p_grab = NULL;
        p_stream->ops = &stream_ops;
551
    }
552

553
    if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
554
        SDPHandleUrl( p_stream, "sap://" );
555

556
557
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
    if( psz != NULL )
558
    {
559
        config_chain_t *p_cfg;
560

561
        SDPHandleUrl( p_stream, psz );
562
563

        for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
Laurent Aimar's avatar
Laurent Aimar committed
564
        {
565
            if( !strcmp( p_cfg->psz_name, "sdp" ) )
566
            {
567
568
569
                if( p_cfg->psz_value == NULL || *p_cfg->psz_value == '\0' )
                    continue;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
570
                /* needed both :sout-rtp-sdp= and rtp{sdp=} can be used */
571
                if( !strcmp( p_cfg->psz_value, psz ) )
572
573
574
                    continue;

                SDPHandleUrl( p_stream, p_cfg->psz_value );
575
576
            }
        }
577
        free( psz );
578
579
    }

580
581
    if( p_sys->p_mux != NULL )
    {
582
        sout_stream_id_sys_t *id = Add( p_stream, NULL );
583
584
585
586
587
588
589
        if( id == NULL )
        {
            Close( p_this );
            return VLC_EGENERIC;
        }
    }

590
591
592
593
594
595
596
597
598
599
600
    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;

601
602
    if( p_sys->p_mux )
    {
603
        assert( p_sys->i_es <= 1 );
604

605
        sout_MuxDelete( p_sys->p_mux );
606
607
        if ( p_sys->i_es > 0 )
            Del( p_stream, p_sys->es[0] );
608
        sout_AccessOutDelete( p_sys->p_grab );
609

610
611
        if( p_sys->packet )
        {
612
            block_Release( p_sys->packet );
613
614
615
        }
    }

616
617
    if( p_sys->rtsp != NULL )
        RtspUnsetup( p_sys->rtsp );
618

619
    if( p_sys->p_httpd_file )
Laurent Aimar's avatar
Laurent Aimar committed
620
        httpd_FileDelete( p_sys->p_httpd_file );
Laurent Aimar's avatar
Laurent Aimar committed
621

622
    if( p_sys->p_httpd_host )
Laurent Aimar's avatar
Laurent Aimar committed
623
        httpd_HostDelete( p_sys->p_httpd_host );
Laurent Aimar's avatar
Laurent Aimar committed
624

625
    free( p_sys->psz_sdp );
Laurent Aimar's avatar
Laurent Aimar committed
626

627
    if( p_sys->psz_sdp_file != NULL )
Sam Hocevar's avatar
Sam Hocevar committed
628
629
630
631
    {
        unlink( p_sys->psz_sdp_file );
        free( p_sys->psz_sdp_file );
    }
632
    free( p_sys->psz_destination );
633
634
635
    free( p_sys );
}

636
637
638
/*****************************************************************************
 * SDPHandleUrl:
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
639
static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
640
641
642
643
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    vlc_url_t url;

644
    vlc_UrlParse( &url, psz_url );
645
646
647
648
    if( url.psz_protocol && !strcasecmp( url.psz_protocol, "http" ) )
    {
        if( p_sys->p_httpd_file )
        {
649
            msg_Err( p_stream, "you can use sdp=http:// only once" );
650
            goto out;
651
652
653
654
        }

        if( HttpSetup( p_stream, &url ) )
        {
655
            msg_Err( p_stream, "cannot export SDP as HTTP" );
656
657
658
659
        }
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "rtsp" ) )
    {
660
        if( p_sys->rtsp != NULL )
661
        {
662
            msg_Err( p_stream, "you can use sdp=rtsp:// only once" );
663
            goto out;
664
665
        }

666
        if( url.psz_host != NULL && *url.psz_host )
667
        {
668
669
670
671
672
            msg_Warn( p_stream, "\"%s\" RTSP host might be ignored in "
                      "multiple-host configurations, use at your own risks.",
                      url.psz_host );
            msg_Info( p_stream, "Consider passing --rtsp-host=IP on the "
                                "command line instead." );
673
674
675

            var_Create( p_stream, "rtsp-host", VLC_VAR_STRING );
            var_SetString( p_stream, "rtsp-host", url.psz_host );
676
        }
677
        if( url.i_port != 0 )
678
        {
679
680
681
682
683
684
            /* msg_Info( p_stream, "Consider passing --rtsp-port=%u on "
                      "the command line instead.", url.i_port ); */

            var_Create( p_stream, "rtsp-port", VLC_VAR_INTEGER );
            var_SetInteger( p_stream, "rtsp-port", url.i_port );
        }
685

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
686
        p_sys->rtsp = RtspSetup( VLC_OBJECT(p_stream), url.psz_path );
687
688
        if( p_sys->rtsp == NULL )
            msg_Err( p_stream, "cannot export SDP as RTSP" );
689
    }
690
    else if( ( url.psz_protocol && !strcasecmp( url.psz_protocol, "sap" ) ) ||
691
             ( url.psz_host && !strcasecmp( url.psz_host, "sap" ) ) )
692
    {
693
        p_sys->b_export_sap = true;
694
695
696
697
        SapSetup( p_stream );
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "file" ) )
    {
698
        if( p_sys->psz_sdp_file != NULL )
699
        {
700
            msg_Err( p_stream, "you can use sdp=file:// only once" );
701
            goto out;
702
        }
703
        p_sys->psz_sdp_file = vlc_uri2path( psz_url );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
704
705
        if( p_sys->psz_sdp_file == NULL )
            goto out;
706
        FileSetup( p_stream );
707
708
709
710
711
712
    }
    else
    {
        msg_Warn( p_stream, "unknown protocol for SDP (%s)",
                  url.psz_protocol );
    }
713
714

out:
715
716
717
    vlc_UrlClean( &url );
}

718
/*****************************************************************************
719
 * SDPGenerate
720
 *****************************************************************************/
721
/*static*/
722
char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
723
{
724
    sout_stream_sys_t *p_sys = p_stream->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
725
    struct vlc_memstream sdp;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
726
    struct sockaddr_storage dst;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
727
    char *psz_sdp = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
728
    socklen_t dstlen;
729
    int i;
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    /*
     * 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.
     */
745
    bool inclport;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
746

747
    vlc_mutex_lock( &p_sys->lock_es );
sebastien's avatar
sebastien committed
748
    if( unlikely(p_sys->i_es == 0 || (rtsp_url != NULL && !p_sys->es[0]->rtsp_id)) )
749
750
        goto out; /* hmm... */

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
751
    if( p_sys->psz_destination != NULL )
752
    {
753
        inclport = true;
754

755
        /* Oh boy, this is really ugly! */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
756
        dstlen = sizeof( dst );
757
758
        if( p_sys->es[0]->listen.fd != NULL )
            getsockname( p_sys->es[0]->listen.fd[0],
759
760
761
762
                         (struct sockaddr *)&dst, &dstlen );
        else
            getpeername( p_sys->es[0]->sinkv[0].rtp_fd,
                         (struct sockaddr *)&dst, &dstlen );
763
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
764
    else
765
    {
766
        inclport = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
767

768
769
770
771
        /* Check against URL format rtsp://[<ipv6>]:<port>/<path> */
        bool ipv6 = rtsp_url != NULL && strlen( rtsp_url ) > 7
                    && rtsp_url[7] == '[';

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
772
        /* Dummy destination address for RTSP */
773
774
775
776
        dstlen = ipv6 ? sizeof( struct sockaddr_in6 )
                      : sizeof( struct sockaddr_in );
        memset (&dst, 0, dstlen);
        dst.ss_family = ipv6 ? AF_INET6 : AF_INET;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
777
#ifdef HAVE_SA_LEN
778
        dst.ss_len = dstlen;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
779
#endif
780
    }
781

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
782
783
    if( vlc_sdp_Start( &sdp, VLC_OBJECT( p_stream ), SOUT_CFG_PREFIX,
                       NULL, 0, (struct sockaddr *)&dst, dstlen ) )
784
        goto out;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
785
786

    /* TODO: a=source-filter */
787
    if( p_sys->rtcp_mux )
788
        vlc_memstream_puts(&sdp, "a=rtcp-mux\r\n");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
789

790
    if( rtsp_url != NULL )
791
        vlc_memstream_printf(&sdp, "a=control:%s\r\n", rtsp_url);
792

793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
    const char *proto = "RTP/AVP"; /* protocol */
    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:
                return psz_sdp;
        }
    }

811
812
    for( i = 0; i < p_sys->i_es; i++ )
    {
813
        sout_stream_id_sys_t *id = p_sys->es[i];
814
        rtp_format_t *rtp_fmt = &id->rtp_fmt;
815
        const char *mime_major; /* major MIME type */
816

817
        switch( rtp_fmt->cat )
818
819
820
821
822
823
824
825
826
827
828
        {
            case VIDEO_ES:
                mime_major = "video";
                break;
            case AUDIO_ES:
                mime_major = "audio";
                break;
            case SPU_ES:
                mime_major = "text";
                break;
            default:
829
                continue;
830
        }
831

832
        vlc_memstream_printf(&sdp, "m=%s %u %s %"PRIu8"\r\n", mime_major,
833
                             inclport ? id->i_port : 0, proto,
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
                             rtp_fmt->payload_type);

        if (rtp_fmt->bitrate > 0)
            vlc_memstream_printf(&sdp, "b=AS:%u\r\n", rtp_fmt->bitrate);
        vlc_memstream_puts(&sdp, "b=RR:0\r\n");

        /* RTP payload type map */
        vlc_memstream_printf(&sdp, "a=rtpmap:%"PRIu8" %s/%u",
                             rtp_fmt->payload_type, rtp_fmt->ptname,
                             rtp_fmt->clock_rate);
        if (rtp_fmt->cat == AUDIO_ES && rtp_fmt->channels != 1)
            vlc_memstream_printf(&sdp, "/%u", rtp_fmt->channels);
        vlc_memstream_puts(&sdp,