live555.cpp 83 KB
Newer Older
1
/*****************************************************************************
2
 * live555.cpp : LIVE555 Streaming Media support.
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2003-2007 VLC authors and VideoLAN
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
 *          Sébastien Escudier <sebastien-devel celeos eu>
11
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
12 13 14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15 16 17 18
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
22 23 24
 * You should have received a copy of the GNU Lesser 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.
25 26 27 28 29 30
 *****************************************************************************/

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

31 32 33 34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35 36
#include <inttypes.h>

37
#include <vlc_common.h>
38
#include <vlc_plugin.h>
39
#include <vlc_input.h>
Clément Stenac's avatar
Clément Stenac committed
40
#include <vlc_demux.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
41
#include <vlc_dialog.h>
42
#include <vlc_url.h>
43
#include <vlc_strings.h>
44
#include <vlc_interrupt.h>
Thomas Guillem's avatar
Thomas Guillem committed
45
#include <vlc_keystore.h>
46

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
47
#include <limits.h>
48
#include <assert.h>
49

50
#include <new>
51

52
#if defined( _WIN32 )
Gildas Bazin's avatar
 
Gildas Bazin committed
53 54 55
#   include <winsock2.h>
#endif

56 57 58 59
#include <UsageEnvironment.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>
#include <liveMedia.hh>
60
#include <liveMedia_version.hh>
61
#include <Base64.hh>
62

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

67 68 69
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
70 71
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
72

73
#define KASENNA_TEXT N_( "Kasenna RTSP dialect")
JP Dinger's avatar
JP Dinger committed
74 75 76
#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." )
77

78
#define WMSERVER_TEXT N_("WMServer RTSP dialect")
79
#define WMSERVER_LONGTEXT N_("WMServer uses a nonstandard dialect " \
80 81 82
    "of RTSP. Selecting this parameter will tell VLC to assume some " \
    "options contrary to RFC 2326 guidelines.")

83
#define USER_TEXT N_("Username")
JP Dinger's avatar
JP Dinger committed
84 85
#define USER_LONGTEXT N_("Sets the username for the connection, " \
    "if no username or password are set in the url.")
86
#define PASS_TEXT N_("Password")
JP Dinger's avatar
JP Dinger committed
87 88
#define PASS_LONGTEXT N_("Sets the password for the connection, " \
    "if no username or password are set in the url.")
89 90 91 92
#define FRAME_BUFFER_SIZE_TEXT N_("RTSP frame buffer size")
#define FRAME_BUFFER_SIZE_LONGTEXT N_("RTSP start frame buffer size of the video " \
    "track, can be increased in case of broken pictures due " \
    "to too small buffer.")
93
#define DEFAULT_FRAME_BUFFER_SIZE 250000
94

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

    add_submodule ()
        set_description( N_("RTSP/RTP access and demux") )
Julian Scheel's avatar
Julian Scheel committed
106
        add_shortcut( "rtsp", "pnm", "live", "livedotcom" )
107
        set_capability( "access", 300 )
108
        set_callbacks( Open, Close )
109
        add_bool( "rtsp-tcp", false,
Carlo Calabrò's avatar
Carlo Calabrò committed
110
                  N_("Use RTP over RTSP (TCP)"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
111
                  N_("Use RTP over RTSP (TCP)"), true )
112
            change_safe()
113
        add_integer( "rtp-client-port", -1,
114
                  N_("Client port"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
115
                  N_("Port to use for the RTP source of the session"), true )
116
        add_bool( "rtsp-mcast", false,
117
                  N_("Force multicast RTP via RTSP"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
118
                  N_("Force multicast RTP via RTSP"), true )
119
            change_safe()
120
        add_bool( "rtsp-http", false,
121
                  N_("Tunnel RTSP and RTP over HTTP"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
122
                  N_("Tunnel RTSP and RTP over HTTP"), true )
123
            change_safe()
124
        add_integer( "rtsp-http-port", 80,
125
                  N_("HTTP tunnel port"),
126
                  N_("Port to use for tunneling the RTSP/RTP over HTTP."),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
127
                  true )
128
        add_bool(   "rtsp-kasenna", false, KASENNA_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
129
                    KASENNA_LONGTEXT, true )
Greg Farrell's avatar
Greg Farrell committed
130
            change_safe()
131
        add_bool(   "rtsp-wmserver", false, WMSERVER_TEXT,
132 133
                    WMSERVER_LONGTEXT, true)
            change_safe()
134
        add_string( "rtsp-user", NULL, USER_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
135
                    USER_LONGTEXT, true )
136
            change_safe()
137
        add_password("rtsp-pwd", NULL, PASS_TEXT, PASS_LONGTEXT)
138 139 140
        add_integer( "rtsp-frame-buffer-size", DEFAULT_FRAME_BUFFER_SIZE,
                     FRAME_BUFFER_SIZE_TEXT, FRAME_BUFFER_SIZE_LONGTEXT,
                     true )
141
vlc_module_end ()
142 143 144


/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
145
 * Local prototypes
146
 *****************************************************************************/
147

148 149
typedef struct
{
150 151
    demux_t         *p_demux;
    MediaSubsession *sub;
152

153 154
    es_format_t     fmt;
    es_out_id_t     *p_es;
155

156 157 158 159 160 161 162 163
    enum
    {
        SINGLE_STREAM,
        MULTIPLEXED_STREAM,
        QUICKTIME_STREAM,
        ASF_STREAM
    } format;

164
    block_t         *p_asf_block;
165
    bool            b_discard_trunc;
166
    vlc_demux_chained_t *p_out_muxed;    /* for muxed stream */
167

168 169
    uint8_t         *p_buffer;
    unsigned int    i_buffer;
170

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171
    bool            b_rtcp_sync;
172
    bool            b_flushing_discontinuity;
173
    int             i_next_block_flags;
174
    char            waiting;
175 176
    int64_t         i_lastpts;
    int64_t         i_pcr;
177
    double          f_npt;
178

179 180 181 182 183 184 185
    enum
    {
        STATE_NONE,
        STATE_SELECTED,
        STATE_IGNORED,
        STATE_TEARDOWN,
    } state;
186

187 188
} live_track_t;

189 190
class RTSPClientVlc;

191
#define CAP_RATE_CONTROL        (1 << 1)
192 193
#define CAP_SUBSESSION_TEARDOWN (1 << 2)
#define CAP_SUBSESSION_PAUSE    (1 << 3)
194 195
#define CAPS_DEFAULT            CAP_RATE_CONTROL

196 197
struct demux_sys_t
{
198
    char            *p_sdp;    /* XXX mallocated */
199
    char            *psz_pl_url; /* password-less URL */
200
    vlc_url_t       url;
201 202 203 204

    MediaSession     *ms;
    TaskScheduler    *scheduler;
    UsageEnvironment *env ;
205
    RTSPClientVlc    *rtsp;
206
    int              capabilities; /* Server capabilities workaround */
207

208
    /* */
209
    int              i_track;
210
    live_track_t     **track;
211

212
    /* Weird formats */
213
    asf_header_t     asfh;
214
    vlc_demux_chained_t *p_out_asf;
215
    bool             b_real;
216 217

    /* */
Steve Lhomme's avatar
Steve Lhomme committed
218
    mtime_t          i_pcr; /* The clock */
219
    bool             b_rtcp_sync; /* At least one track received sync */
220 221 222
    double           f_npt;
    double           f_npt_length;
    double           f_npt_start;
223

224
    /* timeout thread information */
225
    vlc_timer_t      timer;
226
    vlc_mutex_t      timeout_mutex; /* Serialise calls to live555 in timeout thread w.r.t. Demux()/Control() */
227

228
    /* */
229 230 231
    bool             b_force_mcast;
    bool             b_multicast;   /* if one of the tracks is multicasted */
    bool             b_no_data;     /* if we never received any data */
232 233
    int              i_no_data_ti;  /* consecutive number of TaskInterrupt */

234 235
    char             event_rtsp;
    char             event_data;
236 237

    bool             b_get_param;   /* Does the server support GET_PARAMETER */
238
    bool             b_paused;      /* Are we paused? */
239
    bool             b_error;
240
    int              i_live555_ret; /* live555 callback return code */
241 242

    float            f_seek_request;/* In case we receive a seek request while paused*/
243 244
};

245 246 247 248 249 250 251 252

class RTSPClientVlc : public RTSPClient
{
public:
    RTSPClientVlc( UsageEnvironment& env, char const* rtspURL, int verbosityLevel,
                   char const* applicationName, portNumBits tunnelOverHTTPPortNum,
                   demux_sys_t *p_sys) :
                   RTSPClient( env, rtspURL, verbosityLevel, applicationName,
253 254 255 256 257
                   tunnelOverHTTPPortNum
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1373932800
                   , -1
#endif
                   )
258 259 260 261 262 263
    {
        this->p_sys = p_sys;
    }
    demux_sys_t *p_sys;
};

264 265 266
static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );

267 268 269 270 271
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 * );
272

273 274 275
static void StreamRead  ( void *, unsigned int, unsigned int,
                          struct timeval, unsigned int );
static void StreamClose ( void * );
276 277
static void TaskInterruptData( void * );
static void TaskInterruptRTSP( void * );
278

279
static void TimeoutPrevention( void * );
280

281
static unsigned char* parseH264ConfigStr( char const* configStr,
282
                                          unsigned int& configSize );
283 284
static unsigned char* parseVorbisConfigStr( char const* configStr,
                                            unsigned int& configSize );
285

286 287
static char *passwordLessURL( vlc_url_t *url );

288 289 290
#define PCR_OBS (CLOCK_FREQ / 4)
#define PCR_OFF PCR_OBS

291 292 293
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
294
static int  Open ( vlc_object_t *p_this )
295
{
296
    demux_t     *p_demux = (demux_t*)p_this;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
297
    demux_sys_t *p_sys = NULL;
298

Rémi Duraffort's avatar
Rémi Duraffort committed
299
    int i_return;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
300
    int i_error = VLC_EGENERIC;
301

302 303 304
    if (p_demux->out == NULL)
        return VLC_EGENERIC;

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
    /* if the rtsp URL may contain a sat.ip fake DNS, bail-out early and 
     * let the SAT>IP module handle that */
    if( !strncmp(p_demux->psz_location, "sat.ip", 6) )
    {
        msg_Err( p_demux, "SAT>IP server, bailing out");
        return VLC_EGENERIC;
    }
    /* If satip-host is set on the item, we shall assume it is a rtsp for the
     * SAT>IP module and bail-out early. */
    char *psz_host = var_InheritString(p_demux, "satip-host");
    if (psz_host != NULL) {
        msg_Err( p_demux, "URL is for SAT>IP, bailing out");
        free(psz_host);
        return VLC_EGENERIC;
    }

Laurent Aimar's avatar
Laurent Aimar committed
321
    if( p_demux->s )
322
    {
Laurent Aimar's avatar
Laurent Aimar committed
323 324
        /* See if it looks like a SDP
           v, o, s fields are mandatory and in this order */
325
        const uint8_t *p_peek;
326
        if( vlc_stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
327

328 329
        if( memcmp( p_peek, "v=0\r\n", 5 ) &&
            memcmp( p_peek, "v=0\n", 4 ) &&
Laurent Aimar's avatar
Laurent Aimar committed
330 331 332 333
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            return VLC_EGENERIC;
        }
334 335
    }

336 337
    p_demux->pf_demux  = Demux;
    p_demux->pf_control= Control;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
338
    p_demux->p_sys     = p_sys = (demux_sys_t*)calloc( 1, sizeof( demux_sys_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
339 340
    if( !p_sys ) return VLC_ENOMEM;

341 342 343 344 345 346
    if( vlc_timer_create(&p_sys->timer, TimeoutPrevention, p_demux) )
    {
        free( p_sys );
        return VLC_ENOMEM;
    }

347
    msg_Dbg( p_demux, "version " LIVEMEDIA_LIBRARY_VERSION_STRING );
348

349 350 351 352 353 354 355
    p_sys->capabilities = CAPS_DEFAULT;
    if( var_GetBool( p_demux, "rtsp-kasenna" ) ||
        var_GetBool( p_demux, "rtsp-wmserver" ) )
    {
        p_sys->capabilities &= ~CAP_RATE_CONTROL;
    }

356
    TAB_INIT( p_sys->i_track, p_sys->track );
357
    p_sys->b_no_data = true;
358
    p_sys->b_force_mcast = var_InheritBool( p_demux, "rtsp-mcast" );
359
    p_sys->f_seek_request = -1;
360
    vlc_mutex_init(&p_sys->timeout_mutex);
361

362
    /* parse URL for rtsp://[user:[passwd]@]serverip:port/options */
363
    vlc_UrlParse( &p_sys->url, p_demux->psz_url );
364 365 366 367 368 369

    if( ( p_sys->psz_pl_url = passwordLessURL( &p_sys->url ) ) == NULL )
    {
        i_error = VLC_ENOMEM;
        goto error;
    }
370

371 372
    if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
    {
373
        msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
374 375
        goto error;
    }
376
    if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
377
    {
378
        msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
379 380
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
381

382
    if( p_demux->s != NULL )
383
    {
384
        char *p = p_sys->psz_pl_url;
385
        while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
386 387
    }

388
    if( p_demux->s != NULL )
Laurent Aimar's avatar
Laurent Aimar committed
389 390
    {
        /* Gather the complete sdp file */
391 392 393
        int     i_sdp       = 0;
        int     i_sdp_max   = 1000;
        uint8_t *p_sdp      = (uint8_t*) malloc( i_sdp_max );
394

Jean-Paul Saman's avatar
Jean-Paul Saman committed
395 396 397 398 399 400
        if( !p_sdp )
        {
            i_error = VLC_ENOMEM;
            goto error;
        }

Laurent Aimar's avatar
Laurent Aimar committed
401 402
        for( ;; )
        {
403
            int i_read = vlc_stream_Read( p_demux->s, &p_sdp[i_sdp],
404
                                      i_sdp_max - i_sdp - 1 );
Laurent Aimar's avatar
Laurent Aimar committed
405 406 407
            if( i_read < 0 )
            {
                msg_Err( p_demux, "failed to read SDP" );
408
                free( p_sdp );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
409
                goto error;
Laurent Aimar's avatar
Laurent Aimar committed
410 411 412 413 414 415 416 417 418 419 420
            }

            i_sdp += i_read;

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

            i_sdp_max += 1000;
421
            p_sdp = (uint8_t*)xrealloc( p_sdp, i_sdp_max );
Laurent Aimar's avatar
Laurent Aimar committed
422 423
        }
        p_sys->p_sdp = (char*)p_sdp;
424
    }
425
    else if( ( i_return = Connect( p_demux ) ) != VLC_SUCCESS )
426
    {
427
        msg_Err( p_demux, "Failed to connect with %s", p_sys->psz_pl_url );
428 429
        goto error;
    }
430

431
    if( p_sys->p_sdp == NULL )
432
    {
433
        msg_Err( p_demux, "Failed to retrieve the RTSP Session Description" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
434
        i_error = VLC_ENOMEM;
435
        goto error;
436 437
    }

438 439
    if( ( i_return = SessionsSetup( p_demux ) ) != VLC_SUCCESS )
    {
440
        msg_Err( p_demux, "Nothing to play for %s", p_sys->psz_pl_url );
441 442
        goto error;
    }
443

444
    if( p_sys->b_real ) goto error;
445

446 447
    if( ( i_return = Play( p_demux ) ) != VLC_SUCCESS )
        goto error;
448

449 450 451 452 453 454 455
    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
456 457 458
    if( p_sys->i_track <= 0 )
        goto error;

459 460 461
    return VLC_SUCCESS;

error:
Rémi Duraffort's avatar
Rémi Duraffort committed
462
    Close( p_this );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
463
    return i_error;
464 465 466 467 468
}

/*****************************************************************************
 * DemuxClose:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
469
static void Close( vlc_object_t *p_this )
470
{
471
    demux_t *p_demux = (demux_t*)p_this;
472
    demux_sys_t *p_sys = (demux_sys_t *)p_demux->p_sys;
473

474
    vlc_timer_destroy(p_sys->timer);
475

476
    if( p_sys->rtsp && p_sys->ms ) p_sys->rtsp->sendTeardownCommand( *p_sys->ms, NULL );
477 478 479 480 481
    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();

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

485 486
        if( tk->p_out_muxed )
            vlc_demux_chained_Delete( tk->p_out_muxed );
487
        es_format_Clean( &tk->fmt );
488
        free( tk->p_buffer );
489 490
        free( tk );
    }
491
    TAB_CLEAN( p_sys->i_track, p_sys->track );
492 493
    if( p_sys->p_out_asf )
        vlc_demux_chained_Delete( p_sys->p_out_asf );
494 495
    delete p_sys->scheduler;
    free( p_sys->p_sdp );
496
    free( p_sys->psz_pl_url );
497

498
    vlc_UrlClean( &p_sys->url );
499
    vlc_mutex_destroy(&p_sys->timeout_mutex);
500

501 502 503
    free( p_sys );
}

504 505
static inline Boolean toBool( bool b ) { return b?True:False; } // silly, no?

506 507 508 509 510 511 512
static void default_live555_callback( RTSPClient* client, int result_code, char* result_string )
{
    RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> ( client );
    demux_sys_t *p_sys = client_vlc->p_sys;
    delete []result_string;
    p_sys->i_live555_ret = result_code;
    p_sys->b_error = p_sys->i_live555_ret != 0;
513
    p_sys->event_rtsp = 1;
514 515 516 517 518 519
}

/* return true if the RTSP command succeeded */
static bool wait_Live555_response( demux_t *p_demux, int i_timeout = 0 /* ms */ )
{
    TaskToken task;
520
    demux_sys_t * p_sys = (demux_sys_t *)p_demux->p_sys;
521
    p_sys->event_rtsp = 0;
522 523 524
    if( i_timeout > 0 )
    {
        /* Create a task that will be called if we wait more than timeout ms */
525 526
        task = p_sys->scheduler->scheduleDelayedTask( i_timeout*1000,
                                                      TaskInterruptRTSP,
527 528
                                                      p_demux );
    }
529
    p_sys->event_rtsp = 0;
530 531
    p_sys->b_error = true;
    p_sys->i_live555_ret = 0;
532
    p_sys->scheduler->doEventLoop( &p_sys->event_rtsp );
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
    //here, if b_error is true and i_live555_ret = 0 we didn't receive a response
    if( i_timeout > 0 )
    {
        /* remove the task */
        p_sys->scheduler->unscheduleDelayedTask( task );
    }
    return !p_sys->b_error;
}

static void continueAfterDESCRIBE( RTSPClient* client, int result_code,
                                   char* result_string )
{
    RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> ( client );
    demux_sys_t *p_sys = client_vlc->p_sys;
    p_sys->i_live555_ret = result_code;
548
    if ( result_code == 0 )
549
    {
550 551 552 553 554 555 556 557
        char* sdpDescription = result_string;
        free( p_sys->p_sdp );
        p_sys->p_sdp = NULL;
        if( sdpDescription )
        {
            p_sys->p_sdp = strdup( sdpDescription );
            p_sys->b_error = false;
        }
558
    }
559 560 561
    else
        p_sys->b_error = true;
    delete[] result_string;
562
    p_sys->event_rtsp = 1;
563 564 565 566 567 568
#ifdef VLC_PATCH_RTSPCLIENT_SERVERSTRING
    if( client_vlc->serverString() )
    {
        if( !strncmp(client_vlc->serverString(), "Kasenna", 7) ||
            !strncmp(client_vlc->serverString(), "WMServer", 8) )
            p_sys->capabilities &= ~CAP_RATE_CONTROL;
569 570
        if( !strncmp(client_vlc->serverString(), "VLC/", 4) )
            p_sys->capabilities |= (CAP_SUBSESSION_TEARDOWN|CAP_SUBSESSION_PAUSE);
571 572
    }
#endif
573 574 575 576 577 578 579
}

static void continueAfterOPTIONS( RTSPClient* client, int result_code,
                                  char* result_string )
{
    RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> (client);
    demux_sys_t *p_sys = client_vlc->p_sys;
580 581 582 583
    p_sys->b_get_param =
      // If OPTIONS fails, assume GET_PARAMETER is not supported but
      // still continue on with the stream.  Some servers (foscam)
      // return 501/not implemented for OPTIONS.
584
      result_code == 0
585 586
      && result_string != NULL
      && strstr( result_string, "GET_PARAMETER" ) != NULL;
587
    client->sendDescribeCommand( continueAfterDESCRIBE );
588 589 590
    delete[] result_string;
}

591 592 593 594 595
/*****************************************************************************
 * Connect: connects to the RTSP server to setup the session DESCRIBE
 *****************************************************************************/
static int Connect( demux_t *p_demux )
{
596
    demux_sys_t *p_sys = (demux_sys_t *)p_demux->p_sys;
597
    Authenticator authenticator;
Thomas Guillem's avatar
Thomas Guillem committed
598 599 600
    vlc_credential credential;
    const char *psz_user = NULL;
    const char *psz_pwd  = NULL;
601 602
    int  i_http_port  = 0;
    int  i_ret        = VLC_SUCCESS;
603
    const int i_timeout = var_InheritInteger( p_demux, "ipv4-timeout" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
604

Thomas Guillem's avatar
Thomas Guillem committed
605
    vlc_credential_init( &credential, &p_sys->url );
606

Thomas Guillem's avatar
Thomas Guillem committed
607 608 609 610 611 612
    /* Credentials can be NULL since they may not be needed */
    if( vlc_credential_get( &credential, p_demux, "rtsp-user", "rtsp-pwd",
                            NULL, NULL) )
    {
        psz_user = credential.psz_username;
        psz_pwd = credential.psz_password;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
613 614
    }

615
createnew:
616 617 618
    /* FIXME: This is naive and incorrect; it does not prevent the thread
     * getting stuck in blocking socket operations. */
    if( vlc_killed() )
619
    {
620 621
        i_ret = VLC_EGENERIC;
        goto bailout;
622 623
    }

Sébastien Escudier's avatar
Sébastien Escudier committed
624
    if( var_CreateGetBool( p_demux, "rtsp-http" ) )
625
        i_http_port = var_InheritInteger( p_demux, "rtsp-http-port" );
626

627
    p_sys->rtsp = new (std::nothrow) RTSPClientVlc( *p_sys->env, p_sys->psz_pl_url,
628
                                     var_InheritInteger( p_demux, "verbose" ) > 1 ? 1 : 0,
629
                                     "LibVLC/" VERSION, i_http_port, p_sys );
630
    if( !p_sys->rtsp )
631 632 633
    {
        msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                 p_sys->env->getResultMsg() );
634 635
        i_ret = VLC_EGENERIC;
        goto bailout;
636
    }
637

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
638 639 640 641
    /* 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
642 643 644
     * to spaces in the string or the string being too long. Here we override
     * the default string with a more compact version.
     */
645
    if( var_InheritBool( p_demux, "rtsp-kasenna" ))
646 647 648 649
    {
        p_sys->rtsp->setUserAgentString( "VLC_MEDIA_PLAYER_KA" );
    }

650
describe:
Rémi Duraffort's avatar
Rémi Duraffort committed
651
    authenticator.setUsernameAndPassword( psz_user, psz_pwd );
652

653
    p_sys->rtsp->sendOptionsCommand( &continueAfterOPTIONS, &authenticator );
654

655
    if( !wait_Live555_response( p_demux, i_timeout ) )
656
    {
657
        int i_code = p_sys->i_live555_ret;
658 659 660 661
        if( i_code == 401 )
        {
            msg_Dbg( p_demux, "authentication failed" );

Thomas Guillem's avatar
Thomas Guillem committed
662 663 664
            if( vlc_credential_get( &credential, p_demux, "rtsp-user", "rtsp-pwd",
                                    _("RTSP authentication"),
                                    _("Please enter a valid login name and a password.") ) )
665
            {
Thomas Guillem's avatar
Thomas Guillem committed
666 667
                psz_user = credential.psz_username;
                psz_pwd = credential.psz_password;
668
                msg_Dbg( p_demux, "retrying with user=%s", psz_user );
669
                goto describe;
670 671
            }
        }
672
        else if( i_code > 0 && i_code != 404 && !var_GetBool( p_demux, "rtsp-http" ) )
673 674 675
        {
            /* Perhaps a firewall is being annoying. Try HTTP tunneling mode */
            msg_Dbg( p_demux, "we will now try HTTP tunneling mode" );
Rémi Duraffort's avatar
Rémi Duraffort committed
676
            var_SetBool( p_demux, "rtsp-http", true );
677
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
678
            p_sys->rtsp = NULL;
679 680
            goto createnew;
        }
681 682
        else
        {
683 684
            if( i_code == 0 )
                msg_Dbg( p_demux, "connection timeout" );
685
            else
686
            {
687
                msg_Dbg( p_demux, "connection error %d", i_code );
688
                if( i_code == 403 )
Thomas Guillem's avatar
Thomas Guillem committed
689 690
                    vlc_dialog_display_error( p_demux, _("RTSP connection failed"),
                        _("Access to the stream is denied by the server configuration.") );
691
            }
692
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
693
            p_sys->rtsp = NULL;
694
        }
695 696
        i_ret = VLC_EGENERIC;
    }
Thomas Guillem's avatar
Thomas Guillem committed
697
    else
698
        vlc_credential_store( &credential, p_demux );
699

700
bailout:
Thomas Guillem's avatar
Thomas Guillem committed
701
    vlc_credential_clean( &credential );
702

703 704 705 706
    return i_ret;
}

/*****************************************************************************
707
 * SessionsSetup: prepares the subsessions and does the SETUP
708 709 710
 *****************************************************************************/
static int SessionsSetup( demux_t *p_demux )
{
711
    demux_sys_t *p_sys  = (demux_sys_t *)p_demux->p_sys;
712 713 714
    MediaSubsessionIterator *iter   = NULL;
    MediaSubsession         *sub    = NULL;

715
    bool           b_rtsp_tcp;
716 717
    int            i_client_port;
    int            i_return = VLC_SUCCESS;
718 719
    unsigned int   i_receive_buffer = 0;
    int            i_frame_buffer = DEFAULT_FRAME_BUFFER_SIZE;
720
    unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */
721 722
    const char     *p_sess_lang = NULL;
    const char     *p_lang;
723

724
    b_rtsp_tcp    = var_CreateGetBool( p_demux, "rtsp-tcp" ) ||
Sébastien Escudier's avatar
Sébastien Escudier committed
725
                    var_GetBool( p_demux, "rtsp-http" );
726
    i_client_port = var_InheritInteger( p_demux, "rtp-client-port" );
727

728

729 730 731 732 733 734 735 736
    /* 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;
    }

737 738 739 740 741 742 743 744 745 746 747 748 749 750
    if( strcmp( p_sys->p_sdp, "m=" ) != 0 )
    {
        const char *p_sess_attr_end;

        p_sess_attr_end = strstr( p_sys->p_sdp, "\nm=" );
        if( !p_sess_attr_end )
            p_sess_attr_end = strstr( p_sys->p_sdp, "\rm=" );

        p_sess_lang = p_sess_attr_end ? strstr( p_sys->p_sdp, "a=lang:" ) : NULL;
        if( p_sess_lang &&
            p_sess_lang - p_sys->p_sdp > p_sess_attr_end - p_sys->p_sdp )
            p_sess_lang = NULL;
    }

751 752 753 754 755
    /* Initialise each media subsession */
    iter = new MediaSubsessionIterator( *p_sys->ms );
    while( ( sub = iter->next() ) != NULL )
    {
        Boolean bInit;
756
        live_track_t *tk;
757 758 759

        /* Value taken from mplayer */
        if( !strcmp( sub->mediumName(), "audio" ) )
760
            i_receive_buffer = 100000;
761
        else if( !strcmp( sub->mediumName(), "video" ) )
762 763 764 765 766 767
        {
            int i_var_buf_size = var_InheritInteger( p_demux, "rtsp-frame-buffer-size" );
            if( i_var_buf_size > 0 )
                i_frame_buffer = i_var_buf_size;
            i_receive_buffer = 2000000;
        }
768 769
        else if( !strcmp( sub->mediumName(), "text" ) )
            ;
770 771 772 773 774
        else continue;

        if( strcasestr( sub->codecName(), "REAL" ) )
        {
            msg_Info( p_demux, "real codec detected, using real-RTSP instead" );
775
            p_sys->b_real = true; /* This is a problem, we'll handle it later */
776
            continue;
777 778
        }

779 780 781 782 783 784
        if( p_sys->rtsp && i_client_port != -1 )
        {
            sub->setClientPortNum( i_client_port );
            i_client_port += 2;
        }

785
        if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
786
            bInit = sub->initiate( 0 );
787 788
        else
            bInit = sub->initiate();
789

790 791 792 793 794 795 796 797 798 799 800
        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();
801

802
                /* Increase the buffer size */
803 804
                if( i_receive_buffer > 0 )
                    increaseReceiveBufferTo( *p_sys->env, fd, i_receive_buffer );
805

806 807 808 809 810 811 812 813 814
                /* 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 )
            {
815 816 817 818
                p_sys->rtsp->sendSetupCommand( *sub, default_live555_callback, False,
                                               toBool( b_rtsp_tcp ),
                                               toBool( p_sys->b_force_mcast && !b_rtsp_tcp ) );
                if( !wait_Live555_response( p_demux ) )
819
                {
820 821
                    /* if we get an unsupported transport error, toggle TCP
                     * use and try again */
822 823
                    if( p_sys->i_live555_ret == 461 )
                        p_sys->rtsp->sendSetupCommand( *sub, default_live555_callback, False,
824
                                                       !toBool( b_rtsp_tcp ), False );
825
                    if( p_sys->i_live555_ret != 461 || !wait_Live555_response( p_demux ) )
826
                    {
827 828 829
                        msg_Err( p_demux, "SETUP of'%s/%s' failed %s",
                                 sub->mediumName(), sub->codecName(),
                                 p_sys->env->getResultMsg() );
830 831
                        continue;
                    }
832 833 834 835 836
                    else
                    {
                        var_SetBool( p_demux, "rtsp-tcp", true );
                        b_rtsp_tcp = true;
                    }
837
                }
838
            }
839

840 841
            /* Check if we will receive data from this subsession for
             * this track */
842
            if( sub->readSource() == NULL ) continue;
843 844
            if( !p_sys->b_multicast )
            {
845
                /* We need different rollover behaviour for multicast */
846 847
                p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
            }
848 849

            tk = (live_track_t*)malloc( sizeof( live_track_t ) );
850 851 852 853 854
            if( !tk )
            {
                delete iter;
                return VLC_ENOMEM;
            }
855 856 857
            tk->p_demux     = p_demux;
            tk->sub         = sub;
            tk->p_es        = NULL;
858
            tk->format      = live_track_t::SINGLE_STREAM;
859
            tk->p_asf_block = NULL;
860
            tk->b_discard_trunc = false;
861 862
            tk->p_out_muxed = NULL;
            tk->waiting     = 0;
863
            tk->b_rtcp_sync = false;
864
            tk->b_flushing_discontinuity = false;
865
            tk->i_next_block_flags = 0;
866 867
            tk->i_lastpts   = VLC_TS_INVALID;
            tk->i_pcr       = VLC_TS_INVALID;
868
            tk->f_npt       = 0.;
869
            tk->state       = live_track_t::STATE_SELECTED;
870 871 872
            tk->i_buffer    = i_frame_buffer;
            tk->p_buffer    = (uint8_t *)malloc( i_frame_buffer );

873 874
            if( !tk->p_buffer )
            {
Christophe Mutricy's avatar
Christophe Mutricy committed
875
                free( tk );
876 877 878
                delete iter;
                return VLC_ENOMEM;
            }
879 880 881 882

            /* Value taken from mplayer */
            if( !strcmp( sub->mediumName(), "audio" ) )
            {
883
                es_format_Init( &tk->fmt, AUDIO_ES, VLC_CODEC_UNKNOWN );
884 885 886 887 888 889 890
                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" ) )
                {
891
                    tk->fmt.i_codec = VLC_CODEC_MPGA;
892 893 894 895
                    tk->fmt.audio.i_rate = 0;
                }
                else if( !strcmp( sub->codecName(), "AC3" ) )
                {
896
                    tk->fmt.i_codec = VLC_CODEC_A52;
897 898 899 900
                    tk->fmt.audio.i_rate = 0;
                }
                else if( !strcmp( sub->codecName(), "L16" ) )
                {
901
                    tk->fmt.i_codec = VLC_CODEC_S16B;
902 903
                    tk->fmt.audio.i_bitspersample = 16;
                }
904 905 906 907 908
                else if( !strcmp( sub->codecName(), "L20" ) )
                {
                    tk->fmt.i_codec = VLC_CODEC_S20B;
                    tk->fmt.audio.i_bitspersample = 20;
                }
Sébastien Escudier's avatar
Sébastien Escudier committed
909
                else if( !strcmp( sub->codecName(), "L24" ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
910 911 912 913
                {
                    tk->fmt.i_codec = VLC_CODEC_S24B;
                    tk->fmt.audio.i_bitspersample = 24;
                }
914 915
                else if( !strcmp( sub->codecName(), "L8" ) )
                {
916
                    tk->fmt.i_codec = VLC_CODEC_U8;
917 918
                    tk->fmt.audio.i_bitspersample = 8;
                }
919 920 921 922 923
                else if( !strcmp( sub->codecName(), "DAT12" ) )
                {
                    tk->fmt.i_codec = VLC_CODEC_DAT12;
                    tk->fmt.audio.i_bitspersample = 12;
                }
924 925
                else if( !strcmp( sub->codecName(), "PCMU" ) )
                {
926
                    tk->fmt.i_codec = VLC_CODEC_MULAW;
927
                    tk->fmt.audio.i_bitspersample = 8;