live555.cpp 65.9 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
21
22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
26
27
28
29
 *****************************************************************************/

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

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

34
#include <vlc/vlc.h>
35

zorglub's avatar
zorglub committed
36
37
38
#include <vlc_demux.h>
#include <vlc_interface.h>
#include <vlc_network.h>
39
#include <vlc_url.h>
40
41
42

#include <iostream>

gbazin's avatar
   
gbazin committed
43
44
45
46
#if defined( WIN32 )
#   include <winsock2.h>
#endif

47
#include "UsageEnvironment.hh"
48
49
50
51
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include "liveMedia.hh"

52
53
54
55
extern "C" {
#include "../access/mms/asf.h"  /* Who said ugly ? */
}

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

hartman's avatar
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 );
zorglub's avatar
zorglub committed
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" );
zorglub's avatar
zorglub 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" );
sigmunau's avatar
sigmunau committed
94
        add_shortcut( "sdp" );
Laurent Aimar's avatar
Laurent Aimar committed
95
96
        set_capability( "access_demux", 0 );
        set_callbacks( Open, Close );
hartman's avatar
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
        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"),
108
109
                  N_("Port to use for tunneling the RTSP/RTP over HTTP."),
                  VLC_TRUE );
hartman's avatar
hartman committed
110
111
112
113
        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 );
114
        add_string( "rtsp-user", NULL, NULL, USER_TEXT,
hartman's avatar
hartman committed
115
                    USER_LONGTEXT, VLC_TRUE );
116
        add_string( "rtsp-pwd", NULL, NULL, PASS_TEXT,
hartman's avatar
hartman committed
117
                    PASS_LONGTEXT, VLC_TRUE );
118
119
120
121
vlc_module_end();


/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
122
 * Local prototypes
123
 *****************************************************************************/
124

125
126
typedef struct
{
127
128
    demux_t         *p_demux;
    MediaSubsession *sub;
129

130
131
    es_format_t     fmt;
    es_out_id_t     *p_es;
132

133
134
135
136
    vlc_bool_t      b_muxed;
    vlc_bool_t      b_quicktime;
    vlc_bool_t      b_asf;
    stream_t        *p_out_muxed;    /* for muxed stream */
137

138
139
    uint8_t         *p_buffer;
    unsigned int    i_buffer;
140

141
142
143
144
    vlc_bool_t      b_rtcp_sync;
    char            waiting;
    int64_t         i_pts;
    u_int32_t       i_start_seq;
145

146
147
} live_track_t;

148
149
150
151
152
struct timeout_thread_t
{
    VLC_COMMON_MEMBERS

    int64_t      i_remain;
153
    vlc_bool_t   b_handle_keep_alive;
154
155
156
    demux_sys_t  *p_sys;
};

157
158
struct demux_sys_t
{
hartman's avatar
hartman committed
159
160
    char            *p_sdp;    /* XXX mallocated */
    char            *psz_path; /* URL-encoded path */
161
    vlc_url_t       url;
162
163
164
165
166
167

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

168
    /* */
169
170
171
    int              i_track;
    live_track_t     **track;   /* XXX mallocated */

172
    /* Weird formats */
173
174
    asf_header_t     asfh;
    stream_t         *p_out_asf;
175
    vlc_bool_t       b_real;
176
177

    /* */
178
179
180
181
    int64_t          i_pcr; /* The clock */
    int64_t          i_npt; /* The current time in the stream */
    int64_t          i_npt_length;
    int64_t          i_npt_start;
182

183
184
185
186
187
    /* 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 */

188
    /* */
189
    vlc_bool_t       b_multicast;   /* true if one of the tracks is multicasted */
190
191
192
    vlc_bool_t       b_no_data;     /* true if we never receive any data */
    int              i_no_data_ti;  /* consecutive number of TaskInterrupt */

193
194
195
196
197
198
    char             event;
};

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

hartman's avatar
hartman committed
199
200
201
202
203
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 * );
204

hartman's avatar
hartman committed
205
206
207
static void StreamRead  ( void *, unsigned int, unsigned int,
                          struct timeval, unsigned int );
static void StreamClose ( void * );
208
static void TaskInterrupt( void * );
209

210
211
static void TimeoutPrevention( timeout_thread_t * );

212
static unsigned char* parseH264ConfigStr( char const* configStr,
213
                                          unsigned int& configSize );
214

215
216
217
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
218
static int  Open ( vlc_object_t *p_this )
219
{
220
    demux_t     *p_demux = (demux_t*)p_this;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
221
    demux_sys_t *p_sys = NULL;
222

hartman's avatar
hartman committed
223
224
    MediaSubsessionIterator *iter   = NULL;
    MediaSubsession         *sub    = NULL;
225
    int i, i_return;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
226
    int i_error = VLC_EGENERIC;
227

Laurent Aimar's avatar
Laurent Aimar committed
228
    if( p_demux->s )
229
    {
Laurent Aimar's avatar
Laurent Aimar committed
230
231
        /* See if it looks like a SDP
           v, o, s fields are mandatory and in this order */
232
        const uint8_t *p_peek;
233
234
        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;

235
236
        if( memcmp( p_peek, "v=0\r\n", 5 ) &&
            memcmp( p_peek, "v=0\n", 4 ) &&
Laurent Aimar's avatar
Laurent Aimar committed
237
238
239
240
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            return VLC_EGENERIC;
        }
241
    }
Laurent Aimar's avatar
Laurent Aimar committed
242
    else
243
    {
Laurent Aimar's avatar
Laurent Aimar committed
244
        var_Create( p_demux, "rtsp-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
245
246
    }

247
248
249
    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
250
251
    if( !p_sys ) return VLC_ENOMEM;

252
253
254
255
256
257
258
    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;
259
260
261
262
    p_sys->i_pcr = 0;
    p_sys->i_npt = 0;
    p_sys->i_npt_start = 0;
    p_sys->i_npt_length = 0;
263
    p_sys->p_out_asf = NULL;
264
265
    p_sys->b_no_data = VLC_TRUE;
    p_sys->i_no_data_ti = 0;
266
267
268
    p_sys->p_timeout = NULL;
    p_sys->i_timeout = 0;
    p_sys->b_timeout_call = VLC_FALSE;
269
    p_sys->b_multicast = VLC_FALSE;
270
    p_sys->b_real = VLC_FALSE;
hartman's avatar
hartman committed
271
    p_sys->psz_path = strdup( p_demux->psz_path );
272

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

276
277
    if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
    {
278
        msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
279
280
        goto error;
    }
281
    if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
282
    {
283
        msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
284
285
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
286

287
    if( strcasecmp( p_demux->psz_access, "sdp" ) )
288
    {
289
        char *p = p_sys->psz_path;
290
        while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
291
292
    }

hartman's avatar
hartman committed
293
    if( p_demux->s != NULL )
Laurent Aimar's avatar
Laurent Aimar committed
294
295
    {
        /* Gather the complete sdp file */
hartman's avatar
hartman committed
296
297
298
        int     i_sdp       = 0;
        int     i_sdp_max   = 1000;
        uint8_t *p_sdp      = (uint8_t*) malloc( i_sdp_max );
299

Jean-Paul Saman's avatar
Jean-Paul Saman committed
300
301
302
303
304
305
        if( !p_sdp )
        {
            i_error = VLC_ENOMEM;
            goto error;
        }

Laurent Aimar's avatar
Laurent Aimar committed
306
307
        for( ;; )
        {
308
309
            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
310

Jean-Paul Saman's avatar
Jean-Paul Saman committed
311
            if( p_demux->b_die || p_demux->b_error )
312
313
            {
                free( p_sdp );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
314
                goto error;
315
            }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
316

Laurent Aimar's avatar
Laurent Aimar committed
317
318
319
            if( i_read < 0 )
            {
                msg_Err( p_demux, "failed to read SDP" );
320
                free( p_sdp );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
321
                goto error;
Laurent Aimar's avatar
Laurent Aimar committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
            }

            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;
336
    }
337
338
    else if( ( p_demux->s == NULL ) &&
             !strcasecmp( p_demux->psz_access, "sdp" ) )
hartman's avatar
hartman committed
339
340
341
342
343
    {
        /* sdp:// link from SAP */
        p_sys->p_sdp = strdup( p_sys->psz_path );
    }
    else if( ( i_return = Connect( p_demux ) ) != VLC_SUCCESS )
344
    {
hartman's avatar
hartman committed
345
        msg_Err( p_demux, "Failed to connect with rtsp://%s", p_sys->psz_path );
346
347
        goto error;
    }
348

hartman's avatar
hartman committed
349
    if( p_sys->p_sdp == NULL )
350
    {
hartman's avatar
hartman committed
351
        msg_Err( p_demux, "Failed to retrieve the RTSP Session Description" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
352
        i_error = VLC_ENOMEM;
hartman's avatar
hartman committed
353
        goto error;
354
355
    }

hartman's avatar
hartman committed
356
357
358
359
360
    if( ( i_return = SessionsSetup( p_demux ) ) != VLC_SUCCESS )
    {
        msg_Err( p_demux, "Nothing to play for rtsp://%s", p_sys->psz_path );
        goto error;
    }
361

362
    if( p_sys->b_real ) goto error;
363

hartman's avatar
hartman committed
364
365
    if( ( i_return = Play( p_demux ) ) != VLC_SUCCESS )
        goto error;
366

367
368
369
370
371
372
373
    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
374
375
376
    if( p_sys->i_track <= 0 )
        goto error;

377
378
379
    return VLC_SUCCESS;

error:
380
381
382
383
384
385
386
387
388
389
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

        if( tk->b_muxed ) stream_DemuxDelete( tk->p_out_muxed );
        free( tk->p_buffer );
        free( tk );
    }

    if( p_sys->i_track ) free( p_sys->track );
390
    if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
391
    if( p_sys->rtsp && p_sys->ms ) p_sys->rtsp->teardownMediaSession( *p_sys->ms );
392
    if( p_sys->ms ) Medium::close( p_sys->ms );
hartman's avatar
hartman committed
393
    if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
394
    if( p_sys->env ) p_sys->env->reclaim();
395
396
    if( p_sys->p_timeout )
    {
397
        vlc_object_kill( p_sys->p_timeout );
398
399
400
401
        vlc_thread_join( p_sys->p_timeout );
        vlc_object_detach( p_sys->p_timeout );
        vlc_object_destroy( p_sys->p_timeout );
    }
402
403
404
    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 );
405

406
407
    vlc_UrlClean( &p_sys->url );

408
    free( p_sys );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
409
    return i_error;
410
411
412
413
414
}

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

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

425
        if( tk->b_muxed ) stream_DemuxDelete( tk->p_out_muxed );
426
        free( tk->p_buffer );
427
428
        free( tk );
    }
429
430
431

    if( p_sys->i_track ) free( p_sys->track );
    if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
432
433
434
435
    if( p_sys->rtsp && p_sys->ms ) p_sys->rtsp->teardownMediaSession( *p_sys->ms );
    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();
436
437
    if( p_sys->p_timeout )
    {
438
        vlc_object_kill( p_sys->p_timeout );
439
440
441
442
        vlc_thread_join( p_sys->p_timeout );
        vlc_object_detach( p_sys->p_timeout );
        vlc_object_destroy( p_sys->p_timeout );
    }
443
444
    if( p_sys->scheduler ) delete p_sys->scheduler;
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
445
    if( p_sys->psz_path ) free( p_sys->psz_path );
446

447
448
    vlc_UrlClean( &p_sys->url );

449
450
451
    free( p_sys );
}

hartman's avatar
hartman committed
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/*****************************************************************************
 * 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;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
    psz_url = (char*)malloc( strlen( p_sys->psz_path ) + 8 );
    if( !psz_url ) return VLC_ENOMEM;

    if( p_sys->url.psz_username || p_sys->url.psz_password )
    {
        sprintf( psz_url, "rtsp://%s%s", p_sys->url.psz_host,
                 p_sys->url.psz_path );

        psz_user = strdup( p_sys->url.psz_username );
        psz_pwd  = strdup( p_sys->url.psz_password );
    }
    else
    {
        sprintf( psz_url, "rtsp://%s", p_sys->psz_path );

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

hartman's avatar
hartman committed
487
createnew:
488
489
490
491
492
493
494
495
    if( p_demux->b_die || p_demux->b_error )
    {
        free( psz_user );
        free( psz_pwd );
        free( psz_url );
        return VLC_EGENERIC;
    }

hartman's avatar
hartman committed
496
497
498
    if( var_CreateGetBool( p_demux, "rtsp-http" ) )
        i_http_port = var_CreateGetInteger( p_demux, "rtsp-http-port" );

499
    if( ( p_sys->rtsp = RTSPClient::createNew( *p_sys->env,
500
          p_demux->p_libvlc->i_verbose > 1,
hartman's avatar
hartman committed
501
502
503
504
          "VLC media player", i_http_port ) ) == NULL )
    {
        msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                 p_sys->env->getResultMsg() );
505
506
507
        free( psz_user );
        free( psz_pwd );
        free( psz_url );
hartman's avatar
hartman committed
508
509
        return VLC_EGENERIC;
    }
510

511
512
513
514
515
516
517
518
519
520
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 
     * 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" ))
    {
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1130457500
        p_sys->rtsp->setUserAgentString( "VLC_MEDIA_PLAYER_KA" );
#endif
    }

525
describe:
526
527
528
529
530
531
    authenticator.setUsernameAndPassword( (const char*)psz_user,
                                          (const char*)psz_pwd );

    psz_options = p_sys->rtsp->sendOptionsCmd( psz_url, psz_user, psz_pwd,
                                               &authenticator );
    if( psz_options ) delete [] psz_options;
hartman's avatar
hartman committed
532

Jean-Paul Saman's avatar
Jean-Paul Saman committed
533
    p_sdp = p_sys->rtsp->describeURL( psz_url, &authenticator,
534
                         var_GetBool( p_demux, "rtsp-kasenna" ) );
hartman's avatar
hartman committed
535
536
    if( p_sdp == NULL )
    {
537
        /* failure occurred */
hartman's avatar
hartman committed
538
539
        int i_code = 0;
        const char *psz_error = p_sys->env->getResultMsg();
540

541
        if( var_GetBool( p_demux, "rtsp-http" ) )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
542
543
            sscanf( psz_error, "%*s %*s HTTP GET %*s HTTP/%*u.%*u %3u %*s",
                    &i_code );
544
        else sscanf( psz_error, "%*sRTSP/%*s%3u", &i_code );
545
        msg_Dbg( p_demux, "DESCRIBE failed with %d: %s", i_code, psz_error );
546

hartman's avatar
hartman committed
547
548
        if( i_code == 401 )
        {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
549
            int i_result;
hartman's avatar
hartman committed
550
551
            msg_Dbg( p_demux, "authentication failed" );

Jean-Paul Saman's avatar
Jean-Paul Saman committed
552
553
554
555
556
            if( psz_user ) free( psz_user );
            if( psz_pwd ) free( psz_pwd );
            psz_user = psz_pwd = NULL;

            i_result = intf_UserLoginPassword( p_demux, _("RTSP authentication"),
hartman's avatar
hartman committed
557
                           _("Please enter a valid login name and a password."),
Jean-Paul Saman's avatar
Jean-Paul Saman committed
558
559
                                                   &psz_user, &psz_pwd );
            if( i_result == DIALOG_OK_YES )
hartman's avatar
hartman committed
560
            {
561
                msg_Dbg( p_demux, "retrying with user=%s, pwd=%s",
Jean-Paul Saman's avatar
Jean-Paul Saman committed
562
                         psz_user, psz_pwd );
563
                goto describe;
hartman's avatar
hartman committed
564
565
            }
        }
566
        else if( (i_code != 0) && !var_GetBool( p_demux, "rtsp-http" ) )
hartman's avatar
hartman committed
567
568
569
570
571
572
573
        {
            /* 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( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
574
            p_sys->rtsp = NULL;
hartman's avatar
hartman committed
575
576
            goto createnew;
        }
577
578
        else
        {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
579
            msg_Dbg( p_demux, "connection timeout, retrying" );
580
            if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
581
            p_sys->rtsp = NULL;
582
583
            goto createnew;
        }
hartman's avatar
hartman committed
584
585
586
587
        i_ret = VLC_EGENERIC;
    }

    /* malloc-ated copy */
588
    if( psz_url ) free( psz_url );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
589
590
591
    if( psz_user ) free( psz_user );
    if( psz_pwd ) free( psz_pwd );

hartman's avatar
hartman committed
592
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
593
    p_sys->p_sdp = NULL;
hartman's avatar
hartman committed
594
    if( p_sdp ) p_sys->p_sdp = strdup( (char*)p_sdp );
hartman's avatar
hartman committed
595
596
597
598
599
600
    delete[] p_sdp;

    return i_ret;
}

/*****************************************************************************
601
 * SessionsSetup: prepares the subsessions and does the SETUP
hartman's avatar
hartman committed
602
603
604
605
606
607
608
609
610
611
612
613
614
 *****************************************************************************/
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_return = VLC_SUCCESS;
    unsigned int   i_buffer = 0;
    unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */

615
616
    b_rtsp_tcp    = var_CreateGetBool( p_demux, "rtsp-tcp" ) ||
                    var_GetBool( p_demux, "rtsp-http" );
hartman's avatar
hartman committed
617
618
    i_client_port = var_CreateGetInteger( p_demux, "rtp-client-port" );

619
620
621
622
623
624
625
626
    /* 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
627
628
629
630
631
    /* Initialise each media subsession */
    iter = new MediaSubsessionIterator( *p_sys->ms );
    while( ( sub = iter->next() ) != NULL )
    {
        Boolean bInit;
632
        live_track_t *tk;
hartman's avatar
hartman committed
633

634
635
636
637
638
639
        if( p_demux->b_die || p_demux->b_error )
        {
            delete iter;
            return VLC_EGENERIC;
        }

hartman's avatar
hartman committed
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
        /* 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" );
656
657
            p_sys->b_real = VLC_TRUE; /* This is a problem, we'll handle it later */
            continue;
hartman's avatar
hartman committed
658
659
660
661
662
663
        }

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

hartman's avatar
hartman committed
665
666
667
668
669
670
671
672
673
674
675
        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();
676

hartman's avatar
hartman committed
677
678
679
                /* Increase the buffer size */
                if( i_buffer > 0 )
                    increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
680

hartman's avatar
hartman committed
681
682
683
684
685
686
687
688
689
690
691
692
                /* 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 ) ) )
                {
693
694
695
696
697
698
699
700
701
                    /* 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,
                                                   b_rtsp_tcp ? False : True ) ) )
                    {
                        msg_Err( p_demux, "SETUP of'%s/%s' failed %s", sub->mediumName(),
                                 sub->codecName(), p_sys->env->getResultMsg() );
                        continue;
                    }
hartman's avatar
hartman committed
702
                }
703
            }
hartman's avatar
hartman committed
704

705
706
            /* Check if we will receive data from this subsession for this track */
            if( sub->readSource() == NULL ) continue;
hartman's avatar
hartman committed
707
708
709
710
711
            if( !p_sys->b_multicast )
            {
                /* Check, because we need diff. rollover behaviour for multicast */
                p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
            }
712
713

            tk = (live_track_t*)malloc( sizeof( live_track_t ) );
714
715
716
717
718
            if( !tk )
            {
                delete iter;
                return VLC_ENOMEM;
            }
719
720
721
722
723
724
725
726
727
728
729
730
            tk->p_demux     = p_demux;
            tk->sub         = sub;
            tk->p_es        = NULL;
            tk->b_quicktime = VLC_FALSE;
            tk->b_asf       = VLC_FALSE;
            tk->b_muxed     = VLC_FALSE;
            tk->p_out_muxed = NULL;
            tk->waiting     = 0;
            tk->b_rtcp_sync = VLC_FALSE;
            tk->i_pts       = 0;
            tk->i_buffer    = 65536;
            tk->p_buffer    = (uint8_t *)malloc( 65536 );
731
732
733
734
735
            if( !tk->p_buffer )
            {
                delete iter;
                return VLC_ENOMEM;
            }
736
737
738
739
740
741
742
743
744
745
746
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

            /* 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" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'a' );
                    tk->fmt.audio.i_rate = 0;
                }
                else if( !strcmp( sub->codecName(), "AC3" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'a', '5', '2', ' ' );
                    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" ) )
                {
                    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' );
                }
                else if( !strncmp( sub->codecName(), "G726", 4 ) )
                {
776
                    tk->fmt.i_codec = VLC_FOURCC( 'g', '7', '2', '6' );
777
778
779
780
781
782
783
784
785
786
787
788
789
790
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
                    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" ) )
                {
                    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' );
                }
                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' );

                    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;
                    }
                    /* 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();
                }
                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' );

                    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" ) )
                {
                    tk->b_asf = VLC_TRUE;
                    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" ) )
                {
                    tk->b_quicktime = VLC_TRUE;
                }
843
844
                else if( !strcmp( sub->codecName(), "SPEEX" ) )
                {
845
                    tk->fmt.i_codec = VLC_FOURCC( 's', 'p', 'x', 'r' );
846
847
                    if ( sub->rtpTimestampFrequency() )
                        tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();
848
                    else
849
850
851
852
                    {
                        msg_Warn( p_demux,"Using 8kHz as default sample rate." );
                        tk->fmt.audio.i_rate = 8000;
                    }
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
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
            }
            else if( !strcmp( sub->mediumName(), "video" ) )
            {
                es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
                if( !strcmp( sub->codecName(), "MPV" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'v' );
                }
                else if( !strcmp( sub->codecName(), "H263" ) ||
                         !strcmp( sub->codecName(), "H263-1998" ) ||
                         !strcmp( sub->codecName(), "H263-2000" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '3' );
                }
                else if( !strcmp( sub->codecName(), "H261" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '1' );
                }
                else if( !strcmp( sub->codecName(), "H264" ) )
                {
                    unsigned int i_extra = 0;
                    uint8_t      *p_extra = NULL;

                    tk->fmt.i_codec = VLC_FOURCC( 'h', '2', '6', '4' );
                    tk->fmt.b_packetized = VLC_FALSE;

                    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" ) )
                {
                    tk->fmt.i_codec = VLC_FOURCC( 'M', 'J', 'P', 'G' );
                }
                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' );

                    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" ) )
                {
                    tk->b_quicktime = VLC_TRUE;
                }
                else if( !strcmp( sub->codecName(), "MP2T" ) )
                {
                    tk->b_muxed = VLC_TRUE;
                    tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
                }
                else if( !strcmp( sub->codecName(), "MP2P" ) ||
                         !strcmp( sub->codecName(), "MP1S" ) )
                {
                    tk->b_muxed = VLC_TRUE;
                    tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
                                                       p_demux->out );
                }
                else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
                {
                    tk->b_asf = VLC_TRUE;
                    if( p_sys->p_out_asf == NULL )
                        p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                            p_demux->out );;
                }
            }

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

            if( sub->rtcpInstance() != NULL )
            {
                sub->rtcpInstance()->setByeHandler( StreamClose, tk );
            }

            if( tk->p_es || tk->b_quicktime || tk->b_muxed || tk->b_asf )
            {
                /* 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;
            }
            else
            {
                /* BUG ??? */
                msg_Err( p_demux, "unusable RTSP track. this should not happen" );
                free( tk );
            }
hartman's avatar
hartman committed
961
962
963
        }
    }
    delete iter;
964
965
    if( p_sys->i_track <= 0 ) i_return = VLC_EGENERIC;

966
#if (LIVEMEDIA_LIBRARY_VERSION_INT >= 1199404800)
967
968
    /* Retrieve the starttime if possible */
    p_sys->i_npt_start = (int64_t)( p_sys->ms->playStartTime() * (double)1000000.0 );
969
#else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
970
    p_sys->i_npt_start = (int64_t) -1;
971
#endif
972
973
974
    if( p_sys->i_npt_start < 0 )
        p_sys->i_npt_start = -1;

975
#if (LIVEMEDIA_LIBRARY_VERSION_INT >= 1199404800)
976
977
    /* Retrieve the duration if possible */
    p_sys->i_npt_length = (int64_t)( p_sys->ms->playEndTime() * (double)1000000.0 );
978
979
980
#else
    p_sys->i_npt_length = (int64_t) -1;
#endif
981
982
983
984
    if( p_sys->i_npt_length < 0 )
        p_sys->i_npt_length = -1;

    msg_Dbg( p_demux, "setup start: %lld stop:%lld", p_sys->i_npt_start, p_sys->i_npt_length );
hartman's avatar
hartman committed
985
986
987
988
989
990
991
992
993
    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;
994
995
    int i;

hartman's avatar
hartman committed
996
997
998
    if( p_sys->rtsp )
    {
        /* The PLAY */
999
        if( !p_sys->rtsp->playMediaSession( *p_sys->ms, p_sys->i_npt_start, -1, 1 ) )
hartman's avatar
hartman committed
1000
1001
1002
1003
1004
1005
1006
        {
            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 */
        p_sys->i_timeout = p_sys->rtsp->sessionTimeoutParameter();
1007
1008
        if( p_sys->i_timeout <= 0 )
            p_sys->i_timeout = 60; /* default value from RFC2326 */
1009
1010
1011
1012
1013

        /* start timeout-thread only on x-asf streams (wms), it has rtcp support but doesn't 
         * seem to use it for liveness/keep-alive, get_parameter seems to work for it. get_parameter
         * doesn't work with dss 5.5.4 & 5.5.5, they seems to work with rtcp */
        if( !p_sys->p_timeout && p_sys->p_out_asf )
hartman's avatar
hartman committed
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
        {
            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 );
        }
    }
1028
1029
    p_sys->i_pcr = 0;

1030
#if (LIVEMEDIA_LIBRARY_VERSION_INT >= 1195257600)
1031
1032
    for( i = 0; i < p_sys->i_track; i++ )
    {
1033
1034
        if( !p_sys->track[i]->b_rtcp_sync )
            p_sys->track[i]->i_pts = (int64_t) ( p_sys->track[i]->sub->rtpInfo.timestamp * (double)1000000.0 );
1035
        p_sys->track[i]->i_start_seq = (int)p_sys->track[i]->sub->rtpInfo.seqNum;
1036
        msg_Info( p_demux, "set startseq: %u", p_sys->track[i]->i_start_seq );
1037
1038
1039
    }
#endif

1040
#if (LIVEMEDIA_LIBRARY_VERSION_INT >= 1199404800)
1041
1042
    /* Retrieve the starttime if possible */
    p_sys->i_npt_start = (int64_t)( p_sys->ms->playStartTime() * (double)1000000.0 );
1043
#else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1044
    p_sys->i_npt_start = -1;
1045
#endif
1046
1047
1048
1049
1050
1051
1052
1053
    if( p_sys->i_npt_start < 0 )
    {
        p_sys->i_npt_start = -1;
        p_sys->i_npt = 0;
    }
    else
        p_sys->i_npt = p_sys->i_npt_start;

1054
#if (LIVEMEDIA_LIBRARY_VERSION_INT >= 1199404800)
1055
1056
    /* Retrieve the duration if possible */
    p_sys->i_npt_length = (int64_t)( p_sys->ms->playEndTime() * (double)1000000.0 );
1057
1058
1059
#else
    p_sys->i_npt_length = -1;
#endif
1060
1061
1062
1063
    if( p_sys->i_npt_length < 0 )
        p_sys->i_npt_length = -1;

    msg_Dbg( p_demux, "play start: %lld stop:%lld", p_sys->i_npt_start, p_sys->i_npt_length );
hartman's avatar
hartman committed
1064
1065
1066
1067
    return VLC_SUCCESS;
}


1068
1069
1070
/*****************************************************************************
 * Demux:
 *****************************************************************************/
1071
static int Demux( demux_t *p_demux )
1072
{
1073
    demux_sys_t    *p_sys = p_demux->p_sys;
1074
1075
    TaskToken      task;

1076
    vlc_bool_t      b_send_pcr = VLC_TRUE;
hartman's avatar
hartman committed
1077
    int64_t         i_pcr = 0;
1078
1079
    int             i;

1080
1081
1082
    /* 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 )
    {
1083
1084
        char *psz_bye = NULL;
        p_sys->rtsp->getMediaSessionParameter( *p_sys->ms, NULL, psz_bye );
1085
1086
1087
        p_sys->b_timeout_call = VLC_FALSE;
    }

1088

Laurent Aimar's avatar
Laurent Aimar committed
1089
1090
1091
1092
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

1093
1094
        if( tk->b_asf || tk->b_muxed )
            b_send_pcr = VLC_FALSE;
1095
#if 0
Laurent Aimar's avatar
Laurent Aimar committed
1096
1097
1098
1099
1100
1101
1102
1103
        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 ;
        }
1104
#endif
Laurent Aimar's avatar
Laurent Aimar committed
1105
    }
1106
    if( p_sys->i_pcr > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
1107
    {
1108
        if( b_send_pcr )
1109
            es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
Laurent Aimar's avatar
Laurent Aimar committed
1110
1111
    }

hartman's avatar
hartman committed
1112
    /* First warn we want to read data */
1113
1114
1115
1116
1117
1118
1119
1120
    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;
1121
            tk->sub->readSource()->getNextFrame( tk->p_buffer, tk->i_buffer,
1122
                                          StreamRead, tk, StreamClose, tk );
1123
1124
1125
        }
    }
    /* Create a task that will be called if we wait more than 300ms */
1126
    task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_demux );
1127
1128
1129
1130
1131
1132
1133

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

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

1134
1135
1136
1137
1138
    /* Check for gap in pts value */
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

1139
        if( !tk->b_muxed && !tk->b_rtcp_sync &&
1140
            tk->sub->rtpSource() && tk->sub->rtpSource()->hasBeenSynchronizedUsingRTCP() )
1141
        {
1142
1143
1144
            msg_Dbg( p_demux, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );

            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );