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

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

#include <vlc/vlc.h>
29 30 31
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>

Clément Stenac's avatar
Clément Stenac committed
32 33 34
#include <vlc_demux.h>
#include <vlc_interface.h>
#include <vlc_network.h>
35 36 37

#include <iostream>

Gildas Bazin's avatar
 
Gildas Bazin committed
38 39 40 41
#if defined( WIN32 )
#   include <winsock2.h>
#endif

42 43 44 45
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include "liveMedia.hh"

46 47 48 49
extern "C" {
#include "../access/mms/asf.h"  /* Who said ugly ? */
}

50 51 52 53 54 55
#if (LIVEMEDIA_LIBRARY_VERSION_INT < 1089936000)
#define RECLAIM_ENV(env) delete (env)
#else
#define RECLAIM_ENV(env) (env)->reclaim()
#endif

56 57 58 59 60
using namespace std;

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
61 62
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
63

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
64
#define CACHING_TEXT N_("Caching value (ms)")
Laurent Aimar's avatar
Laurent Aimar committed
65
#define CACHING_LONGTEXT N_( \
Carlo Calabrò's avatar
Carlo Calabrò committed
66
    "Allows you to modify the default caching value for RTSP streams. This " \
67
    "value should be set in millisecond units." )
Laurent Aimar's avatar
Laurent Aimar committed
68

69
#define KASENNA_TEXT N_( "Kasenna RTSP dialect")
70
#define KASENNA_LONGTEXT N_( "Kasenna servers use an old and unstandard " \
71
    "dialect of RTSP. When you set this parameter, VLC will try this dialect "\
72
    "for communication. In this mode you cannot connect to normal RTSP servers." )
73

74 75 76 77 78 79 80
#define USER_TEXT N_("RTSP user name")
#define USER_LONGTEXT N_("Allows you to modify the user name that will " \
    "be used for authenticating the connection.")
#define PASS_TEXT N_("RTSP password")
#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
    "used for the connection.")

81
vlc_module_begin();
82
    set_description( _("RTP/RTSP/SDP demuxer (using Live555)" ) );
83
    set_capability( "demux2", 50 );
84
    set_shortname( "RTP/RTSP");
Laurent Aimar's avatar
Laurent Aimar committed
85
    set_callbacks( Open, Close );
86
    add_shortcut( "live" );
87
    add_shortcut( "livedotcom" );
Clément Stenac's avatar
Clément Stenac committed
88 89
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
90 91

    add_submodule();
Laurent Aimar's avatar
Laurent Aimar committed
92
        set_description( _("RTSP/RTP access and demux") );
93
        add_shortcut( "rtsp" );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
94
        add_shortcut( "sdp" );
Laurent Aimar's avatar
Laurent Aimar committed
95 96
        set_capability( "access_demux", 0 );
        set_callbacks( Open, Close );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
97
        add_bool( "rtsp-tcp", 0, NULL,
Carlo Calabrò's avatar
Carlo Calabrò committed
98 99
                  N_("Use RTP over RTSP (TCP)"),
                  N_("Use RTP over RTSP (TCP)"), VLC_TRUE );
100 101 102
        add_integer( "rtp-client-port", -1, NULL,
                  N_("Client port"),
                  N_("Port to use for the RTP source of the session"), VLC_TRUE );
103 104 105 106 107 108
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500
        add_bool( "rtsp-http", 0, NULL,
                  N_("Tunnel RTSP and RTP over HTTP"),
                  N_("Tunnel RTSP and RTP over HTTP"), VLC_TRUE );
        add_integer( "rtsp-http-port", 80, NULL,
                  N_("HTTP tunnel port"),
109 110
                  N_("Port to use for tunneling the RTSP/RTP over HTTP."),
                  VLC_TRUE );
111
#endif
112 113 114 115
        add_integer("rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
                    CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
        add_bool(   "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT,
                    KASENNA_LONGTEXT, VLC_TRUE );
116
        add_string( "rtsp-user", NULL, NULL, USER_TEXT,
117
                    USER_LONGTEXT, VLC_TRUE );
118
        add_string( "rtsp-pwd", NULL, NULL, PASS_TEXT,
119
                    PASS_LONGTEXT, VLC_TRUE );
120 121 122 123
vlc_module_end();


/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
124
 * Local prototypes
125
 *****************************************************************************/
126

127 128 129 130 131 132
typedef struct
{
    demux_t     *p_demux;

    vlc_bool_t   b_quicktime;
    vlc_bool_t   b_muxed;
133
    vlc_bool_t   b_asf;
134 135 136 137 138 139 140 141 142 143

    es_format_t  fmt;
    es_out_id_t  *p_es;

    stream_t     *p_out_muxed;    /* for muxed stream */

    RTPSource    *rtpSource;
    FramedSource *readSource;
    vlc_bool_t   b_rtcp_sync;

144 145
    uint8_t      *p_buffer;
    unsigned int  i_buffer;
146 147 148

    char         waiting;

149
    int64_t      i_pts;
150

151 152
} live_track_t;

153 154 155 156 157 158 159 160
struct timeout_thread_t
{
    VLC_COMMON_MEMBERS

    int64_t      i_remain;
    demux_sys_t  *p_sys;
};

161 162
struct demux_sys_t
{
163 164
    char            *p_sdp;    /* XXX mallocated */
    char            *psz_path; /* URL-encoded path */
165 166 167 168 169 170

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

171
    /* */
172 173
    int              i_track;
    live_track_t     **track;   /* XXX mallocated */
174 175 176 177
    int64_t          i_pcr;
    int64_t          i_pcr_start;
    int64_t          i_pcr_previous;
    int64_t          i_pcr_repeatdate;
178
    int              i_pcr_repeats;
179

180 181 182 183 184
    /* Asf */
    asf_header_t     asfh;
    stream_t         *p_out_asf;

    /* */
185 186
    int64_t          i_length;
    int64_t          i_start;
187

188 189 190 191 192
    /* timeout thread information */
    int              i_timeout;     /* session timeout value in seconds */
    vlc_bool_t       b_timeout_call;/* mark to send an RTSP call to prevent server timeout */
    timeout_thread_t *p_timeout;    /* the actual thread that makes sure we don't timeout */

193
    /* */
194
    vlc_bool_t       b_multicast;   /* true if one of the tracks is multicasted */
195 196 197
    vlc_bool_t       b_no_data;     /* true if we never receive any data */
    int              i_no_data_ti;  /* consecutive number of TaskInterrupt */

198 199 200 201 202 203
    char             event;
};

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

204 205 206 207 208
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 * );
209

210 211 212
static void StreamRead  ( void *, unsigned int, unsigned int,
                          struct timeval, unsigned int );
static void StreamClose ( void * );
213
static void TaskInterrupt( void * );
214

215 216
static void TimeoutPrevention( timeout_thread_t * );

217
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
218
static unsigned char* parseH264ConfigStr( char const* configStr,
219 220
                                          unsigned int& configSize );
#endif
221

222 223 224
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
225
static int  Open ( vlc_object_t *p_this )
226
{
227
    demux_t     *p_demux = (demux_t*)p_this;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
228
    demux_sys_t *p_sys = NULL;
229 230 231 232
    
    MediaSubsessionIterator *iter   = NULL;
    MediaSubsession         *sub    = NULL;
    int i_return;
233

Laurent Aimar's avatar
Laurent Aimar committed
234
    if( p_demux->s )
235
    {
Laurent Aimar's avatar
Laurent Aimar committed
236 237
        /* See if it looks like a SDP
           v, o, s fields are mandatory and in this order */
238
        uint8_t *p_peek;
239 240
        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;

241 242
        if( memcmp( (char*)p_peek, "v=0\r\n", 5 ) &&
            memcmp( (char*)p_peek, "v=0\n", 4 ) &&
Laurent Aimar's avatar
Laurent Aimar committed
243 244 245 246
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            return VLC_EGENERIC;
        }
247
    }
Laurent Aimar's avatar
Laurent Aimar committed
248
    else
249
    {
Laurent Aimar's avatar
Laurent Aimar committed
250
        var_Create( p_demux, "rtsp-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
251 252
    }

253 254 255
    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
256 257
    if( !p_sys ) return VLC_ENOMEM;

258 259 260 261 262 263 264
    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;
Laurent Aimar's avatar
Laurent Aimar committed
265 266
    p_sys->i_pcr   = 0;
    p_sys->i_pcr_start = 0;
267
    p_sys->i_pcr_previous = 0;
268
    p_sys->i_pcr_repeatdate = 0;
269
    p_sys->i_pcr_repeats = 0;
Laurent Aimar's avatar
Laurent Aimar committed
270 271
    p_sys->i_length = 0;
    p_sys->i_start = 0;
272
    p_sys->p_out_asf = NULL;
273 274
    p_sys->b_no_data = VLC_TRUE;
    p_sys->i_no_data_ti = 0;
275 276 277
    p_sys->p_timeout = NULL;
    p_sys->i_timeout = 0;
    p_sys->b_timeout_call = VLC_FALSE;
278
    p_sys->b_multicast = VLC_FALSE;
279
    p_sys->psz_path = strdup( p_demux->psz_path );
280 281 282

    if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
    {
283
        msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
284 285
        goto error;
    }
286
    if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
287
    {
288
        msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
289 290
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
291

292
    if( strcasecmp( p_demux->psz_access, "sdp" ) )
293
    {
294
        char *p = p_sys->psz_path;
295
        while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
296 297
    }

298
    if( p_demux->s != NULL )
Laurent Aimar's avatar
Laurent Aimar committed
299 300
    {
        /* Gather the complete sdp file */
301 302 303 304
        int     i_sdp       = 0;
        int     i_sdp_max   = 1000;
        uint8_t *p_sdp      = (uint8_t*) malloc( i_sdp_max );
        
Laurent Aimar's avatar
Laurent Aimar committed
305 306
        for( ;; )
        {
307 308
            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
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

            if( i_read < 0 )
            {
                msg_Err( p_demux, "failed to read SDP" );
                free( p_sys );
                return VLC_EGENERIC;
            }

            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;
329
    }
330 331 332 333 334 335
    else if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "sdp" ) )
    {
        /* sdp:// link from SAP */
        p_sys->p_sdp = strdup( p_sys->psz_path );
    }
    else if( ( i_return = Connect( p_demux ) ) != VLC_SUCCESS )
336
    {
337
        msg_Err( p_demux, "Failed to connect with rtsp://%s", p_sys->psz_path );
338 339
        goto error;
    }
340 341
    
    if( p_sys->p_sdp == NULL )
342
    {
343 344
        msg_Err( p_demux, "Failed to retrieve the RTSP Session Description" );
        goto error;
345 346
    }

347 348
    /* Create the session from the SDP */
    if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
349
    {
350 351 352
        msg_Err( p_demux, "Could not create the RTSP Session: %s",
            p_sys->env->getResultMsg() );
        goto error;
353
    }
354 355 356 357 358 359
    
    if( ( i_return = SessionsSetup( p_demux ) ) != VLC_SUCCESS )
    {
        msg_Err( p_demux, "Nothing to play for rtsp://%s", p_sys->psz_path );
        goto error;
    }
360 361 362 363 364 365

    /* Retrieve the duration if possible */
    p_sys->i_length = (int64_t)( p_sys->ms->playEndTime() * 1000000.0 );
    if( p_sys->i_length < 0 )
        p_sys->i_length = -1;

366 367 368
    if( ( i_return = Play( p_demux ) ) != VLC_SUCCESS )
        goto error;
    
369
    /* Create all es struct */
370
    iter = new MediaSubsessionIterator( *p_sys->ms );
371 372 373 374
    while( ( sub = iter->next() ) != NULL )
    {
        live_track_t *tk;

375
        /* Check if we will receive data from this subsession for this track */
376
        if( sub->readSource() == NULL ) continue;
377 378

        tk = (live_track_t*)malloc( sizeof( live_track_t ) );
379
        tk->p_demux = p_demux;
380
        tk->waiting = 0;
381
        tk->i_pts   = 0;
Laurent Aimar's avatar
Laurent Aimar committed
382
        tk->b_quicktime = VLC_FALSE;
383
        tk->b_muxed     = VLC_FALSE;
384
        tk->b_asf       = VLC_FALSE;
385
        tk->b_rtcp_sync = VLC_FALSE;
386 387
        tk->p_out_muxed = NULL;
        tk->p_es        = NULL;
388 389
        tk->i_buffer    = 65536;
        tk->p_buffer    = (uint8_t *)malloc( 65536 );
390 391 392 393

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

            if( !strcmp( sub->codecName(), "MPA" ) ||
                !strcmp( sub->codecName(), "MPA-ROBUST" ) ||
                !strcmp( sub->codecName(), "X-MP3-DRAFT-00" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'a' );
Gildas Bazin's avatar
 
Gildas Bazin committed
403
                tk->fmt.audio.i_rate = 0;
404 405 406 407
            }
            else if( !strcmp( sub->codecName(), "AC3" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'a', '5', '2', ' ' );
Gildas Bazin's avatar
 
Gildas Bazin committed
408
                tk->fmt.audio.i_rate = 0;
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
            }
            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" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'u', 'l', 'a', 'w' );
            }
            else if( !strcmp( sub->codecName(), "PCMA" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'a', 'l', 'a', 'w' );
            }
428 429 430
            else if( !strncmp( sub->codecName(), "G726", 4 ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'g', '7', '2', '6' ); 
431 432
                tk->fmt.audio.i_rate = 8000;
                tk->fmt.audio.i_channels = 1;
433
                if( !strcmp( sub->codecName()+5, "40" ) )
434
                    tk->fmt.i_bitrate = 40000;
435
                else if( !strcmp( sub->codecName()+5, "32" ) )
436
                    tk->fmt.i_bitrate = 32000;
437
                else if( !strcmp( sub->codecName()+5, "24" ) )
438
                    tk->fmt.i_bitrate = 24000;
439
                else if( !strcmp( sub->codecName()+5, "16" ) )
440
                    tk->fmt.i_bitrate = 16000;
441
            }
442 443 444 445 446 447 448 449
            else if( !strcmp( sub->codecName(), "AMR" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 's', 'a', 'm', 'r' );
            }
            else if( !strcmp( sub->codecName(), "AMR-WB" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 's', 'a', 'w', 'b' );
            }
450 451 452 453 454 455 456
            else if( !strcmp( sub->codecName(), "MP4A-LATM" ) )
            {
                unsigned int i_extra;
                uint8_t      *p_extra;

                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'a' );

457 458
                if( ( p_extra = parseStreamMuxConfigStr( sub->fmtp_config(),
                                                         i_extra ) ) )
459 460
                {
                    tk->fmt.i_extra = i_extra;
461
                    tk->fmt.p_extra = malloc( i_extra );
462
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
463
                    delete[] p_extra;
464
                }
465 466

#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1141257600
467 468 469
                /* 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. */
                ((MPEG4LATMAudioRTPSource*)sub->rtpSource())->omitLATMDataLengthField();
470
#endif
471 472 473 474 475 476 477 478
            }
            else if( !strcmp( sub->codecName(), "MPEG4-GENERIC" ) )
            {
                unsigned int i_extra;
                uint8_t      *p_extra;

                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'a' );

479 480
                if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                       i_extra ) ) )
481 482 483 484
                {
                    tk->fmt.i_extra = i_extra;
                    tk->fmt.p_extra = malloc( i_extra );
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
485
                    delete[] p_extra;
486 487
                }
            }
488 489 490 491
            else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            {
                tk->b_asf = VLC_TRUE;
                if( p_sys->p_out_asf == NULL )
492 493
                    p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                        p_demux->out );
494
            }
495 496 497 498 499
            else if( !strcmp( sub->codecName(), "X-QT" ) ||
                     !strcmp( sub->codecName(), "X-QUICKTIME" ) )
            {
                tk->b_quicktime = VLC_TRUE;
            }
500 501 502
        }
        else if( !strcmp( sub->mediumName(), "video" ) )
        {
503
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
504 505 506 507 508
            if( !strcmp( sub->codecName(), "MPV" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'v' );
            }
            else if( !strcmp( sub->codecName(), "H263" ) ||
509 510
                     !strcmp( sub->codecName(), "H263-1998" ) ||
                     !strcmp( sub->codecName(), "H263-2000" ) )
511
            {
512
                tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '3' );
513 514 515
            }
            else if( !strcmp( sub->codecName(), "H261" ) )
            {
516
                tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '1' );
517
            }
518 519
            else if( !strcmp( sub->codecName(), "H264" ) )
            {
520 521 522 523
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
                unsigned int i_extra = 0;
                uint8_t      *p_extra = NULL;
#endif
524
                tk->fmt.i_codec = VLC_FOURCC( 'h', '2', '6', '4' );
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
                tk->fmt.b_packetized = VLC_FALSE;

                /* XXX not the right minimal version I fear */
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
                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;
                }
#endif
            }
540 541
            else if( !strcmp( sub->codecName(), "JPEG" ) )
            {
Laurent Aimar's avatar
Laurent Aimar committed
542
                tk->fmt.i_codec = VLC_FOURCC( 'M', 'J', 'P', 'G' );
543 544 545 546 547 548 549 550
            }
            else if( !strcmp( sub->codecName(), "MP4V-ES" ) )
            {
                unsigned int i_extra;
                uint8_t      *p_extra;

                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'v' );

551 552
                if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                       i_extra ) ) )
553 554 555 556
                {
                    tk->fmt.i_extra = i_extra;
                    tk->fmt.p_extra = malloc( i_extra );
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
557
                    delete[] p_extra;
558 559
                }
            }
560 561 562 563
            else if( !strcmp( sub->codecName(), "X-QT" ) ||
                     !strcmp( sub->codecName(), "X-QUICKTIME" ) ||
                     !strcmp( sub->codecName(), "X-QDM" ) ||
                     !strcmp( sub->codecName(), "X-SV3V-ES" )  ||
564
                     !strcmp( sub->codecName(), "X-SORENSONVIDEO" ) )
Laurent Aimar's avatar
Laurent Aimar committed
565 566 567
            {
                tk->b_quicktime = VLC_TRUE;
            }
568 569 570
            else if( !strcmp( sub->codecName(), "MP2T" ) )
            {
                tk->b_muxed = VLC_TRUE;
571
                tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
572
            }
573 574
            else if( !strcmp( sub->codecName(), "MP2P" ) ||
                     !strcmp( sub->codecName(), "MP1S" ) )
575 576
            {
                tk->b_muxed = VLC_TRUE;
577 578
                tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
                                                   p_demux->out );
579
            }
580 581 582 583
            else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            {
                tk->b_asf = VLC_TRUE;
                if( p_sys->p_out_asf == NULL )
584 585
                    p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                        p_demux->out );;
586
            }
587 588
        }

589 590 591 592
        if( !tk->b_quicktime && !tk->b_muxed && !tk->b_asf )
        {
            tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
        }
593

594 595 596 597 598
        if( sub->rtcpInstance() != NULL )
        {
            sub->rtcpInstance()->setByeHandler( StreamClose, tk );
        }

599
        if( tk->p_es || tk->b_quicktime || tk->b_muxed || tk->b_asf )
600 601
        {
            tk->readSource = sub->readSource();
Laurent Aimar's avatar
Laurent Aimar committed
602
            tk->rtpSource  = sub->rtpSource();
603
            
604 605 606
            /* Append */
            p_sys->track = (live_track_t**)realloc( p_sys->track, sizeof( live_track_t ) * ( p_sys->i_track + 1 ) );
            p_sys->track[p_sys->i_track++] = tk;
607 608 609
        }
        else
        {
610 611
            /* BUG ??? */
            msg_Err( p_demux, "unusable RTSP track. this should not happen" );
612 613 614 615 616 617
            free( tk );
        }
    }

    delete iter;

618 619 620 621 622 623 624
    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
625 626 627
    if( p_sys->i_track <= 0 )
        goto error;

628 629 630
    return VLC_SUCCESS;

error:
631 632
    if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
    if( p_sys->ms ) Medium::close( p_sys->ms );
633
    if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
634
    if( p_sys->env ) RECLAIM_ENV(p_sys->env);
635 636 637 638 639 640 641
    if( p_sys->p_timeout )
    {
        p_sys->p_timeout->b_die = VLC_TRUE;
        vlc_thread_join( p_sys->p_timeout );
        vlc_object_detach( p_sys->p_timeout );
        vlc_object_destroy( p_sys->p_timeout );
    }
642 643 644
    if( p_sys->scheduler ) delete p_sys->scheduler;
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
    if( p_sys->psz_path ) free( p_sys->psz_path );
645

646 647 648 649 650 651 652
    free( p_sys );
    return VLC_EGENERIC;
}

/*****************************************************************************
 * DemuxClose:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
653
static void Close( vlc_object_t *p_this )
654
{
655
    demux_t *p_demux = (demux_t*)p_this;
656 657
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
658 659 660 661 662

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

663
        if( tk->b_muxed ) stream_DemuxDelete( tk->p_out_muxed );
664
        free( tk->p_buffer );
665 666
        free( tk );
    }
667 668 669

    if( p_sys->i_track ) free( p_sys->track );
    if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
670

671
    if( p_sys->rtsp && p_sys->ms )
672 673
    {
        /* TEARDOWN */
674
        p_sys->rtsp->teardownMediaSession( *p_sys->ms );
675
    }
676

677 678
    Medium::close( p_sys->ms );

679 680 681 682 683 684 685 686
    if( p_sys->p_timeout )
    {
        p_sys->p_timeout->b_die = VLC_TRUE;
        vlc_thread_join( p_sys->p_timeout );
        vlc_object_detach( p_sys->p_timeout );
        vlc_object_destroy( p_sys->p_timeout );
    }

687 688
    if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
    if( p_sys->env ) RECLAIM_ENV( p_sys->env );
689 690
    if( p_sys->scheduler ) delete p_sys->scheduler;
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
691
    if( p_sys->psz_path ) free( p_sys->psz_path );
692 693 694
    free( p_sys );
}

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
/*****************************************************************************
 * 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;

    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;

createnew:
    if( var_CreateGetBool( p_demux, "rtsp-http" ) )
        i_http_port = var_CreateGetInteger( p_demux, "rtsp-http-port" );

#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500
    if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1 /*verbose*/,
          "VLC media player", i_http_port ) ) == NULL )
#else
    if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1 /*verbose*/,
          "VLC media player" ) ) == NULL )
#endif
    {
        msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                 p_sys->env->getResultMsg() );
        return VLC_EGENERIC;
    }
    psz_url = (char*)malloc( strlen( p_sys->psz_path ) + 8 );
    sprintf( psz_url, "rtsp://%s", p_sys->psz_path );

    psz_options = p_sys->rtsp->sendOptionsCmd( psz_url );
    if( psz_options ) delete [] psz_options;

    psz_user = var_CreateGetString( p_demux, "rtsp-user" );
    psz_pwd  = var_CreateGetString( p_demux, "rtsp-pwd" );

describe:
    authenticator.setUsernameAndPassword( (const char*)psz_user, (const char*)psz_pwd );
    p_sdp = p_sys->rtsp->describeURL( psz_url,
                &authenticator, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );

    if( psz_user ) free( psz_user );
    if( psz_pwd ) free( psz_pwd );                
    
    if( p_sdp == NULL )
    {
746
        /* failure occurred */
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
        int i_code = 0;
        const char *psz_error = p_sys->env->getResultMsg();
                
        msg_Dbg( p_demux, "DESCRIBE failed with %d: %s", i_code, psz_error );
        sscanf( psz_error, "%*sRTSP/%*s%3u", &i_code );
        
        if( i_code == 401 )
        {
            char *psz_login = NULL; char *psz_password = NULL;
            msg_Dbg( p_demux, "authentication failed" );

            i_ret = intf_UserLoginPassword( p_demux, _("RTSP authentication"),
                           _("Please enter a valid login name and a password."),
                                                   &psz_login, &psz_password );
            if( i_ret == DIALOG_OK_YES )
            {
               msg_Dbg( p_demux, "retrying with user=%s, pwd=%s",
                           psz_login, psz_password );
               
               if( psz_login ) psz_user = psz_login;
               if( psz_password ) psz_pwd = psz_password;
               goto describe;
            }
            if( psz_login ) free( psz_login );
            if( psz_password ) free( psz_password );
        }
        else if( !var_CreateGetBool( p_demux, "rtsp-http" ) )
        {
            /* Perhaps a firewall is being annoying. Try HTTP tunneling mode */
            vlc_value_t val;
            val.b_bool = VLC_TRUE;
            msg_Dbg( p_demux, "we will now try HTTP tunneling mode" );
            var_Set( p_demux, "rtsp-http", val );
            if( psz_url ) free( psz_url );
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
            goto createnew;
        }
        i_ret = VLC_EGENERIC;
    }
    if( psz_url ) free( psz_url );

    /* malloc-ated copy */
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
790
    if( p_sdp ) p_sys->p_sdp = strdup( (char*)p_sdp );
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
    delete[] p_sdp;

    return i_ret;
}

/*****************************************************************************
 * Connect: prepares the subsessions and does the SETUP
 *****************************************************************************/
static int SessionsSetup( demux_t *p_demux )
{
    demux_sys_t             *p_sys  = p_demux->p_sys;
    MediaSubsessionIterator *iter   = NULL;
    MediaSubsession         *sub    = NULL;

    vlc_bool_t     b_rtsp_tcp = VLC_FALSE;
    int            i_client_port;
    int            i_active_sessions = 0;
    int            i_return = VLC_SUCCESS;
    unsigned int   i_buffer = 0;
    unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */

    b_rtsp_tcp    = var_CreateGetBool( p_demux, "rtsp-tcp" );
    i_client_port = var_CreateGetInteger( p_demux, "rtp-client-port" );

    /* Initialise each media subsession */
    iter = new MediaSubsessionIterator( *p_sys->ms );
    while( ( sub = iter->next() ) != NULL )
    {
        Boolean bInit;

        /* 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" );
            i_return = VLC_EGENERIC;
            break;
        }

        if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            bInit = sub->initiate( 4 ); /* Constant ? */
        else
            bInit = sub->initiate();
        
        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();
          
                /* Increase the buffer size */
                if( i_buffer > 0 )
                    increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
                
                /* 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 )
            {
                if( !( p_sys->rtsp->setupMediaSubsession( *sub, False,
                                                   b_rtsp_tcp ? True : False ) ) )
                {
                    msg_Err( p_demux, "SETUP of'%s/%s' failed %s", sub->mediumName(),
                             sub->codecName(), p_sys->env->getResultMsg() );
                }
                else i_active_sessions++;
            } else i_active_sessions++; /* we don't really know, let's just hope it's there */

            if( !p_sys->b_multicast )
            {
                /* Check, because we need diff. rollover behaviour for multicast */
                p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
            }
        }
    }
    delete iter;
    if( i_active_sessions <= 0 ) i_return = VLC_EGENERIC;
    return i_return;
}

/*****************************************************************************
 * Play: starts the actual playback of the stream
 *****************************************************************************/
static int Play( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    
    if( p_sys->rtsp )
    {
        /* The PLAY */
        if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
        {
            msg_Err( p_demux, "RTSP PLAY failed %s", p_sys->env->getResultMsg() );
            return VLC_EGENERIC;
        }

        /* Retrieve the timeout value and set up a timeout prevention thread */
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1138089600
        p_sys->i_timeout = p_sys->rtsp->sessionTimeoutParameter();
#endif
        if( p_sys->i_timeout > 0 && !p_sys->p_timeout )
        {
            msg_Dbg( p_demux, "We have a timeout of %d seconds",  p_sys->i_timeout );
            p_sys->p_timeout = (timeout_thread_t *)vlc_object_create( p_demux, sizeof(timeout_thread_t) );
            p_sys->p_timeout->p_sys = p_demux->p_sys; /* lol, object recursion :D */
            if( vlc_thread_create( p_sys->p_timeout, "liveMedia-timeout", TimeoutPrevention,
                                   VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
            {
                msg_Err( p_demux, "cannot spawn liveMedia timeout thread" );
                vlc_object_destroy( p_sys->p_timeout );
            }
            msg_Dbg( p_demux, "spawned timeout thread" );
            vlc_object_attach( p_sys->p_timeout, p_demux );
        }
    }
    return VLC_SUCCESS;
}


932 933 934
/*****************************************************************************
 * Demux:
 *****************************************************************************/
935
static int Demux( demux_t *p_demux )
936
{
937
    demux_sys_t    *p_sys = p_demux->p_sys;
938 939
    TaskToken      task;

940
    vlc_bool_t      b_send_pcr = VLC_TRUE;
941
    int64_t         i_pcr = 0;
942 943
    int             i;

944 945 946
    /* Check if we need to send the server a Keep-A-Live signal */
    if( p_sys->b_timeout_call && p_sys->rtsp && p_sys->ms )
    {
947
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1138089600
948
        char *psz_bye = NULL;
949
        p_sys->rtsp->getMediaSessionParameter( *p_sys->ms, NULL, psz_bye );
950
#endif
951 952 953
        p_sys->b_timeout_call = VLC_FALSE;
    }

Laurent Aimar's avatar
Laurent Aimar committed
954 955 956 957
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

958 959 960
        if( tk->b_asf || tk->b_muxed )
            b_send_pcr = VLC_FALSE;

Laurent Aimar's avatar
Laurent Aimar committed
961 962 963 964 965 966 967 968 969
        if( i_pcr == 0 )
        {
            i_pcr = tk->i_pts;
        }
        else if( tk->i_pts != 0 && i_pcr > tk->i_pts )
        {
            i_pcr = tk->i_pts ;
        }
    }
Laurent Aimar's avatar
Laurent Aimar committed
970
    if( i_pcr != p_sys->i_pcr && i_pcr > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
971 972
    {
        p_sys->i_pcr = i_pcr;
973

974 975
        if( b_send_pcr )
            es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pcr );
Laurent Aimar's avatar
Laurent Aimar committed
976 977
        if( p_sys->i_pcr_start <= 0 || p_sys->i_pcr_start > i_pcr ||
            ( p_sys->i_length > 0 && i_pcr - p_sys->i_pcr_start > p_sys->i_length ) )
Laurent Aimar's avatar
Laurent Aimar committed
978 979 980 981 982
        {
            p_sys->i_pcr_start = i_pcr;
        }
    }

983 984 985
#if 0
    /* Disabled because it's simply not reliable enough */

986 987
    /* When a On Demand QT stream ends, the last frame keeps going with the same PCR/PTS value */
    /* This tests for that, so we can later decide to end this session */
988
    if( i_pcr > 0 && p_sys->i_pcr == p_sys->i_pcr_previous )
989
    {
990 991
        if( p_sys->i_pcr_repeats == 0 )
            p_sys->i_pcr_repeatdate = mdate();
992 993 994 995 996
        p_sys->i_pcr_repeats++;
    }
    else
    {
        p_sys->i_pcr_previous = p_sys->i_pcr;
997
        p_sys->i_pcr_repeatdate = 0;
998 999
        p_sys->i_pcr_repeats = 0;
    }
1000 1001

    if( p_sys->i_pcr_repeats > 5 && mdate() > p_sys->i_pcr_repeatdate + 1000000 )
1002
    {
1003 1004
        /* We need at least 5 repeats over at least a second of time before we EOF */
        msg_Dbg( p_demux, "suspect EOF due to end of VoD session" );
1005 1006
        return 0;
    }
1007
#endif
1008

1009
    /* First warn we want to read data */
1010 1011 1012 1013 1014 1015 1016 1017
    p_sys->event = 0;
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

        if( tk->waiting == 0 )
        {
            tk->waiting = 1;
1018
            tk->readSource->getNextFrame( tk->p_buffer, tk->i_buffer,
1019
                                          StreamRead, tk, StreamClose, tk );
1020 1021 1022
        }
    }
    /* Create a task that will be called if we wait more than 300ms */
1023
    task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_demux );
1024 1025 1026 1027 1028 1029 1030

    /* Do the read */
    p_sys->scheduler->doEventLoop( &p_sys->event );

    /* remove the task */
    p_sys->scheduler->unscheduleDelayedTask( task );

1031 1032 1033 1034 1035
    /* Check for gap in pts value */
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

1036
        if( !tk->b_muxed && !tk->b_rtcp_sync &&
1037
            tk->rtpSource && tk->rtpSource->hasBeenSynchronizedUsingRTCP() )
1038
        {
1039 1040 1041
            msg_Dbg( p_demux, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );

            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
            tk->b_rtcp_sync = VLC_TRUE;

            /* reset PCR and PCR start, mmh won't work well for multi-stream I fear */
            tk->i_pts = 0;
            p_sys->i_pcr_start = 0;
            p_sys->i_pcr = 0;
            i_pcr = 0;
        }
    }

1052 1053
    if( p_sys->b_multicast && p_sys->b_no_data && p_sys->i_no_data_ti > 120 )
    {
1054
        /* FIXME Make this configurable
1055 1056
        msg_Err( p_demux, "no multicast data received in 36s, aborting" );
        return 0;
1057
        */
1058
    }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1059
    else if( !p_sys->b_multicast && p_sys->b_no_data && p_sys->i_no_data_ti > 34 )
1060 1061 1062 1063 1064
    {
        vlc_bool_t b_rtsp_tcp = var_GetBool( p_demux, "rtsp-tcp" );

        if( !b_rtsp_tcp && p_sys->rtsp && p_sys->ms )
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1065
            msg_Warn( p_demux, "no data received in 10s. Switching to TCP" );
1066 1067 1068 1069 1070 1071
            if( RollOverTcp( p_demux ) )
            {
                msg_Err( p_demux, "TCP rollover failed, aborting" );
                return 0;
            }
        }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1072
        else if( p_sys->i_no_data_ti > 34 )
1073
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1074
            msg_Err( p_demux, "no data received in 10s, aborting" );
1075 1076 1077
            return 0;
        }
    }
1078
    else if( !p_sys->b_multicast && p_sys->b_no_data && p_sys->i_no_data_ti > 34 )
1079 1080
    {
        /* EOF ? */
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1081
        msg_Warn( p_demux, "no data received in 10s, eof ?" );
1082
        return 0;