input.c 109 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
635
        msg_Dbg( p_input, "EOF reached" );
        p_input->p->input.b_eof = true;
636
637
638
639
640
641
642
643
    }
    else if( i_ret < 0 )
    {
        input_ChangeState( p_input, ERROR_S );
    }

    if( i_ret > 0 && p_input->p->i_slave > 0 )
    {
644
645
646
647
        bool b_demux_polled;
        SlaveDemux( p_input, &b_demux_polled );

        *pb_demux_polled |= b_demux_polled;
648
649
650
    }
}

651
652
653
654
655
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
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;
}

697
698
699
700
701
702
/**
 * MainLoopInterface
 * It update the variables used by the interfaces
 */
static void MainLoopInterface( input_thread_t *p_input )
{
703
704
705
    double f_position = 0.0;
    mtime_t i_time = 0;
    mtime_t i_length = 0;
706
707

    /* update input status variables */
708
    if( demux_Control( p_input->p->input.p_demux,
709
710
                       DEMUX_GET_POSITION, &f_position ) )
        f_position = 0.0;
711

712
    if( demux_Control( p_input->p->input.p_demux,
713
714
                       DEMUX_GET_TIME, &i_time ) )
        i_time = 0;
Laurent Aimar's avatar
Laurent Aimar committed
715
    p_input->p->i_time = i_time;
716
717

    if( demux_Control( p_input->p->input.p_demux,
718
719
                       DEMUX_GET_LENGTH, &i_length ) )
        i_length = 0;
720

721
    es_out_SetTimes( p_input->p->p_es_out, f_position, i_time, i_length );
722
723
724
725
726
727
728

    /* 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 );
729
730
731
732
733
734
735
736
}

/**
 * MainLoopStatistic
 * It updates the globals statics
 */
static void MainLoopStatistic( input_thread_t *p_input )
{
737
    stats_ComputeInputStats( p_input, p_input->p->p_item->p_stats );
738
    input_SendEventStatistics( p_input );
739
740
741
742
743
744
745
746
747
}

/**
 * MainLoop
 * The main input loop.
 */
static void MainLoop( input_thread_t *p_input )
{
    mtime_t i_start_mdate = mdate();
748
749
    mtime_t i_intf_update = 0;
    mtime_t i_statistic_update = 0;
750
    bool b_pause_after_eof = var_CreateGetBool( p_input, "play-and-pause" );
751
752
753
754

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

755
    while( vlc_object_alive( p_input ) && !p_input->b_error )
756
757
758
759
    {
        bool b_force_update;
        int i_type;
        vlc_value_t val;
760
        mtime_t i_current;
761
        mtime_t i_deadline;
762
        mtime_t i_wakeup;
763
        bool b_paused;
764
        bool b_demux_polled;
765

766
767
        /* Demux data */
        b_force_update = false;
768
        i_wakeup = 0;
Laurent Aimar's avatar
Laurent Aimar committed
769
        /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
770
771
         * 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
772
        b_paused = p_input->p->i_state == PAUSE_S &&
773
                   ( !es_out_GetBuffering( p_input->p->p_es_out ) || p_input->p->input.b_eof );
774

775
        b_demux_polled = true;
776
        if( !b_paused )
777
        {
778
779
            if( !p_input->p->input.b_eof )
            {
780
                MainLoopDemux( p_input, &b_force_update, &b_demux_polled, i_start_mdate );
781

782
783
                i_wakeup = es_out_GetWakeup( p_input->p->p_es_out );
            }
784
            else if( !es_out_GetEmpty( p_input->p->p_es_out ) )
785
786
787
788
            {
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
                i_wakeup = mdate() + INPUT_IDLE_SLEEP;
            }
789
790
791
792
793
794
795
796
797
            else if( b_pause_after_eof )
            {
                msg_Dbg( p_input, "pausing at EOF (pause after each)");
                val.i_int = PAUSE_S;
                Control( p_input, INPUT_CONTROL_SET_STATE, val );

                b_pause_after_eof = false;
                b_paused = true;
            }
798
799
            else
            {
800
801
                if( MainLoopTryRepeat( p_input, &i_start_mdate ) )
                    break;
802
                b_pause_after_eof = var_GetBool( p_input, "play-and-pause" );
803
            }
804
        }
805
806

        /* */
807
808
        do {
            i_deadline = i_wakeup;
809
            if( b_paused || !b_demux_polled )
810
811
812
813
                i_deadline = __MIN( i_intf_update, i_statistic_update );

            /* Handle control */
            ControlReduce( p_input );
814
            while( !ControlPop( p_input, &i_type, &val, i_deadline ) )
815
            {
816

817
                msg_Dbg( p_input, "control type=%d", i_type );
818

819
820
821
                if( Control( p_input, i_type, val ) )
                    b_force_update = true;
            }
822

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

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

843
    if( !p_input->b_error )
844
        input_ChangeState( p_input, END_S );
845
846
}

847
static void InitStatistics( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
848
{
849
    if( p_input->b_preparsing ) return;
850

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

880
#ifdef ENABLE_SOUT
881
882
static int InitSout( input_thread_t * p_input )
{
883
884
    if( p_input->b_preparsing )
        return VLC_SUCCESS;
885
886

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

915
916
    return VLC_SUCCESS;
}
917
#endif
918

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

Laurent Aimar's avatar
Laurent Aimar committed
923
924
    if( p_input->b_preparsing )
        return;
925

926
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
927
928
929
930
    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;
931
932
933
934
    if( p_input->p->i_title > 0 )
    {
        /* Setup variables */
        input_ControlVarNavigation( p_input );
935
        input_SendEventTitle( p_input, 0 );
936
937
938
    }

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

944
945
946
static void StartTitle( input_thread_t * p_input )
{
    vlc_value_t val;
947

948
949
950
951
952
    /* 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
953

954
955
956
957
958
    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
959
    /* Start/stop/run time */
960
961
962
963
964
965
    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" ));
966
    if( p_input->p->i_run < 0 )
967
    {
968
969
        msg_Warn( p_input, "invalid run-time ignored" );
        p_input->p->i_run = 0;
Rafaël Carré's avatar
Rafaël Carré committed
970
    }
971

972
    if( p_input->p->i_start > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
973
    {
974
        vlc_value_t s;
Laurent Aimar's avatar
Laurent Aimar committed
975

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

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

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

999
1000
        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
1001

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

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

    /* Look for and add subtitle files */