input.c 92.6 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 59
static  void *Run( void * );
static  void *Preparse( void * );
60

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

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

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

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

79 80 81 82
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
83 84
static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );

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

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

96 97
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 );
98

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

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

109
#undef input_Create
110 111 112 113 114 115 116 117 118 119 120 121
/**
 * 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
 */
122 123 124
input_thread_t *input_Create( vlc_object_t *p_parent,
                              input_item_t *p_item,
                              const char *psz_log, input_resource_t *p_resource )
125 126 127 128
{
    return Create( p_parent, p_item, psz_log, false, p_resource );
}

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

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

    vlc_object_release( p_input );
150 151 152
    return VLC_SUCCESS;
}

153 154 155 156 157 158
input_thread_t *input_CreatePreparser( vlc_object_t *parent,
                                       input_item_t *item )
{
    return Create( parent, item, NULL, true, NULL );
}

159 160 161 162 163 164 165 166 167
/**
 * 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 )
{
168 169 170 171 172
    void *(*func)(void *) = Run;

    if( p_input->b_preparsing )
        func = Preparse;

173
    assert( !p_input->p->is_running );
174
    /* Create thread and wait for its readiness. */
175 176
    p_input->p->is_running = !vlc_clone( &p_input->p->thread, func, p_input,
                                         VLC_THREAD_PRIORITY_INPUT );
177
    if( !p_input->p->is_running )
178 179 180 181 182 183 184 185 186 187 188 189 190
    {
        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
 */
191
void input_Stop( input_thread_t *p_input )
192
{
193 194 195 196 197 198 199 200 201 202 203 204 205
    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 );
206
    vlc_interrupt_kill( &sys->interrupt );
207 208
}

Laurent Aimar's avatar
Laurent Aimar committed
209 210 211 212 213
/**
 * Close an input
 *
 * It does not call input_Stop itself.
 */
214
void input_Close( input_thread_t *p_input )
Laurent Aimar's avatar
Laurent Aimar committed
215
{
216 217
    if( p_input->p->is_running )
        vlc_join( p_input->p->thread, NULL );
218
    vlc_interrupt_deinit( &p_input->p->interrupt );
219
    vlc_object_release( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
220 221
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
/**
 * 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 );
}

257 258 259 260 261 262 263 264 265 266 267 268
/**
 * 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;
}

269
/*****************************************************************************
270 271
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
272
 *
Laurent Aimar's avatar
Laurent Aimar committed
273
 * XXX Do not forget to update vlc_input.h if you add new variables.
274
 *****************************************************************************/
275
static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
276
                               const char *psz_header, bool b_quick,
277
                               input_resource_t *p_resource )
Michel Kaempf's avatar
Michel Kaempf committed
278
{
279
    input_thread_t *p_input = NULL;                 /* thread descriptor */
280

281
    /* Allocate descriptor */
282
    p_input = vlc_custom_create( p_parent, sizeof( *p_input ), "input" );
283
    if( p_input == NULL )
284
        return NULL;
285 286 287 288 289 290

    /* 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 );
291

292
    msg_Dbg( p_input, "Creating an input for %s'%s'", b_quick ? "preparsing " : "", psz_name);
293

294 295
    free( psz_name );

296 297
    p_input->p = calloc( 1, sizeof( input_thread_private_t ) );
    if( !p_input->p )
298 299
    {
        vlc_object_release( p_input );
300
        return NULL;
301
    }
302

303
    /* Parse input options */
304
    input_item_ApplyOptions( VLC_OBJECT(p_input), p_item );
305

306
    p_input->b_preparsing = b_quick;
307
    p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
308

309
    /* Init Common fields */
310
    p_input->p->b_can_pace_control = true;
Clément Stenac's avatar
Clément Stenac committed
311
    p_input->p->i_start = 0;
Laurent Aimar's avatar
Laurent Aimar committed
312
    p_input->p->i_time  = 0;
Clément Stenac's avatar
Clément Stenac committed
313 314
    p_input->p->i_stop  = 0;
    p_input->p->i_title = 0;
315
    p_input->p->title = NULL;
Clément Stenac's avatar
Clément Stenac committed
316
    p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
Laurent Aimar's avatar
Laurent Aimar committed
317
    p_input->p->i_state = INIT_S;
318 319
    p_input->p->is_running = false;
    p_input->p->is_stopped = false;
320
    p_input->p->b_recording = false;
321
    p_input->p->i_rate = INPUT_RATE_DEFAULT;
322 323
    memset( &p_input->p->bookmark, 0, sizeof(p_input->p->bookmark) );
    TAB_INIT( p_input->p->i_bookmark, p_input->p->pp_bookmark );
324
    TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
325
    p_input->p->attachment_demux = NULL;
326
    p_input->p->p_sout   = NULL;
327
    p_input->p->b_out_pace_control = false;
328

Pierre's avatar
Pierre committed
329
    vlc_gc_incref( p_item ); /* Released in Destructor() */
330 331 332
    p_input->p->p_item = p_item;

    /* Init Input fields */
333
    p_input->p->master = NULL;
334
    vlc_mutex_lock( &p_item->lock );
335 336

    if( !p_item->p_stats )
337
        p_item->p_stats = stats_NewInputStats( p_input );
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

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

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
    /* */
    if( p_input->b_preparsing )
        p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;

    /* Make sure the interaction option is honored */
    if( !var_InheritBool( p_input, "interact" ) )
        p_input->i_flags |= OBJECT_FLAGS_NOINTERACT;
    else if( p_item->b_preparse_interact )
    {
        /* If true, this item was asked explicitly to interact with the user
         * (via libvlc_MetaRequest). Sub items created from this input won't
         * have this flag and won't interact with the user */
        p_input->i_flags &= ~OBJECT_FLAGS_NOINTERACT;
    }

373
    vlc_mutex_unlock( &p_item->lock );
374

375
    /* No slave */
Clément Stenac's avatar
Clément Stenac committed
376 377
    p_input->p->i_slave = 0;
    p_input->p->slave   = NULL;
378

379
    /* */
380
    if( p_resource )
381 382
    {
        p_input->p->p_resource_private = NULL;
383
        p_input->p->p_resource = input_resource_Hold( p_resource );
384
    }
385
    else
386 387
    {
        p_input->p->p_resource_private = input_resource_New( VLC_OBJECT( p_input ) );
388
        p_input->p->p_resource = input_resource_Hold( p_input->p->p_resource_private );
389
    }
390
    input_resource_SetInput( p_input->p->p_resource, p_input );
391

392
    /* Init control buffer */
393
    vlc_mutex_init( &p_input->p->lock_control );
394
    vlc_cond_init( &p_input->p->wait_control );
Clément Stenac's avatar
Clément Stenac committed
395
    p_input->p->i_control = 0;
396
    vlc_interrupt_init(&p_input->p->interrupt);
397

398 399
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
400

401
    /* Create Objects variables for public Get and Set */
402
    input_ControlVarInit( p_input );
403

404
    /* */
405
    if( !p_input->b_preparsing )
Gildas Bazin's avatar
Gildas Bazin committed
406
    {
407 408
        char *psz_bookmarks = var_GetNonEmptyString( p_input, "bookmarks" );
        if( psz_bookmarks )
Gildas Bazin's avatar
Gildas Bazin committed
409
        {
410 411
            /* FIXME: have a common cfg parsing routine used by sout and others */
            char *psz_parser, *psz_start, *psz_end;
412
            psz_parser = psz_bookmarks;
413
            while( (psz_start = strchr( psz_parser, '{' ) ) )
Gildas Bazin's avatar
Gildas Bazin committed
414
            {
415
                 seekpoint_t *p_seekpoint;
416 417 418 419 420 421 422 423
                 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 = ',';
424 425

                 p_seekpoint = vlc_seekpoint_New();
426 427 428 429 430
                 while( (psz_end = strchr( psz_start, ',' ) ) )
                 {
                     *psz_end = 0;
                     if( !strncmp( psz_start, "name=", 5 ) )
                     {
431
                         p_seekpoint->psz_name = strdup(psz_start + 5);
432 433 434
                     }
                     else if( !strncmp( psz_start, "time=", 5 ) )
                     {
435
                         p_seekpoint->i_time_offset = atoll(psz_start + 5) *
Ilkka Ollakka's avatar
Ilkka Ollakka committed
436
                                                        CLOCK_FREQ;
437 438
                     }
                     psz_start = psz_end + 1;
Gildas Bazin's avatar
Gildas Bazin committed
439
                }
440 441
                msg_Dbg( p_input, "adding bookmark: %s, time=%"PRId64,
                                  p_seekpoint->psz_name,
442 443 444 445
                                  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
446
            }
447
            free( psz_bookmarks );
Gildas Bazin's avatar
Gildas Bazin committed
448 449 450
        }
    }

451
    /* Remove 'Now playing' info as it is probably outdated */
452
    input_item_SetNowPlaying( p_item, NULL );
453
    input_item_SetESNowPlaying( p_item, NULL );
454
    input_SendEventMeta( p_input );
455

456
    /* */
457
    memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
458
    vlc_mutex_init( &p_input->p->counters.counters_lock );
459

460 461 462
    p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
    p_input->p->p_es_out = NULL;

463
    /* Set the destructor when we are sure we are initialized */
464
    vlc_object_set_destructor( p_input, input_Destructor );
465

466 467 468
    return p_input;
}

469
/*****************************************************************************
470
 * Run: main thread loop
471 472
 * This is the "normal" thread that spawns the input processing chain,
 * reads the stream, cleans up and waits
473
 *****************************************************************************/
474
static void *Run( void *obj )
Michel Kaempf's avatar
Michel Kaempf committed
475
{
476
    input_thread_t *p_input = (input_thread_t *)obj;
477

478 479
    vlc_interrupt_set(&p_input->p->interrupt);

480 481
    if( !Init( p_input ) )
    {
482 483 484 485 486 487 488
        if( p_input->p->b_can_pace_control && p_input->p->b_out_pace_control )
        {
            /* We don't want a high input priority here or we'll
             * end-up sucking up all the CPU time */
            vlc_set_priority( p_input->p->thread, VLC_THREAD_PRIORITY_LOW );
        }

489
        MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */
490

491 492 493
        /* Clean up */
        End( p_input );
    }
494

495
    input_SendEventDead( p_input );
496
    return NULL;
497 498
}

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
static void *Preparse( void *obj )
{
    input_thread_t *p_input = (input_thread_t *)obj;

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

    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;

        if ( input_item_ShouldPreparseSubItems( p_input->p->p_item )
          && demux_Control( p_input->p->master->p_demux,
                            DEMUX_IS_PLAYLIST,
                            &b_is_playlist ) )
            b_is_playlist = false;
        if( b_is_playlist )
            MainLoop( p_input, false );
        End( p_input );
    }

    input_SendEventDead( p_input );
    return NULL;
}

524 525 526 527 528 529 530 531 532 533 534
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;
}

535 536 537
/*****************************************************************************
 * Main loop: Fill buffers from access, and demux
 *****************************************************************************/
538 539 540 541 542

/**
 * MainLoopDemux
 * It asks the demuxer to demux some data
 */
543
static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed )
544
{
545
    int i_ret;
546

547
    *pb_changed = false;
548

549
    if( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop )
550 551
        i_ret = 0; /* EOF */
    else
552
        i_ret = demux_Demux( p_input->p->master->p_demux );
553 554

    if( i_ret > 0 )
555
    {
556
        if( p_input->p->master->p_demux->info.i_update )
557
        {
558
            if( p_input->p->master->p_demux->info.i_update & INPUT_UPDATE_TITLE_LIST )
559 560
            {
                UpdateTitleListfromDemux( p_input );
561
                p_input->p->master->p_demux->info.i_update &= ~INPUT_UPDATE_TITLE_LIST;
562
            }
563
            if( p_input->p->master->b_title_demux )
564 565 566 567 568
            {
                i_ret = UpdateTitleSeekpointFromDemux( p_input );
                *pb_changed = true;
            }
            UpdateGenericFromDemux( p_input );
569 570
        }
    }
571

572 573
    if( i_ret == 0 )    /* EOF */
    {
574
        msg_Dbg( p_input, "EOF reached" );
575
        p_input->p->master->b_eof = true;
576
        es_out_Eos(p_input->p->p_es_out);
577 578 579 580 581 582 583
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
584
        SlaveDemux( p_input );
585 586
}

587
static int MainLoopTryRepeat( input_thread_t *p_input )
588 589
{
    int i_repeat = var_GetInteger( p_input, "input-repeat" );
590
    if( i_repeat <= 0 )
591 592 593 594 595 596 597 598 599 600 601 602
        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 */
603 604 605
    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 )
606 607 608 609
        val.i_int = 0;
    input_ControlPush( p_input,
                       INPUT_CONTROL_SET_TITLE, &val );

610 611
    val.i_int = p_input->p->master->i_seekpoint_start -
        p_input->p->master->i_seekpoint_offset;
612 613 614 615 616 617 618
    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 )
    {
619
        val.i_int = p_input->p->i_start;
620 621 622 623
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &val );
    }
    else
    {
624
        val.f_float = 0.f;
625 626 627 628 629 630
        input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &val );
    }

    return VLC_SUCCESS;
}

631
/**
632
 * Update timing infos and statistics.
633
 */
634
static void MainLoopStatistics( input_thread_t *p_input )
635
{
636 637 638
    double f_position = 0.0;
    mtime_t i_time = 0;
    mtime_t i_length = 0;
639 640

    /* update input status variables */
641
    if( demux_Control( p_input->p->master->p_demux,
642 643
                       DEMUX_GET_POSITION, &f_position ) )
        f_position = 0.0;
644

645
    if( demux_Control( p_input->p->master->p_demux,
646 647
                       DEMUX_GET_TIME, &i_time ) )
        i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
648
    p_input->p->i_time = i_time;
649

650
    if( demux_Control( p_input->p->master->p_demux,
651 652
                       DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
653

654
    es_out_SetTimes( p_input->p->p_es_out, f_position, i_time, i_length );
655 656 657 658 659

    /* 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 );
660

661
    stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
662
    input_SendEventStatistics( p_input );
663 664 665 666 667 668
}

/**
 * MainLoop
 * The main input loop.
 */
669
static void MainLoop( input_thread_t *p_input, bool b_interactive )
670
{
671
    mtime_t i_intf_update = 0;
672
    mtime_t i_last_seek_mdate = 0;
673 674

    if( b_interactive && var_InheritBool( p_input, "start-paused" ) )
675
        ControlPause( p_input, mdate() );
676

677
    bool b_pause_after_eof = b_interactive &&
678
                             var_InheritBool( p_input, "play-and-pause" );
679

680
    while( !input_Stopped( p_input ) && p_input->p->i_state != ERROR_S )
681
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
682
        mtime_t i_wakeup = -1;
683
        bool b_paused = p_input->p->i_state == PAUSE_S;
Laurent Aimar's avatar
Laurent Aimar committed
684
        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
685 686
         * is paused -> this may cause problem with some of them
         * The same problem can be seen when seeking while paused */
687
        if( b_paused )
688 689
            b_paused = !es_out_GetBuffering( p_input->p->p_es_out )
                    || p_input->p->master->b_eof;
690 691

        if( !b_paused )
692
        {
693
            if( !p_input->p->master->b_eof )
694
            {
695
                bool b_force_update = false;
696

697
                MainLoopDemux( p_input, &b_force_update );
698

699 700
                if( p_input->p->master->p_demux->pf_demux != NULL )
                    i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
701 702
                if( b_force_update )
                    i_intf_update = 0;
703
            }
704
            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
705 706 707 708
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
709 710 711
            /* 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 )
712
            {
713 714
                vlc_value_t val = { .i_int = PAUSE_S };

715 716 717 718 719
                msg_Dbg( p_input, "pausing at EOF (pause after each)");
                Control( p_input, INPUT_CONTROL_SET_STATE, val );

                b_paused = true;
            }
720 721
            else
            {
722
                if( MainLoopTryRepeat( p_input ) )
723
                    break;
724
            }
725 726 727 728 729 730 731 732

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

735 736 737
        /* Handle control */
        for( ;; )
        {
738
            mtime_t i_deadline = i_wakeup;
739 740 741 742

            /* Postpone seeking until ES buffering is complete or at most
             * 125 ms. */
            bool b_postpone = es_out_GetBuffering( p_input->p->p_es_out )
743
                            && !p_input->p->master->b_eof;
744
            if( b_postpone )
745
            {
746 747 748 749
                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
750
                 && (i_deadline < 0 || i_deadline > now + INT64_C(20000)) )
751 752 753 754
                    i_deadline = now + INT64_C(20000);
                else
                    b_postpone = false;
            }
755

756 757
            int i_type;
            vlc_value_t val;
758

759 760 761 762 763 764
            if( ControlPop( p_input, &i_type, &val, i_deadline, b_postpone ) )
            {
                if( b_postpone )
                    continue;
                break; /* Wake-up time reached */
            }
765

766
#ifndef NDEBUG
767
            msg_Dbg( p_input, "control type=%d", i_type );
768
#endif
769 770 771 772 773
            if( Control( p_input, i_type, val ) )
            {
                if( ControlIsSeekRequest( i_type ) )
                    i_last_seek_mdate = mdate();
                i_intf_update = 0;
774
            }
775

776
            /* Update the wakeup time */
777
            if( i_wakeup != 0 )
778
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
779
        }
780
    }
781 782
}

783
static void InitStatistics( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
784
{
785
    if( p_input->b_preparsing ) return;
786

787
    /* Prepare statistics */
788 789
#define INIT_COUNTER( c, compute ) p_input->p->counters.p_##c = \
 stats_CounterCreate( STATS_##compute);
790
    if( libvlc_stats( p_input ) )
791
    {
792 793 794 795 796 797 798 799 800 801 802 803 804 805
        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 );
806 807 808
        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;
809
    }
810
}
811

812
#ifdef ENABLE_SOUT
813 814
static int InitSout( input_thread_t * p_input )
{
815 816
    if( p_input->b_preparsing )
        return VLC_SUCCESS;
817 818

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

844 845
    return VLC_SUCCESS;
}
846
#endif
847

848 849
static void InitTitle( input_thread_t * p_input )
{
850
    input_source_t *p_master = p_input->p->master;
851

Laurent Aimar's avatar
Laurent Aimar committed
852 853
    if( p_input->b_preparsing )
        return;
854

855
    vlc_mutex_lock( &p_input->p->p_item->lock );
856
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
857 858 859 860
    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;
861 862 863 864
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
865
        input_SendEventTitle( p_input, 0 );
866 867 868
    }

    /* Global flag */
869
    p_input->p->b_can_pace_control    = p_master->b_can_pace_control;
Laurent Aimar's avatar
Laurent Aimar committed
870 871
    p_input->p->b_can_pause        = p_master->b_can_pause;
    p_input->p->b_can_rate_control = p_master->b_can_rate_control;
872
    vlc_mutex_unlock( &p_input->p->p_item->lock );
873
}
874

875 876 877
static void StartTitle( input_thread_t * p_input )
{
    vlc_value_t val;
878

879
    /* Start title/chapter */
880 881 882
    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 )
883
        input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
Laurent Aimar's avatar
Laurent Aimar committed
884

885 886
    val.i_int = p_input->p->master->i_seekpoint_start -
                p_input->p->master->i_seekpoint_offset;
887 888 889
    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
890
    /* Start/stop/run time */
891
    p_input->p->i_start = llroundf(1000000.f
892
                                     * var_GetFloat( p_input, "start-time" ));
893
    p_input->p->i_stop  = llroundf(1000000.f
894
                                     * var_GetFloat( p_input, "stop-time" ));
895
    if( p_input->p->i_stop <= 0 )
896
    {
897 898 899 900 901 902 903 904 905
        p_input->p->i_stop = llroundf(1000000.f
                                     * var_GetFloat( p_input, "run-time" ));
        if( p_input->p->i_stop < 0 )
        {
            msg_Warn( p_input, "invalid run-time ignored" );
            p_input->p->i_stop = 0;
        }
        else
            p_input->p->i_stop += p_input->p->i_start;
Rafaël Carré's avatar
Rafaël Carré committed
906
    }
907

908
    if( p_input->p->i_start > 0 )
909
    {
910
        vlc_value_t s;
911

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

915
        s.i_int = p_input->p->i_start;
916
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
917 918 919 920 921 922
    }
    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;
    }
923
    p_input->p->b_fast_seek = var_GetBool( p_input, "input-fast-seek" );
Laurent Aimar's avatar
Laurent Aimar committed
924
}
925

Laurent Aimar's avatar
Laurent Aimar committed
926 927
static void LoadSubtitles( input_thread_t *p_input )
{
928 929
    /* Load subtitles */
    /* Get fps and set it if not already set */
930
    const float f_fps = p_input->p->master->f_fps;
931
    if( f_fps > 1.f )
932 933 934
    {
        var_Create( p_input, "sub-original-fps", VLC_VAR_FLOAT );
        var_SetFloat( p_input, "sub-original-fps", f_fps );
935

936
        float f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" );
937
        if( f_requested_fps != f_fps )