input.c 92.3 KB
Newer Older
1 2
/*****************************************************************************
 * input.c: input thread
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1998-2007 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
13
 * (at your option) any later version.
14
 *
15 16
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
24

25
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
26
 * Preamble
27
 *****************************************************************************/
28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33

Christophe Mutricy's avatar
Christophe Mutricy committed
34
#include <limits.h>
35
#include <assert.h>
36
#include <math.h>
37
#include <sys/stat.h>
38

39
#include "input_internal.h"
40
#include "event.h"
41
#include "es_out.h"
42
#include "es_out_timeshift.h"
43
#include "demux.h"
44
#include "item.h"
45
#include "resource.h"
46

Clément Stenac's avatar
Clément Stenac committed
47
#include <vlc_sout.h>
48
#include <vlc_dialog.h>
Clément Stenac's avatar
Clément Stenac committed
49 50
#include <vlc_url.h>
#include <vlc_charset.h>
51
#include <vlc_fs.h>
52
#include <vlc_strings.h>
53
#include <vlc_modules.h>
54

55
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
56
 * Local prototypes
57
 *****************************************************************************/
58
static  void *Run            ( void * );
59

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
60
static input_thread_t * Create  ( vlc_object_t *, input_item_t *,
61
                                  const char *, bool, input_resource_t * );
62
static  int             Init    ( input_thread_t *p_input );
63
static void             End     ( input_thread_t *p_input );
64
static void             MainLoop( input_thread_t *p_input, bool b_interactive );
65

66
static inline int ControlPop( input_thread_t *, int *, vlc_value_t *, mtime_t i_deadline, bool b_postpone_seek );
67
static void       ControlRelease( int i_type, vlc_value_t val );
68
static bool       ControlIsSeekRequest( int i_type );
69
static bool       Control( input_thread_t *, int, vlc_value_t );
70
static void       ControlPause( input_thread_t *, mtime_t );
71

72 73
static int  UpdateTitleSeekpointFromDemux( input_thread_t * );
static void UpdateGenericFromDemux( input_thread_t * );
74
static void UpdateTitleListfromDemux( input_thread_t * );
Michel Kaempf's avatar
Michel Kaempf committed
75

76
static void MRLSections( const char *, int *, int *, int *, int *);
77

78 79 80 81
static input_source_t *InputSourceNew( input_thread_t *, const char *,
                                       const char *psz_forced_demux,
                                       bool b_in_can_fail );
static void InputSourceDestroy( input_source_t * );
Laurent Aimar's avatar
Laurent Aimar committed
82 83
static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );

84 85
/* TODO */
//static void InputGetAttachments( input_thread_t *, input_source_t * );
86
static void SlaveDemux( input_thread_t *p_input );
87
static void SlaveSeek( input_thread_t *p_input );
88

89
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta );
90
static void InputUpdateMeta( input_thread_t *p_input, demux_t *p_demux );
91 92 93
static void InputGetExtraFiles( input_thread_t *p_input,
                                int *pi_list, char ***pppsz_list,
                                const char *psz_access, const char *psz_path );
94

95 96
static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment, demux_t ***ppp_attachment_demux,
                              int i_new, input_attachment_t **pp_new, demux_t *p_demux );
97

98 99 100
enum {
    SUB_NOFLAG = 0x00,
    SUB_FORCED = 0x01,
101
    SUB_CANFAIL = 0x02,
102
};
103

104 105
static void input_SubtitleAdd( input_thread_t *, const char *, unsigned );
static void input_SubtitleFileAdd( input_thread_t *, char *, unsigned );
106 107
static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO fix name */

108
#undef input_Create
109 110 111 112 113 114 115 116 117 118 119 120
/**
 * Create a new input_thread_t.
 *
 * You need to call input_Start on it when you are done
 * adding callback on the variables/events you want to monitor.
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \param psz_log an optional prefix for this input logs
 * \param p_resource an optional input ressource
 * \return a pointer to the spawned input thread
 */
121 122 123
input_thread_t *input_Create( vlc_object_t *p_parent,
                              input_item_t *p_item,
                              const char *psz_log, input_resource_t *p_resource )
124 125 126 127
{
    return Create( p_parent, p_item, psz_log, false, p_resource );
}

128
#undef input_Read
129
/**
130
 * Initialize an input thread and run it until it stops by itself.
131 132 133 134 135
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \return an error code, VLC_SUCCESS on success
 */
136
int input_Read( vlc_object_t *p_parent, input_item_t *p_item )
137
{
138
    input_thread_t *p_input = Create( p_parent, p_item, NULL, false, NULL );
139 140 141
    if( !p_input )
        return VLC_EGENERIC;

142 143
    if( !Init( p_input ) )
    {
144
        MainLoop( p_input, false );
145 146 147 148
        End( p_input );
    }

    vlc_object_release( p_input );
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
    return VLC_SUCCESS;
}

/**
 * Initialize an input and initialize it to preparse the item
 * This function is blocking. It will only accept parsing regular files.
 *
 * \param p_parent a vlc_object_t
 * \param p_item an input item
 * \return VLC_SUCCESS or an error
 */
int input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
{
    input_thread_t *p_input;

    /* Allocate descriptor */
    p_input = Create( p_parent, p_item, NULL, true, NULL );
    if( !p_input )
        return VLC_EGENERIC;

169 170 171 172 173
    if( !Init( p_input ) ) {
        /* if the demux is a playlist, call Mainloop that will call
         * demux_Demux in order to fetch sub items */
        bool b_is_playlist = false;

174
        if ( input_item_ShouldPreparseSubItems( p_item )
175
          && demux_Control( p_input->p->master->p_demux,
176 177 178 179 180
                            DEMUX_IS_PLAYLIST,
                            &b_is_playlist ) )
            b_is_playlist = false;
        if( b_is_playlist )
            MainLoop( p_input, false );
181
        End( p_input );
182
    }
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

    vlc_object_release( p_input );

    return VLC_SUCCESS;
}

/**
 * Start a input_thread_t created by input_Create.
 *
 * You must not start an already running input_thread_t.
 *
 * \param the input thread to start
 */
int input_Start( input_thread_t *p_input )
{
198
    assert( !p_input->p->is_running );
199
    /* Create thread and wait for its readiness. */
200 201 202
    p_input->p->is_running = !vlc_clone( &p_input->p->thread,
                                         Run, p_input, VLC_THREAD_PRIORITY_INPUT );
    if( !p_input->p->is_running )
203 204 205 206 207 208 209 210 211 212 213 214 215
    {
        input_ChangeState( p_input, ERROR_S );
        msg_Err( p_input, "cannot create input thread" );
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

/**
 * Request a running input thread to stop and die
 *
 * \param p_input the input thread to stop
 */
216
void input_Stop( input_thread_t *p_input )
217
{
218 219 220 221 222 223 224 225 226 227 228 229 230
    input_thread_private_t *sys = p_input->p;

    vlc_mutex_lock( &sys->lock_control );
    /* Discard all pending controls */
    for( int i = 0; i < sys->i_control; i++ )
    {
        input_control_t *ctrl = &sys->control[i];
        ControlRelease( ctrl->i_type, ctrl->val );
    }
    sys->i_control = 0;
    sys->is_stopped = true;
    vlc_cond_signal( &sys->wait_control );
    vlc_mutex_unlock( &sys->lock_control );
231
    vlc_interrupt_kill( &sys->interrupt );
232 233
}

Laurent Aimar's avatar
Laurent Aimar committed
234 235 236 237 238
/**
 * Close an input
 *
 * It does not call input_Stop itself.
 */
239
void input_Close( input_thread_t *p_input )
Laurent Aimar's avatar
Laurent Aimar committed
240
{
241 242
    if( p_input->p->is_running )
        vlc_join( p_input->p->thread, NULL );
243
    vlc_interrupt_deinit( &p_input->p->interrupt );
244
    vlc_object_release( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
245 246
}

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
/**
 * Input destructor (called when the object's refcount reaches 0).
 */
static void input_Destructor( vlc_object_t *obj )
{
    input_thread_t *p_input = (input_thread_t *)obj;
#ifndef NDEBUG
    char * psz_name = input_item_GetName( p_input->p->p_item );
    msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
    free( psz_name );
#endif

    if( p_input->p->p_es_out_display )
        es_out_Delete( p_input->p->p_es_out_display );

    if( p_input->p->p_resource )
        input_resource_Release( p_input->p->p_resource );
    if( p_input->p->p_resource_private )
        input_resource_Release( p_input->p->p_resource_private );

    vlc_gc_decref( p_input->p->p_item );

    vlc_mutex_destroy( &p_input->p->counters.counters_lock );

    for( int i = 0; i < p_input->p->i_control; i++ )
    {
        input_control_t *p_ctrl = &p_input->p->control[i];
        ControlRelease( p_ctrl->i_type, p_ctrl->val );
    }

    vlc_cond_destroy( &p_input->p->wait_control );
    vlc_mutex_destroy( &p_input->p->lock_control );
    free( p_input->p );
}

282 283 284 285 286 287 288 289 290 291 292 293
/**
 * Get the item from an input thread
 * FIXME it does not increase ref count of the item.
 * if it is used after p_input is destroyed nothing prevent it from
 * being freed.
 */
input_item_t *input_GetItem( input_thread_t *p_input )
{
    assert( p_input && p_input->p );
    return p_input->p->p_item;
}

294
/*****************************************************************************
295 296
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
297
 *
Laurent Aimar's avatar
Laurent Aimar committed
298
 * XXX Do not forget to update vlc_input.h if you add new variables.
299
 *****************************************************************************/
300
static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
301
                               const char *psz_header, bool b_quick,
302
                               input_resource_t *p_resource )
Michel Kaempf's avatar
Michel Kaempf committed
303
{
304
    input_thread_t *p_input = NULL;                 /* thread descriptor */
305

306
    /* Allocate descriptor */
307
    p_input = vlc_custom_create( p_parent, sizeof( *p_input ), "input" );
308
    if( p_input == NULL )
309
        return NULL;
310 311 312 313 314 315

    /* Construct a nice name for the input timer */
    char psz_timer_name[255];
    char * psz_name = input_item_GetName( p_item );
    snprintf( psz_timer_name, sizeof(psz_timer_name),
              "input launching for '%s'", psz_name );
316 317 318

    msg_Dbg( p_input, "Creating an input for '%s'", psz_name);

319 320
    free( psz_name );

321 322
    p_input->p = calloc( 1, sizeof( input_thread_private_t ) );
    if( !p_input->p )
323 324
    {
        vlc_object_release( p_input );
325
        return NULL;
326
    }
327

328
    /* Parse input options */
329
    input_item_ApplyOptions( VLC_OBJECT(p_input), p_item );
330

331
    p_input->b_preparsing = b_quick;
332
    p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
333

334
    /* Init Common fields */
335
    p_input->p->b_can_pace_control = true;
Clément Stenac's avatar
Clément Stenac committed
336
    p_input->p->i_start = 0;
Laurent Aimar's avatar
Laurent Aimar committed
337
    p_input->p->i_time  = 0;
Clément Stenac's avatar
Clément Stenac committed
338
    p_input->p->i_stop  = 0;
339
    p_input->p->i_run   = 0;
Clément Stenac's avatar
Clément Stenac committed
340
    p_input->p->i_title = 0;
341
    p_input->p->title = NULL;
Clément Stenac's avatar
Clément Stenac committed
342
    p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
Laurent Aimar's avatar
Laurent Aimar committed
343
    p_input->p->i_state = INIT_S;
344 345
    p_input->p->is_running = false;
    p_input->p->is_stopped = false;
346
    p_input->p->b_recording = false;
347
    p_input->p->i_rate = INPUT_RATE_DEFAULT;
348 349
    memset( &p_input->p->bookmark, 0, sizeof(p_input->p->bookmark) );
    TAB_INIT( p_input->p->i_bookmark, p_input->p->pp_bookmark );
350
    TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
351
    p_input->p->attachment_demux = NULL;
352
    p_input->p->p_sout   = NULL;
353
    p_input->p->b_out_pace_control = false;
354

Pierre's avatar
Pierre committed
355
    vlc_gc_incref( p_item ); /* Released in Destructor() */
356 357 358
    p_input->p->p_item = p_item;

    /* Init Input fields */
359
    p_input->p->master = NULL;
360
    vlc_mutex_lock( &p_item->lock );
361 362

    if( !p_item->p_stats )
363
        p_item->p_stats = stats_NewInputStats( p_input );
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

    /* setup the preparse depth of the item
     * if we are preparsing, use the i_preparse_depth of the parent item */
    if( !p_input->b_preparsing )
    {
        char *psz_rec = var_InheritString( p_parent, "recursive" );

        if( psz_rec != NULL )
        {
            if ( !strcasecmp( psz_rec, "none" ) )
                p_item->i_preparse_depth = 0;
            else if ( !strcasecmp( psz_rec, "collapse" ) )
                p_item->i_preparse_depth = 1;
            else
                p_item->i_preparse_depth = -1; /* default is expand */
            free (psz_rec);
        } else
            p_item->i_preparse_depth = -1;
    }

384
    vlc_mutex_unlock( &p_item->lock );
385

386
    /* No slave */
Clément Stenac's avatar
Clément Stenac committed
387 388
    p_input->p->i_slave = 0;
    p_input->p->slave   = NULL;
389

390
    /* */
391
    if( p_resource )
392 393
    {
        p_input->p->p_resource_private = NULL;
394
        p_input->p->p_resource = input_resource_Hold( p_resource );
395
    }
396
    else
397 398
    {
        p_input->p->p_resource_private = input_resource_New( VLC_OBJECT( p_input ) );
399
        p_input->p->p_resource = input_resource_Hold( p_input->p->p_resource_private );
400
    }
401
    input_resource_SetInput( p_input->p->p_resource, p_input );
402

403
    /* Init control buffer */
404
    vlc_mutex_init( &p_input->p->lock_control );
405
    vlc_cond_init( &p_input->p->wait_control );
Clément Stenac's avatar
Clément Stenac committed
406
    p_input->p->i_control = 0;
407
    vlc_interrupt_init(&p_input->p->interrupt);
408

409 410
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
411

412
    /* Create Objects variables for public Get and Set */
413
    input_ControlVarInit( p_input );
414

415
    /* */
416
    if( !p_input->b_preparsing )
Gildas Bazin's avatar
Gildas Bazin committed
417
    {
418 419
        char *psz_bookmarks = var_GetNonEmptyString( p_input, "bookmarks" );
        if( psz_bookmarks )
Gildas Bazin's avatar
Gildas Bazin committed
420
        {
421 422
            /* FIXME: have a common cfg parsing routine used by sout and others */
            char *psz_parser, *psz_start, *psz_end;
423
            psz_parser = psz_bookmarks;
424
            while( (psz_start = strchr( psz_parser, '{' ) ) )
Gildas Bazin's avatar
Gildas Bazin committed
425
            {
426
                 seekpoint_t *p_seekpoint;
427 428 429 430 431 432 433 434
                 char backup;
                 psz_start++;
                 psz_end = strchr( psz_start, '}' );
                 if( !psz_end ) break;
                 psz_parser = psz_end + 1;
                 backup = *psz_parser;
                 *psz_parser = 0;
                 *psz_end = ',';
435 436

                 p_seekpoint = vlc_seekpoint_New();
437 438 439 440 441
                 while( (psz_end = strchr( psz_start, ',' ) ) )
                 {
                     *psz_end = 0;
                     if( !strncmp( psz_start, "name=", 5 ) )
                     {
442
                         p_seekpoint->psz_name = strdup(psz_start + 5);
443 444 445
                     }
                     else if( !strncmp( psz_start, "time=", 5 ) )
                     {
446
                         p_seekpoint->i_time_offset = atoll(psz_start + 5) *
Ilkka Ollakka's avatar
Ilkka Ollakka committed
447
                                                        CLOCK_FREQ;
448 449
                     }
                     psz_start = psz_end + 1;
Gildas Bazin's avatar
Gildas Bazin committed
450
                }
451 452
                msg_Dbg( p_input, "adding bookmark: %s, time=%"PRId64,
                                  p_seekpoint->psz_name,
453 454 455 456
                                  p_seekpoint->i_time_offset );
                input_Control( p_input, INPUT_ADD_BOOKMARK, p_seekpoint );
                vlc_seekpoint_Delete( p_seekpoint );
                *psz_parser = backup;
Gildas Bazin's avatar
Gildas Bazin committed
457
            }
458
            free( psz_bookmarks );
Gildas Bazin's avatar
Gildas Bazin committed
459 460 461
        }
    }

462
    /* Remove 'Now playing' info as it is probably outdated */
463
    input_item_SetNowPlaying( p_item, NULL );
464
    input_item_SetESNowPlaying( p_item, NULL );
465
    input_SendEventMeta( p_input );
466

467 468 469 470
    /* */
    if( p_input->b_preparsing )
        p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;

471 472 473 474
    /* Make sure the interaction option is honored */
    if( !var_InheritBool( p_input, "interact" ) )
        p_input->i_flags |= OBJECT_FLAGS_NOINTERACT;

475
    /* */
476
    memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
477
    vlc_mutex_init( &p_input->p->counters.counters_lock );
478

479 480 481
    p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
    p_input->p->p_es_out = NULL;

482
    /* Set the destructor when we are sure we are initialized */
483
    vlc_object_set_destructor( p_input, input_Destructor );
484

485 486 487
    return p_input;
}

488
/*****************************************************************************
489
 * Run: main thread loop
490 491
 * This is the "normal" thread that spawns the input processing chain,
 * reads the stream, cleans up and waits
492
 *****************************************************************************/
493
static void *Run( void *obj )
Michel Kaempf's avatar
Michel Kaempf committed
494
{
495
    input_thread_t *p_input = (input_thread_t *)obj;
496

497 498
    vlc_interrupt_set(&p_input->p->interrupt);

499 500 501
    if( !Init( p_input ) )
    {
        MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */
502

503 504 505
        /* Clean up */
        End( p_input );
    }
506

507
    input_SendEventDead( p_input );
508
    return NULL;
509 510
}

511 512 513 514 515 516 517 518 519 520 521
bool input_Stopped( input_thread_t *input )
{
    input_thread_private_t *sys = input->p;
    bool ret;

    vlc_mutex_lock( &sys->lock_control );
    ret = sys->is_stopped;
    vlc_mutex_unlock( &sys->lock_control );
    return ret;
}

522 523 524
/*****************************************************************************
 * Main loop: Fill buffers from access, and demux
 *****************************************************************************/
525 526 527 528 529

/**
 * MainLoopDemux
 * It asks the demuxer to demux some data
 */
530
static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed, mtime_t i_start_mdate )
531
{
532
    int i_ret;
533

534
    *pb_changed = false;
535

Laurent Aimar's avatar
Laurent Aimar committed
536
    if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||
537
        ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )
538 539
        i_ret = 0; /* EOF */
    else
540
        i_ret = demux_Demux( p_input->p->master->p_demux );
541 542

    if( i_ret > 0 )
543
    {
544
        if( p_input->p->master->p_demux->info.i_update )
545
        {
546
            if( p_input->p->master->p_demux->info.i_update & INPUT_UPDATE_TITLE_LIST )
547 548
            {
                UpdateTitleListfromDemux( p_input );
549
                p_input->p->master->p_demux->info.i_update &= ~INPUT_UPDATE_TITLE_LIST;
550
            }
551
            if( p_input->p->master->b_title_demux )
552 553 554 555 556
            {
                i_ret = UpdateTitleSeekpointFromDemux( p_input );
                *pb_changed = true;
            }
            UpdateGenericFromDemux( p_input );
557 558
        }
    }
559

560 561
    if( i_ret == 0 )    /* EOF */
    {
562
        msg_Dbg( p_input, "EOF reached" );
563
        p_input->p->master->b_eof = true;
564
        es_out_Eos(p_input->p->p_es_out);
565 566 567 568 569 570 571
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
572
        SlaveDemux( p_input );
573 574
}

575 576 577
static int MainLoopTryRepeat( input_thread_t *p_input, mtime_t *pi_start_mdate )
{
    int i_repeat = var_GetInteger( p_input, "input-repeat" );
578
    if( i_repeat <= 0 )
579 580 581 582 583 584 585 586 587 588 589 590
        return VLC_EGENERIC;

    vlc_value_t val;

    msg_Dbg( p_input, "repeating the same input (%d)", i_repeat );
    if( i_repeat > 0 )
    {
        i_repeat--;
        var_SetInteger( p_input, "input-repeat", i_repeat );
    }

    /* Seek to start title/seekpoint */
591 592 593
    val.i_int = p_input->p->master->i_title_start -
        p_input->p->master->i_title_offset;
    if( val.i_int < 0 || val.i_int >= p_input->p->master->i_title )
594 595 596 597
        val.i_int = 0;
    input_ControlPush( p_input,
                       INPUT_CONTROL_SET_TITLE, &val );

598 599
    val.i_int = p_input->p->master->i_seekpoint_start -
        p_input->p->master->i_seekpoint_offset;
600 601 602 603 604 605 606
    if( val.i_int > 0 /* TODO: check upper boundary */ )
        input_ControlPush( p_input,
                           INPUT_CONTROL_SET_SEEKPOINT, &val );

    /* Seek to start position */
    if( p_input->p->i_start > 0 )
    {
607
        val.i_int = p_input->p->i_start;
608 609 610 611
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &val );
    }
    else
    {
612
        val.f_float = 0.f;
613 614 615 616 617 618 619 620
        input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &val );
    }

    /* */
    *pi_start_mdate = mdate();
    return VLC_SUCCESS;
}

621
/**
622
 * Update timing infos and statistics.
623
 */
624
static void MainLoopStatistics( input_thread_t *p_input )
625
{
626 627 628
    double f_position = 0.0;
    mtime_t i_time = 0;
    mtime_t i_length = 0;
629 630

    /* update input status variables */
631
    if( demux_Control( p_input->p->master->p_demux,
632 633
                       DEMUX_GET_POSITION, &f_position ) )
        f_position = 0.0;
634

635
    if( demux_Control( p_input->p->master->p_demux,
636 637
                       DEMUX_GET_TIME, &i_time ) )
        i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
638
    p_input->p->i_time = i_time;
639

640
    if( demux_Control( p_input->p->master->p_demux,
641 642
                       DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
643

644
    es_out_SetTimes( p_input->p->p_es_out, f_position, i_time, i_length );
645 646 647 648 649

    /* update current bookmark */
    vlc_mutex_lock( &p_input->p->p_item->lock );
    p_input->p->bookmark.i_time_offset = i_time;
    vlc_mutex_unlock( &p_input->p->p_item->lock );
650

651
    stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
652
    input_SendEventStatistics( p_input );
653 654 655 656 657 658
}

/**
 * MainLoop
 * The main input loop.
 */
659
static void MainLoop( input_thread_t *p_input, bool b_interactive )
660 661
{
    mtime_t i_start_mdate = mdate();
662
    mtime_t i_intf_update = 0;
663
    mtime_t i_last_seek_mdate = 0;
664 665 666 667

    if( b_interactive && var_InheritBool( p_input, "start-paused" ) )
        ControlPause( p_input, i_start_mdate );

668
    bool b_pause_after_eof = b_interactive &&
669
                             var_InheritBool( p_input, "play-and-pause" );
670

671
    while( !input_Stopped( p_input ) && p_input->p->i_state != ERROR_S )
672
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
673
        mtime_t i_wakeup = -1;
674
        bool b_paused = p_input->p->i_state == PAUSE_S;
Laurent Aimar's avatar
Laurent Aimar committed
675
        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
676 677
         * is paused -> this may cause problem with some of them
         * The same problem can be seen when seeking while paused */
678
        if( b_paused )
679 680
            b_paused = !es_out_GetBuffering( p_input->p->p_es_out )
                    || p_input->p->master->b_eof;
681 682

        if( !b_paused )
683
        {
684
            if( !p_input->p->master->b_eof )
685
            {
686
                bool b_force_update = false;
687

688
                MainLoopDemux( p_input, &b_force_update, i_start_mdate );
689

690 691
                if( p_input->p->master->p_demux->pf_demux != NULL )
                    i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
692 693
                if( b_force_update )
                    i_intf_update = 0;
694
            }
695
            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
696 697 698 699
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
700 701 702
            /* Pause after eof only if the input is pausable.
             * This way we won't trigger timeshifting for nothing */
            else if( b_pause_after_eof && p_input->p->b_can_pause )
703
            {
704 705
                vlc_value_t val = { .i_int = PAUSE_S };

706 707 708 709 710
                msg_Dbg( p_input, "pausing at EOF (pause after each)");
                Control( p_input, INPUT_CONTROL_SET_STATE, val );

                b_paused = true;
            }
711 712
            else
            {
713 714
                if( MainLoopTryRepeat( p_input, &i_start_mdate ) )
                    break;
715
            }
716 717 718 719 720 721 722 723

            /* Update interface and statistics */
            mtime_t now = mdate();
            if( now >= i_intf_update )
            {
                MainLoopStatistics( p_input );
                i_intf_update = now + INT64_C(250000);
            }
724
        }
725

726 727 728
        /* Handle control */
        for( ;; )
        {
729
            mtime_t i_deadline = i_wakeup;
730 731 732 733

            /* Postpone seeking until ES buffering is complete or at most
             * 125 ms. */
            bool b_postpone = es_out_GetBuffering( p_input->p->p_es_out )
734
                            && !p_input->p->master->b_eof;
735
            if( b_postpone )
736
            {
737 738 739 740
                mtime_t now = mdate();

                /* Recheck ES buffer level every 20 ms when seeking */
                if( now < i_last_seek_mdate + INT64_C(125000)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
741
                 && (i_deadline < 0 || i_deadline > now + INT64_C(20000)) )
742 743 744 745
                    i_deadline = now + INT64_C(20000);
                else
                    b_postpone = false;
            }
746

747 748
            int i_type;
            vlc_value_t val;
749

750 751 752 753 754 755
            if( ControlPop( p_input, &i_type, &val, i_deadline, b_postpone ) )
            {
                if( b_postpone )
                    continue;
                break; /* Wake-up time reached */
            }
756

757
#ifndef NDEBUG
758
            msg_Dbg( p_input, "control type=%d", i_type );
759
#endif
760 761 762 763 764
            if( Control( p_input, i_type, val ) )
            {
                if( ControlIsSeekRequest( i_type ) )
                    i_last_seek_mdate = mdate();
                i_intf_update = 0;
765
            }
766

767
            /* Update the wakeup time */
768
            if( i_wakeup != 0 )
769
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
770
        }
771
    }
772 773
}

774
static void InitStatistics( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
775
{
776
    if( p_input->b_preparsing ) return;
777

778
    /* Prepare statistics */
779 780
#define INIT_COUNTER( c, compute ) p_input->p->counters.p_##c = \
 stats_CounterCreate( STATS_##compute);
781
    if( libvlc_stats( p_input ) )
782
    {
783 784 785 786 787 788 789 790 791 792 793 794 795 796
        INIT_COUNTER( read_bytes, COUNTER );
        INIT_COUNTER( read_packets, COUNTER );
        INIT_COUNTER( demux_read, COUNTER );
        INIT_COUNTER( input_bitrate, DERIVATIVE );
        INIT_COUNTER( demux_bitrate, DERIVATIVE );
        INIT_COUNTER( demux_corrupted, COUNTER );
        INIT_COUNTER( demux_discontinuity, COUNTER );
        INIT_COUNTER( played_abuffers, COUNTER );
        INIT_COUNTER( lost_abuffers, COUNTER );
        INIT_COUNTER( displayed_pictures, COUNTER );
        INIT_COUNTER( lost_pictures, COUNTER );
        INIT_COUNTER( decoded_audio, COUNTER );
        INIT_COUNTER( decoded_video, COUNTER );
        INIT_COUNTER( decoded_sub, COUNTER );
797 798 799
        p_input->p->counters.p_sout_send_bitrate = NULL;
        p_input->p->counters.p_sout_sent_packets = NULL;
        p_input->p->counters.p_sout_sent_bytes = NULL;
800
    }
801
}
802

803
#ifdef ENABLE_SOUT
804 805
static int InitSout( input_thread_t * p_input )
{
806 807
    if( p_input->b_preparsing )
        return VLC_SUCCESS;
808 809

    /* Find a usable sout and attach it to p_input */
810
    char *psz = var_GetNonEmptyString( p_input, "sout" );
811
    if( psz && strncasecmp( p_input->p->p_item->psz_uri, "vlc:", 4 ) )
812
    {
813
        p_input->p->p_sout  = input_resource_RequestSout( p_input->p->p_resource, NULL, psz );
814
        if( !p_input->p->p_sout )
Clément Stenac's avatar
Clément Stenac committed
815
        {
816 817 818 819 820
            input_ChangeState( p_input, ERROR_S );
            msg_Err( p_input, "cannot start stream output instance, " \
                              "aborting" );
            free( psz );
            return VLC_EGENERIC;
821
        }
822
        if( libvlc_stats( p_input ) )
823
        {
824 825 826
            INIT_COUNTER( sout_sent_packets, COUNTER );
            INIT_COUNTER( sout_sent_bytes, COUNTER );
            INIT_COUNTER( sout_send_bitrate, DERIVATIVE );
827
        }
828
    }
829
    else
830
    {
831
        input_resource_RequestSout( p_input->p->p_resource, NULL, NULL );
832
    }
833
    free( psz );
834

835 836
    return VLC_SUCCESS;
}
837
#endif
838

839 840
static void InitTitle( input_thread_t * p_input )
{
841
    input_source_t *p_master = p_input->p->master;
842

Laurent Aimar's avatar
Laurent Aimar committed
843 844
    if( p_input->b_preparsing )
        return;
845

846
    vlc_mutex_lock( &p_input->p->p_item->lock );
847
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
848 849 850 851
    p_input->p->i_title = p_master->i_title;
    p_input->p->title   = p_master->title;
    p_input->p->i_title_offset = p_master->i_title_offset;
    p_input->p->i_seekpoint_offset = p_master->i_seekpoint_offset;
852 853 854 855
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
856
        input_SendEventTitle( p_input, 0 );
857 858 859
    }

    /* Global flag */
860
    p_input->p->b_can_pace_control    = p_master->b_can_pace_control;
Laurent Aimar's avatar
Laurent Aimar committed
861 862
    p_input->p->b_can_pause        = p_master->b_can_pause;
    p_input->p->b_can_rate_control = p_master->b_can_rate_control;
863
    vlc_mutex_unlock( &p_input->p->p_item->lock );
864
}
865

866 867 868
static void StartTitle( input_thread_t * p_input )
{
    vlc_value_t val;
869

870
    /* Start title/chapter */
871 872 873
    val.i_int = p_input->p->master->i_title_start -
                p_input->p->master->i_title_offset;
    if( val.i_int > 0 && val.i_int < p_input->p->master->i_title )
874
        input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
Laurent Aimar's avatar
Laurent Aimar committed
875

876 877
    val.i_int = p_input->p->master->i_seekpoint_start -
                p_input->p->master->i_seekpoint_offset;
878 879 880
    if( val.i_int > 0 /* TODO: check upper boundary */ )
        input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val );

Laurent Aimar's avatar
Laurent Aimar committed
881
    /* Start/stop/run time */
882
    p_input->p->i_start = llroundf(1000000.f
883
                                     * var_GetFloat( p_input, "start-time" ));
884
    p_input->p->i_stop  = llroundf(1000000.f
885
                                     * var_GetFloat( p_input, "stop-time" ));
886
    p_input->p->i_run   = llroundf(1000000.f
887
                                     * var_GetFloat( p_input, "run-time" ));
888
    if( p_input->p->i_run < 0 )
889
    {
890 891
        msg_Warn( p_input, "invalid run-time ignored" );
        p_input->p->i_run = 0;
Rafaël Carré's avatar
Rafaël Carré committed
892
    }
893

894
    if( p_input->p->i_start > 0 )
895
    {
896
        vlc_value_t s;
897

898
        msg_Dbg( p_input, "starting at time: %ds",
Ilkka Ollakka's avatar
Ilkka Ollakka committed
899
                 (int)( p_input->p->i_start / CLOCK_FREQ ) );
900

901
        s.i_int = p_input->p->i_start;
902
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
903 904 905 906 907 908
    }
    if( p_input->p->i_stop > 0 && p_input->p->i_stop <= p_input->p->i_start )
    {
        msg_Warn( p_input, "invalid stop-time ignored" );
        p_input->p->i_stop = 0;
    }
909
    p_input->p->b_fast_seek = var_GetBool( p_input, "input-fast-seek" );
Laurent Aimar's avatar
Laurent Aimar committed
910
}
911

Laurent Aimar's avatar
Laurent Aimar committed
912 913
static void LoadSubtitles( input_thread_t *p_input )
{
914 915
    /* Load subtitles */
    /* Get fps and set it if not already set */
916
    const float f_fps = p_input->p->master->f_fps;
917
    if( f_fps > 1.f )
918 919 920
    {
        var_Create( p_input, "sub-original-fps", VLC_VAR_FLOAT );
        var_SetFloat( p_input, "sub-original-fps", f_fps );
921

922
        float f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" );
923
        if( f_requested_fps != f_fps )
924
        {
925 926 927
            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT|
                                            VLC_VAR_DOINHERIT );
            var_SetF