livedotcom.cpp 39.9 KB
Newer Older
1
2
3
/*****************************************************************************
 * live.cpp : live.com support.
 *****************************************************************************
4
 * Copyright (C) 2003-2004 VideoLAN
Carlo Calabrò's avatar
Carlo Calabrò committed
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/input.h>

#include <iostream>

gbazin's avatar
   
gbazin committed
34
35
36
37
#if defined( WIN32 )
#   include <winsock2.h>
#endif

38
39
40
41
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include "liveMedia.hh"

42
43
44
45
extern "C" {
#include "../access/mms/asf.h"  /* Who said ugly ? */
}

hartman's avatar
hartman committed
46
47
48
49
50
51
#if (LIVEMEDIA_LIBRARY_VERSION_INT < 1089936000)
#define RECLAIM_ENV(env) delete (env)
#else
#define RECLAIM_ENV(env) (env)->reclaim()
#endif

52
53
54
55
56
using namespace std;

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
57
58
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
59

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

65
#define KASENNA_TEXT N_( "Kasenna RTSP dialect")
66
67
68
#define KASENNA_LONGTEXT N_( "Kasenna server speak an old and unstandard " \
    "dialect of RTSP. When you set this parameter, VLC will try this dialect "\
    "for communication. In this mode you cannot talk to normal RTSP servers." )
69

70
71
vlc_module_begin();
    set_description( _("live.com (RTSP/RTP/SDP) demuxer" ) );
72
    set_capability( "demux2", 50 );
zorglub's avatar
zorglub committed
73
    set_shortname( "Live.com RTP/RTSP");
Laurent Aimar's avatar
Laurent Aimar committed
74
    set_callbacks( Open, Close );
75
    add_shortcut( "live" );
zorglub's avatar
zorglub committed
76
77
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
78
79

    add_submodule();
Laurent Aimar's avatar
Laurent Aimar committed
80
        set_description( _("RTSP/RTP access and demux") );
81
        add_shortcut( "rtsp" );
sigmunau's avatar
sigmunau committed
82
        add_shortcut( "sdp" );
Laurent Aimar's avatar
Laurent Aimar committed
83
84
        set_capability( "access_demux", 0 );
        set_callbacks( Open, Close );
hartman's avatar
hartman committed
85
        add_bool( "rtsp-tcp", 0, NULL,
Carlo Calabrò's avatar
Carlo Calabrò committed
86
87
                  N_("Use RTP over RTSP (TCP)"),
                  N_("Use RTP over RTSP (TCP)"), VLC_TRUE );
hartman's avatar
hartman committed
88
89
        add_integer( "rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
            CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
90
91
        add_bool( "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT,
                  KASENNA_LONGTEXT, VLC_TRUE );
92
93
94
vlc_module_end();

/* TODO:
95
 *  - Improve support of PS/TS
Laurent Aimar's avatar
Laurent Aimar committed
96
 *  - Support X-QT/X-QUICKTIME generic codec for audio.
97
 *
Laurent Aimar's avatar
Laurent Aimar committed
98
99
 *  - Check memory leak, delete/free -> still one when using rtsp-tcp but I'm
 *  not sure if it comes from me.
100
101
102
103
 *
 */

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
104
 * Local prototypes
105
106
107
108
109
110
111
 *****************************************************************************/
typedef struct
{
    demux_t     *p_demux;

    vlc_bool_t   b_quicktime;
    vlc_bool_t   b_muxed;
112
    vlc_bool_t   b_asf;
113
114
115
116
117
118
119
120
121
122

    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;

123
124
    uint8_t      *p_buffer;
    unsigned int  i_buffer;
125
126
127
128

    char         waiting;

    mtime_t      i_pts;
129

130
131
132
133
134
135
136
137
138
139
140
} live_track_t;

struct demux_sys_t
{
    char         *p_sdp;    /* XXX mallocated */

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

141
    /* */
142
143
144
145
146
    int              i_track;
    live_track_t     **track;   /* XXX mallocated */
    mtime_t          i_pcr;
    mtime_t          i_pcr_start;

147
148
149
150
151
    /* Asf */
    asf_header_t     asfh;
    stream_t         *p_out_asf;

    /* */
152
153
154
    mtime_t          i_length;
    mtime_t          i_start;

155
156
157
158
    /* */
    vlc_bool_t       b_no_data;     /* true if we never receive any data */
    int              i_no_data_ti;  /* consecutive number of TaskInterrupt */

159
160
161
162
163
164
    char             event;
};

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

165
166
static int ParseASF( demux_t * );

167
168
static int RollOverTcp( demux_t * );

169
170
171
172
static void StreamRead( void *, unsigned int, unsigned int,
                        struct timeval, unsigned int );
static void StreamClose( void * );
static void TaskInterrupt( void * );
173

174
175
176
/*****************************************************************************
 * DemuxOpen:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
177
static int  Open ( vlc_object_t *p_this )
178
{
179
180
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
181
182
183
184

    MediaSubsessionIterator *iter;
    MediaSubsession *sub;

Laurent Aimar's avatar
Laurent Aimar committed
185
    vlc_bool_t b_rtsp_tcp;
186
187
188
189
190
191
    uint8_t *p_peek;

    int     i_sdp;
    int     i_sdp_max;
    uint8_t *p_sdp;

Laurent Aimar's avatar
Laurent Aimar committed
192
    if( p_demux->s )
193
    {
Laurent Aimar's avatar
Laurent Aimar committed
194
195
        /* See if it looks like a SDP
           v, o, s fields are mandatory and in this order */
196
197
        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;

198
199
        if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
            strncmp( (char*)p_peek, "v=0\n", 4 ) &&
Laurent Aimar's avatar
Laurent Aimar committed
200
201
202
203
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            return VLC_EGENERIC;
        }
204
    }
Laurent Aimar's avatar
Laurent Aimar committed
205
    else
206
    {
Laurent Aimar's avatar
Laurent Aimar committed
207
        var_Create( p_demux, "rtsp-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
208
209
    }

210
211
212
    p_demux->pf_demux  = Demux;
    p_demux->pf_control= Control;
    p_demux->p_sys     = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
213
214
215
216
217
218
219
    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
220
221
    p_sys->i_pcr   = 0;
    p_sys->i_pcr_start = 0;
Laurent Aimar's avatar
Laurent Aimar committed
222
223
    p_sys->i_length = 0;
    p_sys->i_start = 0;
224
    p_sys->p_out_asf = NULL;
225
226
    p_sys->b_no_data = VLC_TRUE;
    p_sys->i_no_data_ti = 0;
227
228
229
230


    if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
    {
231
        msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
232
233
        goto error;
    }
234
    if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
235
    {
236
        msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
237
238
        goto error;
    }
Laurent Aimar's avatar
Laurent Aimar committed
239
240

    if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "rtsp" ) )
241
242
243
244
    {
        char *psz_url;
        char *psz_options;

245
246
        if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/,
              "VLC Media Player" ) ) == NULL )
247
        {
248
249
            msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                     p_sys->env->getResultMsg() );
250
251
            goto error;
        }
252
253
        psz_url = (char*)malloc( strlen( p_demux->psz_path ) + 8 );
        sprintf( psz_url, "rtsp://%s", p_demux->psz_path );
254
255

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

259
260
        p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url,
                              NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );
Laurent Aimar's avatar
Laurent Aimar committed
261
262
        if( p_sdp == NULL )
        {
263
264
            msg_Err( p_demux, "describeURL failed (%s)",
                     p_sys->env->getResultMsg() );
Laurent Aimar's avatar
Laurent Aimar committed
265
266
267
            free( psz_url );
            goto error;
        }
268
        free( psz_url );
Laurent Aimar's avatar
Laurent Aimar committed
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

        /* malloc-ated copy */
        p_sys->p_sdp = strdup( (char*)p_sdp );
        delete[] p_sdp;
        fprintf( stderr, "sdp=%s\n", p_sys->p_sdp );
    }
    else if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "sdp" ) )
    {
        p_sys->p_sdp = strdup( p_demux->psz_path );
    }
    else
    {
        /* Gather the complete sdp file */
        i_sdp = 0;
        i_sdp_max = 1000;
        p_sdp = (uint8_t*)malloc( i_sdp_max );
        for( ;; )
        {
287
288
            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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309

            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;

310
        msg_Dbg( p_demux, "sdp=%s\n", p_sys->p_sdp );
311
    }
312
    if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
313
    {
314
        msg_Err( p_demux, "MediaSession::createNew failed" );
315
316
317
        goto error;
    }

Laurent Aimar's avatar
Laurent Aimar committed
318
    b_rtsp_tcp = var_CreateGetBool( p_demux, "rtsp-tcp" );
319
320
321
322
323
324

    /* Initialise each media subsession */
    iter = new MediaSubsessionIterator( *p_sys->ms );
    while( ( sub = iter->next() ) != NULL )
    {
        unsigned int i_buffer = 0;
325
        Boolean bInit;
326
327
328
329
330
331
332
333
334

        /* Value taken from mplayer */
        if( !strcmp( sub->mediumName(), "audio" ) )
            i_buffer = 100000;
        else if( !strcmp( sub->mediumName(), "video" ) )
            i_buffer = 2000000;
        else
            continue;

335
336
337
338
339
340
        if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            bInit = sub->initiate( 4 ); /* Constant ? */
        else
            bInit = sub->initiate();

        if( !bInit )
341
        {
342
343
344
            msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
                      sub->mediumName(), sub->codecName(),
                      p_sys->env->getResultMsg() );
345
346
347
        }
        else
        {
348
349
350
351
352
353
            if( sub->rtpSource() )
            {
                int fd = sub->rtpSource()->RTPgs()->socketNum();
                /* Increase the buffer size */
                increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
            }
354

355
356
            msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
                     sub->codecName() );
357
358
359
360

            /* Issue the SETUP */
            if( p_sys->rtsp )
            {
361
362
                p_sys->rtsp->setupMediaSubsession( *sub, False,
                                                   b_rtsp_tcp ? True : False );
363
364
365
366
367
368
369
370
371
            }
        }
    }

    if( p_sys->rtsp )
    {
        /* The PLAY */
        if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
        {
372
            msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
            goto error;
        }
    }

    /* Create all es struct */
    iter->reset();
    while( ( sub = iter->next() ) != NULL )
    {
        live_track_t *tk;

        if( sub->readSource() == NULL )
        {
            continue;
        }

        tk = (live_track_t*)malloc( sizeof( live_track_t ) );
389
        tk->p_demux = p_demux;
390
        tk->waiting = 0;
391
        tk->i_pts   = 0;
Laurent Aimar's avatar
Laurent Aimar committed
392
        tk->b_quicktime = VLC_FALSE;
393
        tk->b_muxed     = VLC_FALSE;
394
        tk->b_asf       = VLC_FALSE;
395
        tk->b_rtcp_sync = VLC_FALSE;
396
397
        tk->p_out_muxed = NULL;
        tk->p_es        = NULL;
398
399
        tk->i_buffer    = 65536;
        tk->p_buffer    = (uint8_t *)malloc( 65536 );
400
401
402
403

        /* Value taken from mplayer */
        if( !strcmp( sub->mediumName(), "audio" ) )
        {
404
            es_format_Init( &tk->fmt, AUDIO_ES, VLC_FOURCC('u','n','d','f') );
405
            tk->fmt.audio.i_channels = sub->numChannels();
gbazin's avatar
   
gbazin committed
406
            tk->fmt.audio.i_rate = sub->rtpSource()->timestampFrequency();
407
408
409
410
411
412

            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' );
gbazin's avatar
   
gbazin committed
413
                tk->fmt.audio.i_rate = 0;
414
415
416
417
            }
            else if( !strcmp( sub->codecName(), "AC3" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'a', '5', '2', ' ' );
gbazin's avatar
   
gbazin committed
418
                tk->fmt.audio.i_rate = 0;
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
            }
            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( !strcmp( sub->codecName(), "MP4A-LATM" ) )
            {
                unsigned int i_extra;
                uint8_t      *p_extra;

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

445
446
                if( ( p_extra = parseStreamMuxConfigStr( sub->fmtp_config(),
                                                         i_extra ) ) )
447
448
                {
                    tk->fmt.i_extra = i_extra;
449
                    tk->fmt.p_extra = malloc( i_extra );
450
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
451
                    delete[] p_extra;
452
453
454
455
456
457
458
459
460
                }
            }
            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' );

461
462
                if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                       i_extra ) ) )
463
464
465
466
                {
                    tk->fmt.i_extra = i_extra;
                    tk->fmt.p_extra = malloc( i_extra );
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
467
                    delete[] p_extra;
468
469
                }
            }
470
471
472
473
            else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            {
                tk->b_asf = VLC_TRUE;
                if( p_sys->p_out_asf == NULL )
474
475
                    p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                        p_demux->out );
476
            }
477
478
479
        }
        else if( !strcmp( sub->mediumName(), "video" ) )
        {
480
            es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
481
482
483
484
485
            if( !strcmp( sub->codecName(), "MPV" ) )
            {
                tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'v' );
            }
            else if( !strcmp( sub->codecName(), "H263" ) ||
486
487
                     !strcmp( sub->codecName(), "H263-1998" ) ||
                     !strcmp( sub->codecName(), "H263-2000" ) )
488
            {
hartman's avatar
hartman committed
489
                tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '3' );
490
491
492
            }
            else if( !strcmp( sub->codecName(), "H261" ) )
            {
hartman's avatar
hartman committed
493
                tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '1' );
494
495
496
            }
            else if( !strcmp( sub->codecName(), "JPEG" ) )
            {
Laurent Aimar's avatar
Laurent Aimar committed
497
                tk->fmt.i_codec = VLC_FOURCC( 'M', 'J', 'P', 'G' );
498
499
500
501
502
503
504
505
            }
            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' );

506
507
                if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
                                                       i_extra ) ) )
508
509
510
511
                {
                    tk->fmt.i_extra = i_extra;
                    tk->fmt.p_extra = malloc( i_extra );
                    memcpy( tk->fmt.p_extra, p_extra, i_extra );
512
                    delete[] p_extra;
513
514
                }
            }
515
516
517
518
            else if( !strcmp( sub->codecName(), "X-QT" ) ||
                     !strcmp( sub->codecName(), "X-QUICKTIME" ) ||
                     !strcmp( sub->codecName(), "X-QDM" ) ||
                     !strcmp( sub->codecName(), "X-SV3V-ES" )  ||
519
                     !strcmp( sub->codecName(), "X-SORENSONVIDEO" ) )
Laurent Aimar's avatar
Laurent Aimar committed
520
521
522
            {
                tk->b_quicktime = VLC_TRUE;
            }
523
524
525
            else if( !strcmp( sub->codecName(), "MP2T" ) )
            {
                tk->b_muxed = VLC_TRUE;
526
                tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
527
            }
528
529
            else if( !strcmp( sub->codecName(), "MP2P" ) ||
                     !strcmp( sub->codecName(), "MP1S" ) )
530
531
            {
                tk->b_muxed = VLC_TRUE;
532
533
                tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
                                                   p_demux->out );
534
            }
535
536
537
538
            else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
            {
                tk->b_asf = VLC_TRUE;
                if( p_sys->p_out_asf == NULL )
539
540
                    p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
                                                        p_demux->out );;
541
            }
542
543
544
545
        }

        if( tk->fmt.i_codec != VLC_FOURCC( 'u', 'n', 'd', 'f' ) )
        {
546
            tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
547
548
        }

549
550
551
552
553
        if( sub->rtcpInstance() != NULL )
        {
            sub->rtcpInstance()->setByeHandler( StreamClose, tk );
        }

554
        if( tk->p_es || tk->b_quicktime || tk->b_muxed || tk->b_asf )
555
556
        {
            tk->readSource = sub->readSource();
Laurent Aimar's avatar
Laurent Aimar committed
557
            tk->rtpSource  = sub->rtpSource();
558
559
560
561

            /* 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;
562
563
564
565
566
567
568
569
570
        }
        else
        {
            free( tk );
        }
    }

    delete iter;

571
572
573
574
575
576
577
    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
578
579
580
581
582
583
584
    p_sys->i_length = (mtime_t)(p_sys->ms->playEndTime() * 1000000.0);
    if( p_sys->i_length < 0 )
    {
        p_sys->i_length = 0;
    }
    else if( p_sys->i_length > 0 )
    {
585
586
        /* FIXME */
        /* p_input->stream.p_selected_area->i_size = 1000;*/ /* needed for now */
Laurent Aimar's avatar
Laurent Aimar committed
587
588
589
590
    }

    if( p_sys->i_track <= 0 )
    {
591
        msg_Err( p_demux, "no codec supported, aborting" );
Laurent Aimar's avatar
Laurent Aimar committed
592
593
594
        goto error;
    }

595
596
597
    return VLC_SUCCESS;

error:
598
599
600
601
    if( p_sys->p_out_asf )
    {
        stream_DemuxDelete( p_sys->p_out_asf );
    }
602
603
604
605
606
607
608
609
610
611
    if( p_sys->ms )
    {
        Medium::close( p_sys->ms );
    }
    if( p_sys->rtsp )
    {
        Medium::close( p_sys->rtsp );
    }
    if( p_sys->env )
    {
hartman's avatar
hartman committed
612
        RECLAIM_ENV(p_sys->env);
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
    }
    if( p_sys->scheduler )
    {
        delete p_sys->scheduler;
    }
    if( p_sys->p_sdp )
    {
        free( p_sys->p_sdp );
    }
    free( p_sys );
    return VLC_EGENERIC;
}

/*****************************************************************************
 * DemuxClose:
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
629
static void Close( vlc_object_t *p_this )
630
{
631
    demux_t *p_demux = (demux_t*)p_this;
632
633
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;
634
635
636
637
638

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

639
        if( tk->b_muxed ) stream_DemuxDelete( tk->p_out_muxed );
640
        free( tk->p_buffer );
641
642
        free( tk );
    }
643
644
645

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

647
    if( p_sys->rtsp && p_sys->ms )
648
649
    {
        /* TEARDOWN */
650
        p_sys->rtsp->teardownMediaSession( *p_sys->ms );
651
    }
652

653
654
    Medium::close( p_sys->ms );

655
656
657
658
    if( p_sys->rtsp ) Medium::close( p_sys->rtsp );
    if( p_sys->env ) RECLAIM_ENV(p_sys->env);
    if( p_sys->scheduler ) delete p_sys->scheduler;
    if( p_sys->p_sdp ) free( p_sys->p_sdp );
659
660
661
662
663
664
    free( p_sys );
}

/*****************************************************************************
 * Demux:
 *****************************************************************************/
665
static int Demux( demux_t *p_demux )
666
{
667
    demux_sys_t    *p_sys = p_demux->p_sys;
668
669
    TaskToken      task;

670
    vlc_bool_t      b_send_pcr = VLC_TRUE;
Laurent Aimar's avatar
Laurent Aimar committed
671
    mtime_t         i_pcr = 0;
672
673
    int             i;

Laurent Aimar's avatar
Laurent Aimar committed
674
675
676
677
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

678
679
680
        if( tk->b_asf || tk->b_muxed )
            b_send_pcr = VLC_FALSE;

Laurent Aimar's avatar
Laurent Aimar committed
681
682
683
684
685
686
687
688
689
        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
690
    if( i_pcr != p_sys->i_pcr && i_pcr > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
691
692
    {
        p_sys->i_pcr = i_pcr;
693

694
695
        if( b_send_pcr )
            es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pcr );
Laurent Aimar's avatar
Laurent Aimar committed
696
697
        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
698
699
700
701
702
        {
            p_sys->i_pcr_start = i_pcr;
        }
    }

703
704
705
706
707
708
709
710
711
    /* First warm we want to read data */
    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;
712
            tk->readSource->getNextFrame( tk->p_buffer, tk->i_buffer,
713
                                          StreamRead, tk, StreamClose, tk );
714
715
716
        }
    }
    /* Create a task that will be called if we wait more than 300ms */
717
    task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_demux );
718
719
720
721
722
723
724

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

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

725
726
727
728
729
    /* Check for gap in pts value */
    for( i = 0; i < p_sys->i_track; i++ )
    {
        live_track_t *tk = p_sys->track[i];

730
731
        if( !tk->b_muxed && !tk->b_rtcp_sync &&
            tk->rtpSource->hasBeenSynchronizedUsingRTCP() )
732
        {
733
734
735
            msg_Dbg( p_demux, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );

            es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
736
737
738
739
740
741
742
743
744
745
            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;
        }
    }

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
    if( p_sys->b_no_data && p_sys->i_no_data_ti > 3 )
    {
        vlc_bool_t b_rtsp_tcp = var_GetBool( p_demux, "rtsp-tcp" );

        if( !b_rtsp_tcp && p_sys->rtsp && p_sys->ms )
        {
            msg_Warn( p_demux, "no data received in 900ms. Switching to TCP" );
            if( RollOverTcp( p_demux ) )
            {
                msg_Err( p_demux, "TCP rollover failed, aborting" );
                return 0;
            }
            var_SetBool( p_demux, "rtsp-tcp", VLC_TRUE );
        }
        else if( p_sys->i_no_data_ti > 10 )
        {
            msg_Err( p_demux, "no data received in 3s, aborting" );
            return 0;
        }
    }
    else if( p_sys->i_no_data_ti > 10 )
    {
        /* EOF ? */
        msg_Warn( p_demux, "no data received in 3s, eof ?" );
	return 0;
    }

773
    return p_demux->b_error ? 0 : 1;
774
775
}

776
777
778
779
/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
Laurent Aimar's avatar
Laurent Aimar committed
780
{
781
    demux_sys_t *p_sys = p_demux->p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
782
    int64_t *pi64;
Laurent Aimar's avatar
Laurent Aimar committed
783
    double  *pf, f;
784
    vlc_bool_t *pb, b_bool;
785

Laurent Aimar's avatar
Laurent Aimar committed
786
787
788
789
    switch( i_query )
    {
        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
Laurent Aimar's avatar
Laurent Aimar committed
790
791
792
793
794
795
796
797
798
799
800
801
            *pi64 = p_sys->i_pcr - p_sys->i_pcr_start + p_sys->i_start;
            return VLC_SUCCESS;

        case DEMUX_GET_LENGTH:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = p_sys->i_length;
            return VLC_SUCCESS;

        case DEMUX_GET_POSITION:
            pf = (double*)va_arg( args, double* );
            if( p_sys->i_length > 0 )
            {
802
803
                *pf = (double)( p_sys->i_pcr - p_sys->i_pcr_start +
                                p_sys->i_start ) / (double)(p_sys->i_length);
Laurent Aimar's avatar
Laurent Aimar committed
804
805
806
807
808
            }
            else
            {
                *pf = 0;
            }
Laurent Aimar's avatar
Laurent Aimar committed
809
810
            return VLC_SUCCESS;

Laurent Aimar's avatar
Laurent Aimar committed
811
812
813
814
815
816
817
818
819
        case DEMUX_SET_POSITION:
        {
            float time;

            f = (double)va_arg( args, double );
            time = f * (double)p_sys->i_length / 1000000.0;   /* in second */

            if( p_sys->rtsp && p_sys->i_length > 0 )
            {
820
821
822
                MediaSubsessionIterator *iter =
                    new MediaSubsessionIterator( *p_sys->ms );
                MediaSubsession *sub;
Laurent Aimar's avatar
Laurent Aimar committed
823
824
825
826
827
828
829
830
831
832
                int i;

                while( ( sub = iter->next() ) != NULL )
                {
                    p_sys->rtsp->playMediaSubsession( *sub, time );
                }
                delete iter;
                p_sys->i_start = (mtime_t)(f * (double)p_sys->i_length);
                p_sys->i_pcr_start = 0;
                p_sys->i_pcr       = 0;
833
                
Laurent Aimar's avatar
Laurent Aimar committed
834
835
836
837
                for( i = 0; i < p_sys->i_track; i++ )
                {
                    p_sys->track[i]->i_pts = 0;
                }
838

Laurent Aimar's avatar
Laurent Aimar committed
839
840
                return VLC_SUCCESS;
            }
841
            return VLC_SUCCESS;
Laurent Aimar's avatar
Laurent Aimar committed
842
843
        }

Laurent Aimar's avatar
Laurent Aimar committed
844
845
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
846
            pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
847
            if( p_sys->rtsp && p_sys->i_length )
848
849
                /* Not always true, but will be handled in SET_PAUSE_STATE */
                *pb = VLC_TRUE;
850
851
852
853
            else
                *pb = VLC_FALSE;
            return VLC_SUCCESS;

Laurent Aimar's avatar
Laurent Aimar committed
854
855
        case DEMUX_CAN_CONTROL_PACE:
            pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
856
857
858

#if 0       /* Disable for now until we have a clock synchro algo
             * which works with something else than MPEG over UDP */
Laurent Aimar's avatar
Laurent Aimar committed
859
            *pb = VLC_FALSE;
860
861
#endif
            *pb = VLC_TRUE;
Laurent Aimar's avatar
Laurent Aimar committed
862
863
864
            return VLC_SUCCESS;

        case DEMUX_SET_PAUSE_STATE:
865
            double d_npt;
866
867
868
            MediaSubsessionIterator *iter;
            MediaSubsession *sub;

869
870
            d_npt = ( (double)( p_sys->i_pcr - p_sys->i_pcr_start +
                                p_sys->i_start ) ) / 1000000.00;
871

872
873
874
875
876
877
878
879
            b_bool = (vlc_bool_t)va_arg( args, vlc_bool_t );
            if( p_sys->rtsp == NULL )
                return VLC_EGENERIC;

            iter = new MediaSubsessionIterator( *p_sys->ms );
            while( ( sub = iter->next() ) != NULL )
            {
                if( ( b_bool && !p_sys->rtsp->pauseMediaSubsession( *sub ) ) ||
880
881
                    ( !b_bool && !p_sys->rtsp->playMediaSubsession( *sub,
                      d_npt > 0 ? d_npt : -1 ) ) )
882
883
884
885
886
887
                {
                    delete iter;
                    return VLC_EGENERIC;
                }
            }
            delete iter;
888
#if 0
889
890
891
            /* reset PCR and PCR start, mmh won't work well for multi-stream I fear */
            for( i = 0; i < p_sys->i_track; i++ )
            {
892
                p_sys->track[i]->i_pts = 0;
893
894
895
            }
            p_sys->i_pcr_start = 0; /* FIXME Wrong */
            p_sys->i_pcr = 0;
896
#endif
897
898
            return VLC_SUCCESS;

Laurent Aimar's avatar
Laurent Aimar committed
899
900
901
902
903
904
905
        case DEMUX_GET_TITLE_INFO:
        case DEMUX_SET_TITLE:
        case DEMUX_SET_SEEKPOINT:
            return VLC_EGENERIC;

        case DEMUX_GET_PTS_DELAY:
            pi64 = (int64_t*)va_arg( args, int64_t * );
906
            *pi64 = (int64_t)var_GetInteger( p_demux, "rtsp-caching" ) * 1000;
Laurent Aimar's avatar
Laurent Aimar committed
907
908
            return VLC_SUCCESS;

Laurent Aimar's avatar
Laurent Aimar committed
909
        default:
910
            return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
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
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*****************************************************************************
 * RollOverTcp: reopen the rtsp into TCP mode
 * XXX: ugly, a lot of code are duplicated from Open()
 *****************************************************************************/
static int RollOverTcp( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    MediaSubsessionIterator *iter;
    MediaSubsession *sub;
    char *psz_url;
    char *psz_options;
    uint8_t *p_sdp;
    int i_tk;

    /* We close the old RTSP session */
    p_sys->rtsp->teardownMediaSession( *p_sys->ms );

    Medium::close( p_sys->ms );
    Medium::close( p_sys->rtsp );

    p_sys->ms = NULL;
    p_sys->rtsp = NULL;

    /* Reopen rtsp client */
    if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/,
          "VLC Media Player" ) ) == NULL )
    {
        msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
                 p_sys->env->getResultMsg() );
        return VLC_EGENERIC;
    }

    asprintf( &psz_url, "rtsp://%s", p_demux->psz_path );

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

    p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url,
                          NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );
    free( psz_url );
    if( p_sdp == NULL )
    {
        msg_Err( p_demux, "describeURL failed (%s)",
                 p_sys->env->getResultMsg() );
        return VLC_EGENERIC;
    }

    /* malloc-ated copy */
    p_sys->p_sdp = strdup( (char*)p_sdp );
    delete[] p_sdp;

    if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
    {
        msg_Err( p_demux, "MediaSession::createNew failed" );
        return VLC_EGENERIC;
    }

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

        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() );
            continue;
        }
        msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
                 sub->codecName() );

        /* Issue the SETUP */
        p_sys->rtsp->setupMediaSubsession( *sub, False, True /* tcp */ );
    }

    /* The PLAY */
    if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
    {
        msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
        return VLC_EGENERIC;
For faster browsing, not all history is shown. View entire blame