input.c 99.9 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>
Sam Hocevar's avatar
 
Sam Hocevar committed
33

Christophe Mutricy's avatar
Christophe Mutricy committed
34
#include <limits.h>
35
#include <assert.h>
36
#include <errno.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 "access.h"
44
#include "demux.h"
45
#include "stream.h"
46
#include "item.h"
47
#include "resource.h"
48

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

57
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
58
 * Local prototypes
59
 *****************************************************************************/
60 61
static void Destructor( input_thread_t * p_input );

62
static  void *Run            ( void * );
63

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

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

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

79
static void MRLSections( const char *, int *, int *, int *, int *);
80 81

static input_source_t *InputSourceNew( input_thread_t *);
Laurent Aimar's avatar
 
Laurent Aimar committed
82
static int  InputSourceInit( input_thread_t *, input_source_t *,
83 84
                             const char *, const char *psz_forced_demux,
                             bool b_in_can_fail );
85
static void InputSourceClean( input_source_t * );
Laurent Aimar's avatar
Laurent Aimar committed
86 87
static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );

88 89
/* TODO */
//static void InputGetAttachments( input_thread_t *, input_source_t * );
90
static void SlaveDemux( input_thread_t *p_input, bool *pb_demux_polled );
91
static void SlaveSeek( input_thread_t *p_input );
92

93 94
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta );
static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta );
95 96 97
static void InputGetExtraFiles( input_thread_t *p_input,
                                int *pi_list, char ***pppsz_list,
                                const char *psz_access, const char *psz_path );
98

99 100 101
static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
                              int i_new, input_attachment_t **pp_new );

102 103 104
enum {
    SUB_NOFLAG = 0x00,
    SUB_FORCED = 0x01,
105
    SUB_CANFAIL = 0x02,
106
};
107

108 109
static void input_SubtitleAdd( input_thread_t *, const char *, unsigned );
static void input_SubtitleFileAdd( input_thread_t *, char *, unsigned );
110 111
static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO fix name */

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

132
#undef input_CreateAndStart
133 134 135 136 137 138 139
/**
 * Create a new input_thread_t and start it.
 *
 * Provided for convenience.
 *
 * \see input_Create
 */
140 141
input_thread_t *input_CreateAndStart( vlc_object_t *p_parent,
                                      input_item_t *p_item, const char *psz_log )
142
{
143
    input_thread_t *p_input = input_Create( p_parent, p_item, psz_log, NULL );
144 145 146 147 148 149 150 151 152

    if( input_Start( p_input ) )
    {
        vlc_object_release( p_input );
        return NULL;
    }
    return p_input;
}

153
#undef input_Read
154
/**
155
 * Initialize an input thread and run it until it stops by itself.
156 157 158 159 160
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \return an error code, VLC_SUCCESS on success
 */
161
int input_Read( vlc_object_t *p_parent, input_item_t *p_item )
162
{
163
    input_thread_t *p_input = Create( p_parent, p_item, NULL, false, NULL );
164 165 166
    if( !p_input )
        return VLC_EGENERIC;

167 168
    if( !Init( p_input ) )
    {
169
        MainLoop( p_input, false );
170 171 172 173
        End( p_input );
    }

    vlc_object_release( p_input );
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    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;

    if( !Init( p_input ) )
        End( p_input );

    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 )
{
    /* Create thread and wait for its readiness. */
212 213 214
    p_input->p->is_running = !vlc_clone( &p_input->p->thread,
                                         Run, p_input, VLC_THREAD_PRIORITY_INPUT );
    if( !p_input->p->is_running )
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    {
        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
 *
 * b_abort must be true when a user stop is requested and not because you have
 * detected an error or an eof. It will be used to properly send the
 * INPUT_EVENT_ABORT event.
 *
 * \param p_input the input thread to stop
 * \param b_abort true if the input has been aborted by a user request
 */
void input_Stop( input_thread_t *p_input, bool b_abort )
{
    /* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
     * It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
     * unlock the control loop */
238
    ObjectKillChildrens( VLC_OBJECT(p_input) );
239 240 241 242 243 244 245 246

    vlc_mutex_lock( &p_input->p->lock_control );
    p_input->p->b_abort |= b_abort;
    vlc_mutex_unlock( &p_input->p->lock_control );

    input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}

247 248 249 250 251 252 253 254 255 256 257
void input_Join( input_thread_t *p_input )
{
    if( p_input->p->is_running )
        vlc_join( p_input->p->thread, NULL );
}

void input_Release( input_thread_t *p_input )
{
    vlc_object_release( p_input );
}

Laurent Aimar's avatar
Laurent Aimar committed
258 259 260 261 262
/**
 * Close an input
 *
 * It does not call input_Stop itself.
 */
263
void input_Close( input_thread_t *p_input )
Laurent Aimar's avatar
Laurent Aimar committed
264
{
265 266
    input_Join( p_input );
    input_Release( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
267 268
}

269 270 271 272 273 274 275 276 277 278 279 280
/**
 * 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;
}

281
/*****************************************************************************
282 283
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
284
 *
Laurent Aimar's avatar
Laurent Aimar committed
285
 * XXX Do not forget to update vlc_input.h if you add new variables.
286
 *****************************************************************************/
287
static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
288
                               const char *psz_header, bool b_quick,
289
                               input_resource_t *p_resource )
Michel Kaempf's avatar
Michel Kaempf committed
290
{
291
    input_thread_t *p_input = NULL;                 /* thread descriptor */
292
    int i;
293

294
    /* Allocate descriptor */
295
    p_input = vlc_custom_create( p_parent, sizeof( *p_input ), "input" );
296
    if( p_input == NULL )
297
        return NULL;
298 299 300 301 302 303

    /* 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 );
304 305 306

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

307 308
    free( psz_name );

309 310
    p_input->p = calloc( 1, sizeof( input_thread_private_t ) );
    if( !p_input->p )
311 312
    {
        vlc_object_release( p_input );
313
        return NULL;
314
    }
315

316 317 318 319 320 321 322 323
    /* Parse input options */
    vlc_mutex_lock( &p_item->lock );
    assert( (int)p_item->optflagc == p_item->i_options );
    for( i = 0; i < p_item->i_options; i++ )
        var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i],
                         !!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) );
    vlc_mutex_unlock( &p_item->lock );

324
    p_input->b_preparsing = b_quick;
325
    p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
326

327
    /* Init Common fields */
328
    p_input->b_eof = false;
329
    p_input->p->b_can_pace_control = true;
Clément Stenac's avatar
Clément Stenac committed
330
    p_input->p->i_start = 0;
Laurent Aimar's avatar
Laurent Aimar committed
331
    p_input->p->i_time  = 0;
Clément Stenac's avatar
Clément Stenac committed
332
    p_input->p->i_stop  = 0;
333
    p_input->p->i_run   = 0;
Clément Stenac's avatar
Clément Stenac committed
334
    p_input->p->i_title = 0;
335
    p_input->p->title = NULL;
Clément Stenac's avatar
Clément Stenac committed
336
    p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
Laurent Aimar's avatar
Laurent Aimar committed
337
    p_input->p->i_state = INIT_S;
338
    p_input->p->i_rate = INPUT_RATE_DEFAULT;
339
    p_input->p->b_recording = false;
340 341
    memset( &p_input->p->bookmark, 0, sizeof(p_input->p->bookmark) );
    TAB_INIT( p_input->p->i_bookmark, p_input->p->pp_bookmark );
342
    TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
343
    p_input->p->p_sout   = NULL;
344
    p_input->p->b_out_pace_control = false;
345

346
    vlc_gc_incref( p_item ); /* Released in Destructor() */
347 348 349
    p_input->p->p_item = p_item;

    /* Init Input fields */
Clément Stenac's avatar
Clément Stenac committed
350
    p_input->p->input.p_demux  = NULL;
351
    p_input->p->input.b_title_demux = false;
Clément Stenac's avatar
Clément Stenac committed
352 353 354
    p_input->p->input.i_title  = 0;
    p_input->p->input.title    = NULL;
    p_input->p->input.i_title_offset = p_input->p->input.i_seekpoint_offset = 0;
355 356 357 358
    p_input->p->input.b_can_pace_control = true;
    p_input->p->input.b_can_rate_control = true;
    p_input->p->input.b_rescale_ts = true;
    p_input->p->input.b_eof = false;
Clément Stenac's avatar
Clément Stenac committed
359

360
    vlc_mutex_lock( &p_item->lock );
361 362

    if( !p_item->p_stats )
363 364
        p_item->p_stats = stats_NewInputStats( p_input );
    vlc_mutex_unlock( &p_item->lock );
365

366
    /* No slave */
Clément Stenac's avatar
Clément Stenac committed
367 368
    p_input->p->i_slave = 0;
    p_input->p->slave   = NULL;
369

370
    /* */
371
    if( p_resource )
372 373
    {
        p_input->p->p_resource_private = NULL;
374
        p_input->p->p_resource = input_resource_Hold( p_resource );
375
    }
376
    else
377 378
    {
        p_input->p->p_resource_private = input_resource_New( VLC_OBJECT( p_input ) );
379
        p_input->p->p_resource = input_resource_Hold( p_input->p->p_resource_private );
380
    }
381
    input_resource_SetInput( p_input->p->p_resource, p_input );
382

383
    /* Init control buffer */
384
    vlc_mutex_init( &p_input->p->lock_control );
385
    vlc_cond_init( &p_input->p->wait_control );
Clément Stenac's avatar
Clément Stenac committed
386
    p_input->p->i_control = 0;
387
    p_input->p->b_abort = false;
388
    p_input->p->is_running = false;
389

390 391
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
392

393
    /* Create Objects variables for public Get and Set */
394
    input_ControlVarInit( p_input );
395

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

                 p_seekpoint = vlc_seekpoint_New();
418 419 420 421 422
                 while( (psz_end = strchr( psz_start, ',' ) ) )
                 {
                     *psz_end = 0;
                     if( !strncmp( psz_start, "name=", 5 ) )
                     {
423
                         p_seekpoint->psz_name = strdup(psz_start + 5);
424 425 426 427 428 429 430
                     }
                     else if( !strncmp( psz_start, "bytes=", 6 ) )
                     {
                         p_seekpoint->i_byte_offset = atoll(psz_start + 6);
                     }
                     else if( !strncmp( psz_start, "time=", 5 ) )
                     {
431
                         p_seekpoint->i_time_offset = atoll(psz_start + 5) *
Ilkka Ollakka's avatar
Ilkka Ollakka committed
432
                                                        CLOCK_FREQ;
433 434
                     }
                     psz_start = psz_end + 1;
Gildas Bazin's avatar
Gildas Bazin committed
435
                }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
436
                msg_Dbg( p_input, "adding bookmark: %s, bytes=%"PRId64", time=%"PRId64,
437 438 439 440 441
                                  p_seekpoint->psz_name, p_seekpoint->i_byte_offset,
                                  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
442
            }
443
            free( psz_bookmarks );
Gildas Bazin's avatar
Gildas Bazin committed
444 445 446
        }
    }

447
    /* Remove 'Now playing' info as it is probably outdated */
448
    input_item_SetNowPlaying( p_item, NULL );
449
    input_SendEventMeta( p_input );
450

451 452 453 454
    /* */
    if( p_input->b_preparsing )
        p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;

455 456 457 458
    /* Make sure the interaction option is honored */
    if( !var_InheritBool( p_input, "interact" ) )
        p_input->i_flags |= OBJECT_FLAGS_NOINTERACT;

459
    /* */
460
    memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
461
    vlc_mutex_init( &p_input->p->counters.counters_lock );
462

463 464 465
    p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
    p_input->p->p_es_out = NULL;

466 467 468
    /* Set the destructor when we are sure we are initialized */
    vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor );

469 470 471
    return p_input;
}

472 473 474 475
/**
 * Input destructor (called when the object's refcount reaches 0).
 */
static void Destructor( input_thread_t * p_input )
476
{
477
#ifndef NDEBUG
478
    char * psz_name = input_item_GetName( p_input->p->p_item );
479 480 481 482
    msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
    free( psz_name );
#endif

483 484 485
    if( p_input->p->p_es_out_display )
        es_out_Delete( p_input->p->p_es_out_display );

486 487
    if( p_input->p->p_resource )
        input_resource_Release( p_input->p->p_resource );
488
    if( p_input->p->p_resource_private )
489
        input_resource_Release( p_input->p->p_resource_private );
490

491
    vlc_gc_decref( p_input->p->p_item );
492

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

495
    for( int i = 0; i < p_input->p->i_control; i++ )
Laurent Aimar's avatar
Laurent Aimar committed
496 497 498 499
    {
        input_control_t *p_ctrl = &p_input->p->control[i];
        ControlRelease( p_ctrl->i_type, p_ctrl->val );
    }
500

501
    vlc_cond_destroy( &p_input->p->wait_control );
502 503
    vlc_mutex_destroy( &p_input->p->lock_control );
    free( p_input->p );
504
}
505

506
/*****************************************************************************
507
 * Run: main thread loop
508 509
 * This is the "normal" thread that spawns the input processing chain,
 * reads the stream, cleans up and waits
510
 *****************************************************************************/
511
static void *Run( void *obj )
Michel Kaempf's avatar
Michel Kaempf committed
512
{
513
    input_thread_t *p_input = (input_thread_t *)obj;
514
    const int canc = vlc_savecancel();
515

516
    if( Init( p_input ) )
517
        goto exit;
Michel Kaempf's avatar
Michel Kaempf committed
518

519
    MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */
520 521 522

    /* Clean up */
    End( p_input );
523 524

exit:
525
    /* Tell we're dead */
526 527 528 529 530
    vlc_mutex_lock( &p_input->p->lock_control );
    const bool b_abort = p_input->p->b_abort;
    vlc_mutex_unlock( &p_input->p->lock_control );

    if( b_abort )
531
        input_SendEventAbort( p_input );
532
    input_SendEventDead( p_input );
533

534
    vlc_restorecancel( canc );
535
    return NULL;
536 537 538 539 540
}

/*****************************************************************************
 * Main loop: Fill buffers from access, and demux
 *****************************************************************************/
541 542 543 544 545

/**
 * MainLoopDemux
 * It asks the demuxer to demux some data
 */
546
static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed, bool *pb_demux_polled, mtime_t i_start_mdate )
547
{
548
    int i_ret;
549

550
    *pb_changed = false;
551
    *pb_demux_polled = p_input->p->input.p_demux->pf_demux != NULL;
552

Laurent Aimar's avatar
Laurent Aimar committed
553
    if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||
554
        ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )
555 556
        i_ret = 0; /* EOF */
    else
Laurent Aimar's avatar
Laurent Aimar committed
557
        i_ret = demux_Demux( p_input->p->input.p_demux );
558 559

    if( i_ret > 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
560
    {
561
        if( p_input->p->input.p_demux->info.i_update )
562
        {
563 564 565 566 567
            if( p_input->p->input.p_demux->info.i_update & INPUT_UPDATE_TITLE_LIST )
            {
                UpdateTitleListfromDemux( p_input );
                p_input->p->input.p_demux->info.i_update &= ~INPUT_UPDATE_TITLE_LIST;
            }
568 569 570 571 572 573
            if( p_input->p->input.b_title_demux )
            {
                i_ret = UpdateTitleSeekpointFromDemux( p_input );
                *pb_changed = true;
            }
            UpdateGenericFromDemux( p_input );
574 575
        }
    }
576

577 578
    if( i_ret == 0 )    /* EOF */
    {
579 580
        msg_Dbg( p_input, "EOF reached" );
        p_input->p->input.b_eof = true;
581
        es_out_Eos(p_input->p->p_es_out);
582 583 584 585 586 587 588 589
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
    {
590 591 592 593
        bool b_demux_polled;
        SlaveDemux( p_input, &b_demux_polled );

        *pb_demux_polled |= b_demux_polled;
594 595 596
    }
}

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
static int MainLoopTryRepeat( input_thread_t *p_input, mtime_t *pi_start_mdate )
{
    int i_repeat = var_GetInteger( p_input, "input-repeat" );
    if( i_repeat == 0 )
        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 */
    val.i_int = p_input->p->input.i_title_start -
        p_input->p->input.i_title_offset;
    if( val.i_int < 0 || val.i_int >= p_input->p->input.i_title )
        val.i_int = 0;
    input_ControlPush( p_input,
                       INPUT_CONTROL_SET_TITLE, &val );

    val.i_int = p_input->p->input.i_seekpoint_start -
        p_input->p->input.i_seekpoint_offset;
    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 )
    {
        val.i_time = p_input->p->i_start;
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &val );
    }
    else
    {
        val.f_float = 0.0;
        input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &val );
    }

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

643 644 645 646 647 648
/**
 * MainLoopInterface
 * It update the variables used by the interfaces
 */
static void MainLoopInterface( input_thread_t *p_input )
{
649 650 651
    double f_position = 0.0;
    mtime_t i_time = 0;
    mtime_t i_length = 0;
652 653

    /* update input status variables */
654
    if( demux_Control( p_input->p->input.p_demux,
655 656
                       DEMUX_GET_POSITION, &f_position ) )
        f_position = 0.0;
657

658
    if( demux_Control( p_input->p->input.p_demux,
659 660
                       DEMUX_GET_TIME, &i_time ) )
        i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
661
    p_input->p->i_time = i_time;
662 663

    if( demux_Control( p_input->p->input.p_demux,
664 665
                       DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
666

667
    es_out_SetTimes( p_input->p->p_es_out, f_position, i_time, i_length );
668 669 670 671

    /* update current bookmark */
    vlc_mutex_lock( &p_input->p->p_item->lock );
    p_input->p->bookmark.i_time_offset = i_time;
672
    p_input->p->bookmark.i_byte_offset = -1;
673
    vlc_mutex_unlock( &p_input->p->p_item->lock );
674 675 676 677 678 679 680 681
}

/**
 * MainLoopStatistic
 * It updates the globals statics
 */
static void MainLoopStatistic( input_thread_t *p_input )
{
682
    stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
683
    input_SendEventStatistics( p_input );
684 685 686 687 688 689
}

/**
 * MainLoop
 * The main input loop.
 */
690
static void MainLoop( input_thread_t *p_input, bool b_interactive )
691 692
{
    mtime_t i_start_mdate = mdate();
693 694
    mtime_t i_intf_update = 0;
    mtime_t i_statistic_update = 0;
695
    mtime_t i_last_seek_mdate = 0;
696 697
    bool b_pause_after_eof = b_interactive &&
                             var_CreateGetBool( p_input, "play-and-pause" );
698

699
    while( vlc_object_alive( p_input ) && !p_input->b_error )
700 701 702
    {
        bool b_force_update;
        vlc_value_t val;
703
        mtime_t i_current;
704
        mtime_t i_wakeup;
705
        bool b_paused;
706
        bool b_demux_polled;
707

708 709
        /* Demux data */
        b_force_update = false;
710
        i_wakeup = 0;
Laurent Aimar's avatar
Laurent Aimar committed
711
        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
712 713
         * is paused -> this may cause problem with some of them
         * The same problem can be seen when seeking while paused */
Laurent Aimar's avatar
Laurent Aimar committed
714
        b_paused = p_input->p->i_state == PAUSE_S &&
715
                   ( !es_out_GetBuffering( p_input->p->p_es_out ) || p_input->p->input.b_eof );
716

717
        b_demux_polled = true;
718
        if( !b_paused )
719
        {
720 721
            if( !p_input->p->input.b_eof )
            {
722
                MainLoopDemux( p_input, &b_force_update, &b_demux_polled, i_start_mdate );
723

724 725
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
            }
726
            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
727 728 729 730
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
731 732 733
            /* 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 )
734 735 736 737 738 739 740
            {
                msg_Dbg( p_input, "pausing at EOF (pause after each)");
                val.i_int = PAUSE_S;
                Control( p_input, INPUT_CONTROL_SET_STATE, val );

                b_paused = true;
            }
741 742
            else
            {
743 744
                if( MainLoopTryRepeat( p_input, &i_start_mdate ) )
                    break;
745
                b_pause_after_eof = var_GetBool( p_input, "play-and-pause" );
746
            }
747
        }
748 749

        /* */
750
        do {
751
            mtime_t i_deadline = i_wakeup;
752
            if( b_paused || !b_demux_polled )
753 754 755
                i_deadline = __MIN( i_intf_update, i_statistic_update );

            /* Handle control */
756
            for( ;; )
757
            {
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
                mtime_t i_limit = i_deadline;

                /* We will postpone the execution of a seek until we have
                 * finished the ES bufferisation (postpone is limited to
                 * 125ms) */
                bool b_buffering = es_out_GetBuffering( p_input->p->p_es_out ) &&
                                   !p_input->p->input.b_eof;
                if( b_buffering )
                {
                    /* When postpone is in order, check the ES level every 20ms */
                    mtime_t i_current = mdate();
                    if( i_last_seek_mdate + INT64_C(125000) >= i_current )
                        i_limit = __MIN( i_deadline, i_current + INT64_C(20000) );
                }

                int i_type;
                if( ControlPop( p_input, &i_type, &val, i_limit, b_buffering ) )
                {
                    if( b_buffering && i_limit < i_deadline )
                        continue;
                    break;
                }
780

781
#ifndef NDEBUG
782
                msg_Dbg( p_input, "control type=%d", i_type );
783
#endif
784

785
                if( Control( p_input, i_type, val ) )
786 787 788
                {
                    if( ControlIsSeekRequest( i_type ) )
                        i_last_seek_mdate = mdate();
789
                    b_force_update = true;
790
                }
791
            }
792

793 794 795 796 797 798 799 800 801 802 803
            /* Update interface and statistics */
            i_current = mdate();
            if( i_intf_update < i_current || b_force_update )
            {
                MainLoopInterface( p_input );
                i_intf_update = i_current + INT64_C(250000);
                b_force_update = false;
            }
            if( i_statistic_update < i_current )
            {
                MainLoopStatistic( p_input );
Ilkka Ollakka's avatar
Ilkka Ollakka committed
804
                i_statistic_update = i_current + CLOCK_FREQ;
805
            }
806

807
            /* Update the wakeup time */
808
            if( i_wakeup != 0 )
809
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
810
        } while( i_current < i_wakeup );
811
    }
812

813
    if( !p_input->b_error )
814
        input_ChangeState( p_input, END_S );
815 816
}

817
static void InitStatistics( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
818
{
819
    if( p_input->b_preparsing ) return;
820

821
    /* Prepare statistics */
822 823
#define INIT_COUNTER( c, compute ) p_input->p->counters.p_##c = \
 stats_CounterCreate( STATS_##compute);
824
    if( libvlc_stats( p_input ) )
825
    {
826 827 828 829 830 831 832 833 834 835 836 837 838 839
        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 );
840 841 842
        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;
843
    }
844
}
845

846
#ifdef ENABLE_SOUT
847 848
static int InitSout( input_thread_t * p_input )
{
849 850
    if( p_input->b_preparsing )
        return VLC_SUCCESS;
851 852

    /* Find a usable sout and attach it to p_input */
853
    char *psz = var_GetNonEmptyString( p_input, "sout" );
854
    if( psz && strncasecmp( p_input->p->p_item->psz_uri, "vlc:", 4 ) )
855
    {
856
        p_input->p->p_sout  = input_resource_RequestSout( p_input->p->p_resource, NULL, psz );
857
        if( !p_input->p->p_sout )
Clément Stenac's avatar
Clément Stenac committed
858
        {
859 860 861 862 863
            input_ChangeState( p_input, ERROR_S );
            msg_Err( p_input, "cannot start stream output instance, " \
                              "aborting" );
            free( psz );
            return VLC_EGENERIC;
864
        }
865
        if( libvlc_stats( p_input ) )
866
        {
867 868 869
            INIT_COUNTER( sout_sent_packets, COUNTER );
            INIT_COUNTER( sout_sent_bytes, COUNTER );
            INIT_COUNTER( sout_send_bitrate, DERIVATIVE );
870
        }
871
    }
872
    else
873
    {
874
        input_resource_RequestSout( p_input->p->p_resource, NULL, NULL );
875
    }
876
    free( psz );
877

878 879
    return VLC_SUCCESS;
}
880
#endif
881

882 883
static void InitTitle( input_thread_t * p_input )
{
Laurent Aimar's avatar
Laurent Aimar committed
884
    input_source_t *p_master = &p_input->p->input;
885

Laurent Aimar's avatar
Laurent Aimar committed
886 887
    if( p_input->b_preparsing )
        return;
888

889
    vlc_mutex_lock( &p_input->p->p_item->lock );
890
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
891 892 893 894
    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;
895 896 897 898
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
899
        input_SendEventTitle( p_input, 0 );
900 901 902
    }

    /* Global flag */
903
    p_input->p->b_can_pace_control    = p_master->b_can_pace_control;
Laurent Aimar's avatar
Laurent Aimar committed
904 905
    p_input->p->b_can_pause        = p_master->b_can_pause;
    p_input->p->b_can_rate_control = p_master->b_can_rate_control;
906
    vlc_mutex_unlock( &p_input->p->p_item->lock );
907
}
Gildas Bazin's avatar
 
Gildas Bazin committed
908

909 910 911
static void StartTitle( input_thread_t * p_input )
{
    vlc_value_t val;
912

913 914 915 916 917
    /* Start title/chapter */
    val.i_int = p_input->p->input.i_title_start -
                p_input->p->input.i_title_offset;
    if( val.i_int > 0 && val.i_int < p_input->p->input.i_title )
        input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
Laurent Aimar's avatar
Laurent Aimar committed
918

919 920 921 922 923
    val.i_int = p_input->p->input.i_seekpoint_start -
                p_input->p->input.i_seekpoint_offset;
    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
924
    /* Start/stop/run time */
925 926 927 928 929 930
    p_input->p->i_start = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "start-time" ));
    p_input->p->i_stop  = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "stop-time" ));
    p_input->p->i_run   = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "run-time" ));
931
    if( p_input->p->i_run < 0 )
932
    {
933 934
        msg_Warn( p_input, "invalid run-time ignored" );
        p_input->p->i_run = 0;
Rafaël Carré's avatar
Rafaël Carré committed
935
    }
936

937
    if( p_input->p->i_start > 0 )
938
    {
939
        vlc_value_t s;
940

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

944 945
        s.i_time = p_input->p->i_start;
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
946 947 948 949 950 951
    }
    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;
    }
952
    p_input->p->b_fast_seek = var_GetBool( p_input, "input-fast-seek" );
Laurent Aimar's avatar
Laurent Aimar committed
953
}
954

Laurent Aimar's avatar
Laurent Aimar committed
955 956
static void LoadSubtitles( input_thread_t *p_input )
{
957 958
    /* Load subtitles */
    /* Get fps and set it if not already set */
959 960
    const double f_fps = p_input->p->f_fps;
    if( f_fps > 1.0 )
961 962
    {
        float f_requested_fps;
963

964 965
        var_Create( p_input, "sub-original-fps", VLC_VAR_FLOAT );
        var_SetFloat( p_input, "sub-original-fps", f_fps );
Laurent Aimar's avatar
 
Laurent Aimar committed
966

967 968
        f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" );
        if( f_requested_fps != f_fps )
969
        {
970 971 972
            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT|
                                            VLC_VAR_DOINHERIT );
            var_SetFloat( p_input, "sub-fps", f_requested_fps );
973
        }
974
    }
Laurent Aimar's avatar
 
Laurent Aimar committed
975

Laurent Aimar's avatar
Laurent Aimar committed
976
    const int i_delay = var_CreateGetInteger( p_input, "sub-delay" );
977
    if( i_delay != 0 )
978
        var_SetTime( p_input, "spu-delay", (mtime_t)i_delay * 100000 );
979 980

    /* Look for and add subtitle files */
981
    unsigned i_flags = SUB_FORCED;
982

Laurent Aimar's avatar
Laurent Aimar committed
983
    char *psz_subtitle = var_GetNonEmptyString( p_input, "sub-file" );
984 985 986
    if( psz_subtitle != NULL )
    {
        msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
987
        input_SubtitleFileAdd( p_input, psz_subtitle, i_flags );
988
        i_flags = SUB_NOFLAG;
989 990
    }

Laurent Aimar's avatar
Laurent Aimar committed
991
    if( var_GetBool( p_input, "sub-autodetect-file" ) )
992 993
    {
        char *psz_autopath = var_GetNonEmptyString( p_input, "sub-autodetect-path" );
994
        char **ppsz_subs = subtitles_Detect( p_input, psz_autopath,
995
                                             p_input->p->p_item->psz_uri );
996
        free( psz_autopath );
997

998
        for( int i = 0; ppsz_subs && ppsz_subs[i]; i++ )
Laurent Aimar's avatar
 
Laurent Aimar committed
999
        {
1000
            if( !psz_subtitle || strcmp( psz_subtitle, ppsz_subs[i] ) )
1001
            {
1002
                i_flags |= SUB_CANFAIL;
1003
                input_SubtitleFileAdd( p_input, ppsz_subs[i], i_flags );
1004
                i_flags = SUB_NOFLAG;
1005
            }
1006 1007

            free( ppsz_subs[i] );
Laurent Aimar's avatar
 
Laurent Aimar committed
1008
        }
1009
        free( ppsz_subs );
1010 1011
    }
    free( psz_subtitle );
1012 1013 1014

    /* Load subtitles from attachments */
    int i_attachment = 0;
1015
    input_attachment_t **pp_attachment = NULL;
1016 1017 1018 1019 1020 1021

    vlc_mutex_lock( &p_input->p->p_item->lock );
    for( int i = 0; i < p_input->p->i_attachment; i++ )
    {
        const input_attachment_t *a = p_input->p->attachment[i];
        if( !strcmp( a->psz_mime, "application/x-srt" ) )
1022 1023 1024
            TAB_APPEND( i_attachment, pp_attachment,
                        vlc_input_attachment_New( a->psz_name, NULL,
                                                  a->psz_description, NULL, 0 ) );
1025 1026 1027
    }
    vlc_mutex_unlock( &p_input->p->p_item->lock );

1028 1029
    if( i_attachment > 0 )
        var_Create( p_input, "sub-description", VLC_VAR_STRING );
1030 1031
    for( int i = 0; i < i_attachment; i++ )
    {
1032 1033 1034
        input_attachment_t *a = pp_attachment[i];
        if( !a )
            continue;
1035
        char *psz_mrl;
1036 1037
        if( a->psz_name[i] &&
            asprintf( &psz_mrl, "attachment://%s", a->psz_name ) >= 0 )
1038
        {
1039 1040
            var_SetString( p_input, "sub-description", a->psz_description ? a->psz_description : "");

1041
            input_SubtitleAdd( p_input, psz_mrl, i_flags );
1042

1043
            i_flags = SUB_NOFLAG;
1044 1045
            free( psz_mrl );
        }
1046
        vlc_input_attachment_Delete( a );
1047
    }
1048 1049 1050
    free( pp_attachment );
    if( i_attachment > 0 )
        var_Destroy( p_input, "sub-description" );
Laurent Aimar's avatar
Laurent Aimar committed
1051
}
1052

Laurent Aimar's avatar
Laurent Aimar committed
1053 1054 1055 1056 1057 1058 1059 1060
static void LoadSlaves( input_thread_t *p_input )
{
    char *psz = var_GetNonEmptyString( p_input, "input-slave" );
    if( !psz )
        return;

    char *psz_org = psz;
    while( psz && *psz )
1061
    {
Laurent Aimar's avatar
Laurent Aimar committed
1062 1063
        while( *psz == ' ' || *psz == '#' )
            psz++;
1064

Laurent Aimar's avatar
Laurent Aimar committed
1065 1066 1067 1068 1069 1070 1071
        char *psz_delim = strchr( psz, '#' );
        if( psz_delim )
            *psz_delim++ = '\0';

        if( *psz == 0 )
            break;

1072 1073
        char *uri = strstr(psz, "://")
                                   ? strdup( psz ) : vlc_path2uri( psz, NULL );
1074 1075 1076 1077
        psz = psz_delim;
        if( uri == NULL )
            continue;
        msg_Dbg( p_input, "adding slave input '%s'", uri );
Laurent Aimar's avatar
Laurent Aimar committed
1078 1079

        input_source_t *p_slave = InputSourceNew( p_input );
1080
        if( p_slave && !InputSourceInit( p_input, p_slave, uri, NULL, false ) )
Laurent Aimar's avatar
Laurent Aimar committed
1081 1082 1083
            TAB_APPEND( p_input->p->i_slave, p_input->p->slave, p_slave );
        else
            free( p_slave );
1084
        free( uri );
1085
    }
Laurent Aimar's avatar
Laurent Aimar committed
1086
    free( psz_org );
1087 1088
}

1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
static void UpdatePtsDelay( input_thread_t *p_input )
{
    input_thread_private_t *p_sys = p_input->p;

    /* Get max pts delay from input source */
    mtime_t i_pts_delay = p_sys->input.i_pts_delay;
    for( int i = 0; i < p_sys->i_slave; i++ )
        i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );

    if( i_pts_delay < 0 )
        i_pts_delay = 0;

    /* Take care of audio/spu delay */
1102 1103 1104
    const mtime_t i_audio_delay = var_GetTime( p_input, "audio-delay" );
    const mtime_t i_spu_delay   = var_GetTime( p_input, "spu-delay" );
    const mtime_t i_extra_delay = __MIN( i_audio_delay, i_spu_delay );
1105
    if( i_extra_delay < 0 )
1106
        i_pts_delay -= i_extra_delay;
1107 1108 1109 1110 1111

    /* Update cr_average depending on the caching */
    const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;

    /* */
1112 1113
    es_out_SetDelay( p_input->p->p_es_out_display, AUDIO_ES, i_audio_delay );
    es_out_SetDelay( p_input->p->p_es_out_display, SPU_ES, i_spu_delay );
1114
    es_out_SetJitter( p_input->p->p_es_out, i_pts_delay, 0, i_cr_average );
1115 1116
}

1117 1118 1119
static void InitPrograms( input_thread_t * p_input )
{
    int i_es_out_mode;
1120
    vlc_list_t list;
1121

1122 1123 1124
    /* Compute correct pts_delay */
    UpdatePtsDelay( p_input );

1125
    /* Set up es_out */
1126 1127
    i_es_out_mode = ES_OUT_MODE_AUTO;
    if( p_input->p->p_sout )
1128
    {
1129 1130
        char *prgms;

1131
        if( var_GetBool( p_input, "sout-all" ) )
1132
        {
1133 1134 1135
            i_es_out_mode = ES_OUT_MODE_ALL;
        }
        else
1136
        if( (prgms = var_GetNonEmptyString( p_input, "programs" )) != NULL )
1137
        {
1138 1139 1140 1141 1142 1143
            char *buf;

            TAB_INIT( list.i_count, list.p_values );
            for( const char *prgm = strtok_r( prgms, ",", &buf );
                 prgm != NULL;
                 prgm = strtok_r( NULL, ",", &buf ) )
1144
            {
1145
                vlc_value_t val = { .i_int = atoi( prgm ) };
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1146
                INSERT_ELEM( list.p_values, list.i_count, list.i_count, val );
1147 1148 1149
            }

            if( list.i_count > 0 )
1150 1151
                i_es_out_mode = ES_OUT_MODE_PARTIAL;
                /* Note : we should remove the "program" callback. */
1152 1153

            free( prgms );
1154
        }
1155
    }
1156
    es_out_SetMode( p_input->p->p_es_out, i_es_out_mode );
1157

1158 1159 1160 1161 1162 1163 1164 1165
    /* Inform the demuxer about waited group (needed only for DVB) */
    if( i_es_out_mode == ES_OUT_MODE_ALL )
    {
        demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, -1, NULL );
    }
    else if( i_es_out_mode == ES_OUT_MODE_PARTIAL )
    {
        demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, -1,
1166 1167
                       &list );
        TAB_CLEAN( list.i_count, list.p_values );
1168 1169 1170 1171
    }
    else
    {
        demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP,
Laurent Aimar's avatar
Laurent Aimar committed
1172
                       es_out_GetGroupForced( p_input->p->p_es_out ), NULL );
1173 1174 1175 1176 1177 1178
    }
}

static int Init( input_thread_t * p_input )
{
    vlc_meta_t *p_meta;
Laurent Aimar's avatar
Laurent Aimar committed
1179
    int i;
1180

1181
    for( i = 0; i < p_input->p->p_item->i_options; i++ )
1182
    {
1183
        if( !strncmp( p_input->p->p_item->ppsz_options[i], "meta-file", 9 ) )
1184
        {
1185 1186 1187 1188 1189 1190 1191
            msg_Dbg( p_input, "Input is a meta file: disabling unneeded options" );
            var_SetString( p_input, "sout", "" );
            var_SetBool( p_input, "sout-all", false );
            var_SetString( p_input, "input-slave", "" );
            var_SetInteger( p_input, "input-repeat", 0 );
            var_SetString( p_input, "sub-file", "" );
            var_SetBool( p_input, "sub-autodetect-file", false );
1192
        }
1193 1194 1195
    }

    InitStatistics( p_input );
1196
#ifdef ENABLE_SOUT
Laurent Aimar's avatar
Laurent Aimar committed
1197
    if( InitSout( p_input ) )
1198
        goto error;
1199
#endif
1200 1201

    /* Create es out */
1202
    p_input->p->p_es_out = input_EsOutTimeshiftNew( p_input, p_input->p->p_es_out_display, p_input->p->i_rate );
1203

1204 1205
    /* */
    input_ChangeState( p_input, OPENING_S );
1206
    input_SendEventCache( p_input, 0.0 );
1207 1208

    /* */
1209
    if( InputSourceInit( p_input, &p_input->p->input,
1210
                         p_input->p->p_item->psz_uri, NULL, false ) )
1211 1212 1213 1214 1215 1216 1217 1218
    {
        goto error;
    }

    InitTitle( p_input );

    /* Load master infos */
    /* Init length */
1219
    mtime_t i_length;
1220
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_LENGTH,
1221 1222 1223
                         &i_length ) )
        i_length = 0;
    if( i_length <= 0 )
1224
        i_length = input_item_GetDuration( p_input->p->p_item );
1225 1226 1227
    input_SendEventLength( p_input, i_length );

    input_SendEventPosition( p_input, 0.0, 0 );
1228

Laurent Aimar's avatar
Laurent Aimar committed
1229 1230
    if( !p_input->b_preparsing )
    {
Laurent Aimar's avatar
Laurent Aimar committed
1231
        StartTitle( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
1232 1233 1234
        LoadSubtitles( p_input );
        LoadSlaves( p_input );
        InitPrograms( p_input );
1235 1236 1237 1238 1239 1240 1241

        double f_rate = var_InheritFloat( p_input, "rate" );
        if( f_rate != 0.0 && f_rate != 1.0 )
        {
            vlc_value_t val = { .i_int = INPUT_RATE_DEFAULT / f_rate };
            input_ControlPush( p_input, INPUT_CONTROL_SET_RATE, &val );
        }
Laurent Aimar's avatar
Laurent Aimar committed
1242
    }
1243 1244 1245 1246

    if( !p_input->b_preparsing && p_input->p->p_sout )
    {
        p_input->p->b_out_pace_control = (p_input->p->p_sout->i_out_pace_nocontrol > 0);
1247

1248
        if( p_input->p->b_can_pace_control && p_input->p->b_out_pace_control )
1249 1250 1251
        {
            /* We don't want a high input priority here or we'll
             * end-up sucking up all the CPU time */
1252
            vlc_set_priority( p_input->p->thread, VLC_THREAD_PRIORITY_LOW );
1253
        }
1254 1255 1256

        msg_Dbg( p_input, "starting in %s mode",
                 p_input->p->b_out_pace_control ? "async" : "sync" );
1257 1258
    }

1259
    p_meta = vlc_meta_New();
Laurent Aimar's avatar
Laurent Aimar committed
1260 1261 1262 1263
    if( p_meta )
    {
        /* Get meta data from users */
        InputMetaUser( p_input, p_meta );
1264

Laurent Aimar's avatar
Laurent Aimar committed
1265 1266
        /* Get meta data from master input */
        InputSourceMeta( p_input, &p_input->p->input, p_meta );
1267

Laurent Aimar's avatar
Laurent Aimar committed
1268 1269 1270
        /* And from slave */
        for( int i = 0; i < p_input->p->i_slave; i++ )
            InputSourceMeta( p_input, p_input->p->slave[i], p_meta );
1271

Laurent Aimar's avatar
Laurent Aimar committed
1272 1273 1274
        /* */
        InputUpdateMeta( p_input, p_meta );
    }
Rafaël Carré's avatar
Rafaël Carré committed
1275

1276 1277
    msg_Dbg( p_input, "`%s' successfully opened",
             p_input->p->p_item->psz_uri );
1278

1279
    /* initialization is complete */
1280
    input_ChangeState( p_input, PLAYING_S );
1281

1282
    return VLC_SUCCESS;
1283

1284
error:
1285 1286
    input_ChangeState( p_input, ERROR_S );

Clément Stenac's avatar
Clément Stenac committed
1287
    if( p_input->p->p_es_out )
1288
        es_out_Delete( p_input->p->p_es_out );
1289
    es_out_SetMode( p_input->p->p_es_out_display, ES_OUT_MODE_END );
1290
    if( p_input->p->p_resource )
1291
    {
1292
        if( p_input->p->p_sout )
1293
            input_resource_RequestSout( p_input->p->p_resource,
1294
                                         p_input->p->p_sout, NULL );
1295
        input_resource_SetInput( p_input->p->p_resource, NULL );
1296 1297
        if( p_input->p->p_resource_private )
            input_resource_Terminate( p_input->p->p_resource_private );
1298
    }
1299

1300
    if( !p_input->b_preparsing && libvlc_stats( p_input ) )
1301 1302 1303 1304 1305 1306 1307 1308 1309
    {
#define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \
                                   stats_CounterClean( p_input->p->counters.p_##c );\
                               p_input->p->counters.p_##c = NULL; } while(0)
        EXIT_COUNTER( read_bytes );
        EXIT_COUNTER( read_packets );
        EXIT_COUNTER( demux_read );
        EXIT_COUNTER( input_bitrate );
        EXIT_COUNTER( demux_bitrate );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1310 1311
        EXIT_COUNTER( demux_corrupted );
        EXIT_COUNTER( demux_discontinuity );
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
        EXIT_COUNTER( played_abuffers );
        EXIT_COUNTER( lost_abuffers );
        EXIT_COUNTER( displayed_pictures );
        EXIT_COUNTER( lost_pictures );
        EXIT_COUNTER( decoded_audio );
        EXIT_COUNTER( decoded_video );
        EXIT_COUNTER( decoded_sub );

        if( p_input->p->p_sout )
        {
            EXIT_COUNTER( sout_sent_packets );
1323
            EXIT_COUNTER( sout_sent_bytes );
1324 1325 1326 1327 1328
            EXIT_COUNTER( sout_send_bitrate );
        }
#undef EXIT_COUNTER
    }

1329
    /* Mark them deleted */
Clément Stenac's avatar
Clément Stenac committed
1330 1331 1332
    p_input->p->input.p_demux = NULL;
    p_input->p->p_es_out = NULL;
    p_input->p->p_sout = NULL;
1333

1334
    return VLC_EGENERIC;
Michel Kaempf's avatar
Michel Kaempf committed
1335 1336
}

1337
/*****************************************************************************
1338
 * End: end the input thread
1339
 *****************************************************************************/
1340
static void End( input_thread_t * p_input )
1341
{
1342
    int i;
1343

1344
    /* We are at the end */
1345
    input_ChangeState( p_input, END_S );
1346 1347

    /* Clean control variables */
1348
    input_ControlVarStop( p_input );
1349

1350
    /* Stop es out activity */
1351
    es_out_SetMode( p_input->p->p_es_out, ES_OUT_MODE_NONE );
1352

1353
    /* Clean up master */
1354
    InputSourceClean( &p_input->p->input );
1355

1356
    /* Delete slave */
Clément Stenac's avatar
Clément Stenac committed
1357
    for( i = 0; i < p_input->p->i_slave; i++ )
1358
    {
1359
        InputSourceClean( p_input->p->slave[i] );
Clément Stenac's avatar
Clément Stenac committed
1360
        free( p_input->p->slave[i] );
1361
    }
1362
    free( p_input->p->slave );
1363

1364
    /* Unload all modules */
Clément Stenac's avatar
Clément Stenac committed
1365
    if( p_input->p->p_es_out )
1366
        es_out_Delete( p_input->p->p_es_out );
1367
    es_out_SetMode( p_input->p->p_es_out_display, ES_OUT_MODE_END );
Gildas Bazin's avatar
 
Gildas Bazin committed
1368

1369
    if( !p_input->b_preparsing )
1370
    {
1371
#define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL;
1372
        if( libvlc_stats( p_input ) )
1373
        {
1374
            /* make sure we are up to date */
1375
            stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
1376 1377 1378 1379 1380
            CL_CO( read_bytes );
            CL_CO( read_packets );
            CL_CO( demux_read );
            CL_CO( input_bitrate );
            CL_CO( demux_bitrate );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1381 1382
            CL_CO( demux_corrupted );
            CL_CO( demux_discontinuity );
1383 1384 1385 1386 1387 1388 1389
            CL_CO( played_abuffers );
            CL_CO( lost_abuffers );
            CL_CO( displayed_pictures );
            CL_CO( lost_pictures );
            CL_CO( decoded_audio) ;
            CL_CO( decoded_video );
            CL_CO( decoded_sub) ;
1390
        }
1391

1392 1393 1394 1395 1396 1397
        /* Close optional stream output instance */
        if( p_input->p->p_sout )
        {
            CL_CO( sout_sent_packets );
            CL_CO( sout_sent_bytes );
            CL_CO( sout_send_bitrate );
1398 1399 1400 1401
        }
#undef CL_CO
    }

1402
    vlc_mutex_lock( &p_input->p->p_item->lock );
1403 1404 1405 1406 1407 1408
    if( p_input->p->i_attachment > 0 )
    {
        for( i = 0; i < p_input->p->i_attachment; i++ )
            vlc_input_attachment_Delete( p_input->p->attachment[i] );
        TAB_CLEAN( p_input->p->i_attachment, p_input->p->attachment );
    }
1409
    vlc_mutex_unlock( &p_input->p->p_item->lock );
1410

1411
    /* */
1412
    input_resource_RequestSout( p_input->p->p_resource,
1413
                                 p_input->p->p_sout, NULL );
1414
    input_resource_SetInput( p_input->p->p_resource, NULL );
1415 1416
    if( p_input->p->p_resource_private )
        input_resource_Terminate( p_input->p->p_resource_private );
1417
}
1418

1419 1420 1421
/*****************************************************************************
 * Control
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
1422 1423 1424 1425 1426 1427 1428
void input_ControlPush( input_thread_t *p_input,
                        int i_type, vlc_value_t *p_val )
{
    vlc_mutex_lock( &p_input->p->lock_control );
    if( i_type == INPUT_CONTROL_SET_DIE )
    {
        /* Special case, empty the control */
Laurent Aimar's avatar
Laurent Aimar committed
1429 1430 1431 1432 1433 1434
        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 );
        }
        p_input->p->i_control = 0;
Laurent Aimar's avatar
Laurent Aimar committed
1435
    }
Laurent Aimar's avatar
Laurent Aimar committed
1436 1437

    if( p_input->p->i_control >= INPUT_CONTROL_FIFO_SIZE )
Laurent Aimar's avatar
Laurent Aimar committed
1438 1439 1440
    {
        msg_Err( p_input, "input control fifo overflow, trashing type=%d",
                 i_type );
1441 1442
        if( p_val )
            ControlRelease( i_type, *p_val );
Laurent Aimar's avatar
Laurent Aimar committed
1443 1444 1445
    }
    else
    {
Laurent Aimar's avatar
Laurent Aimar committed
1446 1447
        input_control_t c;
        c.i_type = i_type;
Laurent Aimar's avatar
Laurent Aimar committed
1448
        if( p_val )
Laurent Aimar's avatar
Laurent Aimar committed
1449
            c.val = *p_val;
Laurent Aimar's avatar
Laurent Aimar committed
1450
        else
Laurent Aimar's avatar
Laurent Aimar committed
1451
            memset( &c.val, 0, sizeof(c.val) );
Laurent Aimar's avatar
Laurent Aimar committed
1452

Laurent Aimar's avatar
Laurent Aimar committed
1453
        p_input->p->control[p_input->p->i_control++] = c;
Laurent Aimar's avatar
Laurent Aimar committed
1454 1455 1456 1457 1458
    }
    vlc_cond_signal( &p_input->p->wait_control );
    vlc_mutex_unlock( &p_input->p->lock_control );
}

1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
static int ControlGetReducedIndexLocked( input_thread_t *p_input )
{
    const int i_lt = p_input->p->control[0].i_type;
    int i;
    for( i = 1; i < p_input->p->i_control; i++ )
    {
        const int i_ct = p_input->p->control[i].i_type;

        if( i_lt == i_ct &&
            ( i_ct == INPUT_CONTROL_SET_STATE ||
              i_ct == INPUT_CONTROL_SET_RATE ||
              i_ct == INPUT_CONTROL_SET_POSITION ||
              i_ct == INPUT_CONTROL_SET_TIME ||
              i_ct == INPUT_CONTROL_SET_PROGRAM ||
              i_ct == INPUT_CONTROL_SET_TITLE ||
              i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
              i_ct == INPUT_CONTROL_SET_BOOKMARK ) )
        {
            continue;
        }
        else
        {
            /* TODO but that's not that important
                - merge SET_X with SET_X_CMD
                - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
                - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
                - ?
                */
            break;
        }
    }
    return i - 1;
}


1494 1495
static inline int ControlPop( input_thread_t *p_input,
                              int *pi_type, vlc_value_t *p_val,
1496
                              mtime_t i_deadline, bool b_postpone_seek )
1497
{
1498
    input_thread_private_t *p_sys = p_input->p;
1499

1500
    vlc_mutex_lock( &p_sys->lock_control );
1501 1502
    while( p_sys->i_control <= 0 ||
           ( b_postpone_seek && ControlIsSeekRequest( p_sys->control[0].i_type ) ) )
1503
    {
1504 1505 1506
        if( !vlc_object_alive( p_input ) || i_deadline < 0 )
        {
            vlc_mutex_unlock( &p_sys->lock_control );
1507
            return VLC_EGENERIC;
1508
        }
1509

1510 1511 1512 1513
        if( vlc_cond_timedwait( &p_sys->wait_control, &p_sys->lock_control,
                                i_deadline ) )
        {
            vlc_mutex_unlock( &p_sys->lock_control );
1514
            return VLC_EGENERIC;
1515
        }
1516
    }
1517

1518
    /* */
1519 1520 1521 1522 1523
    const int i_index = ControlGetReducedIndexLocked( p_input );

    /* */
    *pi_type = p_sys->control[i_index].i_type;
    *p_val   = p_sys->control[i_index].val;
Christophe Massiot's avatar
Christophe Massiot committed
1524

1525
    p_sys->i_control -= i_index + 1;
1526
    if( p_sys->i_control > 0 )
1527
        memmove( &p_sys->control[0], &p_sys->control[i_index+1],
1528 1529
                 sizeof(*p_sys->control) * p_sys->i_control );
    vlc_mutex_unlock( &p_sys->lock_control );
1530

1531 1532
    return VLC_SUCCESS;
}
1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545
static bool ControlIsSeekRequest( int i_type )
{
    switch( i_type )
    {
    case INPUT_CONTROL_SET_POSITION:
    case INPUT_CONTROL_SET_TIME:
    case INPUT_CONTROL_SET_TITLE:
    case INPUT_CONTROL_SET_TITLE_NEXT:
    case INPUT_CONTROL_SET_TITLE_PREV:
    case INPUT_CONTROL_SET_SEEKPOINT:
    case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
    case INPUT_CONTROL_SET_SEEKPOINT_PREV:
    case INPUT_CONTROL_SET_BOOKMARK:
1546 1547 1548 1549 1550
    case INPUT_CONTROL_NAV_ACTIVATE:
    case INPUT_CONTROL_NAV_UP:
    case INPUT_CONTROL_NAV_DOWN:
    case INPUT_CONTROL_NAV_LEFT:
    case INPUT_CONTROL_NAV_RIGHT:
1551 1552 1553 1554 1555
        return true;
    default:
        return false;
    }
}
1556

1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
static void ControlRelease( int i_type, vlc_value_t val )
{
    switch( i_type )
    {
    case INPUT_CONTROL_ADD_SUBTITLE:
    case INPUT_CONTROL_ADD_SLAVE:
        free( val.psz_string );
        break;

    default:
        break;
    }
}

1571 1572 1573
/* Pause input */
static void ControlPause( input_thread_t *p_input, mtime_t i_control_date )
{
1574 1575
    int i_ret = VLC_SUCCESS;
    int i_state = PAUSE_S;
1576

1577
    if( p_input->p->b_can_pause )
1578
    {
1579 1580 1581 1582
        demux_t *p_demux = p_input->p->input.p_demux;

        if( p_demux->s != NULL )
            i_ret = stream_Control( p_demux->s, STREAM_SET_PAUSE_STATE, true );
1583
        else
1584
            i_ret = demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, true );
1585 1586 1587 1588

        if( i_ret )
        {
            msg_Warn( p_input, "cannot set pause state" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1589
            return;
1590
        }
1591 1592
    }

1593
    /* */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1594 1595 1596 1597
    i_ret = es_out_SetPauseState( p_input->p->p_es_out,
                                  p_input->p->b_can_pause, true,
                                  i_control_date );
    if( i_ret )
1598
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1599 1600
        msg_Warn( p_input, "cannot set pause state at es_out level" );
        return;
1601 1602
    }

1603
    /* Switch to new state */
1604
    input_ChangeState( p_input, i_state );
1605
}
1606

1607 1608
static void ControlUnpause( input_thread_t *p_input, mtime_t i_control_date )
{
1609
    int i_ret = VLC_SUCCESS;
1610

1611
    if( p_input->p->b_can_pause )
1612
    {
1613 1614 1615 1616
        demux_t *p_demux = p_input->p->input.p_demux;

        if( p_demux->s != NULL )
            i_ret = stream_Control( p_demux->s, STREAM_SET_PAUSE_STATE, false );
1617
        else
1618
            i_ret = demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, false );
1619 1620 1621 1622 1623 1624
        if( i_ret )
        {
            /* FIXME What to do ? */
            msg_Warn( p_input, "cannot unset pause -> EOF" );
            input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
        }
1625 1626 1627
    }

    /* Switch to play */
1628
    input_ChangeState( p_input, PLAYING_S );
1629 1630 1631

    /* */
    if( !i_ret )
1632
        es_out_SetPauseState( p_input->p->p_es_out, false, false, i_control_date );
1633
}
1634

1635 1636
static bool Control( input_thread_t *p_input,
                     int i_type, vlc_value_t val )
1637
{
1638
    const mtime_t i_control_date = mdate();
1639
    /* FIXME b_force_update is abused, it should be carefully checked */
1640
    bool b_force_update = false;
1641

1642 1643
    if( !p_input )
        return b_force_update;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1644

1645 1646 1647
    switch( i_type )
    {
        case INPUT_CONTROL_SET_DIE:
1648
            msg_Dbg( p_input, "control: stopping input" );
1649

1650
            /* Mark all submodules to die */
1651
            ObjectKillChildrens( VLC_OBJECT(p_input) );
1652 1653 1654 1655 1656
            break;

        case INPUT_CONTROL_SET_POSITION:
        {
            double f_pos;
1657 1658 1659 1660 1661 1662

            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
                break;
            }
1663 1664 1665 1666 1667 1668 1669
            f_pos = val.f_float;
            if( i_type != INPUT_CONTROL_SET_POSITION )
                f_pos += var_GetFloat( p_input, "position" );
            if( f_pos < 0.0 )
                f_pos = 0.0;
            else if( f_pos > 1.0 )
                f_pos = 1.0;
1670
            /* Reset the decoders states and clock sync (before calling the demuxer */
1671
            es_out_SetTime( p_input->p->p_es_out, -1 );
1672
            if( demux_Control( p_input->p->input.p_demux, DEMUX_SET_POSITION,
1673
                                f_pos, !p_input->p->b_fast_seek ) )
1674
            {
Gildas Bazin's avatar
Gildas Bazin committed
1675 1676
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
                         "%2.1f%% failed", f_pos * 100 );
1677 1678 1679
            }
            else
            {
Clément Stenac's avatar
Clément Stenac committed
1680
                if( p_input->p->i_slave > 0 )
1681
                    SlaveSeek( p_input );
1682
                p_input->p->input.b_eof = false;
1683

1684
                b_force_update = true;
1685 1686 1687 1688 1689 1690 1691 1692 1693
            }
            break;
        }

        case INPUT_CONTROL_SET_TIME:
        {
            int64_t i_time;
            int i_ret;

1694 1695 1696 1697 1698 1699
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
                break;
            }

1700 1701 1702 1703 1704 1705
            i_time = val.i_time;
            if( i_type != INPUT_CONTROL_SET_TIME )
                i_time += var_GetTime( p_input, "time" );

            if( i_time < 0 )
                i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
1706

1707
            /* Reset the decoders states and clock sync (before calling the demuxer */
1708
            es_out_SetTime( p_input->p->p_es_out, -1 );
Laurent Aimar's avatar
Laurent Aimar committed
1709

1710
            i_ret = demux_Control( p_input->p->input.p_demux,
1711 1712
                                   DEMUX_SET_TIME, i_time,
                                   !p_input->p->b_fast_seek );
1713 1714 1715 1716
            if( i_ret )
            {
                int64_t i_length;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
1717
                /* Emulate it with a SET_POS */
1718 1719
                if( !demux_Control( p_input->p->input.p_demux,
                                    DEMUX_GET_LENGTH, &i_length ) && i_length > 0 )
1720 1721
                {
                    double f_pos = (double)i_time / (double)i_length;
1722
                    i_ret = demux_Control( p_input->p->input.p_demux,
1723 1724
                                            DEMUX_SET_POSITION, f_pos,
                                            !p_input->p->b_fast_seek );
1725 1726 1727 1728
                }
            }
            if( i_ret )
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1729
                msg_Warn( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64
1730
                         " failed or not possible", i_time );
1731 1732 1733
            }
            else
            {
Clément Stenac's avatar
Clément Stenac committed
1734
                if( p_input->p->i_slave > 0 )
1735
                    SlaveSeek( p_input );
1736
                p_input->p->input.b_eof = false;
1737

1738
                b_force_update = true;
1739 1740 1741 1742 1743
            }
            break;
        }

        case INPUT_CONTROL_SET_STATE:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1744 1745 1746
            if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
                msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" );
            else if( p_input->p->i_state == PAUSE_S )
1747
            {
1748
                ControlUnpause( p_input, i_control_date );
1749

1750
                b_force_update = true;
1751
            }
Laurent Aimar's avatar
Laurent Aimar committed
1752
            else if( val.i_int == PAUSE_S && p_input->p->i_state == PLAYING_S /* &&
1753
                     p_input->p->b_can_pause */ )
1754
            {
1755
                ControlPause( p_input, i_control_date );
1756

1757
                b_force_update = true;
1758
            }
1759
            else if( val.i_int == PAUSE_S && !p_input->p->b_can_pause && 0 )
1760
            {
1761
                b_force_update = true;
1762 1763

                /* Correct "state" value */
Laurent Aimar's avatar
Laurent Aimar committed
1764
                input_ChangeState( p_input, p_input->p->i_state );
1765 1766 1767 1768 1769
            }
            break;

        case INPUT_CONTROL_SET_RATE:
        {
1770
            /* Get rate and direction */
1771 1772
            int i_rate = abs( val.i_int );
            int i_rate_sign = val.i_int < 0 ? -1 : 1;
1773

1774 1775
            /* Check rate bound */
            if( i_rate < INPUT_RATE_MIN )
1776 1777 1778 1779 1780 1781 1782 1783 1784
            {
                msg_Dbg( p_input, "cannot set rate faster" );
                i_rate = INPUT_RATE_MIN;
            }
            else if( i_rate > INPUT_RATE_MAX )
            {
                msg_Dbg( p_input, "cannot set rate slower" );
                i_rate = INPUT_RATE_MAX;
            }
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800

            /* Apply direction */
            if( i_rate_sign < 0 )
            {
                if( p_input->p->input.b_rescale_ts )
                {
                    msg_Dbg( p_input, "cannot set negative rate" );
                    i_rate = p_input->p->i_rate;
                    assert( i_rate > 0 );
                }
                else
                {
                    i_rate *= i_rate_sign;
                }
            }

1801
            if( i_rate != INPUT_RATE_DEFAULT &&
Laurent Aimar's avatar
Laurent Aimar committed
1802
                ( ( !p_input->p->b_can_rate_control && !p_input->p->input.b_rescale_ts ) ||
Clément Stenac's avatar
Clément Stenac committed
1803
                  ( p_input->p->p_sout && !p_input->p->b_out_pace_control ) ) )
1804 1805 1806 1807
            {
                msg_Dbg( p_input, "cannot change rate" );
                i_rate = INPUT_RATE_DEFAULT;
            }
1808
            if( i_rate != p_input->p->i_rate &&
1809
                !p_input->p->b_can_pace_control && p_input->p->b_can_rate_control )
1810
            {
1811 1812 1813 1814
                demux_t *p_demux = p_input->p->input.p_demux;
                int i_ret = VLC_EGENERIC;

                if( p_demux->s == NULL )
1815 1816 1817 1818
                {
                    if( !p_input->p->input.b_rescale_ts )
                        es_out_Control( p_input->p->p_es_out, ES_OUT_RESET_PCR );

1819
                    i_ret = demux_Control( p_input->p->input.p_demux,
1820
                                            DEMUX_SET_RATE, &i_rate );
1821
                }
1822 1823 1824 1825 1826 1827 1828 1829
                if( i_ret )
                {
                    msg_Warn( p_input, "ACCESS/DEMUX_SET_RATE failed" );
                    i_rate = p_input->p->i_rate;
                }
            }

            /* */
Clément Stenac's avatar
Clément Stenac committed
1830
            if( i_rate != p_input->p->i_rate )
1831
            {
1832
                p_input->p->i_rate = i_rate;
1833
                input_SendEventRate( p_input, i_rate );
1834

1835
                if( p_input->p->input.b_rescale_ts )
1836
                {
1837
                    const int i_rate_source = (p_input->p->b_can_pace_control || p_input->p->b_can_rate_control ) ? i_rate : INPUT_RATE_DEFAULT;
1838 1839
                    es_out_SetRate( p_input->p->p_es_out, i_rate_source, i_rate );
                }
1840

1841
                b_force_update = true;
1842 1843 1844 1845 1846 1847
            }
            break;
        }

        case INPUT_CONTROL_SET_PROGRAM:
            /* No need to force update, es_out does it if needed */
Clément Stenac's avatar
Clément Stenac committed
1848
            es_out_Control( p_input->p->p_es_out,
1849
                            ES_OUT_SET_GROUP, val.i_int );
1850

1851
            demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, val.i_int,
1852
                            NULL );
1853 1854 1855 1856
            break;

        case INPUT_CONTROL_SET_ES:
            /* No need to force update, es_out does it if needed */
1857 1858
            es_out_Control( p_input->p->p_es_out_display,
                            ES_OUT_SET_ES_BY_ID, (int)val.i_int );
1859 1860

            demux_Control( p_input->p->input.p_demux, DEMUX_SET_ES, (int)val.i_int );
1861 1862 1863
            break;

        case INPUT_CONTROL_RESTART_ES:
1864 1865
            es_out_Control( p_input->p->p_es_out_display,
                            ES_OUT_RESTART_ES_BY_ID, (int)val.i_int );
1866 1867
            break;

1868
        case INPUT_CONTROL_SET_AUDIO_DELAY:
Laurent Aimar's avatar
Laurent Aimar committed
1869 1870
            input_SendEventAudioDelay( p_input, val.i_time );
            UpdatePtsDelay( p_input );
1871 1872 1873
            break;

        case INPUT_CONTROL_SET_SPU_DELAY:
Laurent Aimar's avatar
Laurent Aimar committed
1874 1875
            input_SendEventSubtitleDelay( p_input, val.i_time );
            UpdatePtsDelay( p_input );
1876 1877
            break;

1878 1879 1880
        case INPUT_CONTROL_SET_TITLE:
        case INPUT_CONTROL_SET_TITLE_NEXT:
        case INPUT_CONTROL_SET_TITLE_PREV:
1881
        {
1882 1883 1884 1885 1886
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
                break;
            }
1887 1888
            if( p_input->p->input.i_title <= 0 )
                break;
1889

1890
            int i_title = p_input->p->input.p_demux->info.i_title;
1891 1892 1893 1894 1895 1896 1897 1898
            if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
                i_title--;
            else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
                i_title++;
            else
                i_title = val.i_int;
            if( i_title < 0 || i_title >= p_input->p->input.i_title )
                break;
1899

1900
            es_out_SetTime( p_input->p->p_es_out, -1 );
1901 1902
            demux_Control( p_input->p->input.p_demux,
                           DEMUX_SET_TITLE, i_title );
1903
            input_SendEventTitle( p_input, i_title );
1904
            break;
1905
        }
1906 1907 1908
        case INPUT_CONTROL_SET_SEEKPOINT:
        case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
        case INPUT_CONTROL_SET_SEEKPOINT_PREV:
1909
        {
1910 1911 1912 1913 1914
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
                break;
            }
1915 1916
            if( p_input->p->input.i_title <= 0 )
                break;
1917

1918
            demux_t *p_demux = p_input->p->input.p_demux;
1919

1920 1921
            int i_title = p_demux->info.i_title;
            int i_seekpoint = p_demux->info.i_seekpoint;
1922 1923 1924 1925 1926 1927

            if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
            {
                int64_t i_seekpoint_time = p_input->p->input.title[i_title]->seekpoint[i_seekpoint]->i_time_offset;
                int64_t i_input_time = var_GetTime( p_input, "time" );
                if( i_seekpoint_time >= 0 && i_input_time >= 0 )
1928
                {
1929
                    if( i_input_time < i_seekpoint_time + 3000000 )
1930 1931
                        i_seekpoint--;
                }
1932
                else
1933
                    i_seekpoint--;
1934
            }
1935 1936 1937 1938 1939 1940 1941 1942 1943
            else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
                i_seekpoint++;
            else
                i_seekpoint = val.i_int;
            if( i_seekpoint < 0
             || i_seekpoint >= p_input->p->input.title[i_title]->i_seekpoint )
                break;

            es_out_SetTime( p_input->p->p_es_out, -1 );
1944 1945
            demux_Control( p_input->p->input.p_demux,
                           DEMUX_SET_SEEKPOINT, i_seekpoint );
1946
            input_SendEventSeekpoint( p_input, i_title, i_seekpoint );
1947
            break;
1948
        }
1949

1950 1951
        case INPUT_CONTROL_ADD_SUBTITLE:
            if( val.psz_string )
1952
                input_SubtitleFileAdd( p_input, val.psz_string, true );
1953 1954
            break;

1955 1956 1957
        case INPUT_CONTROL_ADD_SLAVE:
            if( val.psz_string )
            {
1958
                const char *uri = val.psz_string;
1959 1960
                input_source_t *slave = InputSourceNew( p_input );

1961
                if( slave && !InputSourceInit( p_input, slave, uri, NULL, false ) )
1962
                {
1963
                    vlc_meta_t *p_meta;
1964 1965 1966
                    int64_t i_time;

                    /* Add the slave */
1967
                    msg_Dbg( p_input, "adding %s as slave on the fly", uri );
1968 1969

                    /* Set position */
1970
                    if( demux_Control( p_input->p->input.p_demux,
1971 1972 1973
                                        DEMUX_GET_TIME, &i_time ) )
                    {
                        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
1974
                        InputSourceClean( slave );
1975 1976 1977
                        free( slave );
                        break;
                    }
1978
                    if( demux_Control( slave->p_demux,
1979
                                       DEMUX_SET_TIME, i_time, true ) )
1980 1981
                    {
                        msg_Err( p_input, "seek failed for new slave" );
1982
                        InputSourceClean( slave );
1983 1984 1985 1986 1987
                        free( slave );
                        break;
                    }

                    /* Get meta (access and demux) */
1988
                    p_meta = vlc_meta_New();
Laurent Aimar's avatar
Laurent Aimar committed
1989 1990 1991 1992 1993
                    if( p_meta )
                    {
                        demux_Control( slave->p_demux, DEMUX_GET_META, p_meta );
                        InputUpdateMeta( p_input, p_meta );
                    }
1994

Clément Stenac's avatar
Clément Stenac committed
1995
                    TAB_APPEND( p_input->p->i_slave, p_input->p->slave, slave );
1996 1997 1998
                }
                else
                {
1999
                    free( slave );
2000
                    msg_Warn( p_input, "failed to add %s as slave", uri );
2001 2002 2003 2004
                }
            }
            break;

2005
        case INPUT_CONTROL_SET_RECORD_STATE:
2006
            if( !!p_input->p->b_recording != !!val.b_bool )
2007
            {
2008
                if( p_input->p->input.b_can_stream_record )
2009 2010 2011 2012 2013
                {
                    if( demux_Control( p_input->p->input.p_demux,
                                       DEMUX_SET_RECORD_STATE, val.b_bool ) )
                        val.b_bool = false;
                }
2014 2015
                else
                {
2016
                    if( es_out_SetRecordState( p_input->p->p_es_out_display, val.b_bool ) )
2017 2018 2019
                        val.b_bool = false;
                }
                p_input->p->b_recording = val.b_bool;
2020

2021
                input_SendEventRecord( p_input, val.b_bool );
2022 2023 2024 2025 2026

                b_force_update = true;
            }
            break;

2027
        case INPUT_CONTROL_SET_FRAME_NEXT:
Laurent Aimar's avatar
Laurent Aimar committed
2028
            if( p_input->p->i_state == PAUSE_S )
2029
            {
2030
                es_out_SetFrameNext( p_input->p->p_es_out );
2031
            }
Laurent Aimar's avatar
Laurent Aimar committed
2032
            else if( p_input->p->i_state == PLAYING_S )
2033 2034 2035 2036 2037 2038 2039 2040 2041 2042
            {
                ControlPause( p_input, i_control_date );
            }
            else
            {
                msg_Err( p_input, "invalid state for frame next" );
            }
            b_force_update = true;
            break;

2043
        case INPUT_CONTROL_SET_BOOKMARK:
2044
        {
2045
            mtime_t time_offset = -1;
2046 2047 2048 2049 2050

            vlc_mutex_lock( &p_input->p->p_item->lock );
            if( val.i_int >= 0 && val.i_int < p_input->p->i_bookmark )
            {
                const seekpoint_t *p_bookmark = p_input->p->pp_bookmark[val.i_int];
2051
                time_offset = p_bookmark->i_time_offset;
2052 2053 2054
            }
            vlc_mutex_unlock( &p_input->p->p_item->lock );

2055
            if( time_offset < 0 )
2056
            {
2057
                msg_Err( p_input, "invalid bookmark %"PRId64, val.i_int );
2058 2059 2060
                break;
            }

2061 2062
            val.i_time = time_offset;
            b_force_update = Control( p_input, INPUT_CONTROL_SET_TIME, val );
2063 2064 2065
            break;
        }

2066 2067 2068 2069 2070 2071 2072 2073 2074
        case INPUT_CONTROL_NAV_ACTIVATE:
        case INPUT_CONTROL_NAV_UP:
        case INPUT_CONTROL_NAV_DOWN:
        case INPUT_CONTROL_NAV_LEFT:
        case INPUT_CONTROL_NAV_RIGHT:
            demux_Control( p_input->p->input.p_demux, i_type
                           - INPUT_CONTROL_NAV_ACTIVATE + DEMUX_NAV_ACTIVATE );
            break;

2075 2076 2077 2078 2079
        default:
            msg_Err( p_input, "not yet implemented" );
            break;
    }

2080
    ControlRelease( i_type, val );
2081 2082 2083 2084
    return b_force_update;
}

/*****************************************************************************
2085
 * UpdateTitleSeekpoint
2086
 *****************************************************************************/
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112
static int UpdateTitleSeekpoint( input_thread_t *p_input,
                                 int i_title, int i_seekpoint )
{
    int i_title_end = p_input->p->input.i_title_end -
                        p_input->p->input.i_title_offset;
    int i_seekpoint_end = p_input->p->input.i_seekpoint_end -
                            p_input->p->input.i_seekpoint_offset;

    if( i_title_end >= 0 && i_seekpoint_end >= 0 )
    {
        if( i_title > i_title_end ||
            ( i_title == i_title_end && i_seekpoint > i_seekpoint_end ) )
            return 0;
    }
    else if( i_seekpoint_end >= 0 )
    {
        if( i_seekpoint > i_seekpoint_end )
            return 0;
    }
    else if( i_title_end >= 0 )
    {
        if( i_title > i_title_end )
            return 0;
    }
    return 1;
}
2113 2114 2115 2116
/*****************************************************************************
 * Update*FromDemux:
 *****************************************************************************/
static int UpdateTitleSeekpointFromDemux( input_thread_t *p_input )
2117
{
Clément Stenac's avatar
Clément Stenac committed
2118
    demux_t *p_demux = p_input->p->input.p_demux;
2119

2120
    /* TODO event-like */
2121 2122
    if( p_demux->info.i_update & INPUT_UPDATE_TITLE )
    {
2123
        input_SendEventTitle( p_input, p_demux->info.i_title );
2124 2125 2126 2127 2128

        p_demux->info.i_update &= ~INPUT_UPDATE_TITLE;
    }
    if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT )
    {
2129 2130
        input_SendEventSeekpoint( p_input,
                                  p_demux->info.i_title, p_demux->info.i_seekpoint );
2131 2132 2133

        p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
    }
2134 2135

    /* Hmmm only works with master input */
Clément Stenac's avatar
Clément Stenac committed
2136
    if( p_input->p->input.p_demux == p_demux )
2137 2138 2139
        return UpdateTitleSeekpoint( p_input,
                                     p_demux->info.i_title,
                                     p_demux->info.i_seekpoint );
2140
    return 1;
2141 2142
}

2143 2144 2145 2146 2147 2148 2149
static void UpdateGenericFromDemux( input_thread_t *p_input )
{
    demux_t *p_demux = p_input->p->input.p_demux;

    if( p_demux->info.i_update & INPUT_UPDATE_META )
    {
        vlc_meta_t *p_meta = vlc_meta_New();
Laurent Aimar's avatar
Laurent Aimar committed
2150 2151 2152 2153 2154
        if( p_meta )
        {
            demux_Control( p_input->p->input.p_demux, DEMUX_GET_META, p_meta );
            InputUpdateMeta( p_input, p_meta );
        }
2155 2156
        p_demux->info.i_update &= ~INPUT_UPDATE_META;
    }
2157 2158 2159 2160 2161 2162 2163
    {
        double quality;
        double strength;

        if( !demux_Control( p_demux, DEMUX_GET_SIGNAL, &quality, &strength ) )
            input_SendEventSignal( p_input, quality, strength );
    }
2164 2165
}

2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189
static void UpdateTitleListfromDemux( input_thread_t *p_input )
{
    input_source_t *in = &p_input->p->input;

    /* Delete the preexisting titles */
    if( in->i_title > 0 )
    {
        for( int i = 0; i < in->i_title; i++ )
            vlc_input_title_Delete( in->title[i] );
        TAB_CLEAN( in->i_title, in->title );
        in->b_title_demux = false;
    }

    /* Get the new title list */
    if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
                       &in->title, &in->i_title,
                       &in->i_title_offset, &in->i_seekpoint_offset ) )
        TAB_INIT( in->i_title, in->title );
    else
        in->b_title_demux = true;

    InitTitle( p_input );
}

2190

2191 2192 2193 2194 2195
/*****************************************************************************
 * InputSourceNew:
 *****************************************************************************/
static input_source_t *InputSourceNew( input_thread_t *p_input )
{
2196
    VLC_UNUSED(p_input);
Laurent Aimar's avatar
Laurent Aimar committed
2197 2198

    return calloc( 1,  sizeof( input_source_t ) );
2199 2200 2201 2202 2203 2204
}

/*****************************************************************************
 * InputSourceInit:
 *****************************************************************************/
static int InputSourceInit( input_thread_t *p_input,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2205
                            input_source_t *in, const char *psz_mrl,
2206
                            const char *psz_forced_demux, bool b_in_can_fail )
2207
{
2208
    const char *psz_access, *psz_demux, *psz_path, *psz_anchor = NULL;
2209
    char *psz_var_demux = NULL;
2210
    double f_fps;
2211

2212
    assert( psz_mrl );
2213 2214 2215 2216
    char *psz_dup = strdup( psz_mrl );

    if( psz_dup == NULL )
        goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2217

2218
    /* Split uri */
2219
    input_SplitMRL( &psz_access, &psz_demux, &psz_path, &psz_anchor, psz_dup );
2220 2221 2222

    msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
             psz_mrl, psz_access, psz_demux, psz_path );
2223
    if( !p_input->b_preparsing )
2224
    {
2225 2226 2227
        /* Find optional titles and seekpoints */
        MRLSections( psz_anchor, &in->i_title_start, &in->i_title_end,
                     &in->i_seekpoint_start, &in->i_seekpoint_end );
2228
        if( psz_forced_demux && *psz_forced_demux )
2229
        {
2230
            psz_demux = psz_forced_demux;
2231
        }
2232
        else if( *psz_demux == '\0' )
2233 2234 2235
        {
            /* special hack for forcing a demuxer with --demux=module
             * (and do nothing with a list) */
2236
            psz_var_demux = var_GetNonEmptyString( p_input, "demux" );
2237 2238
            psz_demux = psz_var_demux;
            msg_Dbg( p_input, "specified demux `%s'", psz_demux );
2239
        }
2240

2241
        /* Try access_demux first */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2242 2243
        in->p_demux = demux_New( p_input, p_input, psz_access, psz_demux,
                                 psz_path, NULL, p_input->p->p_es_out, false );
2244 2245
    }
    else
2246
    {
2247
        /* Preparsing is only for file:// */
2248
        if( *psz_demux )
2249 2250 2251
            goto error;
        if( strcmp( psz_access, "file" ) )
            goto error;
2252
        msg_Dbg( p_input, "trying to pre-parse %s",  psz_path );
2253 2254
    }

2255 2256
    mtime_t i_pts_delay;

2257 2258 2259
    if( in->p_demux )
    {
        /* Get infos from access_demux */
2260
        in->b_title_demux = true;
2261
        if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2262 2263
                            &in->title, &in->i_title,
                            &in->i_title_offset, &in->i_seekpoint_offset ) )
2264
        {
2265
            TAB_INIT( in->i_title, in->title );
2266
        }
2267
        if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
2268
                            &in->b_can_pace_control ) )
2269
            in->b_can_pace_control = false;
2270

2271 2272
        assert( in->p_demux->pf_demux != NULL || !in->b_can_pace_control );

2273 2274
        if( !in->b_can_pace_control )
        {
2275
            if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_RATE,
2276 2277
                                &in->b_can_rate_control, &in->b_rescale_ts ) )
            {
2278 2279
                in->b_can_rate_control = false;
                in->b_rescale_ts = true; /* not used */
2280 2281 2282 2283
            }
        }
        else
        {
2284 2285
            in->b_can_rate_control = true;
            in->b_rescale_ts = true;
2286
        }
2287
        if( demux_Control( in->p_demux, DEMUX_CAN_PAUSE,
2288
                            &in->b_can_pause ) )
2289
            in->b_can_pause = false;
Laurent Aimar's avatar
Laurent Aimar committed
2290
        var_SetBool( p_input, "can-pause", in->b_can_pause || !in->b_can_pace_control ); /* XXX temporary because of es_out_timeshift*/
2291
        var_SetBool( p_input, "can-rate", !in->b_can_pace_control || in->b_can_rate_control ); /* XXX temporary because of es_out_timeshift*/
2292
        var_SetBool( p_input, "can-rewind", !in->b_rescale_ts && !in->b_can_pace_control && in->b_can_rate_control );
2293

2294 2295 2296 2297
        bool b_can_seek;
        if( demux_Control( in->p_demux, DEMUX_CAN_SEEK, &b_can_seek ) )
            b_can_seek = false;
        var_SetBool( p_input, "can-seek", b_can_seek );
2298 2299
    }
    else
2300 2301 2302 2303
    {   /* Now try a real access */
        access_t *p_access = access_New( p_input, p_input,
                                         psz_access, psz_demux, psz_path );
        if( p_access == NULL )
2304
        {
2305 2306
            if( vlc_object_alive( p_input ) )
            {
2307
                msg_Err( p_input, "open of `%s' failed", psz_mrl );
2308 2309 2310 2311
                if( !b_in_can_fail )
                    dialog_Fatal( p_input, _("Your input can't be opened"),
                                   _("VLC is unable to open the MRL '%s'."
                                     " Check the log for details."), psz_mrl );
2312
            }
2313 2314 2315
            goto error;
        }

2316
        /* Access-forced demuxer (PARENTAL ADVISORY: EXPLICIT HACK) */
2317
        if( !psz_demux[0] || !strcasecmp( psz_demux, "any" ) )
2318 2319
            psz_demux = p_access->psz_demux;

2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346
        /* */
        int  i_input_list;
        char **ppsz_input_list;

        TAB_INIT( i_input_list, ppsz_input_list );

        /* On master stream only, use input-list */
        if( &p_input->p->input == in )
        {
            char *psz_list;
            char *psz_parser;

            psz_list =
            psz_parser = var_CreateGetNonEmptyString( p_input, "input-list" );

            while( psz_parser && *psz_parser )
            {
                char *p = strchr( psz_parser, ',' );
                if( p )
                    *p++ = '\0';

                if( *psz_parser )
                {
                    char *psz_name = strdup( psz_parser );
                    if( psz_name )
                        TAB_APPEND( i_input_list, ppsz_input_list, psz_name );
                }
2347

2348 2349 2350 2351
                psz_parser = p;
            }
            free( psz_list );
        }
2352
        /* Autodetect extra files if none specified */
2353
        if( i_input_list <= 0 )
2354
        {
2355 2356
            InputGetExtraFiles( p_input, &i_input_list, &ppsz_input_list,
                                psz_access, psz_path );
2357
        }
2358 2359
        if( i_input_list > 0 )
            TAB_APPEND( i_input_list, ppsz_input_list, NULL );
2360

2361
        /* Create the stream_t */
2362
        stream_t *p_stream = stream_AccessNew( p_access, ppsz_input_list );
2363 2364 2365 2366 2367 2368
        if( ppsz_input_list )
        {
            for( int i = 0; ppsz_input_list[i] != NULL; i++ )
                free( ppsz_input_list[i] );
            TAB_CLEAN( i_input_list, ppsz_input_list );
        }
2369

2370
        if( p_stream == NULL )
2371 2372 2373 2374 2375
        {
            msg_Warn( p_input, "cannot create a stream_t from access" );
            goto error;
        }

Laurent Aimar's avatar
Laurent Aimar committed
2376 2377 2378
        /* Add stream filters */
        char *psz_stream_filter = var_GetNonEmptyString( p_input,
                                                         "stream-filter" );
2379 2380
        p_stream = stream_FilterChainNew( p_stream, psz_stream_filter,
                               var_GetBool( p_input, "input-record-native" ) );
Laurent Aimar's avatar
Laurent Aimar committed
2381
        free( psz_stream_filter );
2382

2383 2384 2385 2386
        if( !p_input->b_preparsing )
        {
            bool b;

2387
            stream_Control( p_stream, STREAM_CAN_CONTROL_PACE,
2388 2389 2390 2391
                            &in->b_can_pace_control );
            in->b_can_rate_control = in->b_can_pace_control;
            in->b_rescale_ts = true;

2392
            stream_Control( p_stream, STREAM_CAN_PAUSE, &in->b_can_pause );
2393 2394 2395 2396 2397 2398 2399
            var_SetBool( p_input, "can-pause",
                         in->b_can_pause || !in->b_can_pace_control ); /* XXX temporary because of es_out_timeshift*/
            var_SetBool( p_input, "can-rate",
                         !in->b_can_pace_control || in->b_can_rate_control ); /* XXX temporary because of es_out_timeshift*/
            var_SetBool( p_input, "can-rewind",
                         !in->b_rescale_ts && !in->b_can_pace_control );

2400
            stream_Control( p_stream, STREAM_CAN_SEEK, &b );
2401
            var_SetBool( p_input, "can-seek", b );
2402 2403

            in->b_title_demux = false;
2404 2405

            stream_Control( p_stream, STREAM_GET_PTS_DELAY, &i_pts_delay );
2406 2407
        }

2408 2409
        in->p_demux = demux_New( p_input, p_input, psz_access, psz_demux,
                   /* Take access/stream redirections into account: */
2410 2411
                            p_stream->psz_path ? p_stream->psz_path : psz_path,
                                 p_stream, p_input->p->p_es_out,
2412
                                 p_input->b_preparsing );
2413

2414 2415
        if( in->p_demux == NULL )
        {
2416 2417 2418 2419
            if( vlc_object_alive( p_input ) )
            {
                msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
                         psz_access, psz_demux, psz_path );
2420 2421 2422 2423 2424
                if( !b_in_can_fail )
                    dialog_Fatal( VLC_OBJECT( p_input ),
                                  _("VLC can't recognize the input's format"),
                                  _("The format of '%s' cannot be detected. "
                                    "Have a look at the log for details."), psz_mrl );
2425
            }
Tristan Matthews's avatar
Tristan Matthews committed
2426
            stream_Delete( p_stream );
2427 2428
            goto error;
        }
2429
        assert( in->p_demux->pf_demux != NULL );
2430

2431
        /* Get title from demux */
2432
        if( !p_input->b_preparsing && in->i_title <= 0 )
2433
        {
2434
            if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2435 2436
                                &in->title, &in->i_title,
                                &in->i_title_offset, &in->i_seekpoint_offset ))
2437
            {
2438
                TAB_INIT( in->i_title, in->title );
2439 2440 2441
            }
            else
            {
2442
                in->b_title_demux = true;
2443 2444
            }
        }
2445 2446
    }

2447
    free( psz_var_demux );
2448 2449
    free( psz_dup );

2450 2451 2452 2453
    /* Set record capabilities */
    if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
        in->b_can_stream_record = false;
#ifdef ENABLE_SOUT
2454
    if( !var_GetBool( p_input, "input-record-native" ) )
2455 2456 2457 2458 2459 2460
        in->b_can_stream_record = false;
    var_SetBool( p_input, "can-record", true );
#else
    var_SetBool( p_input, "can-record", in->b_can_stream_record );
#endif

2461 2462
    /* get attachment
     * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2463
    if( !p_input->b_preparsing )
2464 2465 2466
    {
        int i_attachment;
        input_attachment_t **attachment;
2467
        if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS,
2468
                             &attachment, &i_attachment ) )
2469
        {
2470
            vlc_mutex_lock( &p_input->p->p_item->lock );
2471 2472
            AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment,
                              i_attachment, attachment );
2473
            vlc_mutex_unlock( &p_input->p->p_item->lock );
2474
        }
2475 2476 2477 2478 2479 2480

        /* PTS delay: request from demux first. This is required for
         * access_demux and some special cases like SDP demux. Otherwise,
         * fallback to access */
        if( demux_Control( in->p_demux, DEMUX_GET_PTS_DELAY,
                           &in->i_pts_delay ) )
2481
            in->i_pts_delay = i_pts_delay;
2482 2483 2484 2485
        if( in->i_pts_delay > INPUT_PTS_DELAY_MAX )
            in->i_pts_delay = INPUT_PTS_DELAY_MAX;
        else if( in->i_pts_delay < 0 )
            in->i_pts_delay = 0;
2486
    }
2487

2488
    if( !demux_Control( in->p_demux, DEMUX_GET_FPS, &f_fps ) && f_fps > 0.0 )
2489
    {
2490
        vlc_mutex_lock( &p_input->p->p_item->lock );
2491
        p_input->p->f_fps = f_fps;
2492
        vlc_mutex_unlock( &p_input->p->p_item->lock );
2493
    }
2494 2495 2496 2497

    if( var_GetInteger( p_input, "clock-synchro" ) != -1 )
        in->b_can_pace_control = !var_GetInteger( p_input, "clock-synchro" );

2498 2499 2500 2501
    return VLC_SUCCESS;

error:
    if( in->p_demux )
2502
        demux_Delete( in->p_demux );
2503

2504
    free( psz_var_demux );
2505
    free( psz_dup );
2506 2507 2508 2509 2510 2511 2512

    return VLC_EGENERIC;
}

/*****************************************************************************
 * InputSourceClean:
 *****************************************************************************/
2513
static void InputSourceClean( input_source_t *in )
2514
{
2515 2516
    int i;

2517
    if( in->p_demux )
2518
        demux_Delete( in->p_demux );
2519 2520 2521 2522 2523

    if( in->i_title > 0 )
    {
        for( i = 0; i < in->i_title; i++ )
            vlc_input_title_Delete( in->title[i] );
2524
        TAB_CLEAN( in->i_title, in->title );
2525 2526 2527
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538
/*****************************************************************************
 * InputSourceMeta:
 *****************************************************************************/
static void InputSourceMeta( input_thread_t *p_input,
                             input_source_t *p_source, vlc_meta_t *p_meta )
{
    demux_t *p_demux = p_source->p_demux;

    /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
     * is a bad idea */

2539
    bool has_meta = false;
2540

Laurent Aimar's avatar
Laurent Aimar committed
2541
    /* Read demux meta */
2542 2543
    if( !demux_Control( p_demux, DEMUX_GET_META, p_meta ) )
        has_meta = true;
Laurent Aimar's avatar
Laurent Aimar committed
2544

2545 2546 2547 2548 2549 2550 2551
    bool has_unsupported;
    if( demux_Control( p_demux, DEMUX_HAS_UNSUPPORTED_META, &has_unsupported ) )
        has_unsupported = true;

    /* If the demux report unsupported meta data, or if we don't have meta data
     * try an external "meta reader" */
    if( has_meta && !has_unsupported )
Laurent Aimar's avatar
Laurent Aimar committed
2552 2553
        return;

2554
    demux_meta_t *p_demux_meta =
2555
        vlc_custom_create( p_demux, sizeof( *p_demux_meta ), "demux meta" );
Laurent Aimar's avatar
Laurent Aimar committed
2556 2557
    if( !p_demux_meta )
        return;
2558
    p_demux_meta->p_demux = p_demux;
2559
    p_demux_meta->p_item = p_input->p->p_item;
Laurent Aimar's avatar
Laurent Aimar committed
2560

2561
    module_t *p_id3 = module_need( p_demux_meta, "meta reader", NULL, false );
Laurent Aimar's avatar
Laurent Aimar committed
2562 2563 2564 2565 2566 2567 2568 2569 2570 2571
    if( p_id3 )
    {
        if( p_demux_meta->p_meta )
        {
            vlc_meta_Merge( p_meta, p_demux_meta->p_meta );
            vlc_meta_Delete( p_demux_meta->p_meta );
        }

        if( p_demux_meta->i_attachments > 0 )
        {
2572
            vlc_mutex_lock( &p_input->p->p_item->lock );
Laurent Aimar's avatar
Laurent Aimar committed
2573 2574
            AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment,
                              p_demux_meta->i_attachments, p_demux_meta->attachments );
2575
            vlc_mutex_unlock( &p_input->p->p_item->lock );
Laurent Aimar's avatar
Laurent Aimar committed
2576 2577 2578
        }
        module_unneed( p_demux, p_id3 );
    }
2579
    vlc_object_release( p_demux_meta );
Laurent Aimar's avatar
Laurent Aimar committed
2580 2581 2582
}


2583
static void SlaveDemux( input_thread_t *p_input, bool *pb_demux_polled )
2584 2585 2586
{
    int64_t i_time;
    int i;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2587

2588
    *pb_demux_polled = false;
2589
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) )
2590
    {
2591 2592
        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
        return;
2593 2594
    }

Clément Stenac's avatar
Clément Stenac committed
2595
    for( i = 0; i < p_input->p->i_slave; i++ )
2596
    {
Clément Stenac's avatar
Clément Stenac committed
2597
        input_source_t *in = p_input->p->slave[i];
Laurent Aimar's avatar
Laurent Aimar committed
2598
        int i_ret;
2599 2600 2601 2602

        if( in->b_eof )
            continue;

2603 2604 2605 2606 2607 2608 2609
        const bool b_demux_polled = in->p_demux->pf_demux != NULL;
        if( !b_demux_polled )
            continue;

        *pb_demux_polled = true;

        /* Call demux_Demux until we have read enough data */
2610
        if( demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
2611 2612 2613 2614
        {
            for( ;; )
            {
                int64_t i_stime;
2615
                if( demux_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
2616
                {
Gildas Bazin's avatar
Gildas Bazin committed
2617 2618
                    msg_Err( p_input, "slave[%d] doesn't like "
                             "DEMUX_GET_TIME -> EOF", i );
2619 2620 2621
                    i_ret = 0;
                    break;
                }
2622

2623
                if( i_stime >= i_time )
Laurent Aimar's avatar
Laurent Aimar committed
2624 2625
                {
                    i_ret = 1;
2626
                    break;
Laurent Aimar's avatar
Laurent Aimar committed
2627
                }
2628

Laurent Aimar's avatar
Laurent Aimar committed
2629
                if( ( i_ret = demux_Demux( in->p_demux ) ) <= 0 )
2630 2631 2632 2633 2634
                    break;
            }
        }
        else
        {
Laurent Aimar's avatar
Laurent Aimar committed
2635
            i_ret = demux_Demux( in->p_demux );
2636 2637 2638 2639 2640
        }

        if( i_ret <= 0 )
        {
            msg_Dbg( p_input, "slave %d EOF", i );
2641
            in->b_eof = true;
2642 2643 2644 2645 2646 2647 2648 2649 2650
        }
    }
}

static void SlaveSeek( input_thread_t *p_input )
{
    int64_t i_time;
    int i;

2651
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) )
2652 2653 2654 2655 2656
    {
        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
        return;
    }

Clément Stenac's avatar
Clément Stenac committed
2657
    for( i = 0; i < p_input->p->i_slave; i++ )
2658
    {
Clément Stenac's avatar
Clément Stenac committed
2659
        input_source_t *in = p_input->p->slave[i];
2660

2661
        if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time, true ) )
2662
        {
2663 2664
            if( !in->b_eof )
                msg_Err( p_input, "seek failed for slave %d -> EOF", i );
2665
            in->b_eof = true;
2666
        }
2667 2668 2669 2670
        else
        {
            in->b_eof = false;
        }
2671 2672
    }
}
2673

2674 2675 2676
/*****************************************************************************
 * InputMetaUser:
 *****************************************************************************/
2677
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta )
2678
{
Laurent Aimar's avatar
Laurent Aimar committed
2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689
    static const struct { int i_meta; const char *psz_name; } p_list[] = {
        { vlc_meta_Title,       "meta-title" },
        { vlc_meta_Artist,      "meta-artist" },
        { vlc_meta_Genre,       "meta-genre" },
        { vlc_meta_Copyright,   "meta-copyright" },
        { vlc_meta_Description, "meta-description" },
        { vlc_meta_Date,        "meta-date" },
        { vlc_meta_URL,         "meta-url" },
        { 0, NULL }
    };

2690
    /* Get meta information from user */
Laurent Aimar's avatar
Laurent Aimar committed
2691 2692 2693 2694 2695 2696 2697 2698 2699 2700
    for( int i = 0; p_list[i].psz_name; i++ )
    {
        char *psz_string = var_GetNonEmptyString( p_input, p_list[i].psz_name );
        if( !psz_string )
            continue;

        EnsureUTF8( psz_string );
        vlc_meta_Set( p_meta, p_list[i].i_meta, psz_string );
        free( psz_string );
    }
2701
}
2702

2703 2704 2705 2706 2707 2708 2709
static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
                              int i_new, input_attachment_t **pp_new )
{
    int i_attachment = *pi_attachment;
    input_attachment_t **attachment = *ppp_attachment;
    int i;

2710
    attachment = xrealloc( attachment,
2711
                    sizeof(*attachment) * ( i_attachment + i_new ) );
2712 2713
    for( i = 0; i < i_new; i++ )
        attachment[i_attachment++] = pp_new[i];
2714
    free( pp_new );
2715 2716 2717 2718 2719

    /* */
    *pi_attachment = i_attachment;
    *ppp_attachment = attachment;
}
2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749

/*****************************************************************************
 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
 * arturl and locking issue.
 *****************************************************************************/
static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta )
{
    /* If metadata changed, then the attachments might have changed.
       We need to update them in case they contain album art. */
    input_source_t *in = &p_input->p->input;
    int i_attachment;
    input_attachment_t **attachment;
    if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS,
                         &attachment, &i_attachment ) )
    {
        vlc_mutex_lock( &p_input->p->p_item->lock );
        if( p_input->p->i_attachment > 0 )
        {
            for( int i = 0; i < p_input->p->i_attachment; i++ )
                vlc_input_attachment_Delete( p_input->p->attachment[i] );
            TAB_CLEAN( p_input->p->i_attachment, p_input->p->attachment );
        }
        AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment,
                          i_attachment, attachment );
        vlc_mutex_unlock( &p_input->p->p_item->lock );
    }
    es_out_ControlSetMeta( p_input->p->p_es_out, p_meta );
    vlc_meta_Delete( p_meta );
}

Laurent Aimar's avatar
Laurent Aimar committed
2750 2751 2752 2753
/*****************************************************************************
 * InputGetExtraFiles
 *  Autodetect extra input list
 *****************************************************************************/
2754 2755 2756 2757 2758 2759
static void InputGetExtraFilesPattern( input_thread_t *p_input,
                                       int *pi_list, char ***pppsz_list,
                                       const char *psz_path,
                                       const char *psz_match,
                                       const char *psz_format,
                                       int i_start, int i_stop )
Laurent Aimar's avatar
Laurent Aimar committed
2760
{
2761 2762 2763 2764
    int i_list;
    char **ppsz_list;

    TAB_INIT( i_list, ppsz_list );
Laurent Aimar's avatar
Laurent Aimar committed
2765

2766 2767
    char *psz_base = strdup( psz_path );
    if( !psz_base )
2768
        goto exit;
Laurent Aimar's avatar
Laurent Aimar committed
2769

2770 2771 2772 2773
    /* Remove the extension */
    char *psz_end = &psz_base[strlen(psz_base)-strlen(psz_match)];
    assert( psz_end >= psz_base);
    *psz_end = '\0';
Laurent Aimar's avatar
Laurent Aimar committed
2774

2775 2776
    /* Try to list files */
    for( int i = i_start; i <= i_stop; i++ )
Laurent Aimar's avatar
Laurent Aimar committed
2777 2778
    {
        struct stat st;
2779
        char *psz_file;
Laurent Aimar's avatar
Laurent Aimar committed
2780

2781 2782
        if( asprintf( &psz_file, psz_format, psz_base, i ) < 0 )
            break;
Laurent Aimar's avatar
Laurent Aimar committed
2783

2784 2785 2786
        char *psz_tmp_path = get_path( psz_file );

        if( vlc_stat( psz_tmp_path, &st ) || !S_ISREG( st.st_mode ) || !st.st_size )
2787 2788
        {
            free( psz_file );
2789
            free( psz_tmp_path );
2790 2791
            break;
        }
Laurent Aimar's avatar
Laurent Aimar committed
2792 2793

        msg_Dbg( p_input, "Detected extra file `%s'", psz_file );
2794
        TAB_APPEND( i_list, ppsz_list, psz_file );
2795
        free( psz_tmp_path );
Laurent Aimar's avatar
Laurent Aimar committed
2796
    }
2797
    free( psz_base );
2798 2799 2800
exit:
    *pi_list = i_list;
    *pppsz_list = ppsz_list;
Laurent Aimar's avatar
Laurent Aimar committed
2801 2802
}

2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841
static void InputGetExtraFiles( input_thread_t *p_input,
                                int *pi_list, char ***pppsz_list,
                                const char *psz_access, const char *psz_path )
{
    static const struct
    {
        const char *psz_match;
        const char *psz_format;
        int i_start;
        int i_stop;
    } p_pattern[] = {
        /* XXX the order is important */
        { ".001",         "%s.%.3d",        2, 999 },
        { NULL, NULL, 0, 0 }
    };

    TAB_INIT( *pi_list, *pppsz_list );

    if( ( psz_access && *psz_access && strcmp( psz_access, "file" ) ) || !psz_path )
        return;

    const size_t i_path = strlen(psz_path);

    for( int i = 0; p_pattern[i].psz_match != NULL; i++ )
    {
        const size_t i_ext = strlen(p_pattern[i].psz_match );

        if( i_path < i_ext )
            continue;
        if( !strcmp( &psz_path[i_path-i_ext], p_pattern[i].psz_match ) )
        {
            InputGetExtraFilesPattern( p_input, pi_list, pppsz_list,
                                       psz_path,
                                       p_pattern[i].psz_match, p_pattern[i].psz_format,
                                       p_pattern[i].i_start, p_pattern[i].i_stop );
            return;
        }
    }
}
Laurent Aimar's avatar
Laurent Aimar committed
2842 2843

/* */
2844 2845
static void input_ChangeState( input_thread_t *p_input, int i_state )
{
Laurent Aimar's avatar
Laurent Aimar committed
2846
    const bool b_changed = p_input->p->i_state != i_state;
2847

Laurent Aimar's avatar
Laurent Aimar committed
2848
    p_input->p->i_state = i_state;
2849 2850 2851 2852 2853 2854
    if( i_state == ERROR_S )
        p_input->b_error = true;
    else if( i_state == END_S )
        p_input->b_eof = true;

    if( b_changed )
2855
    {
2856
        input_item_SetErrorWhenReading( p_input->p->p_item, p_input->b_error );
2857
        input_SendEventState( p_input, i_state );
2858
    }
2859 2860
}

2861

2862 2863 2864 2865
/*****************************************************************************
 * MRLSplit: parse the access, demux and url part of the
 *           Media Resource Locator.
 *****************************************************************************/
2866 2867
void input_SplitMRL( const char **access, const char **demux,
                     const char **path, const char **anchor, char *buf )
2868
{
2869
    char *p;
2870

2871
    /* Separate <path> from <access>[/<demux>]:// */
2872
    p = strstr( buf, "://" );
2873
    if( p != NULL )
2874
    {
2875 2876
        *p = '\0';
        p += 3; /* skips "://" */
2877
        *path = p;
2878 2879 2880

        /* Remove HTML anchor if present (not supported).
         * The hash symbol itself should be URI-encoded. */
2881
        p = strchr( p, '#' );
2882 2883 2884 2885 2886 2887 2888
        if( p != NULL )
        {
            *(p++) = '\0';
            *anchor = p;
        }
        else
            *anchor = "";
2889 2890 2891
    }
    else
    {
2892
#ifndef NDEBUG
2893
        fprintf( stderr, "%s(\"%s\") probably not a valid URI!\n", __func__,
2894
                 buf );
2895
#endif
2896
        /* Note: this is a valid non const pointer to "": */
2897
        *path = buf + strlen( buf );
2898 2899 2900
    }

    /* Separate access from demux */
2901
    p = strchr( buf, '/' );
2902 2903 2904 2905 2906
    if( p != NULL )
    {
        *(p++) = '\0';
        if( p[0] == '$' )
            p++;
2907
        *demux = p;
2908
    }
2909
    else
2910
        *demux = "";
2911

2912
    /* We really don't want module name substitution here! */
2913
    p = buf;
2914 2915
    if( p[0] == '$' )
        p++;
2916
    *access = p;
2917
}
2918

2919
static const char *MRLSeekPoint( const char *str, int *title, int *chapter )
2920 2921
{
    char *end;
2922 2923 2924 2925 2926 2927 2928 2929 2930
    unsigned long u;

    /* Look for the title */
    u = strtoul( str, &end, 0 );
    *title = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
    str = end;

    /* Look for the chapter */
    if( *str == ':' )
2931
    {
2932 2933 2934 2935
        str++;
        u = strtoul( str, &end, 0 );
        *chapter = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
        str = end;
2936
    }
2937 2938 2939 2940
    else
        *chapter = -1;

    return str;
2941 2942
}

2943

2944 2945 2946 2947
/*****************************************************************************
 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
 *
 * Syntax:
2948
 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
2949
 *****************************************************************************/
2950
static void MRLSections( const char *p,
2951 2952 2953
                         int *pi_title_start, int *pi_title_end,
                         int *pi_chapter_start, int *pi_chapter_end )
{
2954
    *pi_title_start = *pi_title_end = *pi_chapter_start = *pi_chapter_end = -1;
2955

2956
    int title_start, chapter_start, title_end, chapter_end;
2957

2958 2959 2960
    if( !p )
        return;

2961 2962 2963 2964
    if( *p != '-' )
        p = MRLSeekPoint( p, &title_start, &chapter_start );
    else
        title_start = chapter_start = -1;
2965

2966 2967 2968 2969
    if( *p == '-' )
        p = MRLSeekPoint( p + 1, &title_end, &chapter_end );
    else
        title_end = chapter_end = -1;
2970

2971 2972
    if( *p ) /* syntax error */
        return;
2973

2974 2975 2976 2977
    *pi_title_start = title_start;
    *pi_title_end = title_end;
    *pi_chapter_start = chapter_start;
    *pi_chapter_end = chapter_end;
2978
}
2979 2980

/*****************************************************************************
2981
 * input_AddSubtitles: add a subtitle file and enable it
2982
 *****************************************************************************/
2983 2984
static void input_SubtitleAdd( input_thread_t *p_input,
                               const char *url, unsigned i_flags )
2985
{
2986 2987 2988 2989
    input_source_t *sub = InputSourceNew( p_input );
    if( sub == NULL )
        return;

2990
    vlc_value_t count;
2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005

    var_Change( p_input, "spu-es", VLC_VAR_CHOICESCOUNT, &count, NULL );

    if( InputSourceInit( p_input, sub, url, "subtitle",
                         (i_flags & SUB_CANFAIL) ) )
    {
        free( sub );
        return;
    }
    TAB_APPEND( p_input->p->i_slave, p_input->p->slave, sub );

    if( !(i_flags & SUB_FORCED) )
        return;

    /* Select the ES */
3006 3007
    vlc_value_t list;

3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026
    if( var_Change( p_input, "spu-es", VLC_VAR_GETLIST, &list, NULL ) )
        return;
    if( count.i_int == 0 )
        count.i_int++;
    /* if it was first one, there is disable too */

    if( count.i_int < list.p_list->i_count )
    {
        const int i_id = list.p_list->p_values[count.i_int].i_int;

        es_out_Control( p_input->p->p_es_out_display, ES_OUT_SET_ES_DEFAULT_BY_ID, i_id );
        es_out_Control( p_input->p->p_es_out_display, ES_OUT_SET_ES_BY_ID, i_id );
    }
    var_FreeList( &list, NULL );
}

static void input_SubtitleFileAdd( input_thread_t *p_input, char *psz_subtitle,
                                   unsigned i_flags )
{
3027 3028
    /* if we are provided a subtitle.sub file,
     * see if we don't have a subtitle.idx and use it instead */
3029 3030
    char *psz_path = strdup( psz_subtitle );
    if( likely(psz_path != NULL) )
3031
    {
3032
        char *psz_extension = strrchr( psz_path, '.');
3033 3034
        if( psz_extension && strcmp( psz_extension, ".sub" ) == 0 )
        {
3035
            struct stat st;
3036 3037

            strcpy( psz_extension, ".idx" );
3038

3039
            if( !vlc_stat( psz_path, &st ) && S_ISREG( st.st_mode ) )
3040
            {
3041
                msg_Dbg( p_input, "using %s as subtitle file instead of %s",
3042
                         psz_path, psz_subtitle );
3043
                strcpy( psz_subtitle, psz_path ); /* <- FIXME! constify */
3044 3045 3046 3047 3048
            }
        }
        free( psz_path );
    }

3049
    char *url = vlc_path2uri( psz_subtitle, NULL );
3050
    if( url == NULL )
3051 3052
        return;

3053 3054
    input_SubtitleAdd( p_input, url, i_flags );
    free( url );
3055 3056
}

3057 3058 3059 3060 3061 3062
/*****************************************************************************
 * Statistics
 *****************************************************************************/
void input_UpdateStatistic( input_thread_t *p_input,
                            input_statistic_t i_type, int i_delta )
{
Laurent Aimar's avatar
Laurent Aimar committed
3063
    assert( p_input->p->i_state != INIT_S );
3064 3065 3066 3067

    vlc_mutex_lock( &p_input->p->counters.counters_lock);
    switch( i_type )
    {
3068
#define I(c) stats_Update( p_input->p->counters.c, i_delta, NULL )
3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083
    case INPUT_STATISTIC_DECODED_VIDEO:
        I(p_decoded_video);
        break;
    case INPUT_STATISTIC_DECODED_AUDIO:
        I(p_decoded_audio);
        break;
    case INPUT_STATISTIC_DECODED_SUBTITLE:
        I(p_decoded_sub);
        break;
    case INPUT_STATISTIC_SENT_PACKET:
        I(p_sout_sent_packets);
        break;
#undef I
    case INPUT_STATISTIC_SENT_BYTE:
    {
3084 3085 3086 3087
        uint64_t bytes;

        stats_Update( p_input->p->counters.p_sout_sent_bytes, i_delta, &bytes );
        stats_Update( p_input->p->counters.p_sout_send_bitrate, bytes, NULL );
3088 3089 3090 3091 3092 3093 3094 3095
        break;
    }
    default:
        msg_Err( p_input, "Invalid statistic type %d (internal error)", i_type );
        break;
    }
    vlc_mutex_unlock( &p_input->p->counters.counters_lock);
}
3096 3097 3098

/**/
/* TODO FIXME nearly the same logic that snapshot code */
3099
char *input_CreateFilename( input_thread_t *input, const char *psz_path, const char *psz_prefix, const char *psz_extension )
3100 3101 3102 3103
{
    char *psz_file;
    DIR *path;

3104
    path = vlc_opendir( psz_path );
3105 3106 3107 3108
    if( path )
    {
        closedir( path );

3109
        char *psz_tmp = str_format( input, psz_prefix );
3110 3111 3112
        if( !psz_tmp )
            return NULL;

3113
        filename_sanitize( psz_tmp );
3114

3115 3116
        if( asprintf( &psz_file, "%s"DIR_SEP"%s%s%s",
                      psz_path, psz_tmp,
3117 3118 3119
                      psz_extension ? "." : "",
                      psz_extension ? psz_extension : "" ) < 0 )
            psz_file = NULL;
3120
        free( psz_tmp );
3121 3122 3123 3124
        return psz_file;
    }
    else
    {
3125
        psz_file = str_format( input, psz_path );
3126 3127 3128 3129
        path_sanitize( psz_file );
        return psz_file;
    }
}