live555.cpp 63.5 KB
Newer Older
1
/*****************************************************************************
2
 * live555.cpp : LIVE555 Streaming Media support.
3
 *****************************************************************************
4
 * Copyright (C) 2003-2007 the VideoLAN team
Carlo Calabrò's avatar
Carlo Calabrò committed
5
 * $Id$
6
7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
9
 *          Derk-Jan Hartman <hartman at videolan. org>
 *          Derk-Jan Hartman <djhartman at m2x .dot. nl> for M2X
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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.
 *
JP Dinger's avatar
JP Dinger committed
21
22
23
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
26
27
28
29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

30
31
32
33
34
/* For inttypes.h
 * Note: config.h may include inttypes.h, so make sure we define this option
 * early enough. */
#define __STDC_CONSTANT_MACROS 1

35
36
37
38
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

39
40
#include <inttypes.h>

41
#include <vlc_common.h>
42
#include <vlc_plugin.h>
43
#include <vlc_input.h>
zorglub's avatar
zorglub committed
44
#include <vlc_demux.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
45
#include <vlc_dialog.h>
46
#include <vlc_url.h>
47
#include <vlc_strings.h>
48
49

#include <iostream>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
50
#include <limits.h>
51
#include <assert.h>
52

53

gbazin's avatar
   
gbazin committed
54
55
56
57
#if defined( WIN32 )
#   include <winsock2.h>
#endif

58
59
60
61
#include <UsageEnvironment.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>
#include <liveMedia.hh>
62

63
64
65
66
extern "C" {
#include "../access/mms/asf.h"  /* Who said ugly ? */
}

67
68
69
70
71
using namespace std;

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
72
73
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
74

hartman's avatar
hartman committed
75
#define CACHING_TEXT N_("Caching value (ms)")
Laurent Aimar's avatar
Laurent Aimar committed
76
#define CACHING_LONGTEXT N_( \
Carlo Calabrò's avatar
Carlo Calabrò committed
77
    "Allows you to modify the default caching value for RTSP streams. This " \
78
    "value should be set in millisecond units." )
Laurent Aimar's avatar
Laurent Aimar committed
79

80
#define KASENNA_TEXT N_( "Kasenna RTSP dialect")
JP Dinger's avatar
JP Dinger committed
81
82
83
#define KASENNA_LONGTEXT N_( "Kasenna servers use an old and nonstandard " \
    "dialect of RTSP. With this parameter VLC will try this dialect, but "\
    "then it cannot connect to normal RTSP servers." )
84

85
86
87
88
89
#define WMSERVER_TEXT N_("WMServer RTSP dialect")
#define WMSERVER_LONGTEXT N_("WMServer uses an unstandard dialect " \
    "of RTSP. Selecting this parameter will tell VLC to assume some " \
    "options contrary to RFC 2326 guidelines.")

90
#define USER_TEXT N_("RTSP user name")
JP Dinger's avatar
JP Dinger committed
91
92
#define USER_LONGTEXT N_("Sets the username for the connection, " \
    "if no username or password are set in the url.")
93
#define PASS_TEXT N_("RTSP password")
JP Dinger's avatar
JP Dinger committed
94
95
#define PASS_LONGTEXT N_("Sets the password for the connection, " \
    "if no username or password are set in the url.")
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
vlc_module_begin ()
    set_description( N_("RTP/RTSP/SDP demuxer (using Live555)" ) )
    set_capability( "demux", 50 )
    set_shortname( "RTP/RTSP")
    set_callbacks( Open, Close )
    add_shortcut( "live" )
    add_shortcut( "livedotcom" )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )

    add_submodule ()
        set_description( N_("RTSP/RTP access and demux") )
        add_shortcut( "rtsp" )
        add_shortcut( "sdp" )
111
112
        add_shortcut( "live" )
        add_shortcut( "livedotcom" )
113
114
        set_capability( "access_demux", 0 )
        set_callbacks( Open, Close )
hartman's avatar
hartman committed
115
        add_bool( "rtsp-tcp", 0, NULL,
Carlo Calabrò's avatar
Carlo Calabrò committed
116
                  N_("Use RTP over RTSP (TCP)"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
117
                  N_("Use RTP over RTSP (TCP)"), true )
118
            change_safe()
119
120
        add_integer( "rtp-client-port", -1, NULL,
                  N_("Client port"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
121
                  N_("Port to use for the RTP source of the session"), true )
122
123
        add_bool( "rtsp-mcast", false, NULL,
                  N_("Force multicast RTP via RTSP"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
124
                  N_("Force multicast RTP via RTSP"), true )
125
            change_safe()
126
127
        add_bool( "rtsp-http", 0, NULL,
                  N_("Tunnel RTSP and RTP over HTTP"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
128
                  N_("Tunnel RTSP and RTP over HTTP"), true )
129
            change_safe()
130
131
        add_integer( "rtsp-http-port", 80, NULL,
                  N_("HTTP tunnel port"),
132
                  N_("Port to use for tunneling the RTSP/RTP over HTTP."),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
133
                  true )
hartman's avatar
hartman committed
134
        add_integer("rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
135
                    CACHING_TEXT, CACHING_LONGTEXT, true )
136
            change_safe()
137
        add_bool(   "rtsp-kasenna", false, NULL, KASENNA_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
138
                    KASENNA_LONGTEXT, true )
139
140
141
        add_bool(   "rtsp-wmserver", false, NULL, WMSERVER_TEXT,
                    WMSERVER_LONGTEXT, true)
            change_safe()
142
        add_string( "rtsp-user", NULL, NULL, USER_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
143
                    USER_LONGTEXT, true )
144
145
        add_password( "rtsp-pwd", NULL, NULL, PASS_TEXT,
                      PASS_LONGTEXT, true )
146
vlc_module_end ()
147
148
149


/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
150
 * Local prototypes
151
 *****************************************************************************/
152

153
154
typedef struct
{
155
156
    demux_t         *p_demux;
    MediaSubsession *sub;
157

158
159
    es_format_t     fmt;
    es_out_id_t     *p_es;
160

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
161
162
163
    bool            b_muxed;
    bool            b_quicktime;
    bool            b_asf;
164
    bool            b_discard_trunc;
165
    stream_t        *p_out_muxed;    /* for muxed stream */
166

167
168
    uint8_t         *p_buffer;
    unsigned int    i_buffer;
169

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
170
    bool            b_rtcp_sync;
171
172
    char            waiting;
    int64_t         i_pts;
173
    float           i_npt;
174

175
176
} live_track_t;

177
178
179
struct timeout_thread_t
{
    demux_sys_t  *p_sys;
180
181
    vlc_thread_t handle;
    bool         b_handle_keep_alive;
182
183
};

184
185
struct demux_sys_t
{
hartman's avatar
hartman committed
186
187
    char            *p_sdp;    /* XXX mallocated */
    char            *psz_path; /* URL-encoded path */
188
    vlc_url_t       url;
189
190
191
192
193
194

    MediaSession     *ms;
    TaskScheduler    *scheduler;
    UsageEnvironment *env ;
    RTSPClient       *rtsp;

195
    /* */
196
197
198
    int              i_track;
    live_track_t     **track;   /* XXX mallocated */

199
    /* Weird formats */
200
201
    asf_header_t     asfh;
    stream_t         *p_out_asf;
hartman's avatar
hartman committed
202
    bool             b_real;
203
204

    /* */
205
    int64_t          i_pcr; /* The clock */
206
    float            i_npt;
207
208
    float            i_npt_length;
    float            i_npt_start;
209

210
211
    /* timeout thread information */
    int              i_timeout;     /* session timeout value in seconds */
hartman's avatar
hartman committed
212
    bool             b_timeout_call;/* mark to send an RTSP call to prevent server timeout */
213
214
    timeout_thread_t *p_timeout;    /* the actual thread that makes sure we don't timeout */

215
    /* */
216
217
218
    bool             b_force_mcast;
    bool             b_multicast;   /* if one of the tracks is multicasted */
    bool             b_no_data;     /* if we never received any data */
219
220
    int              i_no_data_ti;  /* consecutive number of TaskInterrupt */

221
    char             event;
222
223

    bool             b_get_param;   /* Does the server support GET_PARAMETER */
224
    bool             b_paused;      /* Are we paused? */
225
226
227
228
229
};

static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );

hartman's avatar
hartman committed
230
231
232
233
234
static int Connect      ( demux_t * );
static int SessionsSetup( demux_t * );
static int Play         ( demux_t *);
static int ParseASF     ( demux_t * );
static int RollOverTcp  ( demux_t * );
235

hartman's avatar
hartman committed
236
237
238
static void StreamRead  ( void *, unsigned int, unsigned int,
                          struct timeval, unsigned int );
static void StreamClose ( void * );
239
static void TaskInterrupt( void * );
240

241
static void* TimeoutPrevention( void * );
242

243
static unsigned char* parseH264ConfigStr( char const* configStr,
244
                                          unsigned int& configSize );
245

246
247
248
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
249
static int  Open ( vlc_object_t *p_this )
250
{
251
    demux_t     *p_demux = (demux_t*)p_this;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
252
    demux_sys_t *p_sys = NULL;
253

ivoire's avatar
ivoire committed
254
    int i_return;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
255
    int i_error = VLC_EGENERIC;
256

Laurent Aimar's avatar
Laurent Aimar committed
257
    if( p_demux->s )
258
    {
Laurent Aimar's avatar
Laurent Aimar committed
259
260
        /* See if it looks like a SDP
           v, o, s fields are mandatory and in this order */
261
        const uint8_t *p_peek;
262
263
        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;

264
265
        if( memcmp( p_peek, "v=0\r\n", 5 ) &&
            memcmp( p_peek, "v=0\n", 4 ) &&
Laurent Aimar's avatar
Laurent Aimar committed
266
267
268
269
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            return VLC_EGENERIC;
        }
270
    }
Laurent Aimar's avatar
Laurent Aimar committed
271
    else
272
    {
Laurent Aimar's avatar
Laurent Aimar committed
273
        var_Create( p_demux, "rtsp-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
274
275
    }

276
277
278
    p_demux->pf_demux  = Demux;
    p_demux->pf_control= Control;
    p_demux->p_sys     = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
279
280
    if( !p_sys ) return VLC_ENOMEM;

281
282
283
284
285
286
287
    p_sys->p_sdp = NULL;
    p_sys->scheduler = NULL;
    p_sys->env = NULL;
    p_sys->ms = NULL;
    p_sys->rtsp = NULL;
    p_sys->i_track = 0;
    p_sys->track   = NULL;
288
    p_sys->i_pcr = 0;
289
290
291
    p_sys->i_npt = 0.;
    p_sys->i_npt_start = 0.;
    p_sys->i_npt_length = 0.;
292
    p_sys->p_out_asf = NULL;
293
    p_sys->b_no_data = true;
294
    p_sys->i_no_data_ti = 0;
295
296
    p_sys->p_timeout = NULL;
    p_sys->i_timeout = 0;
297
298
299
    p_sys->b_timeout_call = false;
    p_sys->b_multicast = false;
    p_sys->b_real = false;
hartman's avatar
hartman committed
300
    p_sys->psz_path = strdup( p_demux->psz_path );
301
    p_sys->b_force_mcast = var_CreateGetBool( p_demux, "rtsp-mcast" );
302
    p_sys->b_get_param = false;
303
    p_sys->b_paused = false;
304

305
306
307
    /* parse URL for rtsp://[user:[passwd]@]serverip:port/options */
    vlc_UrlParse( &p_sys->url, p_sys->psz_path, 0 );

308
309
    if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
    {
310
        msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
311
312
        goto error;
    }
313
    if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
314
    {
315
        msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
316
317
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
318

319
    if( strcasecmp( p_demux->psz_access, "sdp" ) )
320
    {
321
        char *p = p_sys->psz_path;
322
        while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
323
324
    }

hartman's avatar
hartman committed
325
    if( p_demux->s != NULL )
Laurent Aimar's avatar
Laurent Aimar committed
326
327
    {
        /* Gather the complete sdp file */
hartman's avatar
hartman committed
328
329
330
        int     i_sdp       = 0;
        int     i_sdp_max   = 1000;
        uint8_t *p_sdp      = (uint8_t*) malloc( i_sdp_max );
331

Jean-Paul Saman's avatar
Jean-Paul Saman committed
332
333
334
335
336
337
        if( !p_sdp )
        {
            i_error = VLC_ENOMEM;
            goto error;
        }

Laurent Aimar's avatar
Laurent Aimar committed
338
339
        for( ;; )
        {
340
341
            int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp],
                                      i_sdp_max - i_sdp - 1 );
Laurent Aimar's avatar
Laurent Aimar committed
342

343
            if( !vlc_object_alive (p_demux) || p_demux->b_error )
344
345
            {
                free( p_sdp );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
346
                goto error;
347
            }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
348

Laurent Aimar's avatar
Laurent Aimar committed
349
350
351
            if( i_read < 0 )
            {
                msg_Err( p_demux, "failed to read SDP" );
352
                free( p_sdp );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
353
                goto error;
Laurent Aimar's avatar
Laurent Aimar committed
354
355
356
357
358
359
360
361
362
363
364
365
366
367
            }

            i_sdp += i_read;

            if( i_read < i_sdp_max - i_sdp - 1 )
            {
                p_sdp[i_sdp] = '\0';
                break;
            }

            i_sdp_max += 1000;
            p_sdp = (uint8_t*)realloc( p_sdp, i_sdp_max );
        }
        p_sys->p_sdp = (char*)p_sdp;
368
    }
369
370
    else if( ( p_demux->s == NULL ) &&
             !strcasecmp( p_demux->psz_access, "sdp" ) )
hartman's avatar
hartman committed
371
372
373
374
375
    {
        /* sdp:// link from SAP */
        p_sys->p_sdp = strdup( p_sys->psz_path );
    }
    else if( ( i_return = Connect( p_demux ) ) != VLC_SUCCESS )
376
    {
hartman's avatar
hartman committed
377
        msg_Err( p_demux, "Failed to connect with rtsp://%s", p_sys->psz_path );
378
379
        goto error;
    }
380

hartman's avatar
hartman committed
381
    if( p_sys->p_sdp == NULL )
382
    {
hartman's avatar
hartman committed
383
        msg_Err( p_demux, "Failed to retrieve the RTSP Session Description" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
384
        i_error = VLC_ENOMEM;
hartman's avatar
hartman committed
385
        goto error;
386
387
    }

hartman's avatar
hartman committed
388
389
390
391
392
    if( ( i_return = SessionsSetup( p_demux ) ) != VLC_SUCCESS )
    {
        msg_Err( p_demux, "Nothing to play for rtsp://%s", p_sys->psz_path );
        goto error;
    }
393

394
    if( p_sys->b_real ) goto error;
395

hartman's avatar
hartman committed
396
397
    if( ( i_return = Play( p_demux ) ) != VLC_SUCCESS )
        goto error;
398

399
400
401
402
403
404
405
    if( p_sys->p_out_asf && ParseASF( p_demux ) )
    {
        msg_Err( p_demux, "cannot find a usable asf header" );
        /* TODO Clean tracks */
        goto error;
    }

Laurent Aimar's avatar
Laurent Aimar committed
406
407
408
    if( p_sys->i_track <= 0 )
        goto error;

409
410
411
    return VLC_SUCCESS;

error:
ivoire's avatar
ivoire committed
412
    Close( p_this );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
413
    return i_error;
414
415
416
417
418
}

/*****************************************************************************
 * DemuxClose:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
419
static void Close( vlc_object_t *p_this )
420
{
421
    demux_t *p_demux = (demux_t*)p_this;
422
423
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
424
425
426
427
428

    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

429
        if( tk->b_muxed ) stream_Delete( tk->p_out_muxed );
430
        es_format_Clean( &tk->fmt );
431
        free( tk->p_buffer );
432
433
        free( tk );
    }
434
435

    if( p_sys->i_track ) free( p_sys->track );
436
    if( p_sys->p_out_asf ) stream_Delete( p_sys->p_out_asf );
437
    if( p_sys->rtsp && p_sys->ms ) p_sys->rtsp->teardownMediaSession( *p_sys->ms );
438
439
    if( p_sys->p_timeout )
    {
440
441
442
        vlc_cancel( p_sys->p_timeout->handle );
        vlc_join( p_sys->p_timeout->handle, NULL );
        free( p_sys->p_timeout );
443
    }
444
445
446
    if( p_sys->ms ) Medium::close( p_sys->ms );
    if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
    if( p_sys->env ) p_sys->env->reclaim();
447
448
449
    delete p_sys->scheduler;
    free( p_sys->p_sdp );
    free( p_sys->psz_path );
450

451
452
    vlc_UrlClean( &p_sys->url );

453
454
455
    free( p_sys );
}

456
457
458
static inline const char *strempty( const char *s ) { return s?s:""; }
static inline Boolean toBool( bool b ) { return b?True:False; } // silly, no?

hartman's avatar
hartman committed
459
460
461
462
463
464
465
/*****************************************************************************
 * Connect: connects to the RTSP server to setup the session DESCRIBE
 *****************************************************************************/
static int Connect( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    Authenticator authenticator;
466
    bool b_firstpass  = true;
hartman's avatar
hartman committed
467
468
469
470
471
472
473
    char *psz_user    = NULL;
    char *psz_pwd     = NULL;
    char *psz_url     = NULL;
    char *psz_options = NULL;
    char *p_sdp       = NULL;
    int  i_http_port  = 0;
    int  i_ret        = VLC_SUCCESS;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
474

475
476
477
478
479
480
481
482
    /* Create the url using the port number if available */
    if( p_sys->url.i_port == 0 )
    {
        p_sys->url.i_port = 554;
        if( asprintf( &psz_url, "rtsp://%s", p_sys->psz_path ) == -1 )
            return VLC_ENOMEM;
    }
    else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
483
    {
484
485
486
487
        if( asprintf( &psz_url, "rtsp://%s:%d%s", p_sys->url.psz_host,
                      p_sys->url.i_port, p_sys->url.psz_path ) == -1 )
            return VLC_ENOMEM;
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
488

489
490
491
    /* Get the user name and password */
    if( p_sys->url.psz_username || p_sys->url.psz_password )
    {
492
493
        psz_user = strdup( strempty( p_sys->url.psz_username ) );
        psz_pwd  = strdup( strempty( p_sys->url.psz_password ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
494
495
496
497
498
499
500
    }
    else
    {
        psz_user = var_CreateGetString( p_demux, "rtsp-user" );
        psz_pwd  = var_CreateGetString( p_demux, "rtsp-pwd" );
    }

hartman's avatar
hartman committed
501
createnew:
502
    if( !vlc_object_alive (p_demux) || p_demux->b_error )
503
    {
504
505
        i_ret = VLC_EGENERIC;
        goto bailout;
506
507
    }

hartman's avatar
hartman committed
508
509
510
    if( var_CreateGetBool( p_demux, "rtsp-http" ) )
        i_http_port = var_CreateGetInteger( p_demux, "rtsp-http-port" );

511
    if( ( p_sys->rtsp = RTSPClient::createNew( *p_sys->env,
512
          var_CreateGetInteger( p_demux, "verbose" ) > 1,
hartman's avatar
hartman committed
513
514
515
516
          "VLC media player", i_http_port ) ) == NULL )
    {
        msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                 p_sys->env->getResultMsg() );
517
518
        i_ret = VLC_EGENERIC;
        goto bailout;
hartman's avatar
hartman committed
519
    }
520

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
521
522
523
524
    /* Kasenna enables KeepAlive by analysing the User-Agent string.
     * Appending _KA to the string should be enough to enable this feature,
     * however, there is a bug where the _KA doesn't get parsed from the
     * default User-Agent as created by VLC/Live555 code. This is probably due
525
526
527
528
529
530
531
532
     * to spaces in the string or the string being too long. Here we override
     * the default string with a more compact version.
     */
    if( var_CreateGetBool( p_demux, "rtsp-kasenna" ))
    {
        p_sys->rtsp->setUserAgentString( "VLC_MEDIA_PLAYER_KA" );
    }

533
describe:
ivoire's avatar
ivoire committed
534
    authenticator.setUsernameAndPassword( psz_user, psz_pwd );
535

536
    /* */
537
    { /* i_timeout hack scope */
538
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1223337600
ivoire's avatar
ivoire committed
539
    const int i_timeout = var_CreateGetInteger(p_demux, "ipv4-timeout") / 1000;
540
    psz_options = p_sys->rtsp->sendOptionsCmd( psz_url, psz_user, psz_pwd,
541
                                               &authenticator, i_timeout );
542
#else
543
544
    psz_options = p_sys->rtsp->sendOptionsCmd( psz_url, psz_user, psz_pwd,
                                               &authenticator );
545
#endif
546
547
548
549
550
    if( psz_options == NULL && authenticator.realm() != NULL )
    {
        // try again, with the realm set this time
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1223337600
        psz_options = p_sys->rtsp->sendOptionsCmd( psz_url, psz_user, psz_pwd,
551
                                               &authenticator, i_timeout );
552
553
554
555
556
#else
        psz_options = p_sys->rtsp->sendOptionsCmd( psz_url, psz_user, psz_pwd,
                                               &authenticator );
#endif
    }
557
    if( psz_options )
558
        p_sys->b_get_param = (bool)strstr( psz_options, "GET_PARAMETER" );
559
560
    delete [] psz_options;

561
562
563
    if( var_CreateGetBool( p_demux, "rtsp-wmserver" ) )
       p_sys->b_get_param = true;

564
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1223337600
565
566
    p_sdp = p_sys->rtsp->describeWithPassword( psz_url, psz_user, psz_pwd,
                          var_GetBool( p_demux, "rtsp-kasenna" ), i_timeout );
567
#else
568
569
    p_sdp = p_sys->rtsp->describeWithPassword( psz_url, psz_user, psz_pwd,
                                     var_GetBool( p_demux, "rtsp-kasenna" ) );
570
#endif
571
    } /* i_timeout scope end */
572

hartman's avatar
hartman committed
573
574
    if( p_sdp == NULL )
    {
575
        /* failure occurred */
hartman's avatar
hartman committed
576
577
        int i_code = 0;
        const char *psz_error = p_sys->env->getResultMsg();
578

579
        if( var_GetBool( p_demux, "rtsp-http" ) )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
580
581
            sscanf( psz_error, "%*s %*s HTTP GET %*s HTTP/%*u.%*u %3u %*s",
                    &i_code );
582
583
584
        else
        {
            const char *psz_tmp = strstr( psz_error, "RTSP" );
585
586
587
588
            if( psz_tmp )
                sscanf( psz_tmp, "RTSP/%*s%3u", &i_code );
            else
                i_code = 0;
589
        }
590
        msg_Dbg( p_demux, "DESCRIBE failed with %d: %s", i_code, psz_error );
591

592
593
594
595
596
        if( b_firstpass )
        {   /* describeURL always returns an "RTSP/1.0 401 Unauthorized" the
             * first time. This is a workaround to avoid asking for a
             * user/passwd the first time the code passess here. */
            i_code = 0;
597
            b_firstpass = false;
598
599
        }

hartman's avatar
hartman committed
600
601
602
603
        if( i_code == 401 )
        {
            msg_Dbg( p_demux, "authentication failed" );

604
605
            free( psz_user );
            free( psz_pwd );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
606
            dialog_Login( p_demux, &psz_user, &psz_pwd,
607
                          _("RTSP authentication"), "%s",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
608
609
                        _("Please enter a valid login name and a password.") );
            if( psz_user != NULL && psz_pwd != NULL )
hartman's avatar
hartman committed
610
            {
ivoire's avatar
ivoire committed
611
                msg_Dbg( p_demux, "retrying with user=%s", psz_user );
612
                goto describe;
hartman's avatar
hartman committed
613
614
            }
        }
615
        else if( (i_code != 0) && !var_GetBool( p_demux, "rtsp-http" ) )
hartman's avatar
hartman committed
616
617
618
        {
            /* Perhaps a firewall is being annoying. Try HTTP tunneling mode */
            msg_Dbg( p_demux, "we will now try HTTP tunneling mode" );
ivoire's avatar
ivoire committed
619
            var_SetBool( p_demux, "rtsp-http", true );
hartman's avatar
hartman committed
620
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
621
            p_sys->rtsp = NULL;
hartman's avatar
hartman committed
622
623
            goto createnew;
        }
624
625
        else
        {
sebastien's avatar
sebastien committed
626
            msg_Dbg( p_demux, "connection timeout" );
627
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
628
            p_sys->rtsp = NULL;
629
        }
hartman's avatar
hartman committed
630
631
632
        i_ret = VLC_EGENERIC;
    }

633
    free( p_sys->p_sdp );
634
    p_sys->p_sdp = NULL;
hartman's avatar
hartman committed
635
    if( p_sdp ) p_sys->p_sdp = strdup( (char*)p_sdp );
hartman's avatar
hartman committed
636
637
    delete[] p_sdp;

638
639
640
641
642
643
bailout:
    /* malloc-ated copy */
    free( psz_url );
    free( psz_user );
    free( psz_pwd );

hartman's avatar
hartman committed
644
645
646
647
    return i_ret;
}

/*****************************************************************************
648
 * SessionsSetup: prepares the subsessions and does the SETUP
hartman's avatar
hartman committed
649
650
651
652
653
654
655
 *****************************************************************************/
static int SessionsSetup( demux_t *p_demux )
{
    demux_sys_t             *p_sys  = p_demux->p_sys;
    MediaSubsessionIterator *iter   = NULL;
    MediaSubsession         *sub    = NULL;

hartman's avatar
hartman committed
656
    bool           b_rtsp_tcp = false;
hartman's avatar
hartman committed
657
658
659
660
661
    int            i_client_port;
    int            i_return = VLC_SUCCESS;
    unsigned int   i_buffer = 0;
    unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */

662
663
    b_rtsp_tcp    = var_CreateGetBool( p_demux, "rtsp-tcp" ) ||
                    var_GetBool( p_demux, "rtsp-http" );
hartman's avatar
hartman committed
664
665
    i_client_port = var_CreateGetInteger( p_demux, "rtp-client-port" );

666
667
668
669
670
671
672
673
    /* Create the session from the SDP */
    if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
    {
        msg_Err( p_demux, "Could not create the RTSP Session: %s",
            p_sys->env->getResultMsg() );
        return VLC_EGENERIC;
    }

hartman's avatar
hartman committed
674
675
676
677
678
    /* Initialise each media subsession */
    iter = new MediaSubsessionIterator( *p_sys->ms );
    while( ( sub = iter->next() ) != NULL )
    {
        Boolean bInit;
679
        live_track_t *tk;
hartman's avatar
hartman committed
680

681
        if( !vlc_object_alive (p_demux) || p_demux->b_error )
682
683
684
685
686
        {
            delete iter;
            return VLC_EGENERIC;
        }

hartman's avatar
hartman committed
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
        /* Value taken from mplayer */
        if( !strcmp( sub->mediumName(), "audio" ) )
            i_buffer = 100000;
        else if( !strcmp( sub->mediumName(), "video" ) )
            i_buffer = 2000000;
        else continue;

        if( i_client_port != -1 )
        {
            sub->setClientPortNum( i_client_port );
            i_client_port += 2;
        }

        if( strcasestr( sub->codecName(), "REAL" ) )
        {
            msg_Info( p_demux, "real codec detected, using real-RTSP instead" );
703
            p_sys->b_real = true; /* This is a problem, we'll handle it later */
704
            continue;
hartman's avatar
hartman committed
705
706
707
708
709
710
        }

        if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            bInit = sub->initiate( 4 ); /* Constant ? */
        else
            bInit = sub->initiate();
711

hartman's avatar
hartman committed
712
713
714
715
716
717
718
719
720
721
722
        if( !bInit )
        {
            msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
                      sub->mediumName(), sub->codecName(),
                      p_sys->env->getResultMsg() );
        }
        else
        {
            if( sub->rtpSource() != NULL )
            {
                int fd = sub->rtpSource()->RTPgs()->socketNum();
723

hartman's avatar
hartman committed
724
725
726
                /* Increase the buffer size */
                if( i_buffer > 0 )
                    increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
727

hartman's avatar
hartman committed
728
729
730
731
732
733
734
735
736
                /* Increase the RTP reorder timebuffer just a bit */
                sub->rtpSource()->setPacketReorderingThresholdTime(thresh);
            }
            msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
                     sub->codecName() );

            /* Issue the SETUP */
            if( p_sys->rtsp )
            {
737
                if( !p_sys->rtsp->setupMediaSubsession( *sub, False,
738
739
                                                        toBool( b_rtsp_tcp ),
                             toBool( p_sys->b_force_mcast && !b_rtsp_tcp ) ) )
hartman's avatar
hartman committed
740
                {
741
742
743
744
745
746
                    /* if we get an unsupported transport error, toggle TCP
                     * use and try again */
                    if( !strstr(p_sys->env->getResultMsg(),
                                "461 Unsupported Transport")
                        || !p_sys->rtsp->setupMediaSubsession( *sub, False,
                                               toBool( b_rtsp_tcp ), False ) )
747
                    {
748
749
750
                        msg_Err( p_demux, "SETUP of'%s/%s' failed %s",
                                 sub->mediumName(), sub->codecName(),
                                 p_sys->env->getResultMsg() );
751
752
                        continue;
                    }
hartman's avatar
hartman committed
753
                }
754
            }
hartman's avatar
hartman committed
755

756
757
            /* Check if we will receive data from this subsession for
             * this track */
758
            if( sub->readSource() == NULL ) continue;
hartman's avatar
hartman committed
759
760
            if( !p_sys->b_multicast )
            {
761
                /* We need different rollover behaviour for multicast */
hartman's avatar
hartman committed
762
763
                p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
            }
764
765

            tk = (live_track_t*)malloc( sizeof( live_track_t ) );
766
767
768
769
770
            if( !tk )
            {
                delete iter;
                return VLC_ENOMEM;
            }
771
772
773
            tk->p_demux     = p_demux;
            tk->sub         = sub;
            tk->p_es        = NULL;
774
775
776
            tk->b_quicktime = false;
            tk->b_asf       = false;
            tk->b_muxed     = false;
777
            tk->b_discard_trunc = false;
778
779
            tk->p_out_muxed = NULL;
            tk->waiting     = 0;
780
            tk->b_rtcp_sync = false;
781
            tk->i_pts       = 0;
782
            tk->i_npt       = 0.;
783
784
            tk->i_buffer    = 65536;
            tk->p_buffer    = (uint8_t *)malloc( 65536 );
785
786
            if( !tk->p_buffer )
            {
Christophe Mutricy's avatar
Christophe Mutricy committed
787
                free( tk );
788
789
790
                delete iter;
                return VLC_ENOMEM;
            }
791
792
793
794
795
796
797
798
799
800
801
802

            /* Value taken from mplayer */
            if( !strcmp( sub->mediumName(), "audio" ) )
            {
                es_format_Init( &tk->fmt, AUDIO_ES, VLC_FOURCC('u','n','d','f') );
                tk->fmt.audio.i_channels = sub->numChannels();
                tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();

                if( !strcmp( sub->codecName(), "MPA" ) ||
                    !strcmp( sub->codecName(), "MPA-ROBUST" ) ||
                    !strcmp( sub->codecName(), "X-MP3-DRAFT-00" ) )
                {
803
                    tk->fmt.i_codec = VLC_CODEC_MPGA;
804
805
806
807
                    tk->fmt.audio.i_rate = 0;
                }
                else if( !strcmp( sub->codecName(), "AC3" ) )
                {
808
                    tk->fmt.i_codec = VLC_CODEC_A52;
809
810
811
812
813
814
815
816
817
818
819
820
821
822
                    tk->fmt.audio.i_rate = 0;
                }
                else if( !strcmp( sub->codecName(), "L16" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 't', 'w', 'o', 's' );
                    tk->fmt.audio.i_bitspersample = 16;
                }
                else if( !strcmp( sub->codecName(), "L8" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'a', 'r', 'a', 'w' );
                    tk->fmt.audio.i_bitspersample = 8;
                }
                else if( !strcmp( sub->codecName(), "PCMU" ) )
                {
823
                    tk->fmt.i_codec = VLC_CODEC_MULAW;
824
825
826
                }
                else if( !strcmp( sub->codecName(), "PCMA" ) )
                {
827
                    tk->fmt.i_codec = VLC_CODEC_ALAW;
828
829
830
                }
                else if( !strncmp( sub->codecName(), "G726", 4 ) )
                {
831
                    tk->fmt.i_codec = VLC_CODEC_ADPCM_G726;
832
833
834
835
836
837
838
839
840
841
842
843
844
                    tk->fmt.audio.i_rate = 8000;
                    tk->fmt.audio.i_channels = 1;
                    if( !strcmp( sub->codecName()+5, "40" ) )
                        tk->fmt.i_bitrate = 40000;
                    else if( !strcmp( sub->codecName()+5, "32" ) )
                        tk->fmt.i_bitrate = 32000;
                    else if( !strcmp( sub->codecName()+5, "24" ) )
                        tk->fmt.i_bitrate = 24000;
                    else if( !strcmp( sub->codecName()+5, "16" ) )
                        tk->fmt.i_bitrate = 16000;
                }
                else if( !strcmp( sub->codecName(), "AMR" ) )
                {
845
                    tk->fmt.i_codec = VLC_CODEC_AMR_NB;
846
847
848
                }
                else if( !strcmp( sub->codecName(), "AMR-WB" ) )
                {
849
                    tk->fmt.i_codec = VLC_CODEC_AMR_WB;
850
851
852
853
854
855
                }
                else if( !strcmp( sub->codecName(), "MP4A-LATM" ) )
                {
                    unsigned int i_extra;
                    uint8_t      *p_extra;

856
                    tk->fmt.i_codec = VLC_CODEC_MP4A;
857
858
859
860
861
862
863
864
865

                    if( ( p_extra = parseStreamMuxConfigStr( sub->fmtp_config(),
                                                             i_extra ) ) )
                    {
                        tk->fmt.i_extra = i_extra;
                        tk->fmt.p_extra = malloc( i_extra );
                        memcpy( tk->fmt.p_extra, p_extra, i_extra );
                        delete[] p_extra;
                    }
866
867
868
                    /* Because the "faad" decoder does not handle the LATM
                     * data length field at the start of each returned LATM
                     * frame, tell the RTP source to omit it. */
869
870
871
872
873
874
875
                    ((MPEG4LATMAudioRTPSource*)sub->rtpSource())->omitLATMDataLengthField();
                }
                else if( !strcmp( sub->codecName(), "MPEG4-GENERIC" ) )
                {
                    unsigned int i_extra;
                    uint8_t      *p_extra;

876
                    tk->fmt.i_codec = VLC_CODEC_MP4A;
877
878
879
880
881
882
883
884
885
886
887
888

                    if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                           i_extra ) ) )
                    {
                        tk->fmt.i_extra = i_extra;
                        tk->fmt.p_extra = malloc( i_extra );
                        memcpy( tk->fmt.p_extra, p_extra, i_extra );
                        delete[] p_extra;
                    }
                }
                else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
                {
889
                    tk->b_asf = true;
890
891
892
893
894
895
896
                    if( p_sys->p_out_asf == NULL )
                        p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                            p_demux->out );
                }
                else if( !strcmp( sub->codecName(), "X-QT" ) ||
                         !strcmp( sub->codecName(), "X-QUICKTIME" ) )
                {
897
                    tk->b_quicktime = true;
898
                }
899
900
                else if( !strcmp( sub->codecName(), "SPEEX" ) )
                {
901
                    tk->fmt.i_codec = VLC_FOURCC( 's', 'p', 'x', 'r' );
902
903
                    if ( sub->rtpTimestampFrequency() )
                        tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();
904
                    else
905
906
907
908
                    {
                        msg_Warn( p_demux,"Using 8kHz as default sample rate." );
                        tk->fmt.audio.i_rate = 8000;
                    }
909
                }
910
911
912
913
914
915
            }
            else if( !strcmp( sub->mediumName(), "video" ) )
            {
                es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
                if( !strcmp( sub->codecName(), "MPV" ) )
                {
916
                    tk->fmt.i_codec = VLC_CODEC_MPGV;
917
918
919
920
921
                }
                else if( !strcmp( sub->codecName(), "H263" ) ||
                         !strcmp( sub->codecName(), "H263-1998" ) ||
                         !strcmp( sub->codecName(), "H263-2000" ) )
                {
922
                    tk->fmt.i_codec = VLC_CODEC_H263;
923
924
925
                }
                else if( !strcmp( sub->codecName(), "H261" ) )
                {
926
                    tk->fmt.i_codec = VLC_CODEC_H261;
927
928
929
930
931
932
                }
                else if( !strcmp( sub->codecName(), "H264" ) )
                {
                    unsigned int i_extra = 0;
                    uint8_t      *p_extra = NULL;

933
                    tk->fmt.i_codec = VLC_CODEC_H264;
934
                    tk->fmt.b_packetized = false;
935
936
937
938
939
940
941
942
943
944
945
946
947

                    if((p_extra=parseH264ConfigStr( sub->fmtp_spropparametersets(),
                                                    i_extra ) ) )
                    {
                        tk->fmt.i_extra = i_extra;
                        tk->fmt.p_extra = malloc( i_extra );
                        memcpy( tk->fmt.p_extra, p_extra, i_extra );

                        delete[] p_extra;
                    }
                }
                else if( !strcmp( sub->codecName(), "JPEG" ) )
                {
948
                    tk->fmt.i_codec = VLC_CODEC_MJPG;
949
950
951
952
953
954
                }
                else if( !strcmp( sub->codecName(), "MP4V-ES" ) )
                {
                    unsigned int i_extra;
                    uint8_t      *p_extra;

955
                    tk->fmt.i_codec = VLC_CODEC_MP4V;
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971

                    if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                           i_extra ) ) )
                    {
                        tk->fmt.i_extra = i_extra;
                        tk->fmt.p_extra = malloc( i_extra );
                        memcpy( tk->fmt.p_extra, p_extra, i_extra );
                        delete[] p_extra;
                    }
                }
                else if( !strcmp( sub->codecName(), "X-QT" ) ||
                         !strcmp( sub->codecName(), "X-QUICKTIME" ) ||
                         !strcmp( sub->codecName(), "X-QDM" ) ||
                         !strcmp( sub->codecName(), "X-SV3V-ES" )  ||
                         !strcmp( sub->codecName(), "X-SORENSONVIDEO" ) )
                {
972
                    tk->b_quicktime = true;
973
974
975
                }
                else if( !strcmp( sub->codecName(), "MP2T" ) )
                {
976
                    tk->b_muxed = true;
977
978
979
980
981
                    tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
                }
                else if( !strcmp( sub->codecName(), "MP2P" ) ||
                         !strcmp( sub->codecName(), "MP1S" ) )
                {
982
                    tk->b_muxed = true;
983
984
985
986
987
                    tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
                                                       p_demux->out );
                }
                else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
                {
988
                    tk->b_asf = true;
989
990