input.c 108 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>
Laurent Aimar's avatar
Laurent Aimar committed
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
dionoea's avatar
dionoea 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
#include <errno.h>
38

Laurent Aimar's avatar
Laurent Aimar committed
39
#include "input_internal.h"
40
#include "event.h"
41
#include "es_out.h"
42
#include "es_out_timeshift.h"
43
#include "access.h"
44
#include "demux.h"
45
#include "stream.h"
46
#include "item.h"
47
#include "resource.h"
48

zorglub's avatar
zorglub committed
49
50
#include <vlc_sout.h>
#include "../stream_output/stream_output.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
51

52
#include <vlc_dialog.h>
zorglub's avatar
zorglub committed
53
54
#include <vlc_url.h>
#include <vlc_charset.h>
55
#include <vlc_strings.h>
56

57
58
59
60
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif

61
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
62
 * Local prototypes
63
 *****************************************************************************/
64
65
static void Destructor( input_thread_t * p_input );

66
67
static  void *Run            ( vlc_object_t *p_this );
static  void *RunAndDestroy  ( vlc_object_t *p_this );
Laurent Aimar's avatar
Laurent Aimar committed
68

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69
static input_thread_t * Create  ( vlc_object_t *, input_item_t *,
70
                                  const char *, bool, input_resource_t * );
71
static  int             Init    ( input_thread_t *p_input );
72
73
static void             End     ( input_thread_t *p_input );
static void             MainLoop( input_thread_t *p_input );
Laurent Aimar's avatar
Laurent Aimar committed
74

Laurent Aimar's avatar
Laurent Aimar committed
75
76
static void ObjectKillChildrens( input_thread_t *, vlc_object_t * );

77
static inline int ControlPop( input_thread_t *, int *, vlc_value_t *, mtime_t i_deadline );
Laurent Aimar's avatar
Laurent Aimar committed
78
static void       ControlReduce( input_thread_t * );
79
80
static void       ControlRelease( int i_type, vlc_value_t val );
static bool       Control( input_thread_t *, int, vlc_value_t );
81

82
83
84
85
86
static int  UpdateTitleSeekpointFromAccess( input_thread_t * );
static void UpdateGenericFromAccess( input_thread_t * );

static int  UpdateTitleSeekpointFromDemux( input_thread_t * );
static void UpdateGenericFromDemux( input_thread_t * );
Michel Kaempf's avatar
Michel Kaempf committed
87

88
static void MRLSections( input_thread_t *, char *, int *, int *, int *, int *);
89
90

static input_source_t *InputSourceNew( input_thread_t *);
Laurent Aimar's avatar
   
Laurent Aimar committed
91
static int  InputSourceInit( input_thread_t *, input_source_t *,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
92
                             const char *, const char *psz_forced_demux );
93
static void InputSourceClean( input_source_t * );
Laurent Aimar's avatar
Laurent Aimar committed
94
95
static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );

96
97
/* TODO */
//static void InputGetAttachments( input_thread_t *, input_source_t * );
98
static void SlaveDemux( input_thread_t *p_input, bool *pb_demux_polled );
99
static void SlaveSeek( input_thread_t *p_input );
100

101
102
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 );
103
104
105
static void InputGetExtraFiles( input_thread_t *p_input,
                                int *pi_list, char ***pppsz_list,
                                const char *psz_access, const char *psz_path );
106

107
108
109
static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
                              int i_new, input_attachment_t **pp_new );

110
111
static void SubtitleAdd( input_thread_t *p_input, char *psz_subtitle, bool b_forced );

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

114
/* Do not let a pts_delay from access/demux go beyong 60s */
115
#define INPUT_PTS_DELAY_MAX INT64_C(60000000)
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/**
 * Create a new input_thread_t.
 *
 * You need to call input_Start on it when you are done
 * adding callback on the variables/events you want to monitor.
 *
 * \param p_parent a vlc_object
 * \param p_item an input item
 * \param psz_log an optional prefix for this input logs
 * \param p_resource an optional input ressource
 * \return a pointer to the spawned input thread
 */

input_thread_t *__input_Create( vlc_object_t *p_parent,
                                input_item_t *p_item,
                                const char *psz_log, input_resource_t *p_resource )
{

    return Create( p_parent, p_item, psz_log, false, p_resource );
}

/**
 * Create a new input_thread_t and start it.
 *
 * Provided for convenience.
 *
 * \see input_Create
 */
input_thread_t *__input_CreateAndStart( vlc_object_t *p_parent,
                                        input_item_t *p_item, const char *psz_log )
{
    input_thread_t *p_input = __input_Create( p_parent, p_item, psz_log, NULL );

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

/**
 * Initialize an input thread and run it. This thread will clean after itself,
 * 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 ?
 * \return an error code, VLC_SUCCESS on success
 */
int __input_Read( vlc_object_t *p_parent, input_item_t *p_item,
                   bool b_block )
{
    input_thread_t *p_input;

    p_input = Create( p_parent, p_item, NULL, false, NULL );
    if( !p_input )
        return VLC_EGENERIC;

    if( b_block )
    {
        RunAndDestroy( VLC_OBJECT(p_input) );
        return VLC_SUCCESS;
    }
    else
    {
        if( vlc_thread_create( p_input, "input", RunAndDestroy,
                               VLC_THREAD_PRIORITY_INPUT ) )
        {
            input_ChangeState( p_input, ERROR_S );
            msg_Err( p_input, "cannot create input thread" );
            vlc_object_release( p_input );
            return VLC_EGENERIC;
        }
    }
    return VLC_SUCCESS;
}

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

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

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

    vlc_object_release( p_input );

    return VLC_SUCCESS;
}

/**
 * Start a input_thread_t created by input_Create.
 *
 * You must not start an already running input_thread_t.
 *
 * \param the input thread to start
 */
int input_Start( input_thread_t *p_input )
{
    /* Create thread and wait for its readiness. */
    if( vlc_thread_create( p_input, "input", Run,
                           VLC_THREAD_PRIORITY_INPUT ) )
    {
        input_ChangeState( p_input, ERROR_S );
        msg_Err( p_input, "cannot create input thread" );
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

/**
 * Request a running input thread to stop and die
 *
 * b_abort must be true when a user stop is requested and not because you have
 * detected an error or an eof. It will be used to properly send the
 * INPUT_EVENT_ABORT event.
 *
 * \param p_input the input thread to stop
 * \param b_abort true if the input has been aborted by a user request
 */
void input_Stop( input_thread_t *p_input, bool b_abort )
{
    /* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
     * It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
     * unlock the control loop */
    ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );

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

    input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}

input_resource_t *input_DetachResource( input_thread_t *p_input )
{
    assert( p_input->b_dead );

    input_resource_SetInput( p_input->p->p_resource, NULL );

    input_resource_t *p_resource = input_resource_Detach( p_input->p->p_resource );
    p_input->p->p_sout = NULL;

    return p_resource;
}

/**
 * Get the item from an input thread
 * FIXME it does not increase ref count of the item.
 * if it is used after p_input is destroyed nothing prevent it from
 * being freed.
 */
input_item_t *input_GetItem( input_thread_t *p_input )
{
    assert( p_input && p_input->p );
    return p_input->p->p_item;
}

/*****************************************************************************
 * ObjectKillChildrens
 *****************************************************************************/
static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
{
    vlc_list_t *p_list;
    int i;

    /* FIXME ObjectKillChildrens seems a very bad idea in fact */
    i = vlc_internals( p_obj )->i_object_type;
    if( i == VLC_OBJECT_VOUT ||i == VLC_OBJECT_AOUT ||
        p_obj == VLC_OBJECT(p_input->p->p_sout) ||
300
        i == VLC_OBJECT_DECODER )
301
302
303
304
305
306
307
308
309
310
        return;

    vlc_object_kill( p_obj );

    p_list = vlc_list_children( p_obj );
    for( i = 0; i < p_list->i_count; i++ )
        ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
    vlc_list_release( p_list );
}

311
/*****************************************************************************
312
313
 * This function creates a new input, and returns a pointer
 * to its description. On error, it returns NULL.
Laurent Aimar's avatar
Laurent Aimar committed
314
 *
Laurent Aimar's avatar
Laurent Aimar committed
315
 * XXX Do not forget to update vlc_input.h if you add new variables.
316
 *****************************************************************************/
317
static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
318
                               const char *psz_header, bool b_quick,
319
                               input_resource_t *p_resource )
Michel Kaempf's avatar
Michel Kaempf committed
320
{
321
    static const char input_name[] = "input";
322
    input_thread_t *p_input = NULL;                 /* thread descriptor */
323
    int i;
324

325
    /* Allocate descriptor */
326
327
    p_input = vlc_custom_create( p_parent, sizeof( *p_input ),
                                 VLC_OBJECT_INPUT, input_name );
328
    if( p_input == NULL )
329
        return NULL;
330
331
332
333
334
335

    /* 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 );
336
337
338

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

339
340
341
342
343
344
345
    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 );

346
347
348
    p_input->p = calloc( 1, sizeof( input_thread_private_t ) );
    if( !p_input->p )
        return NULL;
349

350
    p_input->b_preparsing = b_quick;
351
    p_input->psz_header = psz_header ? strdup( psz_header ) : NULL;
352

Laurent Aimar's avatar
Laurent Aimar committed
353
    /* Init Common fields */
354
    p_input->b_eof = false;
355
    p_input->p->b_can_pace_control = true;
zorglub's avatar
zorglub committed
356
    p_input->p->i_start = 0;
Laurent Aimar's avatar
Laurent Aimar committed
357
    p_input->p->i_time  = 0;
zorglub's avatar
zorglub committed
358
    p_input->p->i_stop  = 0;
359
    p_input->p->i_run   = 0;
zorglub's avatar
zorglub committed
360
    p_input->p->i_title = 0;
361
    p_input->p->title = NULL;
zorglub's avatar
zorglub committed
362
    p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
Laurent Aimar's avatar
Laurent Aimar committed
363
    p_input->p->i_state = INIT_S;
364
    p_input->p->i_rate = INPUT_RATE_DEFAULT;
365
    p_input->p->b_recording = false;
366
367
    memset( &p_input->p->bookmark, 0, sizeof(p_input->p->bookmark) );
    TAB_INIT( p_input->p->i_bookmark, p_input->p->pp_bookmark );
368
    TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
369
370
    p_input->p->p_es_out_display = NULL;
    p_input->p->p_es_out = NULL;
371
    p_input->p->p_sout   = NULL;
372
    p_input->p->b_out_pace_control = false;
Laurent Aimar's avatar
Laurent Aimar committed
373

Pierre's avatar
Pierre committed
374
    vlc_gc_incref( p_item ); /* Released in Destructor() */
375
376
377
    p_input->p->p_item = p_item;

    /* Init Input fields */
zorglub's avatar
zorglub committed
378
379
380
    p_input->p->input.p_access = NULL;
    p_input->p->input.p_stream = NULL;
    p_input->p->input.p_demux  = NULL;
381
    p_input->p->input.b_title_demux = false;
zorglub's avatar
zorglub committed
382
383
384
    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;
385
386
387
388
    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;
zorglub's avatar
zorglub committed
389

390
    vlc_mutex_lock( &p_item->lock );
391
392

    if( !p_item->p_stats )
393
394
        p_item->p_stats = stats_NewInputStats( p_input );
    vlc_mutex_unlock( &p_item->lock );
zorglub's avatar
zorglub committed
395

396
    /* No slave */
zorglub's avatar
zorglub committed
397
398
    p_input->p->i_slave = 0;
    p_input->p->slave   = NULL;
399

400
    /* */
401
402
    if( p_resource )
        p_input->p->p_resource = p_resource;
403
    else
404
405
        p_input->p->p_resource = input_resource_New();
    input_resource_SetInput( p_input->p->p_resource, p_input );
406

Laurent Aimar's avatar
Laurent Aimar committed
407
    /* Init control buffer */
408
    vlc_mutex_init( &p_input->p->lock_control );
409
    vlc_cond_init( &p_input->p->wait_control );
zorglub's avatar
zorglub committed
410
    p_input->p->i_control = 0;
411
    p_input->p->b_abort = false;
412

gbazin's avatar
   
gbazin committed
413
    /* Parse input options */
414
    vlc_mutex_lock( &p_item->lock );
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
415
    assert( (int)p_item->optflagc == p_item->i_options );
416
    for( i = 0; i < p_item->i_options; i++ )
417
418
        var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i],
                         !!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) );
419
    vlc_mutex_unlock( &p_item->lock );
gbazin's avatar
   
gbazin committed
420

Laurent Aimar's avatar
Laurent Aimar committed
421
422
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
423

Laurent Aimar's avatar
Laurent Aimar committed
424
    /* Create Objects variables for public Get and Set */
425
    input_ControlVarInit( p_input );
426

427
    /* */
428
    if( !p_input->b_preparsing )
gbazin's avatar
gbazin committed
429
    {
Laurent Aimar's avatar
Laurent Aimar committed
430
431
        char *psz_bookmarks = var_GetNonEmptyString( p_input, "bookmarks" );
        if( psz_bookmarks )
gbazin's avatar
gbazin committed
432
        {
433
434
            /* FIXME: have a common cfg parsing routine used by sout and others */
            char *psz_parser, *psz_start, *psz_end;
Laurent Aimar's avatar
Laurent Aimar committed
435
            psz_parser = psz_bookmarks;
436
            while( (psz_start = strchr( psz_parser, '{' ) ) )
gbazin's avatar
gbazin committed
437
            {
hartman's avatar
hartman committed
438
                 seekpoint_t *p_seekpoint;
439
440
441
442
443
444
445
446
                 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 = ',';
hartman's avatar
hartman committed
447
448

                 p_seekpoint = vlc_seekpoint_New();
449
450
451
452
453
                 while( (psz_end = strchr( psz_start, ',' ) ) )
                 {
                     *psz_end = 0;
                     if( !strncmp( psz_start, "name=", 5 ) )
                     {
454
                         p_seekpoint->psz_name = strdup(psz_start + 5);
455
456
457
458
459
460
461
                     }
                     else if( !strncmp( psz_start, "bytes=", 6 ) )
                     {
                         p_seekpoint->i_byte_offset = atoll(psz_start + 6);
                     }
                     else if( !strncmp( psz_start, "time=", 5 ) )
                     {
zorglub's avatar
zorglub committed
462
463
                         p_seekpoint->i_time_offset = atoll(psz_start + 5) *
                                                        1000000;
464
465
                     }
                     psz_start = psz_end + 1;
gbazin's avatar
gbazin committed
466
                }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
467
                msg_Dbg( p_input, "adding bookmark: %s, bytes=%"PRId64", time=%"PRId64,
468
469
470
471
472
                                  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;
gbazin's avatar
gbazin committed
473
            }
Laurent Aimar's avatar
Laurent Aimar committed
474
            free( psz_bookmarks );
gbazin's avatar
gbazin committed
475
476
477
        }
    }

478
    /* Remove 'Now playing' info as it is probably outdated */
479
    input_item_SetNowPlaying( p_item, NULL );
480
    input_SendEventMeta( p_input );
481

482
483
484
485
    /* */
    if( p_input->b_preparsing )
        p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;

486
    /* */
487
    memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
488
    vlc_mutex_init( &p_input->p->counters.counters_lock );
489

490
491
492
    /* Set the destructor when we are sure we are initialized */
    vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor );

493
494
495
    /* Attach only once we are ready */
    vlc_object_attach( p_input, p_parent );

496
497
498
    return p_input;
}

499
500
501
502
/**
 * Input destructor (called when the object's refcount reaches 0).
 */
static void Destructor( input_thread_t * p_input )
503
{
504
#ifndef NDEBUG
505
    char * psz_name = input_item_GetName( p_input->p->p_item );
506
507
508
509
    msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
    free( psz_name );
#endif

510
511
    stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING );
    stats_TimerClean( p_input, STATS_TIMER_INPUT_LAUNCHING );
512

513
514
    if( p_input->p->p_resource )
        input_resource_Delete( p_input->p->p_resource );
515

516
    vlc_gc_decref( p_input->p->p_item );
Pierre's avatar
Pierre committed
517

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

520
    for( int i = 0; i < p_input->p->i_control; i++ )
Laurent Aimar's avatar
Laurent Aimar committed
521
522
523
524
    {
        input_control_t *p_ctrl = &p_input->p->control[i];
        ControlRelease( p_ctrl->i_type, p_ctrl->val );
    }
525

526
    vlc_cond_destroy( &p_input->p->wait_control );
527
528
    vlc_mutex_destroy( &p_input->p->lock_control );
    free( p_input->p );
529
}
530

531
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
532
 * Run: main thread loop
533
534
 * This is the "normal" thread that spawns the input processing chain,
 * reads the stream, cleans up and waits
535
 *****************************************************************************/
536
static void *Run( vlc_object_t *p_this )
Michel Kaempf's avatar
Michel Kaempf committed
537
{
ivoire's avatar
ivoire committed
538
    input_thread_t *p_input = (input_thread_t *)p_this;
539
    const int canc = vlc_savecancel();
540

541
    if( Init( p_input ) )
542
        goto exit;
Michel Kaempf's avatar
Michel Kaempf committed
543

544
545
546
547
    MainLoop( p_input );

    /* Clean up */
    End( p_input );
548
549

exit:
550
    /* Tell we're dead */
551
552
553
554
555
    vlc_mutex_lock( &p_input->p->lock_control );
    const bool b_abort = p_input->p->b_abort;
    vlc_mutex_unlock( &p_input->p->lock_control );

    if( b_abort )
556
        input_SendEventAbort( p_input );
557
    input_SendEventDead( p_input );
558

559
    vlc_restorecancel( canc );
ivoire's avatar
ivoire committed
560
    return NULL;
561
562
563
}

/*****************************************************************************
564
 * RunAndDestroy: main thread loop
565
566
567
 * This is the "just forget me" thread that spawns the input processing chain,
 * reads the stream, cleans up and releases memory
 *****************************************************************************/
568
static void *RunAndDestroy( vlc_object_t *p_this )
569
{
ivoire's avatar
ivoire committed
570
    input_thread_t *p_input = (input_thread_t *)p_this;
571
    const int canc = vlc_savecancel();
572

573
    if( Init( p_input ) )
574
        goto exit;
575
576
577
578
579
580

    MainLoop( p_input );

    /* Clean up */
    End( p_input );

581
exit:
582
    /* Release memory */
583
    vlc_object_release( p_input );
584
585
    vlc_restorecancel( canc );
    return NULL;
586
587
588
589
590
}

/*****************************************************************************
 * Main loop: Fill buffers from access, and demux
 *****************************************************************************/
591
592
593
594
595

/**
 * MainLoopDemux
 * It asks the demuxer to demux some data
 */
596
static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed, bool *pb_demux_polled, mtime_t i_start_mdate )
597
{
598
    int i_ret;
599

600
    *pb_changed = false;
601
    *pb_demux_polled = p_input->p->input.p_demux->pf_demux != NULL;
602

Laurent Aimar's avatar
Laurent Aimar committed
603
    if( ( p_input->p->i_stop > 0 && p_input->p->i_time >= p_input->p->i_stop ) ||
604
        ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) )
605
606
        i_ret = 0; /* EOF */
    else
Laurent Aimar's avatar
Laurent Aimar committed
607
        i_ret = demux_Demux( p_input->p->input.p_demux );
608
609

    if( i_ret > 0 )
Sam Hocevar's avatar
   
Sam Hocevar committed
610
    {
611
        if( p_input->p->input.p_demux->info.i_update )
612
        {
613
614
615
616
617
618
            if( p_input->p->input.b_title_demux )
            {
                i_ret = UpdateTitleSeekpointFromDemux( p_input );
                *pb_changed = true;
            }
            UpdateGenericFromDemux( p_input );
619
        }
620
621
        else if( p_input->p->input.p_access &&
                 p_input->p->input.p_access->info.i_update )
622
        {
623
624
625
626
627
628
            if( !p_input->p->input.b_title_demux )
            {
                i_ret = UpdateTitleSeekpointFromAccess( p_input );
                *pb_changed = true;
            }
            UpdateGenericFromAccess( p_input );
629
630
        }
    }
631

632
633
    if( i_ret == 0 )    /* EOF */
    {
634
        bool b_pause_after_each = var_CreateGetBool( p_input, "play-and-pause" );
635
        msg_Dbg( p_input, "EOF reached" );
636
637
638
639
640
641
642
        if ( b_pause_after_each )
        {
            msg_Dbg( p_input, "pausing at EOF (pause after each)");
            vlc_value_t pause_state;
            pause_state.i_int = PAUSE_S;
            Control( p_input, INPUT_CONTROL_SET_STATE, pause_state );
        }
643
        p_input->p->input.b_eof = true;
644
645
646
647
648
649
650
651
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
    {
652
653
654
655
        bool b_demux_polled;
        SlaveDemux( p_input, &b_demux_polled );

        *pb_demux_polled |= b_demux_polled;
656
657
658
    }
}

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
static int MainLoopTryRepeat( input_thread_t *p_input, mtime_t *pi_start_mdate )
{
    int i_repeat = var_GetInteger( p_input, "input-repeat" );
    if( i_repeat == 0 )
        return VLC_EGENERIC;

    vlc_value_t val;

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

    /* Seek to start title/seekpoint */
    val.i_int = p_input->p->input.i_title_start -
        p_input->p->input.i_title_offset;
    if( val.i_int < 0 || val.i_int >= p_input->p->input.i_title )
        val.i_int = 0;
    input_ControlPush( p_input,
                       INPUT_CONTROL_SET_TITLE, &val );

    val.i_int = p_input->p->input.i_seekpoint_start -
        p_input->p->input.i_seekpoint_offset;
    if( val.i_int > 0 /* TODO: check upper boundary */ )
        input_ControlPush( p_input,
                           INPUT_CONTROL_SET_SEEKPOINT, &val );

    /* Seek to start position */
    if( p_input->p->i_start > 0 )
    {
        val.i_time = p_input->p->i_start;
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &val );
    }
    else
    {
        val.f_float = 0.0;
        input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &val );
    }

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

705
706
707
708
709
710
/**
 * MainLoopInterface
 * It update the variables used by the interfaces
 */
static void MainLoopInterface( input_thread_t *p_input )
{
711
712
713
    double f_position = 0.0;
    mtime_t i_time = 0;
    mtime_t i_length = 0;
714
715

    /* update input status variables */
716
    if( demux_Control( p_input->p->input.p_demux,
717
718
                       DEMUX_GET_POSITION, &f_position ) )
        f_position = 0.0;
719

720
    if( demux_Control( p_input->p->input.p_demux,
721
722
                       DEMUX_GET_TIME, &i_time ) )
        i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
723
    p_input->p->i_time = i_time;
724
725

    if( demux_Control( p_input->p->input.p_demux,
726
727
                       DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
728

729
    es_out_SetTimes( p_input->p->p_es_out, f_position, i_time, i_length );
730
731
732
733
734
735
736

    /* update current bookmark */
    vlc_mutex_lock( &p_input->p->p_item->lock );
    p_input->p->bookmark.i_time_offset = i_time;
    if( p_input->p->input.p_stream )
        p_input->p->bookmark.i_byte_offset = stream_Tell( p_input->p->input.p_stream );
    vlc_mutex_unlock( &p_input->p->p_item->lock );
737
738
739
740
741
742
743
744
}

/**
 * MainLoopStatistic
 * It updates the globals statics
 */
static void MainLoopStatistic( input_thread_t *p_input )
{
745
    stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
746
    input_SendEventStatistics( p_input );
747
748
749
750
751
752
753
754
755
}

/**
 * MainLoop
 * The main input loop.
 */
static void MainLoop( input_thread_t *p_input )
{
    mtime_t i_start_mdate = mdate();
756
757
    mtime_t i_intf_update = 0;
    mtime_t i_statistic_update = 0;
758
759
760
761

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

762
    while( vlc_object_alive( p_input ) && !p_input->b_error )
763
764
765
766
    {
        bool b_force_update;
        int i_type;
        vlc_value_t val;
767
        mtime_t i_current;
768
        mtime_t i_deadline;
769
        mtime_t i_wakeup;
770
        bool b_paused;
771
        bool b_demux_polled;
772

773
774
        /* Demux data */
        b_force_update = false;
775
        i_wakeup = 0;
Laurent Aimar's avatar
Laurent Aimar committed
776
        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
777
778
         * is paused -> this may cause problem with some of them
         * The same problem can be seen when seeking while paused */
Laurent Aimar's avatar
Laurent Aimar committed
779
        b_paused = p_input->p->i_state == PAUSE_S &&
780
                   ( !es_out_GetBuffering( p_input->p->p_es_out ) || p_input->p->input.b_eof );
781

782
        b_demux_polled = true;
783
        if( !b_paused )
784
        {
785
786
            if( !p_input->p->input.b_eof )
            {
787
                MainLoopDemux( p_input, &b_force_update, &b_demux_polled, i_start_mdate );
788

789
790
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
            }
791
            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
792
793
794
795
796
797
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
            else
            {
798
799
                if( MainLoopTryRepeat( p_input, &i_start_mdate ) )
                    break;
800
            }
801
        }
802
803

        /* */
804
805
        do {
            i_deadline = i_wakeup;
806
            if( b_paused || !b_demux_polled )
807
808
809
810
                i_deadline = __MIN( i_intf_update, i_statistic_update );

            /* Handle control */
            ControlReduce( p_input );
811
            while( !ControlPop( p_input, &i_type, &val, i_deadline ) )
812
            {
813

814
                msg_Dbg( p_input, "control type=%d", i_type );
815

816
817
818
                if( Control( p_input, i_type, val ) )
                    b_force_update = true;
            }
819

820
821
822
823
824
825
826
827
828
829
830
831
832
            /* 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);
            }
833

834
            /* Update the wakeup time */
835
            if( i_wakeup != 0 )
836
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
837
        } while( i_current < i_wakeup );
Laurent Aimar's avatar
Laurent Aimar committed
838
    }
839

840
    if( !p_input->b_error )
841
        input_ChangeState( p_input, END_S );
842
843
}

844
static void InitStatistics( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
845
{
846
    if( p_input->b_preparsing ) return;
847

848
849
850
    /* Prepare statistics */
#define INIT_COUNTER( c, type, compute ) p_input->p->counters.p_##c = \
 stats_CounterCreate( p_input, VLC_VAR_##type, STATS_##compute);
851
    if( libvlc_stats( p_input ) )
852
853
854
855
856
857
    {
        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 );
858
859
        INIT_COUNTER( demux_corrupted, INTEGER, COUNTER );
        INIT_COUNTER( demux_discontinuity, INTEGER, COUNTER );
860
861
862
863
864
865
866
867
868
869
870
871
872
873
        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;
874
    }
875
}
876

877
#ifdef ENABLE_SOUT
878
879
static int InitSout( input_thread_t * p_input )
{
880
881
    if( p_input->b_preparsing )
        return VLC_SUCCESS;
882
883

    /* Find a usable sout and attach it to p_input */
884
    char *psz = var_GetNonEmptyString( p_input, "sout" );
885
    if( psz && strncasecmp( p_input->p->p_item->psz_uri, "vlc:", 4 ) )
886
    {
887
        p_input->p->p_sout  = input_resource_RequestSout( p_input->p->p_resource, NULL, psz );
888
        if( !p_input->p->p_sout )
zorglub's avatar
zorglub committed
889
        {
890
891
892
893
894
            input_ChangeState( p_input, ERROR_S );
            msg_Err( p_input, "cannot start stream output instance, " \
                              "aborting" );
            free( psz );
            return VLC_EGENERIC;
895
        }
896
        if( libvlc_stats( p_input ) )
897
        {
898
            INIT_COUNTER( sout_sent_packets, INTEGER, COUNTER );
899
            INIT_COUNTER( sout_sent_bytes, INTEGER, COUNTER );
900
901
902
903
            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;
904
        }
905
    }
906
    else
907
    {
908
        input_resource_RequestSout( p_input->p->p_resource, NULL, NULL );
Laurent Aimar's avatar
Laurent Aimar committed
909
    }
910
    free( psz );
911

912
913
    return VLC_SUCCESS;
}
914
#endif
915

916
917
static void InitTitle( input_thread_t * p_input )
{
Laurent Aimar's avatar
Laurent Aimar committed
918
    input_source_t *p_master = &p_input->p->input;
Laurent Aimar's avatar
Laurent Aimar committed
919

Laurent Aimar's avatar
Laurent Aimar committed
920
921
    if( p_input->b_preparsing )
        return;
922

923
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
924
925
926
927
    p_input->p->i_title = p_master->i_title;
    p_input->p->title   = p_master->title;
    p_input->p->i_title_offset = p_master->i_title_offset;
    p_input->p->i_seekpoint_offset = p_master->i_seekpoint_offset;
928
929
930
931
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
932
        input_SendEventTitle( p_input, 0 );
933
934
935
    }

    /* Global flag */
936
    p_input->p->b_can_pace_control    = p_master->b_can_pace_control;
Laurent Aimar's avatar
Laurent Aimar committed
937
938
    p_input->p->b_can_pause        = p_master->b_can_pause;
    p_input->p->b_can_rate_control = p_master->b_can_rate_control;
939
}
gbazin's avatar
   
gbazin committed
940

941
942
943
static void StartTitle( input_thread_t * p_input )
{
    vlc_value_t val;
944

945
946
947
948
949
    /* Start title/chapter */
    val.i_int = p_input->p->input.i_title_start -
                p_input->p->input.i_title_offset;
    if( val.i_int > 0 && val.i_int < p_input->p->input.i_title )
        input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
Laurent Aimar's avatar
Laurent Aimar committed
950

951
952
953
954
955
    val.i_int = p_input->p->input.i_seekpoint_start -
                p_input->p->input.i_seekpoint_offset;
    if( val.i_int > 0 /* TODO: check upper boundary */ )
        input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val );

Laurent Aimar's avatar
Laurent Aimar committed
956
    /* Start/stop/run time */
957
958
959
960
961
962
    p_input->p->i_start = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "start-time" ));
    p_input->p->i_stop  = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "stop-time" ));
    p_input->p->i_run   = (int64_t)(1000000.0
                                     * var_GetFloat( p_input, "run-time" ));
963
    if( p_input->p->i_run < 0 )
964
    {
965
966
        msg_Warn( p_input, "invalid run-time ignored" );
        p_input->p->i_run = 0;
Rafaël Carré's avatar
Rafaël Carré committed
967
    }
968

969
    if( p_input->p->i_start > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
970
    {
971
        vlc_value_t s;
Laurent Aimar's avatar
Laurent Aimar committed
972

973
974
        msg_Dbg( p_input, "starting at time: %ds",
                 (int)( p_input->p->i_start / INT64_C(1000000) ) );
Laurent Aimar's avatar
Laurent Aimar committed
975

976
977
        s.i_time = p_input->p->i_start;
        input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
978
979
980
981
982
983
    }
    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;
    }
984
    p_input->p->b_fast_seek = var_GetBool( p_input, "input-fast-seek" );
Laurent Aimar's avatar
Laurent Aimar committed
985
}
zorglub's avatar
zorglub committed
986

Laurent Aimar's avatar
Laurent Aimar committed
987
988
static void LoadSubtitles( input_thread_t *p_input )
{
989
990
    /* Load subtitles */
    /* Get fps and set it if not already set */
991
992
    const double f_fps = p_input->p->f_fps;
    if( f_fps > 1.0 )
993
994
    {
        float f_requested_fps;
zorglub's avatar
zorglub committed
995

996
997
        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
998

999
1000
        f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" );
        if( f_requested_fps != f_fps )
1001
        {
1002
1003
1004
            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT|
                                            VLC_VAR_DOINHERIT );
            var_SetFloat( p_input, "sub-fps", f_requested_fps );
1005
        }
1006
    }
Laurent Aimar's avatar
   
Laurent Aimar committed
1007

Laurent Aimar's avatar
Laurent Aimar committed
1008
    const int i_delay = var_CreateGetInteger( p_input, "sub-delay" );
1009
1010
1011
1012
    if( i_delay != 0 )
        var_SetTime( p_input, "spu-delay", (mtime_t)i_delay * 100000 );

    /* Look for and add subtitle files */