rtp.c 56.2 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin 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
Antoine Cellerier's avatar
Antoine Cellerier 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
#include <vlc_common.h>
34
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
35 36
#include <vlc_sout.h>
#include <vlc_block.h>
37

Clément Stenac's avatar
Clément Stenac committed
38 39 40
#include <vlc_httpd.h>
#include <vlc_url.h>
#include <vlc_network.h>
41
#include <vlc_fs.h>
42
#include <vlc_rand.h>
43 44
#ifdef HAVE_SRTP
# include <srtp.h>
45 46
# include <gcrypt.h>
# include <vlc_gcrypt.h>
47
#endif
48

49 50
#include "rtp.h"

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
#ifdef HAVE_UNISTD_H
52
#   include <sys/types.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
53 54
#   include <unistd.h>
#endif
55 56 57
#ifdef HAVE_ARPA_INET_H
#   include <arpa/inet.h>
#endif
58 59 60 61 62 63 64 65 66
#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
67 68 69

#include <errno.h>

70 71
#include <assert.h>

72 73 74
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
75

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

#define NAME_TEXT N_("Session name")
#define NAME_LONGTEXT N_( \
94 95
    "This is the name of the session that will be announced in the SDP " \
    "(Session Descriptor)." )
96
#define DESC_TEXT N_("Session description")
97
#define DESC_LONGTEXT N_( \
98 99
    "This allows you to give a short description with details about the stream, " \
    "that will be announced in the SDP (Session Descriptor)." )
100 101
#define URL_TEXT N_("Session URL")
#define URL_LONGTEXT N_( \
Pierre Ynard's avatar
Pierre Ynard committed
102
    "This allows you to give a URL with more details about the stream " \
103 104
    "(often the website of the streaming organization), that will " \
    "be announced in the SDP (Session Descriptor)." )
105 106
#define EMAIL_TEXT N_("Session email")
#define EMAIL_LONGTEXT N_( \
107 108 109 110 111 112 113
    "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)." )

114 115
#define PORT_TEXT N_("Port")
#define PORT_LONGTEXT N_( \
116
    "This allows you to specify the base port for the RTP streaming." )
117 118
#define PORT_AUDIO_TEXT N_("Audio port")
#define PORT_AUDIO_LONGTEXT N_( \
119
    "This allows you to specify the default audio port for the RTP streaming." )
120 121
#define PORT_VIDEO_TEXT N_("Video port")
#define PORT_VIDEO_LONGTEXT N_( \
122
    "This allows you to specify the default video port for the RTP streaming." )
123

124
#define TTL_TEXT N_("Hop limit (TTL)")
125
#define TTL_LONGTEXT N_( \
126
    "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \
127
    "the multicast packets sent by the stream output (-1 = use operating " \
128
    "system built-in default).")
129

130 131 132 133 134
#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." )

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

140 141 142 143
#define PROTO_TEXT N_("Transport protocol")
#define PROTO_LONGTEXT N_( \
    "This selects which transport protocol to use for RTP." )

144 145 146 147 148 149 150 151 152
#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.")

153 154 155 156 157 158 159
static const char *const ppsz_protos[] = {
    "dccp", "sctp", "tcp", "udp", "udplite",
};

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

161
#define RFC3016_TEXT N_("MP4A LATM")
162
#define RFC3016_LONGTEXT N_( \
163
    "This allows you to stream MPEG4 LATM audio streams (see RFC3016)." )
164

165 166 167 168 169 170 171
#define RTSP_HOST_TEXT N_( "RTSP host address" )
#define RTSP_HOST_LONGTEXT N_( \
    "This defines the address, port and path the RTSP VOD server will listen " \
    "on.\nSyntax is address:port/path. The default is to listen on all "\
    "interfaces (address 0.0.0.0), on port 554, with no path.\nTo listen " \
    "only on the local interface, use \"localhost\" as address." )

172 173 174 175 176 177
#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
178 179 180 181 182 183 184
#define RTSP_USER_TEXT N_("Username")
#define RTSP_USER_LONGTEXT N_("User name that will be " \
                              "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." )

185 186 187
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

188
#define SOUT_CFG_PREFIX "sout-rtp-"
189
#define MAX_EMPTY_BLOCKS 200
190

191 192 193 194
vlc_module_begin ()
    set_shortname( N_("RTP"))
    set_description( N_("RTP stream output") )
    set_capability( "sout stream", 0 )
195
    add_shortcut( "rtp", "vod" )
196 197
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )
198

199
    add_string( SOUT_CFG_PREFIX "dst", "", DEST_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
200
                DEST_LONGTEXT, true )
201
    add_string( SOUT_CFG_PREFIX "sdp", "", SDP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
202
                SDP_LONGTEXT, true )
203
    add_string( SOUT_CFG_PREFIX "mux", "", MUX_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
204
                MUX_LONGTEXT, true )
205
    add_bool( SOUT_CFG_PREFIX "sap", false, SAP_TEXT, SAP_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
206
              true )
207

208
    add_string( SOUT_CFG_PREFIX "name", "", NAME_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
209
                NAME_LONGTEXT, true )
210
    add_string( SOUT_CFG_PREFIX "description", "", DESC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
211
                DESC_LONGTEXT, true )
212
    add_string( SOUT_CFG_PREFIX "url", "", URL_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
213
                URL_LONGTEXT, true )
214
    add_string( SOUT_CFG_PREFIX "email", "", EMAIL_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
                EMAIL_LONGTEXT, true )
216
    add_string( SOUT_CFG_PREFIX "phone", "", PHONE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
217
                PHONE_LONGTEXT, true )
218

219
    add_string( SOUT_CFG_PREFIX "proto", "udp", PROTO_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
220
                PROTO_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221
        change_string_list( ppsz_protos, ppsz_protocols, NULL )
222
    add_integer( SOUT_CFG_PREFIX "port", 5004, PORT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223
                 PORT_LONGTEXT, true )
224
    add_integer( SOUT_CFG_PREFIX "port-audio", 0, PORT_AUDIO_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
225
                 PORT_AUDIO_LONGTEXT, true )
226
    add_integer( SOUT_CFG_PREFIX "port-video", 0, PORT_VIDEO_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
227
                 PORT_VIDEO_LONGTEXT, true )
228

229
    add_integer( SOUT_CFG_PREFIX "ttl", -1, TTL_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
230
                 TTL_LONGTEXT, true )
231
    add_bool( SOUT_CFG_PREFIX "rtcp-mux", false,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
232
              RTCP_MUX_TEXT, RTCP_MUX_LONGTEXT, false )
233
    add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000,
234
                 CACHING_TEXT, CACHING_LONGTEXT, true )
235

236
#ifdef HAVE_SRTP
237
    add_string( SOUT_CFG_PREFIX "key", "",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
238
                SRTP_KEY_TEXT, SRTP_KEY_LONGTEXT, false )
239
    add_string( SOUT_CFG_PREFIX "salt", "",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
240
                SRTP_SALT_TEXT, SRTP_SALT_LONGTEXT, false )
241
#endif
242

243
    add_bool( SOUT_CFG_PREFIX "mp4a-latm", false, RFC3016_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
244
                 RFC3016_LONGTEXT, false )
245

246
    set_callbacks( Open, Close )
247 248 249 250 251 252

    add_submodule ()
    set_shortname( N_("RTSP VoD" ) )
    set_description( N_("RTSP VoD server") )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_VOD )
Pierre Ynard's avatar
Pierre Ynard committed
253
    set_capability( "vod server", 10 )
254 255 256 257
    set_callbacks( OpenVoD, CloseVoD )
    add_shortcut( "rtsp" )
    add_string ( "rtsp-host", NULL, RTSP_HOST_TEXT,
                 RTSP_HOST_LONGTEXT, true )
258 259
    add_integer( "rtsp-timeout", 60, RTSP_TIMEOUT_TEXT,
                 RTSP_TIMEOUT_LONGTEXT, true )
260
    add_string( "sout-rtsp-user", "",
Pierre Ynard's avatar
Pierre Ynard committed
261
                RTSP_USER_TEXT, RTSP_USER_LONGTEXT, true )
262
    add_password( "sout-rtsp-pwd", "",
Pierre Ynard's avatar
Pierre Ynard committed
263
                  RTSP_PASS_TEXT, RTSP_PASS_LONGTEXT, true )
264

265
vlc_module_end ()
266 267 268 269

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
270
static const char *const ppsz_sout_options[] = {
271
    "dst", "name", "port", "port-audio", "port-video", "*sdp", "ttl", "mux",
272
    "sap", "description", "url", "email", "phone",
273
    "proto", "rtcp-mux", "caching", "key", "salt",
274
    "mp4a-latm", NULL
275 276
};

277
static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
278
static int               Del ( sout_stream_t *, sout_stream_id_t * );
Gildas Bazin's avatar
 
Gildas Bazin committed
279
static int               Send( sout_stream_t *, sout_stream_id_t *,
280
                               block_t* );
281 282 283 284
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* );
285

286
static sout_access_out_t *GrabberCreate( sout_stream_t *p_sout );
287
static void* ThreadSend( void * );
288
static void *rtp_listen_thread( void * );
289

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

292
static int SapSetup( sout_stream_t *p_stream );
293
static int FileSetup( sout_stream_t *p_stream );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
294
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t * );
295

296 297 298
static int64_t rtp_init_ts( const vod_media_t *p_media,
                            const char *psz_vod_session );

299 300
struct sout_stream_sys_t
{
301
    /* SDP */
302 303 304
    char    *psz_sdp;
    vlc_mutex_t  lock_sdp;

305
    /* SDP to disk */
306
    char *psz_sdp_file;
307 308

    /* SDP via SAP */
309
    bool b_export_sap;
310 311
    session_descriptor_t *p_session;

312
    /* SDP via HTTP */
313 314 315
    httpd_host_t *p_httpd_host;
    httpd_file_t *p_httpd_file;

316
    /* RTSP */
317 318
    rtsp_stream_t *rtsp;

319 320 321 322 323 324
    /* RTSP NPT and timestamp computations */
    mtime_t      i_npt_zero;    /* when NPT=0 packet is sent */
    int64_t      i_pts_zero;    /* predicts PTS of NPT=0 packet */
    int64_t      i_pts_offset;  /* matches actual PTS to prediction */
    vlc_mutex_t  lock_ts;

325
    /* */
326 327 328 329
    char     *psz_destination;
    uint16_t  i_port;
    uint16_t  i_port_audio;
    uint16_t  i_port_video;
330 331 332
    uint8_t   proto;
    bool      rtcp_mux;
    bool      b_latm;
333

334 335 336 337
    /* VoD */
    vod_media_t *p_vod_media;
    char     *psz_vod_session;

338 339 340 341 342 343 344 345 346 347 348
    /* 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;
};

349 350 351
typedef struct rtp_sink_t
{
    int rtp_fd;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
352
    rtcp_sender_t *rtcp;
353 354
} rtp_sink_t;

355 356 357 358
struct sout_stream_id_t
{
    sout_stream_t *p_stream;
    /* rtp field */
359
    uint16_t    i_sequence;
Pierre Ynard's avatar
Pierre Ynard committed
360
    bool        b_first_packet;
361 362
    bool        b_ts_init;
    uint32_t    i_ts_offset;
363 364
    uint8_t     ssrc[4];

365 366 367
    /* for rtsp */
    uint16_t    i_seq_sent_next;

368
    /* for sdp */
369
    rtp_format_t rtp_fmt;
370
    int          i_port;
371 372

    /* Packetizer specific fields */
373
    int                 i_mtu;
374
#ifdef HAVE_SRTP
375
    srtp_session_t     *srtp;
376
#endif
377

378
    /* Packets sinks */
379
    vlc_thread_t      thread;
380
    vlc_mutex_t       lock_sink;
381 382
    int               sinkc;
    rtp_sink_t       *sinkv;
383
    rtsp_stream_id_t *rtsp_id;
384 385 386 387
    struct {
        int          *fd;
        vlc_thread_t  thread;
    } listen;
388 389 390

    block_fifo_t     *p_fifo;
    int64_t           i_caching;
391 392
};

393 394 395 396 397 398
/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
399
    sout_instance_t     *p_sout = p_stream->p_sout;
400
    sout_stream_sys_t   *p_sys = NULL;
401
    config_chain_t      *p_cfg = NULL;
402
    char                *psz;
403
    bool          b_rtsp = false;
404

405 406
    config_ChainParse( p_stream, SOUT_CFG_PREFIX,
                       ppsz_sout_options, p_stream->p_cfg );
407 408

    p_sys = malloc( sizeof( sout_stream_sys_t ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
409 410
    if( p_sys == NULL )
        return VLC_ENOMEM;
411

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

414 415 416
    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" );
417
    p_sys->rtcp_mux     = var_GetBool( p_stream, SOUT_CFG_PREFIX "rtcp-mux" );
418

419
    if( p_sys->i_port_audio && p_sys->i_port_video == p_sys->i_port_audio )
420
    {
421
        msg_Err( p_stream, "audio and video RTP port must be distinct" );
422 423
        free( p_sys->psz_destination );
        free( p_sys );
424
        return VLC_EGENERIC;
425
    }
426

427
    for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
428
    {
429 430 431
        if( !strcmp( p_cfg->psz_name, "sdp" )
         && ( p_cfg->psz_value != NULL )
         && !strncasecmp( p_cfg->psz_value, "rtsp:", 5 ) )
432
        {
433
            b_rtsp = true;
434
            break;
435
        }
436 437 438
    }
    if( !b_rtsp )
    {
439 440 441
        psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
        if( psz != NULL )
        {
442
            if( !strncasecmp( psz, "rtsp:", 5 ) )
443
                b_rtsp = true;
444 445
            free( psz );
        }
446
    }
447

448 449
    /* Transport protocol */
    p_sys->proto = IPPROTO_UDP;
450
    psz = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"proto");
451

452 453 454 455
    if ((psz == NULL) || !strcasecmp (psz, "udp"))
        (void)0; /* default */
    else
    if (!strcasecmp (psz, "dccp"))
456
    {
457
        p_sys->proto = IPPROTO_DCCP;
458
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
459
    }
460
#if 0
461
    else
462
    if (!strcasecmp (psz, "sctp"))
463 464
    {
        p_sys->proto = IPPROTO_TCP;
465
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
466
    }
467 468
#endif
#if 0
469
    else
470 471 472
    if (!strcasecmp (psz, "tcp"))
    {
        p_sys->proto = IPPROTO_TCP;
473
        p_sys->rtcp_mux = true; /* Force RTP/RTCP mux */
474
    }
475
#endif
476 477
    else
    if (!strcasecmp (psz, "udplite") || !strcasecmp (psz, "udp-lite"))
478
        p_sys->proto = IPPROTO_UDPLITE;
479 480 481 482
    else
        msg_Warn (p_this, "unknown or unsupported transport protocol \"%s\"",
                  psz);
    free (psz);
483
    var_Create (p_this, "dccp-service", VLC_VAR_STRING);
484

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
    p_sys->p_vod_media = NULL;
    p_sys->psz_vod_session = NULL;

    if (! strcmp(p_stream->psz_name, "vod"))
    {
        /* The VLM stops all instances before deleting a media, so this
         * reference will remain valid during the lifetime of the rtp
         * stream output. */
        p_sys->p_vod_media = var_InheritAddress(p_stream, "vod-media");

        if (p_sys->p_vod_media != NULL)
        {
            p_sys->psz_vod_session = var_InheritString(p_stream, "vod-session");
            if (p_sys->psz_vod_session == NULL)
            {
                msg_Err(p_stream, "missing VoD session");
                free(p_sys);
                return VLC_EGENERIC;
            }

            const char *mux = vod_get_mux(p_sys->p_vod_media);
            var_SetString(p_stream, SOUT_CFG_PREFIX "mux", mux);
        }
    }

    if( p_sys->psz_destination == NULL && !b_rtsp
        && p_sys->p_vod_media == NULL )
512
    {
513
        msg_Err( p_stream, "missing destination and not in RTSP mode" );
514 515 516
        free( p_sys );
        return VLC_EGENERIC;
    }
517

518 519
    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
520
    {
521 522
        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
523 524
    }

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

527 528
    /* 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
529 530 531
     * without waiting (and already did in the VoD case). So until then,
     * we use an arbitrary reference PTS for timestamp computations, and
     * then actual PTS will catch up using offsets. */
532
    p_sys->i_npt_zero = VLC_TS_INVALID;
533 534
    p_sys->i_pts_zero = rtp_init_ts(p_sys->p_vod_media,
                                    p_sys->psz_vod_session); 
535 536
    p_sys->i_es = 0;
    p_sys->es   = NULL;
537
    p_sys->rtsp = NULL;
538 539
    p_sys->psz_sdp = NULL;

540
    p_sys->b_export_sap = false;
541
    p_sys->p_session = NULL;
542
    p_sys->psz_sdp_file = NULL;
543

544 545
    p_sys->p_httpd_host = NULL;
    p_sys->p_httpd_file = NULL;
546 547

    p_stream->p_sys     = p_sys;
548

549
    vlc_mutex_init( &p_sys->lock_sdp );
550
    vlc_mutex_init( &p_sys->lock_ts );
551
    vlc_mutex_init( &p_sys->lock_es );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
552

553 554
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
    if( psz != NULL )
555 556
    {
        /* Check muxer type */
557 558 559
        if( strncasecmp( psz, "ps", 2 )
         && strncasecmp( psz, "mpeg1", 5 )
         && strncasecmp( psz, "ts", 2 ) )
560
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
561
            msg_Err( p_stream, "unsupported muxer type for RTP (only TS/PS)" );
562
            free( psz );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
563
            vlc_mutex_destroy( &p_sys->lock_sdp );
Pierre Ynard's avatar
Pierre Ynard committed
564
            vlc_mutex_destroy( &p_sys->lock_ts );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
565
            vlc_mutex_destroy( &p_sys->lock_es );
566
            free( p_sys->psz_vod_session );
567
            free( p_sys->psz_destination );
568
            free( p_sys );
569 570 571
            return VLC_EGENERIC;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
572 573 574
        p_sys->p_grab = GrabberCreate( p_stream );
        p_sys->p_mux = sout_MuxNew( p_sout, psz, p_sys->p_grab );
        free( psz );
575

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
576
        if( p_sys->p_mux == NULL )
577
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
578 579 580
            msg_Err( p_stream, "cannot create muxer" );
            sout_AccessOutDelete( p_sys->p_grab );
            vlc_mutex_destroy( &p_sys->lock_sdp );
Pierre Ynard's avatar
Pierre Ynard committed
581
            vlc_mutex_destroy( &p_sys->lock_ts );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
582
            vlc_mutex_destroy( &p_sys->lock_es );
583
            free( p_sys->psz_vod_session );
584
            free( p_sys->psz_destination );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
585 586
            free( p_sys );
            return VLC_EGENERIC;
587
        }
588

589
        p_sys->packet = NULL;
590

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
591 592 593
        p_stream->pf_add  = MuxAdd;
        p_stream->pf_del  = MuxDel;
        p_stream->pf_send = MuxSend;
594 595 596
    }
    else
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
597 598 599
        p_sys->p_mux    = NULL;
        p_sys->p_grab   = NULL;

600 601 602
        p_stream->pf_add    = Add;
        p_stream->pf_del    = Del;
        p_stream->pf_send   = Send;
603
    }
604

605 606 607
    if( var_GetBool( p_stream, SOUT_CFG_PREFIX"sap" ) )
        SDPHandleUrl( p_stream, "sap" );

608 609
    psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "sdp" );
    if( psz != NULL )
610
    {
611
        config_chain_t *p_cfg;
612

613
        SDPHandleUrl( p_stream, psz );
614 615

        for( p_cfg = p_stream->p_cfg; p_cfg != NULL; p_cfg = p_cfg->p_next )
616
        {
617
            if( !strcmp( p_cfg->psz_name, "sdp" ) )
618
            {
619 620 621
                if( p_cfg->psz_value == NULL || *p_cfg->psz_value == '\0' )
                    continue;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
622
                /* needed both :sout-rtp-sdp= and rtp{sdp=} can be used */
623
                if( !strcmp( p_cfg->psz_value, psz ) )
624 625 626
                    continue;

                SDPHandleUrl( p_stream, p_cfg->psz_value );
627 628
            }
        }
629
        free( psz );
630 631
    }

632 633 634
    /* update p_sout->i_out_pace_nocontrol */
    p_stream->p_sout->i_out_pace_nocontrol++;

635 636 637 638 639 640 641 642 643 644
    if( p_sys->p_mux != NULL )
    {
        sout_stream_id_t *id = Add( p_stream, NULL );
        if( id == NULL )
        {
            Close( p_this );
            return VLC_EGENERIC;
        }
    }

645 646 647 648 649 650 651 652 653 654 655
    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;

656 657 658
    /* update p_sout->i_out_pace_nocontrol */
    p_stream->p_sout->i_out_pace_nocontrol--;

659 660
    if( p_sys->p_mux )
    {
661
        assert( p_sys->i_es <= 1 );
662

663
        sout_MuxDelete( p_sys->p_mux );
664 665
        if ( p_sys->i_es > 0 )
            Del( p_stream, p_sys->es[0] );
666
        sout_AccessOutDelete( p_sys->p_grab );
667

668 669
        if( p_sys->packet )
        {
670
            block_Release( p_sys->packet );
671 672 673
        }
    }

674 675
    if( p_sys->rtsp != NULL )
        RtspUnsetup( p_sys->rtsp );
676

677
    vlc_mutex_destroy( &p_sys->lock_sdp );
678
    vlc_mutex_destroy( &p_sys->lock_ts );
679
    vlc_mutex_destroy( &p_sys->lock_es );
680 681

    if( p_sys->p_httpd_file )
682
        httpd_FileDelete( p_sys->p_httpd_file );
Laurent Aimar's avatar
Laurent Aimar committed
683

684
    if( p_sys->p_httpd_host )
685
        httpd_HostDelete( p_sys->p_httpd_host );
Laurent Aimar's avatar
Laurent Aimar committed
686

687
    free( p_sys->psz_sdp );
Laurent Aimar's avatar
Laurent Aimar committed
688

689
    if( p_sys->psz_sdp_file != NULL )
Sam Hocevar's avatar
Sam Hocevar committed
690 691 692 693 694 695
    {
#ifdef HAVE_UNISTD_H
        unlink( p_sys->psz_sdp_file );
#endif
        free( p_sys->psz_sdp_file );
    }
696
    free( p_sys->psz_vod_session );
697
    free( p_sys->psz_destination );
698 699 700
    free( p_sys );
}

701 702 703
/*****************************************************************************
 * SDPHandleUrl:
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
704
static void SDPHandleUrl( sout_stream_t *p_stream, const char *psz_url )
705 706 707 708 709 710 711 712 713
{
    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 )
        {
714
            msg_Err( p_stream, "you can use sdp=http:// only once" );
715
            goto out;
716 717 718 719
        }

        if( HttpSetup( p_stream, &url ) )
        {
720
            msg_Err( p_stream, "cannot export SDP as HTTP" );
721 722 723 724
        }
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "rtsp" ) )
    {
725
        if( p_sys->rtsp != NULL )
726
        {
727
            msg_Err( p_stream, "you can use sdp=rtsp:// only once" );
728
            goto out;
729 730
        }

731
        if( url.psz_host != NULL && *url.psz_host )
732
        {
733
            /* msg_Err( p_stream, "\"%s\" RTSP host ignored", url.psz_host );
734
            msg_Info( p_stream, "Pass --rtsp-host=%s on the command line "
735 736 737 738
                      "instead.", url.psz_host ); */

            var_Create( p_stream, "rtsp-host", VLC_VAR_STRING );
            var_SetString( p_stream, "rtsp-host", url.psz_host );
739
        }
740
        /* if( url.i_port != 0 )
741 742 743 744
        {
            msg_Err( p_stream, "\"%u\" RTSP port ignored", url.i_port );
            msg_Info( p_stream, "Pass --rtsp-port=%u on the command line "
                      "instead.", url.i_port );
745 746 747 748 749 750
        } */

        if( url.i_port <= 0 ) url.i_port = 554;
        var_Create( p_stream, "rtsp-port", VLC_VAR_INTEGER );
        var_SetInteger( p_stream, "rtsp-port", url.i_port );

751
        p_sys->rtsp = RtspSetup( VLC_OBJECT(p_stream), NULL, url.psz_path );
752 753
        if( p_sys->rtsp == NULL )
            msg_Err( p_stream, "cannot export SDP as RTSP" );
754
    }
755
    else if( ( url.psz_protocol && !strcasecmp( url.psz_protocol, "sap" ) ) ||
756
             ( url.psz_host && !strcasecmp( url.psz_host, "sap" ) ) )
757
    {
758
        p_sys->b_export_sap = true;
759 760 761 762
        SapSetup( p_stream );
    }
    else if( url.psz_protocol && !strcasecmp( url.psz_protocol, "file" ) )
    {
763
        if( p_sys->psz_sdp_file != NULL )
764
        {
765
            msg_Err( p_stream, "you can use sdp=file:// only once" );
766
            goto out;
767
        }
768
        p_sys->psz_sdp_file = make_path( psz_url );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
769 770
        if( p_sys->psz_sdp_file == NULL )
            goto out;
771
        FileSetup( p_stream );
772 773 774 775 776 777
    }
    else
    {
        msg_Warn( p_stream, "unknown protocol for SDP (%s)",
                  url.psz_protocol );
    }
778 779

out:
780 781 782
    vlc_UrlClean( &url );
}

783
/*****************************************************************************
784
 * SDPGenerate
785
 *****************************************************************************/
786
/*static*/
787
char *SDPGenerate( sout_stream_t *p_stream, const char *rtsp_url )
788
{
789
    sout_stream_sys_t *p_sys = p_stream->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
790
    char *psz_sdp = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
791 792
    struct sockaddr_storage dst;
    socklen_t dstlen;
793
    int i;
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
    /*
     * 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
809 810
    int inclport;

811
    vlc_mutex_lock( &p_sys->lock_es );
Sébastien Escudier's avatar
Sébastien Escudier committed
812
    if( unlikely(p_sys->i_es == 0 || (rtsp_url != NULL && !p_sys->es[0]->rtsp_id)) )
813 814
        goto out; /* hmm... */

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
815
    if( p_sys->psz_destination != NULL )
816
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
817
        inclport = 1;
818

819
        /* Oh boy, this is really ugly! */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
820
        dstlen = sizeof( dst );
821 822
        if( p_sys->es[0]->listen.fd != NULL )
            getsockname( p_sys->es[0]->listen.fd[0],
823 824 825 826
                         (struct sockaddr *)&dst, &dstlen );
        else
            getpeername( p_sys->es[0]->sinkv[0].rtp_fd,
                         (struct sockaddr *)&dst, &dstlen );
827
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
828
    else
829
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
830 831
        inclport = 0;

832 833 834 835
        /* 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
836
        /* Dummy destination address for RTSP */
837 838 839 840
        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
841
#ifdef HAVE_SA_LEN
842
        dst.ss_len = dstlen;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
843
#endif
844
    }
845

846 847
    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
848
    if( psz_sdp == NULL )
849
        goto out;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
850 851

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

855 856 857
    if( rtsp_url != NULL )
        sdp_AddAttribute ( &psz_sdp, "control", "%s", rtsp_url );

858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
    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;
        }
    }

876 877 878
    for( i = 0; i < p_sys->i_es; i++ )
    {
        sout_stream_id_t *id = p_sys->es[i];
879
        rtp_format_t *rtp_fmt = &id->rtp_fmt;
880
        const char *mime_major; /* major MIME type */
881

882
        switch( rtp_fmt->cat )
883 884 885 886 887 888 889 890 891 892 893
        {
            case VIDEO_ES:
                mime_major = "video";
                break;
            case AUDIO_ES:
                mime_major = "audio";
                break;
            case SPU_ES:
                mime_major = "text";
                break;
            default:
894
                continue;
895
        }
896

897
        sdp_AddMedia( &psz_sdp, mime_major, proto, inclport * id->i_port,
898 899 900
                      rtp_fmt->payload_type, false, rtp_fmt->bitrate,
                      rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
                      rtp_fmt->fmtp);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
901

902 903
        /* cf RFC4566 §5.14 */
        if( inclport && !p_sys->rtcp_mux && (id->i_port & 1) )
904 905
            sdp_AddAttribute ( &psz_sdp, "rtcp", "%u", id->i_port + 1 );

906
        if( rtsp_url != NULL )
907
        {
908 909 910 911 912 913
            char *track_url = RtspAppendTrackPath( id->rtsp_id, rtsp_url );
            if( track_url != NULL )
            {
                sdp_AddAttribute ( &psz_sdp, "control", "%s", track_url );
                free( track_url );
            }
914
        }
915 916
        else
        {
917
            if( id->listen.fd != NULL )
918 919
                sdp_AddAttribute( &psz_sdp, "setup", "passive" );
            if( p_sys->proto == IPPROTO_DCCP )
920
                sdp_AddAttribute( &psz_sdp, "dccp-service-code",
921 922
                                  "SC:RTP%c",
                                  toupper( (unsigned char)mime_major[0] ) );
923
        }
924
    }
925 926
out:
    vlc_mutex_unlock( &p_sys->lock_es );
927
    return psz_sdp;
928 929 930
}

/*****************************************************************************
931
 * RTP mux
932 933
 *****************************************************************************/

934 935 936 937 938 939 940
/**
 * Shrink the MTU down to a fixed packetization time (for audio).
 */
static void
rtp_set_ptime (sout_stream_id_t *id, unsigned ptime_ms, size_t bytes)
{
    /* Samples per second */
941 942
    size_t spl = (id->rtp_fmt.clock_rate - 1) * ptime_ms / 1000 + 1;
    bytes *= id->rtp_fmt.channels;
943 944 945 946 947
    spl *= bytes;

    if (spl < rtp_mtu (id)) /* MTU is big enough for ptime */
        id->i_mtu = 12 + spl;
    else /* MTU is too small for ptime, align to a sample boundary */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
948
        id->i_mtu = 12 + (((id->i_mtu - 12) / bytes) * bytes);
949
}
950

951
uint32_t rtp_compute_ts( unsigned i_clock_rate, int64_t i_pts )
952 953 954
{
    /* NOTE: this plays nice with offsets because the calculations are
     * linear. */
955
    return i_pts * (int64_t)i_clock_rate / CLOCK_FREQ;
956 957
}

958
/** Add an ES as a new RTP stream */
Gildas Bazin's avatar
 
Gildas Bazin committed
959
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
960
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
961 962
    /* NOTE: As a special case, if we use a non-RTP
     * mux (TS/PS), then p_fmt is NULL. */
963
    sout_stream_sys_t *p_sys = p_stream->p_sys;
964
    char              *psz_sdp;
965

966 967
    sout_stream_id_t *id = malloc( sizeof( *id ) );
    if( unlikely(id == NULL) )
968
        return NULL;
969
    id->p_stream   = p_stream;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
970

971
    id->i_mtu = var_InheritInteger( p_stream, "mtu" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
972 973 974
    if( id->i_mtu <= 12 + 16 )
        id->i_mtu = 576 - 20 - 8; /* pessimistic */
    msg_Dbg( p_stream, "maximum RTP packet size: %d bytes", id->i_mtu );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
975

976 977
#ifdef HAVE_SRTP
    id->srtp = NULL;
978 979 980 981 982 983 984
#endif
    vlc_mutex_init( &id->lock_sink );
    id->sinkc = 0;
    id->sinkv = NULL;
    id->rtsp_id = NULL;
    id->p_fifo = NULL;
    id->listen.fd = NULL;
985

Pierre Ynard's avatar
Pierre Ynard committed
986
    id->b_first_packet = true;
987 988 989
    id->i_caching =
        (int64_t)1000 * var_GetInteger( p_stream, SOUT_CFG_PREFIX "caching");

990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
    vlc_rand_bytes (&id->i_sequence, sizeof (id->i_sequence));
    vlc_rand_bytes (id->ssrc, sizeof (id->ssrc));

    bool format = false;

    if (p_sys->p_vod_media != NULL)
    {
        id->rtp_fmt.ptname = NULL;
        uint32_t ssrc;
        int val = vod_init_id(p_sys->p_vod_media, p_sys->psz_vod_session,
                              p_fmt ? p_fmt->i_id : 0, id, &id->rtp_fmt,
                              &ssrc, &id->i_seq_sent_next);
        if (val == VLC_SUCCESS)
        {
            memcpy(id->ssrc, &ssrc, sizeof(id->ssrc));
            /* This is ugly, but id->i_seq_sent_next needs to be
             * initialized inside vod_init_id() to avoid race
             * conditions. */
            id->i_sequence = id->i_seq_sent_next;
        }
        /* vod_init_id() may fail either because the ES wasn't found in
1011
         * the VoD media, or because the RTSP session is gone. In the
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
         * former case, id->rtp_fmt was left untouched. */
        format = (id->rtp_fmt.ptname != NULL);
    }

    if (!format)
    {
        id->rtp_fmt.fmtp = NULL; /* don't free() garbage on error */
        char *psz = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "mux" );
        if (p_fmt == NULL && psz == NULL)
            goto error;
        int val = rtp_get_fmt(VLC_OBJECT(p_stream), p_fmt, psz, &id->rtp_fmt);
        free( psz );
        if (val != VLC_SUCCESS)
            goto error;
    }

1028
#ifdef HAVE_SRTP
1029
    char *key = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"key");
1030 1031
    if (key)
    {
1032
        vlc_gcrypt_init ();
1033 1034 1035 1036 1037 1038 1039 1040
        id->srtp = srtp_create (SRTP_ENCR_AES_CM, SRTP_AUTH_HMAC_SHA1, 10,
                                   SRTP_PRF_AES_CM, SRTP_RCC_MODE1);
        if (id->srtp == NULL)
        {
            free (key);
            goto error;
        }

1041
        char *salt = var_GetNonEmptyString (p_stream, SOUT_CFG_PREFIX"salt");
1042 1043 1044 1045 1046 1047 1048 1049
        errno = srtp_setkeystring (id->srtp, key, salt ? salt : "");
        free (salt);
        free (key);
        if (errno)
        {
            msg_Err (p_stream, "bad SRTP key/salt combination (%m)");
            goto error;
        }
1050
        id->i_sequence = 0; /* FIXME: awful hack for libvlc_srtp */
1051
    }
1052
#endif
1053

1054 1055
    id->i_seq_sent_next = id->i_sequence;

1056
    int mcast_fd = -1;
1057
    if( p_sys->psz_destination != NULL )
1058
    {
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
        /* Choose the port */
        uint16_t i_port = 0;
        if( p_fmt == NULL )
            ;
        else
        if( p_fmt->i_cat == AUDIO_ES && p_sys->i_port_audio > 0 )
            i_port = p_sys->i_port_audio;
        else
        if( p_fmt->i_cat == VIDEO_ES && p_sys->i_port_video > 0 )
            i_port = p_sys->i_port_video;

        /* We do not need the ES lock (p_sys->lock_es) here, because
         * this is the only one thread that can *modify* the ES table.
         * The ES lock protects the other threads from our modifications
         * (TAB_APPEND, TAB_REMOVE). */
        for (int i = 0; i_port && (i < p_sys->i_es); i++)
             if (i_port == p_sys->es[i]->i_port)
                 i_port = 0; /* Port already in use! */
        for (uint16_t p = p_sys->i_port; i_port == 0; p += 2)
        {
            if (p == 0)
            {
                msg_Err (p_stream, "too many RTP elementary streams");
                goto error;
            }
            i_port = p;
            for (int i = 0; i_port && (i < p_sys->i_es); i++)
                 if (p == p_sys->es[i]->i_port)
                     i_port = 0;
        }

        id->i_port = i_port;

1092 1093
        int type = SOCK_STREAM;

1094
        switch( p_sys->proto )
1095
        {
1096
#ifdef SOCK_DCCP
1097
            case IPPROTO_DCCP:
1098 1099
            {
                const char *code;
1100
                switch (id->rtp_fmt.cat)
1101 1102 1103
                {
                    case VIDEO_ES: code = "RTPV";     break;
                    case AUDIO_ES: code = "RTPARTPV"; break;
1104
                    case SPU_ES:   code = "RTPTRTPV"; break;
1105 1106 1107
                    default:       code = "RTPORTPV"; break;
                }
                var_SetString (p_stream, "dccp-service", code);
1108
                type = SOCK_DCCP;
1109
            }   /* fall through */
1110
#endif
1111
            case IPPROTO_TCP:
1112
                id->listen.fd = net_Listen( VLC_OBJECT(p_stream),
1113
                                            p_sys->psz_destination, i_port,
1114
                                            type, p_sys->proto );
1115
                if( id->listen.fd == NULL )
1116 1117 1118 1119
                {
                    msg_Err( p_stream, "passive COMEDIA RTP socket failed" );
                    goto error;
                }
1120 1121 1122 1123 1124 1125 1126
                if( vlc_clone( &id->listen.thread, rtp_listen_thread, id,
                               VLC_THREAD_PRIORITY_LOW ) )
                {
                    net_ListenClose( id->listen.fd );
                    id->listen.fd = NULL;
                    goto error;
                }
1127 1128 1129 1130 1131
                break;

            default:
            {
                int fd = net_ConnectDgram( p_stream, p_sys->psz_destination,
1132
                                           i_port, -1, p_sys->proto );
1133 1134 1135 1136 1137
                if( fd == -1 )
                {
                    msg_Err( p_stream, "cannot create RTP socket" );
                    goto error;
                }
1138 1139 1140 1141
                /* Ignore any unexpected incoming packet (including RTCP-RR
                 * packets in case of rtcp-mux) */
                setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 },
                            sizeof (int));
1142
                rtp_add_sink( id, fd, p_sys->rtcp_mux, NULL );
1143 1144
                /* FIXME: test if this is multicast  */
                mcast_fd = fd;
1145
            }
1146
        }
1147
    }
1148

1149
    if( p_fmt != NULL )
1150
    switch( p_fmt->i_codec )
1151
    {
1152 1153
        case VLC_CODEC_MULAW:
        case VLC_CODEC_ALAW:
1154
        case VLC_CODEC_U8:
1155
            rtp_set_ptime (id, 20, 1);
1156
            break;
1157
        case VLC_CODEC_S16B:
1158
        case VLC_CODEC_S16L:
1159
            rtp_set_ptime (id, 20, 2);
1160
            break;
1161
        default:
1162
            break;
1163 1164
    }

1165
#if 0 /* No payload formats sets this at the moment */
1166
    int cscov = -1;
1167 1168 1169 1170
    if( cscov != -1 )
        cscov += 8 /* UDP */ + 12 /* RTP */;
    if( id->sinkc > 0 )
        net_SetCSCov( id->sinkv[0].rtp_fd, cscov, -1 );
1171
#endif
1172

1173 1174 1175 1176
    vlc_mutex_lock( &p_sys->lock_ts );
    id->b_ts_init = ( p_sys->i_npt_zero != VLC_TS_INVALID );
    vlc_mutex_unlock( &p_sys->lock_ts );
    if( id->b_ts_init )
1177 1178
        id->i_ts_offset = rtp_compute_ts( id->rtp_fmt.clock_rate,
                                          p_sys->i_pts_offset );
1179

1180
    if( p_sys->rtsp != NULL )
1181 1182
        id->rtsp_id = RtspAddId( p_sys->rtsp, id, GetDWBE( id->ssrc ),
                                 id->rtp_fmt.clock_rate, mcast_fd );
1183

1184
    id->p_fifo = block_FifoNew();
1185
    if( unlikely(id->p_fifo == NULL) )
1186
        goto error;
1187 1188 1189 1190 1191 1192
    if( vlc_clone( &id->thread, ThreadSend, id, VLC_THREAD_PRIORITY_HIGHEST ) )
    {
        block_FifoRelease( id->p_fifo );
        id->p_fifo = NULL;
        goto error;
    }
1193

1194
    /* Update p_sys context */
1195
    vlc_mutex_lock( &p_sys->lock_es );
1196
    TAB_APPEND( p_sys->i_es, p_sys->es, id );
1197 1198
    vlc_mutex_unlock( &p_sys->lock_es );

1199
    psz_sdp = SDPGenerate( p_stream, NULL );
1200

1201 1202 1203 1204
    vlc_mutex_lock( &p_sys->lock_sdp );
    free( p_sys->psz_sdp );
    p_sys->psz_sdp = psz_sdp;
    vlc_mutex_unlock( &p_sys->lock_sdp );
1205

1206
    msg_Dbg( p_stream, "sdp=\n%s", p_sys->psz_sdp );
1207

1208 1209
    /* Update SDP (sap/file) */
    if( p_sys->b_export_sap ) SapSetup( p_stream );
1210
    if( p_sys->psz_sdp_file != NULL ) FileSetup( p_stream );
1211 1212

    return id;
1213 1214 1215 1216

error:
    Del( p_stream, id );
    return NULL;
1217 1218
}

Gildas Bazin's avatar
 
Gildas Bazin committed
1219
static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
1220
{
1221 1222
    sout_stream_sys_t *p_sys = p_stream->p_sys;

1223
    vlc_mutex_lock( &p_sys->lock_es );
1224
    TAB_REMOVE( p_sys->i_es, p_sys->es, id );
1225
    vlc_mutex_unlock( &p_sys->lock_es );
1226

1227 1228 1229 1230 1231 1232 1233
    if( likely(id->p_fifo != NULL) )
    {
        vlc_cancel( id->thread );
        vlc_join( id->thread, NULL );
        block_FifoRelease( id->p_fifo );
    }

1234
    free( id->rtp_fmt.fmtp );
1235

1236 1237
    if (p_sys->p_vod_media != NULL)
        vod_detach_id(p_sys->p_vod_media, p_sys->psz_vod_session, id);
1238
    if( id->rtsp_id )
1239
        RtspDelId( p_sys->rtsp, id->rtsp_id );
1240 1241 1242 1243 1244 1245
    if( id->listen.fd != NULL )
    {
        vlc_cancel( id->listen.thread );
        vlc_join( id->listen.thread, NULL );
        net_ListenClose( id->listen.fd );
    }
1246 1247 1248 1249
    /* Delete remaining sinks (incoming connections or explicit
     * outgoing dst=) */
    while( id->sinkc > 0 )
        rtp_del_sink( id, id->sinkv[0].rtp_fd );
1250
#ifdef HAVE_SRTP
1251 1252
    if( id->srtp != NULL )
        srtp_destroy( id->srtp );
1253
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1254 1255

    vlc_mutex_destroy( &id->lock_sink );
1256

1257
    /* Update SDP (sap/file) */
1258
    if( p_sys->b_export_sap ) SapSetup( p_stream );
1259
    if( p_sys->psz_sdp_file != NULL ) FileSetup( p_stream );
1260

1261
    free( id );
1262 1263 1264
    return VLC_SUCCESS;
}

Gildas Bazin's avatar
 
Gildas Bazin committed
1265
static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
1266
                 block_t *p_buffer )
1267
{
1268
    block_t *p_next;
1269

1270
    assert( p_stream->p_sys->p_mux == NULL );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1271
    (void)p_stream;
1272

1273
    while( p_buffer != NULL )
1274 1275
    {
        p_next = p_buffer->p_next;
1276 1277 1278

        /* Send a Vorbis/Theora Packed Configuration packet (RFC 5215 §3.1)
         * as the first packet of the stream */
Pierre Ynard's avatar
Pierre Ynard committed
1279 1280 1281 1282 1283
        if (id->b_first_packet)
        {
            id->b_first_packet = false;
            if (!strcmp(id->rtp_fmt.ptname, "vorbis") ||
                !strcmp(id->rtp_fmt.ptname, "theora"))
1284 1285
                rtp_packetize_xiph_config(id, id->rtp_fmt.fmtp,
                                          p_buffer->i_pts);
Pierre Ynard's avatar
Pierre Ynard committed
1286
        }
1287

1288
        if( id->rtp_fmt.pf_packetize( id, p_buffer ) )
1289 1290
            break;

1291
        block_Release( p_buffer );
1292 1293 1294 1295 1296
        p_buffer = p_next;
    }
    return VLC_SUCCESS;
}

1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
/****************************************************************************
 * SAP:
 ****************************************************************************/
static int SapSetup( sout_stream_t *p_stream )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_instance_t   *p_sout = p_stream->p_sout;

    /* Remove the previous session */
    if( p_sys->p_session != NULL)
    {
        sout_AnnounceUnRegister( p_sout, p_sys->p_session);
        p_sys->p_session = NULL;
    }

1312
    if( p_sys->i_es > 0 && p_sys->psz_sdp && *p_sys->psz_sdp )
1313
        p_sys->p_session = sout_AnnounceRegisterSDP( p_sout,
1314
                                                     p_sys->psz_sdp,
1315
                                                     p_sys->psz_destination );
1316 1317 1318 1319

    return VLC_SUCCESS;
}

1320 1321 1322 1323 1324 1325 1326 1327
/****************************************************************************
* File:
****************************************************************************/
static int FileSetup( sout_stream_t *p_stream )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    FILE            *f;

1328 1329 1330
    if( p_sys->psz_sdp == NULL )
        return VLC_EGENERIC; /* too early */

1331
    if( ( f = vlc_fopen( p_sys->psz_sdp_file, "wt" ) ) == NULL )
1332
    {
1333 1334
        msg_Err( p_stream, "cannot open file '%s' (%m)",
                 p_sys->psz_sdp_file );
1335 1336 1337
        return VLC_EGENERIC;
    }

1338
    fputs( p_sys->psz_sdp, f );
1339 1340 1341 1342 1343
    fclose( f );

    return VLC_SUCCESS;
}

1344 1345 1346 1347 1348 1349 1350
/****************************************************************************
 * HTTP:
 ****************************************************************************/
static int  HttpCallback( httpd_file_sys_t *p_args,
                          httpd_file_t *, uint8_t *p_request,
                          uint8_t **pp_data, int *pi_data );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1351
static int HttpSetup( sout_stream_t *p_stream, const vlc_url_t *url)
1352 1353 1354
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;

1355
    p_sys->p_httpd_host = vlc_http_HostNew( VLC_OBJECT(p_stream) );
1356 1357 1358 1359 1360
    if( p_sys->p_httpd_host )
    {
        p_sys->p_httpd_file = httpd_FileNew( p_sys->p_httpd_host,
                                             url->psz_path ? url->psz_path : "/",
                                             "application/sdp",
1361
                                             NULL, NULL, NULL,
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
                                             HttpCallback, (void*)p_sys );
    }
    if( p_sys->p_httpd_file == NULL )
    {
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

static int  HttpCallback( httpd_file_sys_t *p_args,
                          httpd_file_t *f, uint8_t *p_request,
                          uint8_t **pp_data, int *pi_data )
{
1375
    VLC_UNUSED(f); VLC_UNUSED(p_request);
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
    sout_stream_sys_t *p_sys = (sout_stream_sys_t*)p_args;

    vlc_mutex_lock( &p_sys->lock_sdp );
    if( p_sys->psz_sdp && *p_sys->psz_sdp )
    {
        *pi_data = strlen( p_sys->psz_sdp );
        *pp_data = malloc( *pi_data );
        memcpy( *pp_data, p_sys->psz_sdp, *pi_data );
    }
    else
    {
        *pp_data = NULL;
        *pi_data = 0;
    }
    vlc_mutex_unlock( &p_sys->lock_sdp );

    return VLC_SUCCESS;
}

/****************************************************************************
1396
 * RTP send
1397
 ****************************************************************************/
1398
static void* ThreadSend( void *data )
1399
{
1400 1401 1402 1403
#ifdef WIN32
# define ECONNREFUSED WSAECONNREFUSED
# define ENOPROTOOPT  WSAENOPROTOOPT
# define EHOSTUNREACH WSAEHOSTUNREACH
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1404
# define ENETUNREACH  WSAENETUNREACH
1405 1406
# define ENETDOWN     WSAENETDOWN
# define ENOBUFS      WSAENOBUFS
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1407
# define EAGAIN       WSAEWOULDBLOCK
1408 1409
# define EWOULDBLOCK  WSAEWOULDBLOCK
#endif
1410
    sout_stream_id_t *id = data;
1411
    unsigned i_caching = id->i_caching;
1412

1413
    for (;;)
1414 1415
    {
        block_t *out = block_FifoGet( id->p_fifo );
1416
        block_cleanup_push (out);
1417

1418
#ifdef HAVE_SRTP
1419 1420 1421
        if( id->srtp )
        {   /* FIXME: this is awfully inefficient */
            size_t len = out->i_buffer;
1422 1423 1424
            out = block_Realloc( out, 0, len + 10 );
            out->i_buffer = len;

1425
            int canc = vlc_savecancel ();
1426
            int val = srtp_send( id->srtp, out->p_buffer, &len, len + 10 );
1427
            vlc_restorecancel (canc);
1428 1429 1430
            if( val )
            {
                errno = val;
1431
                msg_Dbg( id->p_stream, "SRTP sending error: %m" );
1432
                block_Release( out );
1433
                out = NULL;
1434
            }
1435 1436
            else
                out->i_buffer = len;
1437
        }
1438 1439 1440 1441 1442
        if (out)
            mwait (out->i_dts + i_caching);
        vlc_cleanup_pop ();
        if (out == NULL)
            continue;
1443 1444 1445 1446
#else
        mwait (out->i_dts + i_caching);
        vlc_cleanup_pop ();
#endif
1447

1448 1449
        ssize_t len = out->i_buffer;
        int canc = vlc_savecancel ();
1450

1451
        vlc_mutex_lock( &id->lock_sink );
1452 1453 1454
        unsigned deadc = 0; /* How many dead sockets? */
        int deadv[id->sinkc]; /* Dead sockets list */

1455
        for( int i = 0; i < id->sinkc; i++ )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1456
        {
1457
#ifdef HAVE_SRTP
1458
            if( !id->srtp ) /* FIXME: SRTCP support */
1459
#endif
1460
                SendRTCP( id->sinkv[i].rtcp, out );
1461

1462 1463 1464
            if( send( id->sinkv[i].rtp_fd, out->p_buffer, len, 0 ) == -1
             && net_errno != EAGAIN && net_errno != EWOULDBLOCK
             && net_errno != ENOBUFS && net_errno != ENOMEM )
1465
            {
1466 1467 1468 1469 1470
                int type;
                getsockopt( id->sinkv[i].rtp_fd, SOL_SOCKET, SO_TYPE,
                            &type, &(socklen_t){ sizeof(type) });
                if( type == SOCK_DGRAM )
                    /* ICMP soft error: ignore and retry */
1471
                    send( id->sinkv[i].rtp_fd, out->p_buffer, len, 0 );
1472 1473 1474
                else
                    /* Broken connection */
                    deadv[deadc++] = id->sinkv[i].rtp_fd;
1475
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1476
        }
1477
        id->i_seq_sent_next = ntohs(((uint16_t *) out->p_buffer)[1]) + 1;
1478 1479
        vlc_mutex_unlock( &id->lock_sink );
        block_Release( out );
1480 1481 1482

        for( unsigned i = 0; i < deadc; i++ )
        {
1483
            msg_Dbg( id->p_stream, "removing socket %d", deadv[i] );
1484 1485
            rtp_del_sink( id, deadv[i] );
        }
1486
        vlc_restorecancel (canc);
1487
    }
1488
    return NULL;
1489
}
1490

1491 1492 1493 1494 1495 1496 1497 1498 1499 1500

/* This thread dequeues incoming connections (DCCP streaming) */
static void *rtp_listen_thread( void *data )
{
    sout_stream_id_t *id = data;

    assert( id->listen.fd != NULL );

    for( ;; )
    {
1501
        int fd = net_Accept( id->p_stream, id->listen.fd );
1502 1503 1504
        if( fd == -1 )
            continue;
        int canc = vlc_savecancel( );
1505
        rtp_add_sink( id, fd, true, NULL );
1506 1507 1508 1509 1510 1511 1512
        vlc_restorecancel( canc );
    }

    assert( 0 );
}


1513
int rtp_add_sink( sout_stream_id_t *id, int fd, bool rtcp_mux, uint16_t *seq )
1514
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1515
    rtp_sink_t sink = { fd, NULL };
1516 1517
    sink.rtcp = OpenRTCP( VLC_OBJECT( id->p_stream ), fd, IPPROTO_UDP,
                          rtcp_mux );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1518
    if( sink.rtcp == NULL )
1519
        msg_Err( id->p_stream, "RTCP failed!" );
1520

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1521
    vlc_mutex_lock( &id->lock_sink );
1522
    INSERT_ELEM( id->sinkv, id->sinkc, id->sinkc, sink );
1523 1524
    if( seq != NULL )
        *seq = id->i_seq_sent_next;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1525
    vlc_mutex_unlock( &id->lock_sink );
1526
    return VLC_SUCCESS;
1527 1528
}

1529
void rtp_del_sink( sout_stream_id_t *id, int fd )
1530
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1531 1532
    rtp_sink_t sink = { fd, NULL };

1533
    /* NOTE: must be safe to use if fd is not included */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1534
    vlc_mutex_lock( &id->lock_sink );
1535 1536 1537 1538
    for( int i = 0; i < id->sinkc; i++ )
    {
        if (id->sinkv[i].rtp_fd == fd)
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1539
            sink = id->sinkv[i];
1540 1541 1542 1543
            REMOVE_ELEM( id->sinkv, id->sinkc, i );
            break;
        }
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1544
    vlc_mutex_unlock( &id->lock_sink );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1545 1546 1547

    CloseRTCP( sink.rtcp );
    net_Close( sink.rtp_fd );
1548 1549
}

1550
uint16_t rtp_get_seq( sout_stream_id_t *id )
1551
{
1552 1553 1554 1555 1556 1557 1558 1559
    /* This will return values for the next packet. */
    uint16_t seq;

    vlc_mutex_lock( &id->lock_sink );
    seq = id->i_seq_sent_next;
    vlc_mutex_unlock( &id->lock_sink );

    return seq;
1560 1561
}

1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
/* Return an arbitrary initial timestamp for RTP timestamp computations.
 * RFC 3550 states that the resulting initial RTP timestamps SHOULD be
 * random (although we use the same reference for all the ES as a
 * feature). In the VoD case, this function is called independently
 * from several parts of the code, so we need to always return the same
 * value. */
static int64_t rtp_init_ts( const vod_media_t *p_media,
                            const char *psz_vod_session )
{
    if (p_media == NULL || psz_vod_session == NULL)
        return mdate();

    uint64_t i_ts_init;
    /* As per RFC 2326, session identifiers are at least 8 bytes long */
    strncpy((char *)&i_ts_init, psz_vod_session, sizeof(uint64_t));
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1577
    i_ts_init ^= (uintptr_t)p_media;
1578 1579 1580 1581 1582 1583
    /* Limit the timestamp to 48 bytes, this is enough and allows us
     * to stay away from overflows */
    i_ts_init &= 0xFFFFFFFFFFFF;
    return i_ts_init;
}

1584
/* Return a timestamp corresponding to packets being sent now, and that
1585
 * can be passed to rtp_compute_ts() to get rtptime values for each ES.
1586 1587 1588
 * Also return the NPT corresponding to this timestamp. If the stream
 * output is not started, the initial timestamp that will be used with
 * the first packets for NPT=0 is returned instead. */
1589
int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id,
1590 1591
                    const vod_media_t *p_media, const char *psz_vod_session,
                    int64_t *p_npt )
1592
{
1593 1594 1595
    if (p_npt != NULL)
        *p_npt = 0;

1596 1597 1598 1599 1600 1601
    if (id != NULL)
        p_stream = id->p_stream;

    if (p_stream == NULL)
        return rtp_init_ts(p_media, psz_vod_session);

1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    mtime_t i_npt_zero;
    vlc_mutex_lock( &p_sys->lock_ts );
    i_npt_zero = p_sys->i_npt_zero;
    vlc_mutex_unlock( &p_sys->lock_ts );

    if( i_npt_zero == VLC_TS_INVALID )
        return p_sys->i_pts_zero;

    mtime_t now = mdate();
    if( now < i_npt_zero )
        return p_sys->i_pts_zero;

1615 1616 1617 1618 1619
    int64_t npt = now - i_npt_zero;
    if (p_npt != NULL)
        *p_npt = npt;

    return p_sys->i_pts_zero + npt; 
1620 1621
}

1622 1623
void rtp_packetize_common( sout_stream_id_t *id, block_t *out,
                           int b_marker, int64_t i_pts )
1624
{
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
    if( !id->b_ts_init )
    {
        sout_stream_sys_t *p_sys = id->p_stream->p_sys;
        vlc_mutex_lock( &p_sys->lock_ts );
        if( p_sys->i_npt_zero == VLC_TS_INVALID )
        {
            /* This is the first packet of any ES. We initialize the
             * NPT=0 time reference, and the offset to match the
             * arbitrary PTS reference. */
            p_sys->i_npt_zero = i_pts + id->i_caching;
            p_sys->i_pts_offset = p_sys->i_pts_zero - i_pts;
        }
        vlc_mutex_unlock( &p_sys->lock_ts );

        /* And in any case this is the first packet of this ES, so we
         * initialize the offset for this ES. */
1641 1642
        id->i_ts_offset = rtp_compute_ts( id->rtp_fmt.clock_rate,
                                          p_sys->i_pts_offset );
1643 1644 1645
        id->b_ts_init = true;
    }

1646 1647
    uint32_t i_timestamp = rtp_compute_ts( id->rtp_fmt.clock_rate, i_pts )
                           + id->i_ts_offset;
1648 1649

    out->p_buffer[0] = 0x80;
1650
    out->p_buffer[1] = (b_marker?0x80:0x00)|id->rtp_fmt.payload_type;
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
    out->p_buffer[2] = ( id->i_sequence >> 8)&0xff;
    out->p_buffer[3] = ( id->i_sequence     )&0xff;
    out->p_buffer[4] = ( i_timestamp >> 24 )&0xff;
    out->p_buffer[5] = ( i_timestamp >> 16 )&0xff;
    out->p_buffer[6] = ( i_timestamp >>  8 )&0xff;
    out->p_buffer[7] = ( i_timestamp       )&0xff;

    memcpy( out->p_buffer + 8, id->ssrc, 4 );

    out->i_buffer = 12;
    id->i_sequence++;
}

1664
void rtp_packetize_send( sout_stream_id_t *id, block_t *out )
1665
{
1666
    block_FifoPut( id->p_fifo, out );
1667
}
1668

1669 1670 1671 1672 1673
/**
 * @return configured max RTP payload size (including payload type-specific
 * headers, excluding RTP and transport headers)
 */
size_t rtp_mtu (const sout_stream_id_t *id)
1674
{
1675
    return id->i_mtu - 12;
1676 1677
}

1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
/*****************************************************************************
 * Non-RTP mux
 *****************************************************************************/

/** Add an ES to a non-RTP muxed stream */
static sout_stream_id_t *MuxAdd( sout_stream_t *p_stream, es_format_t *p_fmt )
{
    sout_input_t      *p_input;
    sout_mux_t *p_mux = p_stream->p_sys->p_mux;
    assert( p_mux != NULL );

    p_input = sout_MuxAddStream( p_mux, p_fmt );
    if( p_input == NULL )
    {
        msg_Err( p_stream, "cannot add this stream to the muxer" );
        return NULL;
    }

    return (sout_stream_id_t *)p_input;
}


static int MuxSend( sout_stream_t *p_stream, sout_stream_id_t *id,
                    block_t *p_buffer )
{
    sout_mux_t *p_mux = p_stream->p_sys->p_mux;
    assert( p_mux != NULL );

    sout_MuxSendBuffer( p_mux, (sout_input_t *)id, p_buffer );
    return VLC_SUCCESS;
}


/** Remove an ES from a non-RTP muxed stream */
static int MuxDel( sout_stream_t *p_stream, sout_stream_id_t *id )
{
    sout_mux_t *p_mux = p_stream->p_sys->p_mux;
    assert( p_mux != NULL );

    sout_MuxDeleteStream( p_mux, (sout_input_t *)id );
    return VLC_SUCCESS;
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1722 1723
static ssize_t AccessOutGrabberWriteBuffer( sout_stream_t *p_stream,
                                            const block_t *p_buffer )
1724 1725 1726 1727 1728 1729 1730
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t *id = p_sys->es[0];

    int64_t  i_dts = p_buffer->i_dts;

    uint8_t         *p_data = p_buffer->p_buffer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1731 1732
    size_t          i_data  = p_buffer->i_buffer;
    size_t          i_max   = id->i_mtu - 12;
1733

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1734
    size_t i_packet = ( p_buffer->i_buffer + i_max - 1 ) / i_max;
1735 1736 1737

    while( i_data > 0 )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1738
        size_t i_size;
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751

        /* output complete packet */
        if( p_sys->packet &&
            p_sys->packet->i_buffer + i_data > i_max )
        {
            rtp_packetize_send( id, p_sys->packet );
            p_sys->packet = NULL;
        }

        if( p_sys->packet == NULL )
        {
            /* allocate a new packet */
            p_sys->packet = block_New( p_stream, id->i_mtu );
1752
            rtp_packetize_common( id, p_sys->packet, 1, i_dts );
1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772
            p_sys->packet->i_dts = i_dts;
            p_sys->packet->i_length = p_buffer->i_length / i_packet;
            i_dts += p_sys->packet->i_length;
        }

        i_size = __MIN( i_data,
                        (unsigned)(id->i_mtu - p_sys->packet->i_buffer) );

        memcpy( &p_sys->packet->p_buffer[p_sys->packet->i_buffer],
                p_data, i_size );

        p_sys->packet->i_buffer += i_size;
        p_data += i_size;
        i_data -= i_size;
    }

    return VLC_SUCCESS;
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1773 1774
static ssize_t AccessOutGrabberWrite( sout_access_out_t *p_access,
                                      block_t *p_buffer )
1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790
{
    sout_stream_t *p_stream = (sout_stream_t*)p_access->p_sys;

    while( p_buffer )
    {
        block_t *p_next;

        AccessOutGrabberWriteBuffer( p_stream, p_buffer );

        p_next = p_buffer->p_next;
        block_Release( p_buffer );
        p_buffer = p_next;
    }

    return VLC_SUCCESS;
}
1791 1792 1793 1794 1795 1796


static sout_access_out_t *GrabberCreate( sout_stream_t *p_stream )
{
    sout_access_out_t *p_grab;

1797
    p_grab = vlc_object_create( p_stream, sizeof( *p_grab ) );
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809
    if( p_grab == NULL )
        return NULL;

    p_grab->p_module    = NULL;
    p_grab->psz_access  = strdup( "grab" );
    p_grab->p_cfg       = NULL;
    p_grab->psz_path    = strdup( "" );
    p_grab->p_sys       = (sout_access_out_sys_t *)p_stream;
    p_grab->pf_seek     = NULL;
    p_grab->pf_write    = AccessOutGrabberWrite;
    return p_grab;
}