input.c 104 KB
Newer Older
1 2
/*****************************************************************************
 * input.c: input thread
3
 *****************************************************************************
4
 * Copyright (C) 1998-2007 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8
 *          Laurent Aimar <fenrir@via.ecp.fr>
9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
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
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
19
 *
20 21
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * 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

34
#include <ctype.h>
Christophe Mutricy's avatar
Christophe Mutricy committed
35
#include <limits.h>
36
#include <assert.h>
37

38
#include "input_internal.h"
39
#include "event.h"
40
#include "es_out.h"
41
#include "es_out_timeshift.h"
42
#include "access.h"
43
#include "demux.h"
44
#include "stream.h"
45

Clément Stenac's avatar
Clément Stenac committed
46 47
#include <vlc_sout.h>
#include "../stream_output/stream_output.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
48

Clément Stenac's avatar
Clément Stenac committed
49 50 51
#include <vlc_interface.h>
#include <vlc_url.h>
#include <vlc_charset.h>
52
#include <vlc_strings.h>
53

54 55 56 57
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif

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

63 64
static  void *Run            ( vlc_object_t *p_this );
static  void *RunAndDestroy  ( vlc_object_t *p_this );
65

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
66
static input_thread_t * Create  ( vlc_object_t *, input_item_t *,
67
                                  const char *, bool, sout_instance_t * );
68
static  int             Init    ( input_thread_t *p_input );
69
static void             WaitDie   ( input_thread_t *p_input );
70 71
static void             End     ( input_thread_t *p_input );
static void             MainLoop( input_thread_t *p_input );
72

73
static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t *, mtime_t i_deadline );
74
static void       ControlReduce( input_thread_t * );
75
static bool Control( input_thread_t *, int, vlc_value_t );
76

77 78
static int  UpdateFromAccess( input_thread_t * );
static int  UpdateFromDemux( input_thread_t * );
Michel Kaempf's avatar
Michel Kaempf committed
79

80
static void MRLSections( input_thread_t *, char *, int *, int *, int *, int *);
81 82

static input_source_t *InputSourceNew( input_thread_t *);
Laurent Aimar's avatar
 
Laurent Aimar committed
83
static int  InputSourceInit( input_thread_t *, input_source_t *,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
84
                             const char *, const char *psz_forced_demux );
85
static void InputSourceClean( input_source_t * );
86 87
/* TODO */
//static void InputGetAttachments( input_thread_t *, input_source_t * );
88 89
static void SlaveDemux( input_thread_t *p_input );
static void SlaveSeek( input_thread_t *p_input );
90

91 92
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 );
93 94
static char *InputGetExtraFiles( input_thread_t *p_input,
                                 const char *psz_access, const char *psz_path );
95

96
static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_demux );
97
static void AccessMeta( input_thread_t * p_input, vlc_meta_t *p_meta );
98 99 100
static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
                              int i_new, input_attachment_t **pp_new );

101 102
static void SubtitleAdd( input_thread_t *p_input, char *psz_subtitle, bool b_forced );

103 104
static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO fix name */

105
/*****************************************************************************
106 107
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
108
 *
Laurent Aimar's avatar
Laurent Aimar committed
109
 * XXX Do not forget to update vlc_input.h if you add new variables.
110
 *****************************************************************************/
111
static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
112
                               const char *psz_header, bool b_quick,
113
                               sout_instance_t *p_sout )
Michel Kaempf's avatar
Michel Kaempf committed
114
{
115
    static const char input_name[] = "input";
116
    input_thread_t *p_input = NULL;                 /* thread descriptor */
117 118
    vlc_value_t val;
    int i;
119

120
    /* Allocate descriptor */
121 122
    p_input = vlc_custom_create( p_parent, sizeof( *p_input ),
                                 VLC_OBJECT_INPUT, input_name );
123
    if( p_input == NULL )
124
        return NULL;
125 126 127 128 129 130

    /* 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 );
131 132 133

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

134 135 136 137 138 139 140
    free( psz_name );

    /* Start a timer to mesure how long it takes
     * to launch an input */
    stats_TimerStart( p_input, psz_timer_name,
        STATS_TIMER_INPUT_LAUNCHING );

141 142 143
    p_input->p = calloc( 1, sizeof( input_thread_private_t ) );
    if( !p_input->p )
        return NULL;
144 145 146

    /* One "randomly" selected input thread is responsible for computing
     * the global stats. Check if there is already someone doing this */
147
    if( p_input->p_libvlc->p_stats && !b_quick )
148
    {
149
        libvlc_priv_t *p_private = libvlc_priv( p_input->p_libvlc );
150
        vlc_mutex_lock( &p_input->p_libvlc->p_stats->lock );
151 152
        if( p_private->p_stats_computer == NULL )
            p_private->p_stats_computer = p_input;
153
        vlc_mutex_unlock( &p_input->p_libvlc->p_stats->lock );
154 155
    }

156
    p_input->b_preparsing = b_quick;
157
    p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
158

159
    /* Init events */
160 161 162
    vlc_event_manager_t * p_em = &p_input->p->event_manager;
    vlc_event_manager_init_with_vlc_object( p_em, p_input );
    vlc_event_manager_register_event_type( p_em, vlc_InputStateChanged );
163
    vlc_event_manager_register_event_type( p_em, vlc_InputSelectedStreamChanged );
164

165
    /* Init Common fields */
166 167
    p_input->b_eof = false;
    p_input->b_can_pace_control = true;
Clément Stenac's avatar
Clément Stenac committed
168
    p_input->p->i_start = 0;
169
    p_input->i_time     = 0;
Clément Stenac's avatar
Clément Stenac committed
170
    p_input->p->i_stop  = 0;
171
    p_input->p->i_run   = 0;
Clément Stenac's avatar
Clément Stenac committed
172
    p_input->p->i_title = 0;
173
    p_input->p->title = NULL;
Clément Stenac's avatar
Clément Stenac committed
174
    p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
175
    p_input->i_state = INIT_S;
176
    p_input->p->i_rate = INPUT_RATE_DEFAULT;
177
    p_input->p->b_recording = false;
178 179
    TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark );
    TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
180 181
    p_input->p->p_es_out_display = NULL;
    p_input->p->p_es_out = NULL;
182
    p_input->p->p_sout   = NULL;
183
    p_input->p->b_out_pace_control = false;
184 185 186
    p_input->i_pts_delay = 0;

    /* Init Input fields */
187
    vlc_gc_incref( p_item ); /* Released in Destructor() */
Clément Stenac's avatar
Clément Stenac committed
188 189 190 191
    p_input->p->input.p_item = p_item;
    p_input->p->input.p_access = NULL;
    p_input->p->input.p_stream = NULL;
    p_input->p->input.p_demux  = NULL;
192
    p_input->p->input.b_title_demux = false;
Clément Stenac's avatar
Clément Stenac committed
193 194 195
    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;
196 197 198 199
    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
200 201
    p_input->p->input.i_cr_average = 0;

202
    vlc_mutex_lock( &p_item->lock );
203 204

    if( !p_item->p_stats )
205 206
        p_item->p_stats = stats_NewInputStats( p_input );
    vlc_mutex_unlock( &p_item->lock );
207

208
    /* No slave */
Clément Stenac's avatar
Clément Stenac committed
209 210
    p_input->p->i_slave = 0;
    p_input->p->slave   = NULL;
211

212
    /* Init control buffer */
213
    vlc_mutex_init( &p_input->p->lock_control );
214
    vlc_cond_init( &p_input->p->wait_control );
Clément Stenac's avatar
Clément Stenac committed
215
    p_input->p->i_control = 0;
216

Gildas Bazin's avatar
 
Gildas Bazin committed
217
    /* Parse input options */
218
    vlc_mutex_lock( &p_item->lock );
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
219
    assert( (int)p_item->optflagc == p_item->i_options );
220
    for( i = 0; i < p_item->i_options; i++ )
221 222
        var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i],
                         !!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) );
223
    vlc_mutex_unlock( &p_item->lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
224

225 226
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
227

228
    /* Create Objects variables for public Get and Set */
229
    input_ControlVarInit( p_input );
230

231
    /* */
Clément Stenac's avatar
Clément Stenac committed
232
    p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
Gildas Bazin's avatar
Gildas Bazin committed
233

234
    if( !p_input->b_preparsing )
Gildas Bazin's avatar
Gildas Bazin committed
235
    {
236 237
        var_Get( p_input, "bookmarks", &val );
        if( val.psz_string )
Gildas Bazin's avatar
Gildas Bazin committed
238
        {
239 240 241 242
            /* FIXME: have a common cfg parsing routine used by sout and others */
            char *psz_parser, *psz_start, *psz_end;
            psz_parser = val.psz_string;
            while( (psz_start = strchr( psz_parser, '{' ) ) )
Gildas Bazin's avatar
Gildas Bazin committed
243
            {
244
                 seekpoint_t *p_seekpoint;
245 246 247 248 249 250 251 252
                 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 = ',';
253 254

                 p_seekpoint = vlc_seekpoint_New();
255 256 257 258 259
                 while( (psz_end = strchr( psz_start, ',' ) ) )
                 {
                     *psz_end = 0;
                     if( !strncmp( psz_start, "name=", 5 ) )
                     {
260
                         p_seekpoint->psz_name = strdup(psz_start + 5);
261 262 263 264 265 266 267
                     }
                     else if( !strncmp( psz_start, "bytes=", 6 ) )
                     {
                         p_seekpoint->i_byte_offset = atoll(psz_start + 6);
                     }
                     else if( !strncmp( psz_start, "time=", 5 ) )
                     {
268 269
                         p_seekpoint->i_time_offset = atoll(psz_start + 5) *
                                                        1000000;
270 271
                     }
                     psz_start = psz_end + 1;
Gildas Bazin's avatar
Gildas Bazin committed
272
                }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
273
                msg_Dbg( p_input, "adding bookmark: %s, bytes=%"PRId64", time=%"PRId64,
274 275 276 277 278
                                  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
279
            }
280
            free( val.psz_string );
Gildas Bazin's avatar
Gildas Bazin committed
281 282 283
        }
    }

284
    /* Remove 'Now playing' info as it is probably outdated */
285
    input_item_SetNowPlaying( p_item, NULL );
286
    input_SendEventMeta( p_input );
287

288 289 290 291
    /* */
    if( p_input->b_preparsing )
        p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;

292 293 294 295
    /* */
    if( p_sout )
        p_input->p->p_sout = p_sout;

296
    memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
297
    vlc_mutex_init( &p_input->p->counters.counters_lock );
298

299 300 301
    /* Set the destructor when we are sure we are initialized */
    vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor );

302 303 304
    /* Attach only once we are ready */
    vlc_object_attach( p_input, p_parent );

305 306 307
    return p_input;
}

308 309 310 311
/**
 * Input destructor (called when the object's refcount reaches 0).
 */
static void Destructor( input_thread_t * p_input )
312
{
313 314 315 316 317 318
#ifndef NDEBUG
    char * psz_name = input_item_GetName( p_input->p->input.p_item );
    msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
    free( psz_name );
#endif

319 320
    vlc_event_manager_fini( &p_input->p->event_manager );

321 322
    stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING );
    stats_TimerClean( p_input, STATS_TIMER_INPUT_LAUNCHING );
323
#ifdef ENABLE_SOUT
324 325
    if( p_input->p->p_sout )
        sout_DeleteInstance( p_input->p->p_sout );
326
#endif
327 328
    vlc_gc_decref( p_input->p->input.p_item );

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

331
    vlc_cond_destroy( &p_input->p->wait_control );
332 333
    vlc_mutex_destroy( &p_input->p->lock_control );
    free( p_input->p );
334
}
335

336
/**
337 338
 * Initialize an input thread and run it. You will need to monitor the
 * thread to clean up after it is done
339 340 341 342 343 344 345
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \return a pointer to the spawned input thread
 */
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
                                      input_item_t *p_item )
346
{
347
    return __input_CreateThreadExtended( p_parent, p_item, NULL, NULL );
348 349
}

350 351 352 353
/* */
input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent,
                                              input_item_t *p_item,
                                              const char *psz_log, sout_instance_t *p_sout )
354
{
355
    input_thread_t *p_input;
356

357
    p_input = Create( p_parent, p_item, psz_log, false, p_sout );
358 359 360
    if( !p_input )
        return NULL;

Sam Hocevar's avatar
Sam Hocevar committed
361
    /* Create thread and wait for its readiness. */
362
    if( vlc_thread_create( p_input, "input", Run,
363
                           VLC_THREAD_PRIORITY_INPUT, false ) )
Michel Kaempf's avatar
Michel Kaempf committed
364
    {
365
        input_ChangeState( p_input, ERROR_S );
366
        msg_Err( p_input, "cannot create input thread" );
367 368
        vlc_object_detach( p_input );
        vlc_object_release( p_input );
369
        return NULL;
Michel Kaempf's avatar
Michel Kaempf committed
370
    }
371

372
    return p_input;
Michel Kaempf's avatar
Michel Kaempf committed
373 374
}

375
/**
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
376
 * Initialize an input thread and run it. This thread will clean after itself,
377 378 379 380 381
 * you can forget about it. It can work either in blocking or non-blocking mode
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \param b_block should we block until read is finished ?
382
 * \return an error code, VLC_SUCCESS on success
383
 */
384
int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
385
                   bool b_block )
386
{
387
    input_thread_t *p_input;
388

389
    p_input = Create( p_parent, p_item, NULL, false, NULL );
390
    if( !p_input )
391 392
        return VLC_EGENERIC;

393
    if( b_block )
394
    {
395
        RunAndDestroy( VLC_OBJECT(p_input) );
Clément Stenac's avatar
Clément Stenac committed
396
        return VLC_SUCCESS;
397
    }
398
    else
399
    {
400
        if( vlc_thread_create( p_input, "input", RunAndDestroy,
401
                               VLC_THREAD_PRIORITY_INPUT, false ) )
402
        {
403
            input_ChangeState( p_input, ERROR_S );
404
            msg_Err( p_input, "cannot create input thread" );
405
            vlc_object_release( p_input );
Clément Stenac's avatar
Clément Stenac committed
406
            return VLC_EGENERIC;
407
        }
408
    }
409
    return VLC_SUCCESS;
410
}
411

412 413 414 415 416 417 418 419 420 421
/**
 * Initialize an input and initialize it to preparse the item
 * This function is blocking. It will only accept to parse 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 )
{
422
    input_thread_t *p_input;
423

424
    /* Allocate descriptor */
425
    p_input = Create( p_parent, p_item, NULL, true, NULL );
426 427 428
    if( !p_input )
        return VLC_EGENERIC;

429 430
    if( !Init( p_input ) )
        End( p_input );
431

432 433
    vlc_object_detach( p_input );
    vlc_object_release( p_input );
434 435 436 437

    return VLC_SUCCESS;
}

438 439 440 441 442
/**
 * Request a running input thread to stop and die
 *
 * \param the input thread to stop
 */
443
static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
Michel Kaempf's avatar
Michel Kaempf committed
444
{
445 446
    vlc_list_t *p_list;
    int i;
447 448 449 450 451 452

    if( p_obj->i_object_type == VLC_OBJECT_VOUT ||
        p_obj->i_object_type == VLC_OBJECT_AOUT ||
        p_obj == VLC_OBJECT(p_input->p->p_sout) )
        return;

453
    vlc_object_kill( p_obj );
454

455
    p_list = vlc_list_children( p_obj );
456
    for( i = 0; i < p_list->i_count; i++ )
457
        ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
458
    vlc_list_release( p_list );
459 460 461 462 463 464
}
void input_StopThread( input_thread_t *p_input )
{
    /* 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 */
465
    ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
Michel Kaempf's avatar
Michel Kaempf committed
466

467
    input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
Sam Hocevar's avatar
 
Sam Hocevar committed
468 469
}

470
sout_instance_t *input_DetachSout( input_thread_t *p_input )
471
{
472
    assert( p_input->b_dead );
473 474 475 476
    sout_instance_t *p_sout = p_input->p->p_sout;
    vlc_object_detach( p_sout );
    p_input->p->p_sout = NULL;
    return p_sout;
477 478
}

479
/*****************************************************************************
480
 * Run: main thread loop
481 482
 * This is the "normal" thread that spawns the input processing chain,
 * reads the stream, cleans up and waits
483
 *****************************************************************************/
484
static void *Run( vlc_object_t *p_this )
Michel Kaempf's avatar
Michel Kaempf committed
485
{
486
    input_thread_t *p_input = (input_thread_t *)p_this;
487
    const int canc = vlc_savecancel();
488

489
    if( Init( p_input ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
490 491
    {
        /* If we failed, wait before we are killed, and exit */
492
        WaitDie( p_input );
493
        goto exit;
Sam Hocevar's avatar
 
Sam Hocevar committed
494
    }
Michel Kaempf's avatar
Michel Kaempf committed
495

496 497
    MainLoop( p_input );

498
    /* Wait until we are asked to die */
499
    if( !p_input->b_die )
500
        WaitDie( p_input );
501 502 503

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

exit:
    p_input->b_dead = true;
507
    vlc_restorecancel( canc );
508
    return NULL;
509 510 511
}

/*****************************************************************************
512
 * RunAndDestroy: main thread loop
513 514 515
 * This is the "just forget me" thread that spawns the input processing chain,
 * reads the stream, cleans up and releases memory
 *****************************************************************************/
516
static void *RunAndDestroy( vlc_object_t *p_this )
517
{
518
    input_thread_t *p_input = (input_thread_t *)p_this;
519
    const int canc = vlc_savecancel();
520

521
    if( Init( p_input ) )
522
        goto exit;
523 524 525 526 527 528

    MainLoop( p_input );

    /* Clean up */
    End( p_input );

529
exit:
530
    /* Release memory */
531
    vlc_object_release( p_input );
532 533
    vlc_restorecancel( canc );
    return NULL;
534 535 536 537 538
}

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

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

548
    *pb_changed = false;
549

550 551 552 553 554 555 556
    if( ( p_input->p->i_stop > 0 && p_input->i_time >= p_input->p->i_stop ) ||
        ( p_input->p->i_run > 0 && *pi_start_mdate+p_input->p->i_run < mdate() ) )
        i_ret = 0; /* EOF */
    else
        i_ret = p_input->p->input.p_demux->pf_demux(p_input->p->input.p_demux);

    if( i_ret > 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
557
    {
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
        /* TODO */
        if( p_input->p->input.b_title_demux &&
            p_input->p->input.p_demux->info.i_update )
        {
            i_ret = UpdateFromDemux( p_input );
            *pb_changed = true;
        }
        else if( !p_input->p->input.b_title_demux &&
                  p_input->p->input.p_access &&
                  p_input->p->input.p_access->info.i_update )
        {
            i_ret = UpdateFromAccess( p_input );
            *pb_changed = true;
        }
    }
573

574 575 576 577 578 579
    if( i_ret == 0 )    /* EOF */
    {
        vlc_value_t repeat;

        var_Get( p_input, "input-repeat", &repeat );
        if( repeat.i_int == 0 )
580
        {
581 582 583 584 585 586 587 588
            /* End of file - we do not set b_die because only the
             * playlist is allowed to do so. */
            msg_Dbg( p_input, "EOF reached" );
            p_input->p->input.b_eof = true;
        }
        else
        {
            vlc_value_t val;
589

590 591 592
            msg_Dbg( p_input, "repeating the same input (%d)",
                     repeat.i_int );
            if( repeat.i_int > 0 )
593
            {
594 595
                repeat.i_int--;
                var_Set( p_input, "input-repeat", repeat );
596
            }
597

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
            /* 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 )
614
            {
615 616 617
                val.i_time = p_input->p->i_start;
                input_ControlPush( p_input, INPUT_CONTROL_SET_TIME,
                                   &val );
618
            }
619
            else
620
            {
621 622 623
                val.f_float = 0.0;
                input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION,
                                   &val );
624
            }
625

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
            /* */
            *pi_start_mdate = mdate();
        }
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
    {
        SlaveDemux( p_input );
    }
}

/**
 * MainLoopInterface
 * It update the variables used by the interfaces
 */
static void MainLoopInterface( input_thread_t *p_input )
{
647 648 649 650 651
    input_event_times_t ev;

    ev.f_position = 0.0;
    ev.i_time = 0;
    ev.i_length = 0;
652 653

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

658 659 660 661 662 663 664
    if( demux_Control( p_input->p->input.p_demux,
                       DEMUX_GET_TIME, &ev.i_time ) )
        ev.i_time = 0;

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

666
    input_SendEventTimes( p_input, &ev );
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
}

/**
 * MainLoopStatistic
 * It updates the globals statics
 */
static void MainLoopStatistic( input_thread_t *p_input )
{
    stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats );
    /* Are we the thread responsible for computing global stats ? */
    if( libvlc_priv( p_input->p_libvlc )->p_stats_computer == p_input )
    {
        stats_ComputeGlobalStats( p_input->p_libvlc,
                                  p_input->p_libvlc->p_stats );
    }
682
    input_SendEventStatistics( p_input );
683 684 685 686 687 688 689 690 691
}

/**
 * MainLoop
 * The main input loop.
 */
static void MainLoop( input_thread_t *p_input )
{
    mtime_t i_start_mdate = mdate();
692 693
    mtime_t i_intf_update = 0;
    mtime_t i_statistic_update = 0;
694 695 696 697

    /* Start the timer */
    stats_TimerStop( p_input, STATS_TIMER_INPUT_LAUNCHING );

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

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

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

723 724 725 726 727 728 729 730 731 732 733
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
            }
            else if( !p_input->b_eof && !es_out_GetEmpty( p_input->p->p_es_out ) )
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
            else
            {
                break;
            }
734
        }
735 736

        /* */
737 738
        do {
            i_deadline = i_wakeup;
739
            if( b_paused )
740 741 742 743 744 745 746 747
                i_deadline = __MIN( i_intf_update, i_statistic_update );

            /* Handle control */
            vlc_mutex_lock( &p_input->p->lock_control );
            ControlReduce( p_input );
            while( !ControlPopNoLock( p_input, &i_type, &val, i_deadline ) )
            {
                msg_Dbg( p_input, "control type=%d", i_type );
748

749 750 751 752
                if( Control( p_input, i_type, val ) )
                    b_force_update = true;
            }
            vlc_mutex_unlock( &p_input->p->lock_control );
753

754 755 756 757 758 759 760 761 762 763 764 765 766
            /* 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 );
                i_statistic_update = i_current + INT64_C(1000000);
            }
767 768 769 770

            /* Check if i_wakeup is still valid */
            if( i_wakeup != 0 )
            {
771
                mtime_t i_new_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
772 773 774
                if( !i_new_wakeup )
                    i_wakeup = 0;
            }
775
        } while( i_current < i_wakeup );
776
    }
777

778
    if( !p_input->b_error )
779
        input_ChangeState( p_input, END_S );
780 781
}

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

786 787 788
    /* Prepare statistics */
#define INIT_COUNTER( c, type, compute ) p_input->p->counters.p_##c = \
 stats_CounterCreate( p_input, VLC_VAR_##type, STATS_##compute);
789
    if( libvlc_stats( p_input ) )
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    {
        INIT_COUNTER( read_bytes, INTEGER, COUNTER );
        INIT_COUNTER( read_packets, INTEGER, COUNTER );
        INIT_COUNTER( demux_read, INTEGER, COUNTER );
        INIT_COUNTER( input_bitrate, FLOAT, DERIVATIVE );
        INIT_COUNTER( demux_bitrate, FLOAT, DERIVATIVE );
        INIT_COUNTER( played_abuffers, INTEGER, COUNTER );
        INIT_COUNTER( lost_abuffers, INTEGER, COUNTER );
        INIT_COUNTER( displayed_pictures, INTEGER, COUNTER );
        INIT_COUNTER( lost_pictures, INTEGER, COUNTER );
        INIT_COUNTER( decoded_audio, INTEGER, COUNTER );
        INIT_COUNTER( decoded_video, INTEGER, COUNTER );
        INIT_COUNTER( decoded_sub, INTEGER, COUNTER );
        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;
        if( p_input->p->counters.p_demux_bitrate )
            p_input->p->counters.p_demux_bitrate->update_interval = 1000000;
        if( p_input->p->counters.p_input_bitrate )
            p_input->p->counters.p_input_bitrate->update_interval = 1000000;
810
    }
811
}
812

813
#ifdef ENABLE_SOUT
814 815 816 817 818 819 820 821 822
static int InitSout( input_thread_t * p_input )
{
    char *psz;

    if( p_input->b_preparsing ) return VLC_SUCCESS;

    /* Find a usable sout and attach it to p_input */
    psz = var_GetNonEmptyString( p_input, "sout" );
    if( psz && strncasecmp( p_input->p->input.p_item->psz_uri, "vlc:", 4 ) )
823
    {
824 825
        /* Check the validity of the provided sout */
        if( p_input->p->p_sout )
Clément Stenac's avatar
Clément Stenac committed
826
        {
827
            if( strcmp( p_input->p->p_sout->psz_sout, psz ) )
828
            {
829
                msg_Dbg( p_input, "destroying unusable sout" );
830

831 832
                sout_DeleteInstance( p_input->p->p_sout );
                p_input->p->p_sout = NULL;
833
            }
834
        }
835

836 837 838 839 840 841 842 843 844 845 846 847 848
        if( p_input->p->p_sout )
        {
            /* Reuse it */
            msg_Dbg( p_input, "sout keep: reusing sout" );
            msg_Dbg( p_input, "sout keep: you probably want to use "
                              "gather stream_out" );
            vlc_object_attach( p_input->p->p_sout, p_input );
        }
        else
        {
            /* Create a new one */
            p_input->p->p_sout = sout_NewInstance( p_input, psz );
            if( !p_input->p->p_sout )
Clément Stenac's avatar
Clément Stenac committed
849
            {
850 851 852 853 854
                input_ChangeState( p_input, ERROR_S );
                msg_Err( p_input, "cannot start stream output instance, " \
                                  "aborting" );
                free( psz );
                return VLC_EGENERIC;
Clément Stenac's avatar
Clément Stenac committed
855
            }
856
        }
857
        if( libvlc_stats( p_input ) )
858
        {
859
            INIT_COUNTER( sout_sent_packets, INTEGER, COUNTER );
860
            INIT_COUNTER( sout_sent_bytes, INTEGER, COUNTER );
861 862 863 864
            INIT_COUNTER( sout_send_bitrate, FLOAT, DERIVATIVE );
            if( p_input->p->counters.p_sout_send_bitrate )
                 p_input->p->counters.p_sout_send_bitrate->update_interval =
                         1000000;
865
        }
866
    }
867
    else if( p_input->p->p_sout )
868
    {
869 870 871 872
        msg_Dbg( p_input, "destroying useless sout" );

        sout_DeleteInstance( p_input->p->p_sout );
        p_input->p->p_sout = NULL;
873
    }
874
    free( psz );
875

876 877
    return VLC_SUCCESS;
}
878
#endif
879

880 881 882
static void InitTitle( input_thread_t * p_input )
{
    vlc_value_t val;
883

884
    if( p_input->b_preparsing ) return;
885

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
    /* Create global title (from master) */
    p_input->p->i_title = p_input->p->input.i_title;
    p_input->p->title   = p_input->p->input.title;
    p_input->p->i_title_offset = p_input->p->input.i_title_offset;
    p_input->p->i_seekpoint_offset = p_input->p->input.i_seekpoint_offset;
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
        input_ControlVarTitle( p_input, 0 );
    }

    /* Global flag */
    p_input->b_can_pace_control = p_input->p->input.b_can_pace_control;
    p_input->p->b_can_pause        = p_input->p->input.b_can_pause;
    p_input->p->b_can_rate_control = p_input->p->input.b_can_rate_control;

    /* Fix pts delay */
    if( p_input->i_pts_delay < 0 )
        p_input->i_pts_delay = 0;

    /* If the desynchronisation requested by the user is < 0, we need to
     * cache more data. */
    var_Get( p_input, "audio-desync", &val );
910 911
    if( val.i_int < 0 )
        p_input->i_pts_delay -= (val.i_int * 1000);
912 913 914 915 916 917

    /* Update cr_average depending on the caching */
    p_input->p->input.i_cr_average *= (10 * p_input->i_pts_delay / 200000);
    p_input->p->input.i_cr_average /= 10;
    if( p_input->p->input.i_cr_average < 10 ) p_input->p->input.i_cr_average = 10;
}
Gildas Bazin's avatar
 
Gildas Bazin committed
918

919 920 921 922
static void StartTitle( input_thread_t * p_input )
{
    double f_fps;
    vlc_value_t val;
923
    int i_delay;
924 925
    char *psz;
    char *psz_subtitle;
926
    int64_t i_length;
927

928 929 930
    /* Start title/chapter */

    if( p_input->b_preparsing )
931
    {
932 933
        p_input->p->i_start = 0;
        return;
934
    }
935 936 937 938 939 940 941 942 943 944 945 946

    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 );
    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 );

    /* Start time*/
    /* Set start time */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
947 948 949
    p_input->p->i_start = INT64_C(1000000) * var_GetInteger( p_input, "start-time" );
    p_input->p->i_stop  = INT64_C(1000000) * var_GetInteger( p_input, "stop-time" );
    p_input->p->i_run   = INT64_C(1000000) * var_GetInteger( p_input, "run-time" );
950
    i_length = var_GetTime( p_input, "length" );
951
    if( p_input->p->i_run < 0 )
952
    {
953 954
        msg_Warn( p_input, "invalid run-time ignored" );
        p_input->p->i_run = 0;
Rafaël Carré's avatar
Rafaël Carré committed
955
    }
956

957
    if( p_input->p->i_start > 0 )
958
    {
959
        if( p_input->p->i_start >= i_length )
960
        {
961
            msg_Warn( p_input, "invalid start-time ignored" );
962
        }
963
        else
964
        {
965
            vlc_value_t s;
966

967
            msg_Dbg( p_input, "starting at time: %ds",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
968
                              (int)( p_input->p->i_start / INT64_C(1000000) ) );
969

970 971
            s.i_time = p_input->p->i_start;
            input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
972
        }
973 974 975 976 977 978
    }
    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;
    }
979

980 981 982 983 984 985
    /* Load subtitles */
    /* Get fps and set it if not already set */
    if( !demux_Control( p_input->p->input.p_demux, DEMUX_GET_FPS, &f_fps ) &&
        f_fps > 1.0 )
    {
        float f_requested_fps;
986

987 988
        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
989

990 991
        f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" );
        if( f_requested_fps != f_fps )
992
        {
993 994 995
            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT|
                                            VLC_VAR_DOINHERIT );
            var_SetFloat( p_input, "sub-fps", f_requested_fps );
996
        }
997
    }
Laurent Aimar's avatar
 
Laurent Aimar committed
998

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
    i_delay = var_CreateGetInteger( p_input, "sub-delay" );
    if( i_delay != 0 )
    {
        var_SetTime( p_input, "spu-delay", (mtime_t)i_delay * 100000 );
    }

    /* Look for and add subtitle files */
    psz_subtitle = var_GetNonEmptyString( p_input, "sub-file" );
    if( psz_subtitle != NULL )
    {
        msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
1010
        SubtitleAdd( p_input, psz_subtitle, true );
1011 1012 1013 1014 1015 1016
    }

    var_Get( p_input, "sub-autodetect-file", &val );
    if( val.b_bool )
    {
        char *psz_autopath = var_GetNonEmptyString( p_input, "sub-autodetect-path" );
1017 1018 1019
        char **ppsz_subs = subtitles_Detect( p_input, psz_autopath,
                                             p_input->p->input.p_item->psz_uri );
        free( psz_autopath );
1020

1021
        for( int i = 0; ppsz_subs && ppsz_subs[i]; i++ )
Laurent Aimar's avatar
 
Laurent Aimar committed
1022
        {
1023 1024 1025
            /* Try to autoselect the first autodetected subtitles file
             * if no subtitles file was specified */
            bool b_forced = i == 0 && !psz_subtitle;
1026

1027 1028 1029 1030
            if( !psz_subtitle || strcmp( psz_subtitle, ppsz_subs[i] ) )
                SubtitleAdd( p_input, ppsz_subs[i], b_forced );

            free( ppsz_subs[i] );
Laurent Aimar's avatar
 
Laurent Aimar committed
1031
        }
1032
        free( ppsz_subs );
1033 1034
    }
    free( psz_subtitle );
1035

1036 1037 1038 1039 1040 1041 1042
    /* Look for slave */
    psz = var_GetNonEmptyString( p_input, "input-slave" );
    if( psz != NULL )
    {
        char *psz_delim;
        input_source_t *slave;
        while( psz && *psz )
1043
        {
1044
            while( *psz == ' ' || *psz == '#' )
1045
            {
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
                psz++;
            }
            if( ( psz_delim = strchr( psz, '#' ) ) )
            {
                *psz_delim++ = '\0';
            }
            if( *psz == 0 )
            {
                break;
            }
1056

1057 1058 1059 1060 1061
            msg_Dbg( p_input, "adding slave input '%s'", psz );
            slave = InputSourceNew( p_input );
            if( !InputSourceInit( p_input, slave, psz, NULL ) )
            {
                TAB_APPEND( p_input->p->i_slave, p_input->p->slave, slave );
1062
            }
1063 1064
            else free( slave );
            psz = psz_delim;
1065
        }
1066
        free( psz );
1067
    }
1068 1069 1070 1071 1072 1073 1074 1075
}

static void InitPrograms( input_thread_t * p_input )
{
    int i_es_out_mode;
    vlc_value_t val;

    if( p_input->b_preparsing ) return;
1076 1077

    /* Set up es_out */
1078 1079 1080 1081
    es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, true );
    i_es_out_mode = ES_OUT_MODE_AUTO;
    val.p_list = NULL;
    if( p_input->p->p_sout )
1082
    {
1083
        var_Get( p_input, "sout-all", &val );
1084
        if( val.b_bool )
1085
        {
1086 1087 1088 1089 1090 1091
            i_es_out_mode = ES_OUT_MODE_ALL;
            val.p_list = NULL;
        }
        else
        {
            var_Get( p_input, "programs", &val );
1092
            if( val.p_list && val.p_list->i_count )
1093
            {
1094 1095
                i_es_out_mode = ES_OUT_MODE_PARTIAL;
                /* Note : we should remove the "program" callback. */
1096 1097
            }
            else
1098
            {
1099 1100
                var_Change( p_input, "programs", VLC_VAR_FREELIST, &val,
                            NULL );
1101
            }
1102
        }
1103 1104
    }
    es_out_Control( p_input->p->p_es_out, ES_OUT_SET_MODE, i_es_out_mode );
1105

1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
    /* 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,
                        val.p_list );
    }
    else
    {
        demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP,
                       (int) var_GetInteger( p_input, "program" ), NULL );
    }
}

static int Init( input_thread_t * p_input )
{
    vlc_meta_t *p_meta;
    int i, ret;

    for( i = 0; i < p_input->p->input.p_item->i_options; i++ )
    {
        if( !strncmp( p_input->p->input.p_item->ppsz_options[i], "meta-file", 9 ) )
1131
        {
1132 1133 1134 1135 1136 1137 1138
            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 );
1139
        }
1140 1141 1142
    }

    InitStatistics( p_input );
1143
#ifdef ENABLE_SOUT
1144 1145 1146
    ret = InitSout( p_input );
    if( ret != VLC_SUCCESS )
        return ret; /* FIXME: goto error; should be better here */
1147
#endif
1148 1149

    /* Create es out */
1150
    p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
1151
    p_input->p->p_es_out         = input_EsOutTimeshiftNew( p_input, p_input->p->p_es_out_display, p_input->p->i_rate );
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
    es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, false );
    es_out_Control( p_input->p->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );

    var_Create( p_input, "bit-rate", VLC_VAR_INTEGER );
    var_Create( p_input, "sample-rate", VLC_VAR_INTEGER );

    if( InputSourceInit( p_input, &p_input->p->input,
                         p_input->p->input.p_item->psz_uri, NULL ) )
    {
        goto error;
    }

    InitTitle( p_input );

    /* Load master infos */
    /* Init length */
1168 1169 1170 1171 1172 1173 1174 1175 1176
    input_event_times_t ev_times;
    ev_times.f_position = 0;
    ev_times.i_time = 0;
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_LENGTH,
                         &ev_times.i_length ) )
        ev_times.i_length = 0;
    if( ev_times.i_length <= 0 )
        ev_times.i_length = input_item_GetDuration( p_input->p->input.p_item );
    input_SendEventTimes( p_input, &ev_times );
1177

1178
    StartTitle( p_input );
1179

1180 1181 1182 1183 1184
    InitPrograms( p_input );

    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);
1185

1186 1187 1188 1189 1190
        if( p_input->b_can_pace_control && p_input->p->b_out_pace_control )
        {
            /* We don't want a high input priority here or we'll
             * end-up sucking up all the CPU time */
            vlc_thread_set_priority( p_input, VLC_THREAD_PRIORITY_LOW );
1191
        }
1192 1193 1194

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

1197
    p_meta = vlc_meta_New();
1198

1199
    /* Get meta data from users */
1200 1201
    InputMetaUser( p_input, p_meta );

1202
    /* Get meta data from master input */
1203
    DemuxMeta( p_input, p_meta, p_input->p->input.p_demux );
1204

1205
    /* Access_file does not give any meta, and there are no slave */
1206
    AccessMeta( p_input, p_meta );
1207

Rafaël Carré's avatar
Rafaël Carré committed
1208 1209
    InputUpdateMeta( p_input, p_meta );

1210
    if( !p_input->b_preparsing )
1211
    {
1212
        msg_Dbg( p_input, "`%s' successfully opened",
Clément Stenac's avatar
Clément Stenac committed
1213
                 p_input->p->input.p_item->psz_uri );
1214 1215

    }
1216

1217
    /* initialization is complete */
1218
    input_ChangeState( p_input, PLAYING_S );
1219

1220
    return VLC_SUCCESS;
1221

1222
error:
1223 1224
    input_ChangeState( p_input, ERROR_S );

Clément Stenac's avatar
Clément Stenac committed
1225
    if( p_input->p->p_es_out )
1226
        es_out_Delete( p_input->p->p_es_out );
1227 1228
    if( p_input->p->p_es_out_display )
        es_out_Delete( p_input->p->p_es_out_display );
1229
#ifdef ENABLE_SOUT
Clément Stenac's avatar
Clément Stenac committed
1230
    if( p_input->p->p_sout )
1231
    {
1232
        vlc_object_detach( p_input->p->p_sout );
1233 1234
        sout_DeleteInstance( p_input->p->p_sout );
    }
1235
#endif
1236

1237
    if( !p_input->b_preparsing && libvlc_stats( p_input ) )
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
    {
#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 );
        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 );
1258
            EXIT_COUNTER( sout_sent_bytes );
1259 1260 1261 1262 1263
            EXIT_COUNTER( sout_send_bitrate );
        }
#undef EXIT_COUNTER
    }

1264
    /* Mark them deleted */
Clément Stenac's avatar
Clément Stenac committed
1265 1266 1267 1268
    p_input->p->input.p_demux = NULL;
    p_input->p->input.p_stream = NULL;
    p_input->p->input.p_access = NULL;
    p_input->p->p_es_out = NULL;
1269
    p_input->p->p_es_out_display = NULL;
Clément Stenac's avatar
Clément Stenac committed
1270
    p_input->p->p_sout = NULL;
1271

1272
    return VLC_EGENERIC;
Michel Kaempf's avatar
Michel Kaempf committed
1273 1274
}

1275
/*****************************************************************************
1276
 * WaitDie: Wait until we are asked to die.
1277
 *****************************************************************************
Sam Hocevar's avatar
Sam Hocevar committed
1278
 * This function is called when an error occurred during thread main's loop.
1279
 *****************************************************************************/
1280
static void WaitDie( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
1281
{
1282
    input_ChangeState( p_input, p_input->b_error ? ERROR_S : END_S );
1283 1284 1285 1286 1287 1288

    /* Wait a die order */
    vlc_object_lock( p_input );
    while( vlc_object_alive( p_input ) )
        vlc_object_wait( p_input );
    vlc_object_unlock( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
1289 1290
}

1291
/*****************************************************************************
1292
 * End: end the input thread
1293
 *****************************************************************************/
1294
static void End( input_thread_t * p_input )
1295
{
1296
    int i;
1297

1298
    /* We are at the end */
1299
    input_ChangeState( p_input, END_S );
1300 1301

    /* Clean control variables */
1302
    input_ControlVarStop( p_input );
1303

1304
    /* Clean up master */
1305
    InputSourceClean( &p_input->p->input );
1306

1307
    /* Delete slave */
Clément Stenac's avatar
Clément Stenac committed
1308
    for( i = 0; i < p_input->p->i_slave; i++ )
1309
    {
1310
        InputSourceClean( p_input->p->slave[i] );
Clément Stenac's avatar
Clément Stenac committed
1311
        free( p_input->p->slave[i] );
1312
    }
1313
    free( p_input->p->slave );
1314

1315
    /* Unload all modules */
Clément Stenac's avatar
Clément Stenac committed
1316
    if( p_input->p->p_es_out )
1317
        es_out_Delete( p_input->p->p_es_out );
1318 1319
    if( p_input->p->p_es_out_display )
        es_out_Delete( p_input->p->p_es_out_display );
Gildas Bazin's avatar
 
Gildas Bazin committed
1320

1321
    if( !p_input->b_preparsing )
1322
    {
1323
#define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL;
1324
        if( libvlc_stats( p_input ) )
1325
        {
1326
            libvlc_priv_t *p_private = libvlc_priv( p_input->p_libvlc );
1327

1328 1329
            /* make sure we are up to date */
            stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats );
1330
            if( p_private->p_stats_computer == p_input )
1331
            {
1332 1333
                stats_ComputeGlobalStats( p_input->p_libvlc,
                                          p_input->p_libvlc->p_stats );
1334
                p_private->p_stats_computer = NULL;
1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
            }
            CL_CO( read_bytes );
            CL_CO( read_packets );
            CL_CO( demux_read );
            CL_CO( input_bitrate );
            CL_CO( demux_bitrate );
            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) ;
1348
        }
1349

1350 1351 1352 1353 1354 1355
        /* 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 );
1356

1357 1358 1359 1360 1361
            vlc_object_detach( p_input->p->p_sout );
        }
#undef CL_CO
    }

1362 1363 1364 1365 1366 1367 1368
    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 );
    }

1369
    /* Tell we're dead */
1370
    p_input->b_dead = true;
1371
}
1372

1373 1374 1375
/*****************************************************************************
 * Control
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
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 */
        p_input->p->i_control = 1;
        p_input->p->control[0].i_type = i_type;
        memset( &p_input->p->control[0].val, 0, sizeof( vlc_value_t ) );
    }
    else if( p_input->p->i_control >= INPUT_CONTROL_FIFO_SIZE )
    {
        msg_Err( p_input, "input control fifo overflow, trashing type=%d",
                 i_type );
    }
    else
    {
        p_input->p->control[p_input->p->i_control].i_type = i_type;
        if( p_val )
            p_input->p->control[p_input->p->i_control].val = *p_val;
        else
            memset( &p_input->p->control[p_input->p->i_control].val, 0,
                    sizeof( vlc_value_t ) );

        p_input->p->i_control++;
    }
    vlc_cond_signal( &p_input->p->wait_control );
    vlc_mutex_unlock( &p_input->p->lock_control );
}

1407
static inline int ControlPopNoLock( input_thread_t *p_input,
1408 1409
                                    int *pi_type, vlc_value_t *p_val,
                                    mtime_t i_deadline )
1410
{
1411 1412

    while( p_input->p->i_control <= 0 )
1413
    {
1414 1415 1416
        if( !vlc_object_alive( p_input ) )
            return VLC_EGENERIC;

1417
        if( i_deadline < 0 )
1418 1419 1420 1421
            return VLC_EGENERIC;

        if( vlc_cond_timedwait( &p_input->p->wait_control, &p_input->p->lock_control, i_deadline ) )
            return VLC_EGENERIC;
1422
    }
1423

Clément Stenac's avatar
Clément Stenac committed
1424 1425
    *pi_type = p_input->p->control[0].i_type;
    *p_val   = p_input->p->control[0].val;
Sam Hocevar's avatar
 
Sam Hocevar committed
1426

Clément Stenac's avatar
Clément Stenac committed
1427 1428
    p_input->p->i_control--;
    if( p_input->p->i_control > 0 )
1429 1430
    {
        int i;
Christophe Massiot's avatar
Christophe Massiot committed
1431

Clément Stenac's avatar
Clément Stenac committed
1432
        for( i = 0; i < p_input->p->i_control; i++ )
1433
        {
Clément Stenac's avatar
Clément Stenac committed
1434 1435
            p_input->p->control[i].i_type = p_input->p->control[i+1].i_type;
            p_input->p->control[i].val    = p_input->p->control[i+1].val;
1436 1437
        }
    }
1438

1439 1440
    return VLC_SUCCESS;
}
1441

1442 1443 1444
static void ControlReduce( input_thread_t *p_input )
{
    int i;
1445 1446 1447 1448

    if( !p_input )
        return;

Clément Stenac's avatar
Clément Stenac committed
1449
    for( i = 1; i < p_input->p->i_control; i++ )
1450
    {
Clément Stenac's avatar
Clément Stenac committed
1451 1452
        const int i_lt = p_input->p->control[i-1].i_type;
        const int i_ct = p_input->p->control[i].i_type;
1453 1454

        /* XXX We can't merge INPUT_CONTROL_SET_ES */
Clément Stenac's avatar
Clément Stenac committed
1455
/*        msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->p->i_control,
Gildas Bazin's avatar
Gildas Bazin committed
1456
                 i_lt, i_ct );
1457
*/
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
        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 ) )
        {
            int j;
1469
//            msg_Dbg( p_input, "merged at %d", i );
1470
            /* Remove the i-1 */
Clément Stenac's avatar
Clément Stenac committed
1471 1472 1473
            for( j = i; j <  p_input->p->i_control; j++ )
                p_input->p->control[j-1] = p_input->p->control[j];
            p_input->p->i_control--;
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484
        }
        else
        {
            /* TODO but that's not that important
                - merge SET_X with SET_X_CMD
                - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
                - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
                - ?
                */
        }
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
1485
}
1486 1487 1488
/* Pause input */
static void ControlPause( input_thread_t *p_input, mtime_t i_control_date )
{
1489 1490
    int i_ret = VLC_SUCCESS;
    int i_state = PAUSE_S;
1491

1492
    if( p_input->p->b_can_pause )
1493
    {
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
        if( p_input->p->input.p_access )
            i_ret = access_Control( p_input->p->input.p_access,
                                     ACCESS_SET_PAUSE_STATE, true );
        else
            i_ret = demux_Control( p_input->p->input.p_demux,
                                    DEMUX_SET_PAUSE_STATE, true );

        if( i_ret )
        {
            msg_Warn( p_input, "cannot set pause state" );
            i_state = p_input->i_state;
        }
1506 1507
    }

1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
    /* */
    if( !i_ret )
    {
        i_ret = es_out_SetPauseState( p_input->p->p_es_out, p_input->p->b_can_pause, true, i_control_date );
        if( i_ret )
        {
            msg_Warn( p_input, "cannot set pause state at es_out level" );
            i_state = p_input->i_state;
        }
    }

1519
    /* Switch to new state */
1520
    input_ChangeState( p_input, i_state );
1521 1522

}
1523

1524 1525
static void ControlUnpause( input_thread_t *p_input, mtime_t i_control_date )
{
1526
    int i_ret = VLC_SUCCESS;
1527

1528
    if( p_input->p->b_can_pause )
1529
    {
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
        if( p_input->p->input.p_access )
            i_ret = access_Control( p_input->p->input.p_access,
                                     ACCESS_SET_PAUSE_STATE, false );
        else
            i_ret = demux_Control( p_input->p->input.p_demux,
                                    DEMUX_SET_PAUSE_STATE, false );
        if( i_ret )
        {
            /* FIXME What to do ? */
            msg_Warn( p_input, "cannot unset pause -> EOF" );
            vlc_mutex_unlock( &p_input->p->lock_control );
            input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
            vlc_mutex_lock( &p_input->p->lock_control );
        }
1544 1545 1546
    }

    /* Switch to play */
1547
    input_ChangeState( p_input, PLAYING_S );
1548 1549 1550

    /* */
    if( !i_ret )
1551
        es_out_SetPauseState( p_input->p->p_es_out, false, false, i_control_date );
1552
}
1553

1554
static bool Control( input_thread_t *p_input, int i_type,
Gildas Bazin's avatar
Gildas Bazin committed
1555
                           vlc_value_t val )
1556
{
1557
    const mtime_t i_control_date = mdate();
1558
    /* FIXME b_force_update is abused, it should be carefully checked */
1559
    bool b_force_update = false;
1560

1561 1562
    if( !p_input )
        return b_force_update;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1563

1564 1565 1566
    switch( i_type )
    {
        case INPUT_CONTROL_SET_DIE:
1567
            msg_Dbg( p_input, "control: stopping input" );
1568

1569
            /* Mark all submodules to die */
1570
            ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
1571 1572 1573 1574 1575 1576
            break;

        case INPUT_CONTROL_SET_POSITION:
        case INPUT_CONTROL_SET_POSITION_OFFSET:
        {
            double f_pos;
1577 1578 1579 1580 1581 1582

            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
                break;
            }
1583 1584 1585 1586 1587 1588 1589
            if( i_type == INPUT_CONTROL_SET_POSITION )
            {
                f_pos = val.f_float;
            }
            else
            {
                /* Should not fail */
1590
                demux_Control( p_input->p->input.p_demux,
Gildas Bazin's avatar
Gildas Bazin committed
1591
                                DEMUX_GET_POSITION, &f_pos );
1592 1593 1594 1595
                f_pos += val.f_float;
            }
            if( f_pos < 0.0 ) f_pos = 0.0;
            if( f_pos > 1.0 ) f_pos = 1.0;
1596
            /* Reset the decoders states and clock sync (before calling the demuxer */
1597
            es_out_SetTime( p_input->p->p_es_out, -1 );
1598
            if( demux_Control( p_input->p->input.p_demux, DEMUX_SET_POSITION,
Gildas Bazin's avatar
Gildas Bazin committed
1599
                                f_pos ) )
1600
            {
Gildas Bazin's avatar
Gildas Bazin committed
1601 1602
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
                         "%2.1f%% failed", f_pos * 100 );
1603 1604 1605
            }
            else
            {
Clément Stenac's avatar
Clément Stenac committed
1606
                if( p_input->p->i_slave > 0 )
1607
                    SlaveSeek( p_input );
1608
                p_input->p->input.b_eof = false;
1609

1610
                b_force_update = true;
1611 1612 1613 1614 1615 1616 1617 1618 1619 1620
            }
            break;
        }

        case INPUT_CONTROL_SET_TIME:
        case INPUT_CONTROL_SET_TIME_OFFSET:
        {
            int64_t i_time;
            int i_ret;

1621 1622 1623 1624 1625 1626
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
                break;
            }

1627 1628 1629 1630 1631 1632 1633
            if( i_type == INPUT_CONTROL_SET_TIME )
            {
                i_time = val.i_time;
            }
            else
            {
                /* Should not fail */
1634
                demux_Control( p_input->p->input.p_demux,
1635 1636 1637 1638
                                DEMUX_GET_TIME, &i_time );
                i_time += val.i_time;
            }
            if( i_time < 0 ) i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
1639

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

1643
            i_ret = demux_Control( p_input->p->input.p_demux,
1644 1645 1646 1647 1648
                                    DEMUX_SET_TIME, i_time );
            if( i_ret )
            {
                int64_t i_length;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
1649
                /* Emulate it with a SET_POS */
1650
                demux_Control( p_input->p->input.p_demux,
1651 1652 1653 1654
                                DEMUX_GET_LENGTH, &i_length );
                if( i_length > 0 )
                {
                    double f_pos = (double)i_time / (double)i_length;
1655
                    i_ret = demux_Control( p_input->p->input.p_demux,
1656 1657 1658 1659 1660
                                            DEMUX_SET_POSITION, f_pos );
                }
            }
            if( i_ret )
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1661
                msg_Warn( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64
1662
                         " failed or not possible", i_time );
1663 1664 1665
            }
            else
            {
Clément Stenac's avatar
Clément Stenac committed
1666
                if( p_input->p->i_slave > 0 )
1667
                    SlaveSeek( p_input );
1668
                p_input->p->input.b_eof = false;
1669

1670
                b_force_update = true;
1671 1672 1673 1674 1675 1676 1677 1678
            }
            break;
        }

        case INPUT_CONTROL_SET_STATE:
            if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) ||
                ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) )
            {
1679
                ControlUnpause( p_input, i_control_date );
1680

1681
                b_force_update = true;
1682
            }
1683 1684
            else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S /* &&
                     p_input->p->b_can_pause */ )
1685
            {
1686
                ControlPause( p_input, i_control_date );
1687

1688
                b_force_update = true;
1689
            }
1690
            else if( val.i_int == PAUSE_S && !p_input->p->b_can_pause && 0 )
1691
            {
1692
                b_force_update = true;
1693 1694

                /* Correct "state" value */
1695
                input_ChangeState( p_input, p_input->i_state );
1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
            }
            else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
            {
                msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" );
            }
            break;

        case INPUT_CONTROL_SET_RATE:
        case INPUT_CONTROL_SET_RATE_SLOWER:
        case INPUT_CONTROL_SET_RATE_FASTER:
        {
            int i_rate;
1708
            int i_rate_sign;
1709

1710
            /* Get rate and direction */
1711 1712
            if( i_type == INPUT_CONTROL_SET_RATE )
            {
1713 1714
                i_rate = abs( val.i_int );
                i_rate_sign = val.i_int < 0 ? -1 : 1;
1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727
            }
            else
            {
                static const int ppi_factor[][2] = {
                    {1,64}, {1,32}, {1,16}, {1,8}, {1,4}, {1,3}, {1,2}, {2,3},
                    {1,1},
                    {3,2}, {2,1}, {3,1}, {4,1}, {8,1}, {16,1}, {32,1}, {64,1},
                    {0,0}
                };
                int i_error;
                int i_idx;
                int i;

1728 1729
                i_rate_sign = p_input->p->i_rate < 0 ? -1 : 1;

1730 1731 1732 1733 1734
                i_error = INT_MAX;
                i_idx = -1;
                for( i = 0; ppi_factor[i][0] != 0; i++ )
                {
                    const int i_test_r = INPUT_RATE_DEFAULT * ppi_factor[i][0] / ppi_factor[i][1];
1735
                    const int i_test_e = abs( abs( p_input->p->i_rate ) - i_test_r );
1736 1737 1738 1739 1740 1741
                    if( i_test_e < i_error )
                    {
                        i_idx = i;
                        i_error = i_test_e;
                    }
                }
1742

1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
                assert( i_idx >= 0 && ppi_factor[i_idx][0] != 0 );

                if( i_type == INPUT_CONTROL_SET_RATE_SLOWER )
                {
                    if( ppi_factor[i_idx+1][0] > 0 )
                        i_rate = INPUT_RATE_DEFAULT * ppi_factor[i_idx+1][0] / ppi_factor[i_idx+1][1];
                    else
                        i_rate = INPUT_RATE_MAX+1;
                }
                else
                {
                    assert( i_type == INPUT_CONTROL_SET_RATE_FASTER );
                    if( i_idx > 0 )
                        i_rate = INPUT_RATE_DEFAULT * ppi_factor[i_idx-1][0] / ppi_factor[i_idx-1][1];
                    else
                        i_rate = INPUT_RATE_MIN-1;
                }
            }
1761

1762 1763
            /* Check rate bound */
            if( i_rate < INPUT_RATE_MIN )
1764 1765 1766 1767 1768 1769 1770 1771 1772
            {
                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;
            }
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788

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

1789
            if( i_rate != INPUT_RATE_DEFAULT &&
Laurent Aimar's avatar
Laurent Aimar committed
1790
                ( ( !p_input->p->b_can_rate_control && !p_input->p->input.b_rescale_ts ) ||
Clément Stenac's avatar
Clément Stenac committed
1791
                  ( p_input->p->p_sout && !p_input->p->b_out_pace_control ) ) )
1792 1793 1794 1795
            {
                msg_Dbg( p_input, "cannot change rate" );
                i_rate = INPUT_RATE_DEFAULT;
            }
1796 1797 1798 1799 1800
            if( i_rate != p_input->p->i_rate &&
                !p_input->b_can_pace_control && p_input->p->b_can_rate_control )
            {
                int i_ret;
                if( p_input->p->input.p_access )
1801
                {
1802
                    i_ret = VLC_EGENERIC;
1803
                }
1804
                else
1805 1806 1807 1808
                {
                    if( !p_input->p->input.b_rescale_ts )
                        es_out_Control( p_input->p->p_es_out, ES_OUT_RESET_PCR );

1809
                    i_ret = demux_Control( p_input->p->input.p_demux,
1810
                                            DEMUX_SET_RATE, &i_rate );
1811
                }
1812 1813 1814 1815 1816 1817 1818 1819
                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
1820
            if( i_rate != p_input->p->i_rate )
1821
            {
1822
                p_input->p->i_rate = i_rate;
1823
                input_SendEventRate( p_input, i_rate );
1824

1825
                if( p_input->p->input.b_rescale_ts )
1826 1827 1828 1829
                {
                    const int i_rate_source = (p_input->b_can_pace_control || p_input->p->b_can_rate_control ) ? i_rate : INPUT_RATE_DEFAULT;
                    es_out_SetRate( p_input->p->p_es_out, i_rate_source, i_rate );
                }
1830

1831
                b_force_update = true;
1832 1833 1834 1835 1836 1837
            }
            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
1838
            es_out_Control( p_input->p->p_es_out,
1839
                            ES_OUT_SET_GROUP, val.i_int );
1840

1841
            demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, val.i_int,
1842
                            NULL );
1843 1844 1845 1846
            break;

        case INPUT_CONTROL_SET_ES:
            /* No need to force update, es_out does it if needed */
1847
            es_out_Control( p_input->p->p_es_out_display, ES_OUT_SET_ES_BY_ID, val.i_int );
1848 1849 1850
            break;

        case INPUT_CONTROL_RESTART_ES:
1851
            es_out_Control( p_input->p->p_es_out_display, ES_OUT_RESTART_ES_BY_ID, val.i_int );
1852 1853
            break;

1854
        case INPUT_CONTROL_SET_AUDIO_DELAY:
1855
            if( !es_out_SetDelay( p_input->p->p_es_out_display, AUDIO_ES, val.i_time ) )
1856
                input_SendEventAudioDelay( p_input, val.i_time );
1857 1858 1859
            break;

        case INPUT_CONTROL_SET_SPU_DELAY:
1860
            if( !es_out_SetDelay( p_input->p->p_es_out_display, SPU_ES, val.i_time ) )
1861
                input_SendEventSubtitleDelay( p_input, val.i_time );
1862 1863
            break;

1864 1865 1866
        case INPUT_CONTROL_SET_TITLE:
        case INPUT_CONTROL_SET_TITLE_NEXT:
        case INPUT_CONTROL_SET_TITLE_PREV:
1867 1868 1869 1870 1871
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
                break;
            }
Clément Stenac's avatar
Clément Stenac committed
1872 1873
            if( p_input->p->input.b_title_demux &&
                p_input->p->input.i_title > 0 )
1874 1875 1876
            {
                /* TODO */
                /* FIXME handle demux title */
Clément Stenac's avatar
Clément Stenac committed
1877
                demux_t *p_demux = p_input->p->input.p_demux;
1878 1879 1880 1881 1882 1883 1884 1885 1886
                int i_title;

                if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
                    i_title = p_demux->info.i_title - 1;
                else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
                    i_title = p_demux->info.i_title + 1;
                else
                    i_title = val.i_int;

Clément Stenac's avatar
Clément Stenac committed
1887
                if( i_title >= 0 && i_title < p_input->p->input.i_title )
1888
                {
1889
                    es_out_SetTime( p_input->p->p_es_out, -1 );
1890

1891
                    demux_Control( p_demux, DEMUX_SET_TITLE, i_title );
1892 1893 1894
                    input_ControlVarTitle( p_input, i_title );
                }
            }
Clément Stenac's avatar
Clément Stenac committed
1895
            else if( p_input->p->input.i_title > 0 )
1896
            {
Clément Stenac's avatar
Clément Stenac committed
1897
                access_t *p_access = p_input->p->input.p_access;
1898 1899 1900 1901 1902 1903 1904 1905 1906
                int i_title;

                if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
                    i_title = p_access->info.i_title - 1;
                else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
                    i_title = p_access->info.i_title + 1;
                else
                    i_title = val.i_int;

Clément Stenac's avatar
Clément Stenac committed
1907
                if( i_title >= 0 && i_title < p_input->p->input.i_title )
1908
                {
1909
                    es_out_SetTime( p_input->p->p_es_out, -1 );
1910

1911
                    access_Control( p_access, ACCESS_SET_TITLE, i_title );
Clément Stenac's avatar
Clément Stenac committed
1912
                    stream_AccessReset( p_input->p->input.p_stream );
1913 1914 1915 1916 1917 1918
                }
            }
            break;
        case INPUT_CONTROL_SET_SEEKPOINT:
        case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
        case INPUT_CONTROL_SET_SEEKPOINT_PREV:
1919 1920 1921 1922 1923 1924
            if( p_input->p->b_recording )
            {
                msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
                break;
            }

Clément Stenac's avatar
Clément Stenac committed
1925 1926
            if( p_input->p->input.b_title_demux &&
                p_input->p->input.i_title > 0 )
1927
            {
Clément Stenac's avatar
Clément Stenac committed
1928
                demux_t *p_demux = p_input->p->input.p_demux;
1929
                int i_seekpoint;
1930 1931
                int64_t i_input_time;
                int64_t i_seekpoint_time;
1932 1933

                if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
1934 1935
                {
                    i_seekpoint = p_demux->info.i_seekpoint;
Clément Stenac's avatar
Clément Stenac committed
1936
                    i_seekpoint_time = p_input->p->input.title[p_demux->info.i_title]->seekpoint[i_seekpoint]->i_time_offset;
1937
                    if( i_seekpoint_time >= 0 &&
1938
                         !demux_Control( p_demux,
1939
                                          DEMUX_GET_TIME, &i_input_time ) )
1940
                    {
1941
                        if( i_input_time < i_seekpoint_time + 3000000 )
1942 1943 1944 1945 1946
                            i_seekpoint--;
                    }
                    else
                        i_seekpoint--;
                }
Gildas Bazin's avatar
Gildas Bazin committed
1947
                else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
1948 1949 1950 1951
                    i_seekpoint = p_demux->info.i_seekpoint + 1;
                else
                    i_seekpoint = val.i_int;

Gildas Bazin's avatar
Gildas Bazin committed
1952
                if( i_seekpoint >= 0 && i_seekpoint <
Clément Stenac's avatar
Clément Stenac committed
1953
                    p_input->p->input.title[p_demux->info.i_title]->i_seekpoint )
1954
                {
Laurent Aimar's avatar
Laurent Aimar committed
1955

1956
                    es_out_SetTime( p_input->p->p_es_out, -1 );
1957

1958
                    demux_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
1959 1960
                }
            }
Clément Stenac's avatar
Clément Stenac committed
1961
            else if( p_input->p->input.i_title > 0 )
1962
            {
Clément Stenac's avatar
Clément Stenac committed
1963 1964
                demux_t *p_demux = p_input->p->input.p_demux;
                access_t *p_access = p_input->p->input.p_access;
1965
                int i_seekpoint;
1966 1967
                int64_t i_input_time;
                int64_t i_seekpoint_time;
1968 1969

                if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
1970 1971
                {
                    i_seekpoint = p_access->info.i_seekpoint;
Clément Stenac's avatar
Clément Stenac committed
1972
                    i_seekpoint_time = p_input->p->input.title[p_access->info.i_title]->seekpoint[i_seekpoint]->i_time_offset;
1973
                    if( i_seekpoint_time >= 0 &&
1974
                        demux_Control( p_demux,
1975
                                        DEMUX_GET_TIME, &i_input_time ) )
1976
                    {
1977
                        if( i_input_time < i_seekpoint_time + 3000000 )
1978 1979 1980 1981 1982
                            i_seekpoint--;
                    }
                    else
                        i_seekpoint--;
                }
1983
                else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
1984 1985 1986 1987
                    i_seekpoint = p_access->info.i_seekpoint + 1;
                else
                    i_seekpoint = val.i_int;

Gildas Bazin's avatar
Gildas Bazin committed
1988
                if( i_seekpoint >= 0 && i_seekpoint <
Clément Stenac's avatar
Clément Stenac committed
1989
                    p_input->p->input.title[p_access->info.i_title]->i_seekpoint )
1990
                {
1991
                    es_out_SetTime( p_input->p->p_es_out, -1 );
1992

1993
                    access_Control( p_access, ACCESS_SET_SEEKPOINT,
1994
                                    i_seekpoint );
Clément Stenac's avatar
Clément Stenac committed
1995
                    stream_AccessReset( p_input->p->input.p_stream );
1996 1997 1998 1999
                }
            }
            break;

2000 2001 2002 2003 2004 2005 2006 2007
        case INPUT_CONTROL_ADD_SUBTITLE:
            if( val.psz_string )
            {
                SubtitleAdd( p_input, val.psz_string, true );
                free( val.psz_string );
            }
            break;

2008 2009 2010 2011 2012
        case INPUT_CONTROL_ADD_SLAVE:
            if( val.psz_string )
            {
                input_source_t *slave = InputSourceNew( p_input );

2013
                if( !InputSourceInit( p_input, slave, val.psz_string, NULL ) )
2014
                {
2015
                    vlc_meta_t *p_meta;
2016 2017 2018 2019 2020 2021 2022
                    int64_t i_time;

                    /* Add the slave */
                    msg_Dbg( p_input, "adding %s as slave on the fly",
                             val.psz_string );

                    /* Set position */
2023
                    if( demux_Control( p_input->p->input.p_demux,
2024 2025 2026
                                        DEMUX_GET_TIME, &i_time ) )
                    {
                        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
2027
                        InputSourceClean( slave );
2028 2029 2030
                        free( slave );
                        break;
                    }
2031
                    if( demux_Control( slave->p_demux,
2032
                                       DEMUX_SET_TIME, i_time ) )
2033 2034
                    {
                        msg_Err( p_input, "seek failed for new slave" );
2035
                        InputSourceClean( slave );
2036 2037 2038 2039 2040
                        free( slave );
                        break;
                    }

                    /* Get meta (access and demux) */
2041
                    p_meta = vlc_meta_New();
2042
                    access_Control( slave->p_access, ACCESS_GET_META,
2043
                                     p_meta );
2044
                    demux_Control( slave->p_demux, DEMUX_GET_META, p_meta );
2045
                    InputUpdateMeta( p_input, p_meta );
2046

Clément Stenac's avatar
Clément Stenac committed
2047
                    TAB_APPEND( p_input->p->i_slave, p_input->p->slave, slave );
2048 2049 2050
                }
                else
                {
2051
                    free( slave );
2052 2053 2054 2055 2056 2057 2058 2059
                    msg_Warn( p_input, "failed to add %s as slave",
                              val.psz_string );
                }

                free( val.psz_string );
            }
            break;

2060
        case INPUT_CONTROL_SET_RECORD_STATE:
2061
            if( !!p_input->p->b_recording != !!val.b_bool )
2062
            {
2063
                if( p_input->p->input.b_can_stream_record )
2064 2065 2066 2067 2068
                {
                    if( demux_Control( p_input->p->input.p_demux,
                                       DEMUX_SET_RECORD_STATE, val.b_bool ) )
                        val.b_bool = false;
                }
2069 2070
                else
                {
2071
                    if( es_out_SetRecordState( p_input->p->p_es_out_display, val.b_bool ) )
2072 2073 2074
                        val.b_bool = false;
                }
                p_input->p->b_recording = val.b_bool;
2075

2076
                input_SendEventRecord( p_input, val.b_bool );
2077 2078 2079 2080 2081

                b_force_update = true;
            }
            break;

2082 2083 2084
        case INPUT_CONTROL_SET_FRAME_NEXT:
            if( p_input->i_state == PAUSE_S )
            {
2085
                es_out_SetFrameNext( p_input->p->p_es_out );
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097
            }
            else if( p_input->i_state == PLAYING_S )
            {
                ControlPause( p_input, i_control_date );
            }
            else
            {
                msg_Err( p_input, "invalid state for frame next" );
            }
            b_force_update = true;
            break;

2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
        case INPUT_CONTROL_SET_BOOKMARK:
        default:
            msg_Err( p_input, "not yet implemented" );
            break;
    }

    return b_force_update;
}

/*****************************************************************************
 * UpdateFromDemux:
 *****************************************************************************/
2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
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;
}
2136
static int UpdateFromDemux( input_thread_t *p_input )
2137
{
Clément Stenac's avatar
Clément Stenac committed
2138
    demux_t *p_demux = p_input->p->input.p_demux;
2139

2140
    /* TODO event-like */
2141 2142
    if( p_demux->info.i_update & INPUT_UPDATE_TITLE )
    {
2143
        input_SendEventTitle( p_input, p_demux->info.i_title );
2144 2145 2146 2147 2148

        p_demux->info.i_update &= ~INPUT_UPDATE_TITLE;
    }
    if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT )
    {
2149 2150
        input_SendEventSeekpoint( p_input,
                                  p_demux->info.i_title, p_demux->info.i_seekpoint );
2151 2152 2153 2154

        p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
    }
    p_demux->info.i_update &= ~INPUT_UPDATE_SIZE;
2155 2156

    /* Hmmm only works with master input */
Clément Stenac's avatar
Clément Stenac committed
2157
    if( p_input->p->input.p_demux == p_demux )
2158 2159 2160
        return UpdateTitleSeekpoint( p_input,
                                     p_demux->info.i_title,
                                     p_demux->info.i_seekpoint );
2161
    return 1;
2162 2163 2164 2165 2166
}

/*****************************************************************************
 * UpdateFromAccess:
 *****************************************************************************/
2167
static int UpdateFromAccess( input_thread_t *p_input )
2168
{
Clément Stenac's avatar
Clément Stenac committed
2169
    access_t *p_access = p_input->p->input.p_access;
2170 2171 2172

    if( p_access->info.i_update & INPUT_UPDATE_TITLE )
    {
2173
        input_SendEventTitle( p_input, p_access->info.i_title );
2174

Clément Stenac's avatar
Clément Stenac committed
2175
        stream_AccessUpdate( p_input->p->input.p_stream );
2176

2177 2178 2179 2180
        p_access->info.i_update &= ~INPUT_UPDATE_TITLE;
    }
    if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT )
    {
2181 2182 2183
        input_SendEventSeekpoint( p_input,
                                  p_access->info.i_title, p_access->info.i_seekpoint );

2184 2185
        p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
    }
2186 2187 2188
    if( p_access->info.i_update & INPUT_UPDATE_META )
    {
        /* TODO maybe multi - access ? */
2189
        vlc_meta_t *p_meta = vlc_meta_New();
2190
        access_Control( p_input->p->input.p_access,ACCESS_GET_META, p_meta );
2191
        InputUpdateMeta( p_input, p_meta );
2192 2193
        p_access->info.i_update &= ~INPUT_UPDATE_META;
    }
2194 2195 2196 2197 2198 2199 2200 2201
    if( p_access->info.i_update & INPUT_UPDATE_SIGNAL )
    {
        double f_quality;
        double f_strength;

        if( access_Control( p_access, ACCESS_GET_SIGNAL, &f_quality, &f_strength ) )
            f_quality = f_strength = -1;

2202
        input_SendEventSignal( p_input, f_quality, f_strength );
2203 2204 2205

        p_access->info.i_update &= ~INPUT_UPDATE_SIGNAL;
    }
2206

2207
    p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
2208 2209

    /* Hmmm only works with master input */
Clément Stenac's avatar
Clément Stenac committed
2210
    if( p_input->p->input.p_access == p_access )
2211 2212 2213
        return UpdateTitleSeekpoint( p_input,
                                     p_access->info.i_title,
                                     p_access->info.i_seekpoint );
2214
    return 1;
2215 2216
}

2217 2218 2219 2220 2221
/*****************************************************************************
 * InputSourceNew:
 *****************************************************************************/
static input_source_t *InputSourceNew( input_thread_t *p_input )
{
2222 2223
    VLC_UNUSED(p_input);
    input_source_t *in = malloc( sizeof( input_source_t ) );
Rafaël Carré's avatar
Rafaël Carré committed
2224 2225
    if( in )
        memset( in, 0, sizeof( input_source_t ) );
2226 2227 2228 2229 2230 2231 2232
    return in;
}

/*****************************************************************************
 * InputSourceInit:
 *****************************************************************************/
static int InputSourceInit( input_thread_t *p_input,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2233
                            input_source_t *in, const char *psz_mrl,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2234
                            const char *psz_forced_demux )
2235
{
2236 2237
    const bool b_master = in == &p_input->p->input;

2238
    char psz_dup[strlen(psz_mrl) + 1];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2239 2240
    const char *psz_access;
    const char *psz_demux;
2241
    char *psz_path;
2242
    char *psz_tmp;
2243
    char *psz;
2244
    vlc_value_t val;
2245
    double f_fps;
2246

2247
    strcpy( psz_dup, psz_mrl );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2248

Jean-Paul Saman's avatar
Jean-Paul Saman committed
2249
    if( !in ) return VLC_EGENERIC;
2250
    if( !p_input ) return VLC_EGENERIC;
2251

2252
    /* Split uri */
2253
    input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup );
2254 2255 2256

    msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
             psz_mrl, psz_access, psz_demux, psz_path );
2257
    if( !p_input->b_preparsing )
2258 2259 2260 2261 2262
    {
        /* Hack to allow udp://@:port syntax */
        if( !psz_access ||
            (strncmp( psz_access, "udp", 3 ) &&
             strncmp( psz_access, "rtp", 3 )) )
2263 2264 2265
        {
            /* Find optional titles and seekpoints */
            MRLSections( p_input, psz_path, &in->i_title_start, &in->i_title_end,
2266
                     &in->i_seekpoint_start, &in->i_seekpoint_end );
2267
        }
2268

2269
        if( psz_forced_demux && *psz_forced_demux )
2270
        {
2271
            psz_demux = psz_forced_demux;
2272
        }
2273
        else if( *psz_demux == '\0' )
2274 2275 2276
        {
            /* special hack for forcing a demuxer with --demux=module
             * (and do nothing with a list) */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2277
            char *psz_var_demux = var_GetNonEmptyString( p_input, "demux" );
2278

Rémi Denis-Courmont's avatar
typo  
Rémi Denis-Courmont committed
2279
            if( psz_var_demux != NULL &&
2280 2281 2282 2283 2284
                !strchr(psz_var_demux, ',' ) &&
                !strchr(psz_var_demux, ':' ) )
            {
                psz_demux = psz_var_demux;

2285
                msg_Dbg( p_input, "enforced demux ` %s'", psz_demux );
2286 2287
            }
        }
2288

2289 2290 2291
        /* Try access_demux first */
        in->p_demux = demux_New( p_input, psz_access, psz_demux, psz_path,
                                  NULL, p_input->p->p_es_out, false );
2292 2293
    }
    else
2294
    {
2295
        /* Preparsing is only for file:// */
2296
        if( *psz_demux )
2297
            goto error;
2298
        if( !*psz_access ) /* path without scheme:// */
2299 2300 2301
            psz_access = "file";
        if( strcmp( psz_access, "file" ) )
            goto error;
2302
        msg_Dbg( p_input, "trying to pre-parse %s",  psz_path );
2303 2304 2305 2306 2307 2308 2309
    }

    if( in->p_demux )
    {
        int64_t i_pts_delay;

        /* Get infos from access_demux */
2310
        demux_Control( in->p_demux,
2311 2312 2313
                        DEMUX_GET_PTS_DELAY, &i_pts_delay );
        p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );

2314
        in->b_title_demux = true;
2315
        if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2316 2317
                            &in->title, &in->i_title,
                            &in->i_title_offset, &in->i_seekpoint_offset ) )
2318
        {
2319
            TAB_INIT( in->i_title, in->title );
2320
        }
2321
        if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
2322
                            &in->b_can_pace_control ) )
2323
            in->b_can_pace_control = false;
2324 2325 2326

        if( !in->b_can_pace_control )
        {
2327
            if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_RATE,
2328 2329
                                &in->b_can_rate_control, &in->b_rescale_ts ) )
            {
2330 2331
                in->b_can_rate_control = false;
                in->b_rescale_ts = true; /* not used */
2332 2333 2334 2335
            }
        }
        else
        {
2336 2337
            in->b_can_rate_control = true;
            in->b_rescale_ts = true;
2338
        }
2339
        if( demux_Control( in->p_demux, DEMUX_CAN_PAUSE,
2340
                            &in->b_can_pause ) )
2341
            in->b_can_pause = false;
Laurent Aimar's avatar
Laurent Aimar committed
2342
        var_SetBool( p_input, "can-pause", in->b_can_pause || !in->b_can_pace_control ); /* XXX temporary because of es_out_timeshift*/
2343
        var_SetBool( p_input, "can-rate", !in->b_can_pace_control || in->b_can_rate_control ); /* XXX temporary because of es_out_timeshift*/
2344
        var_SetBool( p_input, "can-rewind", !in->b_rescale_ts && !in->b_can_pace_control );
2345

2346
        int ret = demux_Control( in->p_demux, DEMUX_CAN_SEEK,
2347
                        &val.b_bool );
2348
        if( ret != VLC_SUCCESS )
2349
            val.b_bool = false;
2350
        var_Set( p_input, "can-seek", val );
2351 2352 2353 2354 2355
    }
    else
    {
        int64_t i_pts_delay;

2356 2357
        if( b_master )
            input_ChangeState( p_input, OPENING_S );
2358

2359
        /* Now try a real access */
2360
        in->p_access = access_New( p_input, psz_access, psz_demux, psz_path );
2361 2362 2363 2364

        /* Access failed, URL encoded ? */
        if( in->p_access == NULL && strchr( psz_path, '%' ) )
        {
2365
            decode_URI( psz_path );
2366

2367
            msg_Dbg( p_input, "retrying with access `%s' demux `%s' path `%s'",
2368 2369
                     psz_access, psz_demux, psz_path );

2370
            in->p_access = access_New( p_input,
2371
                                        psz_access, psz_demux, psz_path );
2372 2373 2374
        }
        if( in->p_access == NULL )
        {
2375 2376
            msg_Err( p_input, "open of `%s' failed: %s", psz_mrl,
                                                         msg_StackMsg() );
2377
            intf_UserFatal( VLC_OBJECT( p_input), false,
2378 2379 2380
                            _("Your input can't be opened"),
                            _("VLC is unable to open the MRL '%s'."
                            " Check the log for details."), psz_mrl );
2381 2382 2383
            goto error;
        }

2384
        /* */
2385
        psz_tmp = psz = var_GetNonEmptyString( p_input, "access-filter" );
2386
        while( psz && *psz )
2387 2388
        {
            access_t *p_access = in->p_access;
2389 2390 2391 2392
            char *end = strchr( psz, ':' );

            if( end )
                *end++ = '\0';
2393

2394
            in->p_access = access_FilterNew( in->p_access, psz );
2395 2396 2397 2398 2399 2400
            if( in->p_access == NULL )
            {
                in->p_access = p_access;
                msg_Warn( p_input, "failed to insert access filter %s",
                          psz );
            }
2401 2402

            psz = end;
2403
        }
2404
        free( psz_tmp );
2405

2406
        /* Get infos from access */
2407
        if( !p_input->b_preparsing )
2408
        {
2409
            access_Control( in->p_access,
2410 2411
                             ACCESS_GET_PTS_DELAY, &i_pts_delay );
            p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
2412

2413
            in->b_title_demux = false;
2414
            if( access_Control( in->p_access, ACCESS_GET_TITLE_INFO,
2415 2416
                                 &in->title, &in->i_title,
                                &in->i_title_offset, &in->i_seekpoint_offset ) )
2417

2418
            {
2419
                TAB_INIT( in->i_title, in->title );
2420
            }
2421
            access_Control( in->p_access, ACCESS_CAN_CONTROL_PACE,
2422
                             &in->b_can_pace_control );
2423
            in->b_can_rate_control = in->b_can_pace_control;
2424
            in->b_rescale_ts = true;
2425

2426
            access_Control( in->p_access, ACCESS_CAN_PAUSE,
2427
                             &in->b_can_pause );
Laurent Aimar's avatar
Laurent Aimar committed
2428
            var_SetBool( p_input, "can-pause", in->b_can_pause || !in->b_can_pace_control ); /* XXX temporary because of es_out_timeshift*/
2429
            var_SetBool( p_input, "can-rate", !in->b_can_pace_control || in->b_can_rate_control ); /* XXX temporary because of es_out_timeshift*/
2430 2431
            var_SetBool( p_input, "can-rewind", !in->b_rescale_ts && !in->b_can_pace_control );

2432
            access_Control( in->p_access, ACCESS_CAN_SEEK,
2433
                             &val.b_bool );
2434
            var_Set( p_input, "can-seek", val );
2435 2436
        }

2437 2438
        if( b_master )
            input_ChangeState( p_input, BUFFERING_S );
2439

2440 2441 2442 2443 2444 2445 2446 2447 2448 2449
        /* Autodetect extra files if none specified */
        char *psz_input_list = var_CreateGetNonEmptyString( p_input, "input-list" );
        if( !psz_input_list )
        {
            char *psz_extra_files = InputGetExtraFiles( p_input, psz_access, psz_path );
            if( psz_extra_files )
                var_SetString( p_input, "input-list", psz_extra_files );
            free( psz_extra_files );
        }

2450
        /* Create the stream_t */
2451
        in->p_stream = stream_AccessNew( in->p_access, p_input->b_preparsing );
2452 2453 2454 2455 2456 2457

        /* Restor old value */
        if( !psz_input_list )
            var_SetString( p_input, "input-list", "" );
        free( psz_input_list );

2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
        if( in->p_stream == NULL )
        {
            msg_Warn( p_input, "cannot create a stream_t from access" );
            goto error;
        }

        /* Open a demuxer */
        if( *psz_demux == '\0' && *in->p_access->psz_demux )
        {
            psz_demux = in->p_access->psz_demux;
        }
2469 2470 2471 2472 2473 2474 2475

        {
            /* Take access redirections into account */
            char *psz_real_path;
            char *psz_buf = NULL;
            if( in->p_access->psz_path )
            {
Laurent Aimar's avatar
Laurent Aimar committed
2476
                const char *psz_a, *psz_d;
2477
                psz_buf = strdup( in->p_access->psz_path );
2478
                input_SplitMRL( &psz_a, &psz_d, &psz_real_path, psz_buf );
2479 2480 2481 2482 2483
            }
            else
            {
                psz_real_path = psz_path;
            }
2484
            in->p_demux = demux_New( p_input, psz_access, psz_demux,
2485 2486 2487 2488 2489 2490
                                      psz_real_path,
                                      in->p_stream, p_input->p->p_es_out,
                                      p_input->b_preparsing );
            free( psz_buf );
        }

2491 2492 2493 2494
        if( in->p_demux == NULL )
        {
            msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
                     psz_access, psz_demux, psz_path );
2495
            intf_UserFatal( VLC_OBJECT( p_input ), false,
2496 2497
                            _("VLC can't recognize the input's format"),
                            _("The format of '%s' cannot be detected. "
2498
                            "Have a look at the log for details."), psz_mrl );
2499 2500 2501
            goto error;
        }

2502
        /* Get title from demux */
2503
        if( !p_input->b_preparsing && in->i_title <= 0 )
2504
        {
2505
            if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2506 2507
                                &in->title, &in->i_title,
                                &in->i_title_offset, &in->i_seekpoint_offset ))
2508
            {
2509
                TAB_INIT( in->i_title, in->title );
2510 2511 2512
            }
            else
            {
2513
                in->b_title_demux = true;
2514 2515
            }
        }
2516 2517
    }

2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528
    /* 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
    if( !var_CreateGetBool( p_input, "input-record-native" ) )
        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

2529 2530 2531 2532 2533 2534
    /* get attachment
     * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
    if( 1 || !p_input->b_preparsing )
    {
        int i_attachment;
        input_attachment_t **attachment;
2535
        if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS,
2536
                             &attachment, &i_attachment ) )
2537
        {
2538
            vlc_mutex_lock( &p_input->p->input.p_item->lock );
2539 2540
            AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment,
                              i_attachment, attachment );
2541
            vlc_mutex_unlock( &p_input->p->input.p_item->lock );
2542
        }
2543
    }
2544
    if( !demux_Control( in->p_demux, DEMUX_GET_FPS, &f_fps ) )
2545 2546 2547 2548 2549
    {
        vlc_mutex_lock( &p_input->p->input.p_item->lock );
        in->f_fps = f_fps;
        vlc_mutex_unlock( &p_input->p->input.p_item->lock );
    }
2550 2551 2552 2553

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

2554 2555 2556
    return VLC_SUCCESS;

error:
2557 2558
    if( b_master )
        input_ChangeState( p_input, ERROR_S );
2559

2560
    if( in->p_demux )
2561
        demux_Delete( in->p_demux );
2562 2563

    if( in->p_stream )
2564
        stream_Delete( in->p_stream );
2565 2566

    if( in->p_access )
2567
        access_Delete( in->p_access );
2568 2569 2570 2571 2572 2573 2574

    return VLC_EGENERIC;
}

/*****************************************************************************
 * InputSourceClean:
 *****************************************************************************/
2575
static void InputSourceClean( input_source_t *in )
2576
{
2577 2578
    int i;

2579
    if( in->p_demux )
2580
        demux_Delete( in->p_demux );
2581 2582

    if( in->p_stream )
2583
        stream_Delete( in->p_stream );
2584 2585

    if( in->p_access )
2586
        access_Delete( in->p_access );
2587 2588 2589 2590 2591

    if( in->i_title > 0 )
    {
        for( i = 0; i < in->i_title; i++ )
            vlc_input_title_Delete( in->title[i] );
2592
        TAB_CLEAN( in->i_title, in->title );
2593 2594 2595 2596 2597 2598 2599
    }
}

static void SlaveDemux( input_thread_t *p_input )
{
    int64_t i_time;
    int i;
2600
    bool b_set_time = true;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2601

2602
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) )
2603
    {
2604 2605
        /* msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" ); */
        b_set_time = false;
2606 2607
    }

Clément Stenac's avatar
Clément Stenac committed
2608
    for( i = 0; i < p_input->p->i_slave; i++ )
2609
    {
Clément Stenac's avatar
Clément Stenac committed
2610
        input_source_t *in = p_input->p->slave[i];
2611 2612 2613 2614 2615
        int i_ret = 1;

        if( in->b_eof )
            continue;

2616
        if( b_set_time && demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
2617 2618 2619 2620
        {
            for( ;; )
            {
                int64_t i_stime;
2621
                if( demux_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
2622
                {
Gildas Bazin's avatar
Gildas Bazin committed
2623 2624
                    msg_Err( p_input, "slave[%d] doesn't like "
                             "DEMUX_GET_TIME -> EOF", i );
2625 2626 2627
                    i_ret = 0;
                    break;
                }
2628

2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643
                if( i_stime >= i_time )
                    break;

                if( ( i_ret = in->p_demux->pf_demux( in->p_demux ) ) <= 0 )
                    break;
            }
        }
        else
        {
            i_ret = in->p_demux->pf_demux( in->p_demux );
        }

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

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

2654 2655
    if( !p_input ) return;

2656
    if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) )
2657 2658 2659 2660 2661
    {
        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
        return;
    }

Clément Stenac's avatar
Clément Stenac committed
2662
    for( i = 0; i < p_input->p->i_slave; i++ )
2663
    {
Clément Stenac's avatar
Clément Stenac committed
2664
        input_source_t *in = p_input->p->slave[i];
2665

2666
        if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time ) )
2667
        {
2668 2669
            if( !in->b_eof )
                msg_Err( p_input, "seek failed for slave %d -> EOF", i );
2670
            in->b_eof = true;
2671
        }
2672 2673 2674 2675
        else
        {
            in->b_eof = false;
        }
2676 2677
    }
}
2678

2679 2680 2681
/*****************************************************************************
 * InputMetaUser:
 *****************************************************************************/
2682
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta )
2683 2684
{
    vlc_value_t val;
2685

2686
    if( !p_meta ) return;
2687 2688

    /* Get meta information from user */
2689
#define GET_META( field, s ) \
2690
    var_Get( p_input, (s), &val );  \
2691 2692
    if( *val.psz_string ) \
        vlc_meta_Set( p_meta, vlc_meta_ ## field, val.psz_string ); \
2693 2694
    free( val.psz_string )

2695 2696 2697 2698 2699 2700 2701
    GET_META( Title, "meta-title" );
    GET_META( Artist, "meta-artist" );
    GET_META( Genre, "meta-genre" );
    GET_META( Copyright, "meta-copyright" );
    GET_META( Description, "meta-description" );
    GET_META( Date, "meta-date" );
    GET_META( URL, "meta-url" );
2702 2703
#undef GET_META
}
2704

2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733
/*****************************************************************************
 * InputGetExtraFiles
 *  Autodetect extra input list
 *****************************************************************************/
static char *InputGetExtraFiles( input_thread_t *p_input,
                                 const char *psz_access, const char *psz_path )
{
    char *psz_list = NULL;

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


    const char *psz_ext = strrchr( psz_path, '.' );
    if( !psz_ext || strcmp( psz_ext, ".001" ) )
        return NULL;

    char *psz_file = strdup( psz_path );
    if( !psz_file )
        return NULL;

    /* Try to list .xyz files */
    for( int i = 2; i < 999; i++ )
    {
        char *psz_ext = strrchr( psz_file, '.' );
        struct stat st;

        snprintf( psz_ext, 5, ".%.3d", i );

2734 2735
        if( utf8_stat( psz_file, &st )
         || !S_ISREG( st.st_mode ) || !st.st_size )
2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759
            continue;

        msg_Dbg( p_input, "Detected extra file `%s'", psz_file );

        if( psz_list )
        {
            char *psz_old = psz_list;
            /* FIXME how to handle file with ',' ?*/
            if( asprintf( &psz_list, "%s,%s", psz_old, psz_file ) < 0 )
            {
                psz_list = psz_old;
                break;
            }
        }
        else
        {
            psz_list = strdup( psz_file );
        }
    }
    free( psz_file );

    return psz_list;
}

2760 2761 2762 2763 2764 2765 2766
/*****************************************************************************
 * 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 )
{
    input_item_t *p_item = p_input->p->input.p_item;
2767
    char * psz_arturl = NULL;
2768 2769 2770 2771 2772
    char *psz_title = NULL;

    if( !p_meta )
        return;

2773 2774
    psz_arturl = input_item_GetArtURL( p_item );

2775
    vlc_mutex_lock( &p_item->lock );
2776

2777 2778
    if( vlc_meta_Get( p_meta, vlc_meta_Title ) && !p_item->b_fixed_name )
        psz_title = strdup( vlc_meta_Get( p_meta, vlc_meta_Title ) );
2779

2780
    vlc_meta_Merge( p_item->p_meta, p_meta );
2781

2782 2783
    vlc_meta_Delete( p_meta );

2784 2785 2786
    if( psz_arturl && *psz_arturl )
    {
        vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_arturl );
2787

2788
        if( !strncmp( psz_arturl, "attachment://", strlen("attachment") ) )
2789
        {
2790 2791 2792 2793 2794 2795
            /* Don't look for art cover if sout
             * XXX It can change when sout has meta data support */
            if( p_input->p->p_sout && !p_input->b_preparsing )
                vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, "" );
            else
                input_ExtractAttachmentAndCacheArt( p_input );
2796
        }
2797
    }
2798
    free( psz_arturl );
2799

2800
    vlc_mutex_unlock( &p_item->lock );
2801 2802

    if( psz_title )
2803
    {
2804
        input_item_SetName( p_item, psz_title );
2805 2806
        free( psz_title );
    }
2807
    input_item_SetPreparsed( p_item, true );
2808

2809
    input_SendEventMeta( p_input );
2810 2811 2812 2813

    /** \todo handle sout meta */
}

2814

2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825
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;

    attachment = realloc( attachment,
                          sizeof(input_attachment_t**) * ( i_attachment + i_new ) );
    for( i = 0; i < i_new; i++ )
        attachment[i_attachment++] = pp_new[i];
2826
    free( pp_new );
2827 2828 2829 2830 2831 2832

    /* */
    *pi_attachment = i_attachment;
    *ppp_attachment = attachment;
}

2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855
static void AccessMeta( input_thread_t * p_input, vlc_meta_t *p_meta )
{
    int i;

    if( p_input->b_preparsing )
        return;

    if( p_input->p->input.p_access )
        access_Control( p_input->p->input.p_access, ACCESS_GET_META,
                         p_meta );

    /* Get meta data from slave input */
    for( i = 0; i < p_input->p->i_slave; i++ )
    {
        DemuxMeta( p_input, p_meta, p_input->p->slave[i]->p_demux );
        if( p_input->p->slave[i]->p_access )
        {
            access_Control( p_input->p->slave[i]->p_access,
                             ACCESS_GET_META, p_meta );
        }
    }
}

2856 2857
static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_demux )
{
2858
    bool b_bool;
2859 2860
    module_t *p_id3;

2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872
#if 0
    /* XXX I am not sure it is a great idea, besides, there is more than that
     * if we want to do it right */
    vlc_mutex_lock( &p_item->lock );
    if( p_item->p_meta && (p_item->p_meta->i_status & ITEM_PREPARSED ) )
    {
        vlc_mutex_unlock( &p_item->lock );
        return;
    }
    vlc_mutex_unlock( &p_item->lock );
#endif

2873 2874
    demux_Control( p_demux, DEMUX_GET_META, p_meta );
    if( demux_Control( p_demux, DEMUX_HAS_UNSUPPORTED_META, &b_bool ) )
2875 2876 2877 2878 2879 2880 2881 2882
        return;
    if( !b_bool )
        return;

    p_demux->p_private = malloc( sizeof( demux_meta_t ) );
    if(! p_demux->p_private )
        return;

2883
    p_id3 = module_need( p_demux, "meta reader", NULL, 0 );
2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900
    if( p_id3 )
    {
        demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private;

        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 )
        {
            vlc_mutex_lock( &p_input->p->input.p_item->lock );
            AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment,
                              p_demux_meta->i_attachments, p_demux_meta->attachments );
            vlc_mutex_unlock( &p_input->p->input.p_item->lock );
        }
2901
        module_unneed( p_demux, p_id3 );
2902 2903 2904 2905
    }
    free( p_demux->p_private );
}

2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916
static void input_ChangeState( input_thread_t *p_input, int i_state )
{
    const bool b_changed = p_input->i_state != i_state;

    p_input->i_state = i_state;
    if( i_state == ERROR_S )
        p_input->b_error = true;
    else if( i_state == END_S )
        p_input->b_eof = true;

    if( b_changed )
2917 2918
    {
        input_item_SetErrorWhenReading( p_input->p->input.p_item, p_input->b_error );
2919
        input_SendEventState( p_input, i_state );
2920
    }
2921 2922
}

2923

2924 2925 2926 2927
/*****************************************************************************
 * MRLSplit: parse the access, demux and url part of the
 *           Media Resource Locator.
 *****************************************************************************/
2928 2929
void input_SplitMRL( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path,
                     char *psz_dup )
2930
{
2931 2932
    char *psz_access = NULL;
    char *psz_demux  = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
2933
    char *psz_path;
2934

2935 2936 2937 2938
    /* Either there is an access/demux specification before ://
     * or we have a plain local file path. */
    psz_path = strstr( psz_dup, "://" );
    if( psz_path != NULL )
2939
    {
2940 2941
        *psz_path = '\0';
        psz_path += 3; /* skips "://" */
2942

2943
        /* Separate access from demux (<access>/<demux>://<path>) */
2944
        psz_access = psz_dup;
2945 2946 2947
        psz_demux = strchr( psz_access, '/' );
        if( psz_demux )
            *psz_demux++ = '\0';
2948 2949 2950 2951 2952 2953

        /* We really don't want module name substitution here! */
        if( psz_access[0] == '$' )
            psz_access++;
        if( psz_demux && psz_demux[0] == '$' )
            psz_demux++;
2954 2955 2956 2957 2958
    }
    else
    {
        psz_path = psz_dup;
    }
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
2959 2960
    *ppsz_access = psz_access ? psz_access : (char*)"";
    *ppsz_demux = psz_demux ? psz_demux : (char*)"";
2961
    *ppsz_path = psz_path;
2962
}
2963

2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977
static inline bool next(char ** src)
{
    char *end;
    errno = 0;
    long result = strtol( *src, &end, 0 );
    if( errno != 0 || result >= LONG_MAX || result <= LONG_MIN ||
        end == *src )
    {
        return false;
    }
    *src = end;
    return true;
}

2978 2979 2980 2981
/*****************************************************************************
 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
 *
 * Syntax:
2982
 * [url][@[title-start][:chapter-start][-[title-end][:chapter-end]]]
2983 2984 2985 2986 2987
 *****************************************************************************/
static void MRLSections( input_thread_t *p_input, char *psz_source,
                         int *pi_title_start, int *pi_title_end,
                         int *pi_chapter_start, int *pi_chapter_end )
{
2988
    char *psz, *psz_end, *psz_next, *psz_check;
2989 2990 2991 2992 2993 2994 2995

    *pi_title_start = *pi_title_end = -1;
    *pi_chapter_start = *pi_chapter_end = -1;

    /* Start by parsing titles and chapters */
    if( !psz_source || !( psz = strrchr( psz_source, '@' ) ) ) return;

2996

2997 2998 2999
    /* Check we are really dealing with a title/chapter section */
    psz_check = psz + 1;
    if( !*psz_check ) return;
3000 3001
    if( isdigit(*psz_check) )
        if(!next(&psz_check)) return;
3002 3003
    if( *psz_check != ':' && *psz_check != '-' && *psz_check ) return;
    if( *psz_check == ':' && ++psz_check )
3004 3005 3006 3007
    {
        if( isdigit(*psz_check) )
            if(!next(&psz_check)) return;
    }
3008 3009
    if( *psz_check != '-' && *psz_check ) return;
    if( *psz_check == '-' && ++psz_check )
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
3010 3011 3012 3013
    {
        if( isdigit(*psz_check) )
            if(!next(&psz_check)) return;
    }
3014 3015
    if( *psz_check != ':' && *psz_check ) return;
    if( *psz_check == ':' && ++psz_check )
3016 3017 3018 3019
    {
        if( isdigit(*psz_check) )
            if(!next(&psz_check)) return;
    }
3020
    if( *psz_check ) return;
3021 3022

    /* Separate start and end */
3023
    *psz++ = 0;
3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054
    if( ( psz_end = strchr( psz, '-' ) ) ) *psz_end++ = 0;

    /* Look for the start title */
    *pi_title_start = strtol( psz, &psz_next, 0 );
    if( !*pi_title_start && psz == psz_next ) *pi_title_start = -1;
    *pi_title_end = *pi_title_start;
    psz = psz_next;

    /* Look for the start chapter */
    if( *psz ) psz++;
    *pi_chapter_start = strtol( psz, &psz_next, 0 );
    if( !*pi_chapter_start && psz == psz_next ) *pi_chapter_start = -1;
    *pi_chapter_end = *pi_chapter_start;

    if( psz_end )
    {
        /* Look for the end title */
        *pi_title_end = strtol( psz_end, &psz_next, 0 );
        if( !*pi_title_end && psz_end == psz_next ) *pi_title_end = -1;
        psz_end = psz_next;

        /* Look for the end chapter */
        if( *psz_end ) psz_end++;
        *pi_chapter_end = strtol( psz_end, &psz_next, 0 );
        if( !*pi_chapter_end && psz_end == psz_next ) *pi_chapter_end = -1;
    }

    msg_Dbg( p_input, "source=`%s' title=%d/%d seekpoint=%d/%d",
             psz_source, *pi_title_start, *pi_chapter_start,
             *pi_title_end, *pi_chapter_end );
}
3055 3056 3057 3058

/*****************************************************************************
 * input_AddSubtitles: add a subtitles file and enable it
 *****************************************************************************/
3059
static void SubtitleAdd( input_thread_t *p_input, char *psz_subtitle, bool b_forced )
3060 3061 3062 3063
{
    input_source_t *sub;
    vlc_value_t count;
    vlc_value_t list;
3064
    char *psz_path, *psz_extension;
3065

3066 3067 3068 3069 3070 3071 3072 3073
    /* if we are provided a subtitle.sub file,
     * see if we don't have a subtitle.idx and use it instead */
    psz_path = strdup( psz_subtitle );
    if( psz_path )
    {
        psz_extension = strrchr( psz_path, '.');
        if( psz_extension && strcmp( psz_extension, ".sub" ) == 0 )
        {
3074
            struct stat st;
3075 3076

            strcpy( psz_extension, ".idx" );
3077

3078
            if( !utf8_stat( psz_path, &st ) && S_ISREG( st.st_mode ) )
3079 3080 3081 3082 3083 3084 3085 3086 3087
            {
                msg_Dbg( p_input, "using %s subtitles file instead of %s",
                         psz_path, psz_subtitle );
                strcpy( psz_subtitle, psz_path );
            }
        }
        free( psz_path );
    }

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

    sub = InputSourceNew( p_input );
3091 3092 3093 3094 3095 3096 3097 3098 3099
    if( InputSourceInit( p_input, sub, psz_subtitle, "subtitle" ) )
    {
        free( sub );
        return;
    }
    TAB_APPEND( p_input->p->i_slave, p_input->p->slave, sub );

    /* Select the ES */
    if( b_forced && !var_Change( p_input, "spu-es", VLC_VAR_GETLIST, &list, NULL ) )
3100
    {
3101 3102 3103
        if( count.i_int == 0 )
            count.i_int++;
        /* if it was first one, there is disable too */
3104

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

3109 3110
            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 );
3111
        }
3112
        var_Change( p_input, "spu-es", VLC_VAR_FREELIST, &list, NULL );
3113
    }
3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124
}

bool input_AddSubtitles( input_thread_t *p_input, char *psz_subtitle,
                               bool b_check_extension )
{
    vlc_value_t val;

    if( b_check_extension && !subtitles_Filter( psz_subtitle ) )
        return false;

    assert( psz_subtitle != NULL );
3125

3126 3127 3128
    val.psz_string = strdup( psz_subtitle );
    if( val.psz_string )
        input_ControlPush( p_input, INPUT_CONTROL_ADD_SUBTITLE, &val );
3129
    return true;
3130
}
3131

3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170
/*****************************************************************************
 * Statistics
 *****************************************************************************/
void input_UpdateStatistic( input_thread_t *p_input,
                            input_statistic_t i_type, int i_delta )
{
    assert( p_input->i_state != INIT_S );

    vlc_mutex_lock( &p_input->p->counters.counters_lock);
    switch( i_type )
    {
#define I(c) stats_UpdateInteger( p_input, p_input->p->counters.c, i_delta, NULL )
    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:
    {
        int i_bytes; /* That's pretty stupid to define it as an integer, it will overflow
                        really fast ... */
        if( !stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_bytes, i_delta, &i_bytes ) )
            stats_UpdateFloat( p_input, p_input->p->counters.p_sout_send_bitrate, i_bytes, NULL );
        break;
    }
    default:
        msg_Err( p_input, "Invalid statistic type %d (internal error)", i_type );
        break;
    }
    vlc_mutex_unlock( &p_input->p->counters.counters_lock);
}
3171 3172 3173
/*****************************************************************************
 * input_get_event_manager
 *****************************************************************************/
3174
vlc_event_manager_t *input_get_event_manager( input_thread_t *p_input )
3175 3176 3177
{
    return &p_input->p->event_manager;
}
3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211

/**/
/* TODO FIXME nearly the same logic that snapshot code */
char *input_CreateFilename( vlc_object_t *p_obj, const char *psz_path, const char *psz_prefix, const char *psz_extension )
{
    char *psz_file;
    DIR *path;

    path = utf8_opendir( psz_path );
    if( path )
    {
        closedir( path );

        char *psz_tmp = str_format( p_obj, psz_prefix );
        if( !psz_tmp )
            return NULL;

        filename_sanitize( psz_tmp );
        if( asprintf( &psz_file, "%s"DIR_SEP"%s%s%s",
                      psz_path, psz_tmp,
                      psz_extension ? "." : "",
                      psz_extension ? psz_extension : "" ) < 0 )
            psz_file = NULL;
        free( psz_tmp );
        return psz_file;
    }
    else
    {
        psz_file = str_format( p_obj, psz_path );
        path_sanitize( psz_file );
        return psz_file;
    }
}