input.c 60.1 KB
Newer Older
1
2
/*****************************************************************************
 * input.c: input thread
3
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 1998-2004 VideoLAN
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
22
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
24

25
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
26
 * Preamble
27
 *****************************************************************************/
28
#include <stdlib.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
29

30
#include <vlc/vlc.h>
31
#include <vlc/input.h>
32
#include <vlc/decoder.h>
33
#include <vlc/vout.h>
Sam Hocevar's avatar
   
Sam Hocevar committed
34

Laurent Aimar's avatar
Laurent Aimar committed
35
#include "input_internal.h"
36

37
38
#include "stream_output.h"

39
#include "vlc_interface.h"
40
#include "vlc_meta.h"
Sam Hocevar's avatar
   
Sam Hocevar committed
41

42
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
43
 * Local prototypes
44
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
45
46
47
48
49
50
51
52
53
static  int Run  ( input_thread_t *p_input );

static  int Init ( input_thread_t *p_input );
static void Error( input_thread_t *p_input );
static void End  ( input_thread_t *p_input );

static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t * );
static void       ControlReduce( input_thread_t * );
static vlc_bool_t Control( input_thread_t *, int, vlc_value_t );
54

55

Laurent Aimar's avatar
Laurent Aimar committed
56
57
static void UpdateFromAccess( input_thread_t * );
static void UpdateFromDemux( input_thread_t * );
Michel Kaempf's avatar
Michel Kaempf committed
58

59
60
61
static void ParseOption( input_thread_t *p_input, const char *psz_option );

static void DecodeUrl  ( char * );
62
static void MRLSplit( input_thread_t *, char *, char **, char **, char ** );
63
64

static input_source_t *InputSourceNew( input_thread_t *);
Laurent Aimar's avatar
   
Laurent Aimar committed
65
66
static int  InputSourceInit( input_thread_t *, input_source_t *,
                             char *, char *psz_forced_demux );
67
68
69
70
static void InputSourceClean( input_thread_t *, input_source_t * );

static void SlaveDemux( input_thread_t *p_input );
static void SlaveSeek( input_thread_t *p_input );
71

72
73
static vlc_meta_t *InputMetaUser( input_thread_t *p_input );

74
/*****************************************************************************
75
 * input_CreateThread: creates a new input thread
76
 *****************************************************************************
77
78
 * 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
79
80
81
82
83
84
85
86
87
88
 *
 * Variables for _public_ use:
 * * Get and Set:
 *  - state
 *  - rate,rate-slower, rate-faster
 *  - position, position-offset
 *  - time, time-offset
 *  - title,title-next,title-prev
 *  - chapter,chapter-next, chapter-prev
 *  - program, audio-es, video-es, spu-es
89
 *  - audio-delay, spu-delay
Laurent Aimar's avatar
Laurent Aimar committed
90
91
92
93
 *  - bookmark
 * * Get only:
 *  - length
 *  - bookmarks
94
 *  - seekable (if you can seek, it doesn't say if 'bar display' has be shown or not, for that check position != 0.0)
Laurent Aimar's avatar
Laurent Aimar committed
95
96
97
98
 * * For intf callback upon changes
 *  - intf-change
 * TODO explain when Callback is called
 * TODO complete this list (?)
99
 *****************************************************************************/
100
101
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
                                      input_item_t *p_item )
102

Michel Kaempf's avatar
Michel Kaempf committed
103
{
104
105
    input_thread_t *p_input;                        /* thread descriptor */
    int             i;
106

107
    /* Allocate descriptor */
108
    p_input = vlc_object_create( p_parent, VLC_OBJECT_INPUT );
109
    if( p_input == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
110
    {
111
        msg_Err( p_parent, "out of memory" );
112
        return NULL;
Michel Kaempf's avatar
Michel Kaempf committed
113
    }
114

Laurent Aimar's avatar
Laurent Aimar committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    /* Init Common fields */
    p_input->b_eof = VLC_FALSE;
    p_input->b_can_pace_control = VLC_TRUE;
    p_input->i_start = 0;
    p_input->i_time  = 0;
    p_input->i_stop  = 0;
    p_input->i_title = 0;
    p_input->title   = NULL;
    p_input->i_state = INIT_S;
    p_input->i_rate  = INPUT_RATE_DEFAULT;
    p_input->i_bookmark = 0;
    p_input->bookmark = NULL;
    p_input->p_es_out = NULL;
    p_input->p_sout  = NULL;
129
    p_input->b_out_pace_control = VLC_FALSE;
Laurent Aimar's avatar
Laurent Aimar committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    p_input->i_pts_delay = 0;


    /* Init Input fields */
    p_input->input.p_item = p_item;
    p_input->input.p_access = NULL;
    p_input->input.p_stream = NULL;
    p_input->input.p_demux  = NULL;
    p_input->input.b_title_demux = VLC_FALSE;
    p_input->input.i_title  = 0;
    p_input->input.title    = NULL;
    p_input->input.b_can_pace_control = VLC_TRUE;
    p_input->input.b_eof = VLC_FALSE;
    p_input->input.i_cr_average = 0;

145
146
147
148
    /* No slave */
    p_input->i_slave = 0;
    p_input->slave   = NULL;

Laurent Aimar's avatar
Laurent Aimar committed
149
150
151
    /* Init control buffer */
    vlc_mutex_init( p_input, &p_input->lock_control );
    p_input->i_control = 0;
152

gbazin's avatar
   
gbazin committed
153
    /* Parse input options */
154
155
    vlc_mutex_lock( &p_item->lock );
    for( i = 0; i < p_item->i_options; i++ )
gbazin's avatar
   
gbazin committed
156
    {
157
158
        msg_Dbg( p_input, "option: %s", p_item->ppsz_options[i] );
        ParseOption( p_input, p_item->ppsz_options[i] );
gbazin's avatar
   
gbazin committed
159
    }
160
    vlc_mutex_unlock( &p_item->lock );
gbazin's avatar
   
gbazin committed
161

Laurent Aimar's avatar
Laurent Aimar committed
162
163
    /* Create Object Variables for private use only */
    input_ConfigVarInit( p_input );
Michel Kaempf's avatar
Michel Kaempf committed
164

Laurent Aimar's avatar
Laurent Aimar committed
165
166
167
    /* Create Objects variables for public Get and Set */
    input_ControlVarInit( p_input );
    p_input->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
gbazin's avatar
gbazin committed
168

Laurent Aimar's avatar
Laurent Aimar committed
169
170
#if 0
    /* TODO */
gbazin's avatar
gbazin committed
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
    var_Get( p_input, "bookmarks", &val );
    if( val.psz_string )
    {
        /* 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, '{' ) ) )
        {
            seekpoint_t seekpoint;
            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 = ',';

            seekpoint.psz_name = 0;
            seekpoint.i_byte_offset = 0;
            seekpoint.i_time_offset = 0;
            while( (psz_end = strchr( psz_start, ',' ) ) )
            {
                *psz_end = 0;
                if( !strncmp( psz_start, "name=", 5 ) )
                {
                    seekpoint.psz_name = psz_start + 5;
                }
                else if( !strncmp( psz_start, "bytes=", 6 ) )
                {
201
                    seekpoint.i_byte_offset = atoll(psz_start + 6);
gbazin's avatar
gbazin committed
202
203
204
                }
                else if( !strncmp( psz_start, "time=", 5 ) )
                {
205
                    seekpoint.i_time_offset = atoll(psz_start + 5) * 1000000;
gbazin's avatar
gbazin committed
206
207
208
209
210
211
212
213
214
215
216
                }
                psz_start = psz_end + 1;
            }
            msg_Dbg( p_input, "adding bookmark: %s, bytes="I64Fd", time="I64Fd,
                     seekpoint.psz_name, seekpoint.i_byte_offset,
                     seekpoint.i_time_offset );
            input_Control( p_input, INPUT_ADD_BOOKMARK, &seekpoint );
            *psz_parser = backup;
        }
        free( val.psz_string );
    }
Laurent Aimar's avatar
Laurent Aimar committed
217
#endif
gbazin's avatar
gbazin committed
218

Laurent Aimar's avatar
Laurent Aimar committed
219
    /* Now we can attach our new input */
220
    vlc_object_attach( p_input, p_parent );
Sam Hocevar's avatar
   
Sam Hocevar committed
221

Sam Hocevar's avatar
Sam Hocevar committed
222
    /* Create thread and wait for its readiness. */
Laurent Aimar's avatar
Laurent Aimar committed
223
    if( vlc_thread_create( p_input, "input", Run,
224
                           VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) )
Michel Kaempf's avatar
Michel Kaempf committed
225
    {
226
        msg_Err( p_input, "cannot create input thread" );
Laurent Aimar's avatar
Laurent Aimar committed
227
228
        vlc_object_detach( p_input );
        vlc_object_destroy( p_input );
229
        return NULL;
Michel Kaempf's avatar
Michel Kaempf committed
230
    }
231

232
    return p_input;
Michel Kaempf's avatar
Michel Kaempf committed
233
234
}

235
/*****************************************************************************
Sam Hocevar's avatar
   
Sam Hocevar committed
236
 * input_StopThread: mark an input thread as zombie
237
 *****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
238
 * This function should not return until the thread is effectively cancelled.
239
 *****************************************************************************/
Sam Hocevar's avatar
Sam Hocevar committed
240
void input_StopThread( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
241
{
Laurent Aimar's avatar
Laurent Aimar committed
242
243
    vlc_list_t *p_list;
    int i;
244

Laurent Aimar's avatar
Laurent Aimar committed
245
246
    /* Set die for input */
    p_input->b_die = VLC_TRUE;
247

Laurent Aimar's avatar
Laurent Aimar committed
248
249
    /* We cannot touch p_input fields directly (we can from another thread),
     * so use the vlc_object_find way, it's perfectly safe */
250

Laurent Aimar's avatar
Laurent Aimar committed
251
252
253
    /* Set die for all access */
    p_list = vlc_list_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD );
    for( i = 0; i < p_list->i_count; i++ )
254
    {
Laurent Aimar's avatar
Laurent Aimar committed
255
        p_list->p_values[i].p_object->b_die = VLC_TRUE;
256
    }
Laurent Aimar's avatar
Laurent Aimar committed
257
    vlc_list_release( p_list );
258

Laurent Aimar's avatar
Laurent Aimar committed
259
260
261
    /* Set die for all stream */
    p_list = vlc_list_find( p_input, VLC_OBJECT_STREAM, FIND_CHILD );
    for( i = 0; i < p_list->i_count; i++ )
262
    {
Laurent Aimar's avatar
Laurent Aimar committed
263
        p_list->p_values[i].p_object->b_die = VLC_TRUE;
264
    }
Laurent Aimar's avatar
Laurent Aimar committed
265
    vlc_list_release( p_list );
266

Laurent Aimar's avatar
Laurent Aimar committed
267
268
269
270
271
272
273
    /* Set die for all demux */
    p_list = vlc_list_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD );
    for( i = 0; i < p_list->i_count; i++ )
    {
        p_list->p_values[i].p_object->b_die = VLC_TRUE;
    }
    vlc_list_release( p_list );
Michel Kaempf's avatar
Michel Kaempf committed
274

Laurent Aimar's avatar
Laurent Aimar committed
275
    input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
Sam Hocevar's avatar
   
Sam Hocevar committed
276
277
278
279
280
281
282
283
284
285
}

/*****************************************************************************
 * input_DestroyThread: mark an input thread as zombie
 *****************************************************************************
 * This function should not return until the thread is effectively cancelled.
 *****************************************************************************/
void input_DestroyThread( input_thread_t *p_input )
{
    /* Join the thread */
286
    vlc_thread_join( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
287

Laurent Aimar's avatar
Laurent Aimar committed
288
289
290
291
292
    /* Delete input lock (only after thread joined) */
    vlc_mutex_destroy( &p_input->lock_control );

    /* TODO: maybe input_DestroyThread should also delete p_input instead
     * of the playlist but I'm not sure if it's possible */
Michel Kaempf's avatar
Michel Kaempf committed
293
294
}

295
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
296
 * Run: main thread loop
297
 *****************************************************************************
298
 * Thread in charge of processing the network packets and demultiplexing.
299
300
301
302
 *
 * TODO:
 *  read subtitle support (XXX take care of spu-delay in the right way).
 *  multi-input support (XXX may be done with subs)
303
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
304
static int Run( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
305
{
Laurent Aimar's avatar
Laurent Aimar committed
306
    int64_t i_intf_update = 0;
307

Laurent Aimar's avatar
Laurent Aimar committed
308
    /* Signal that the thread is launched */
Sam Hocevar's avatar
Sam Hocevar committed
309
310
    vlc_thread_ready( p_input );

Laurent Aimar's avatar
Laurent Aimar committed
311
    if( Init( p_input ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
312
313
    {
        /* If we failed, wait before we are killed, and exit */
Laurent Aimar's avatar
Laurent Aimar committed
314
        p_input->b_error = VLC_TRUE;
315

Laurent Aimar's avatar
Laurent Aimar committed
316
        Error( p_input );
317
318

        /* Tell we're dead */
Laurent Aimar's avatar
Laurent Aimar committed
319
        p_input->b_dead = VLC_TRUE;
320

Sam Hocevar's avatar
   
Sam Hocevar committed
321
        return 0;
Sam Hocevar's avatar
   
Sam Hocevar committed
322
    }
Michel Kaempf's avatar
Michel Kaempf committed
323

Laurent Aimar's avatar
Laurent Aimar committed
324
    /* Main loop */
325
    while( !p_input->b_die && !p_input->b_error && !p_input->input.b_eof )
Sam Hocevar's avatar
   
Sam Hocevar committed
326
    {
Laurent Aimar's avatar
Laurent Aimar committed
327
328
329
330
        vlc_bool_t b_force_update = VLC_FALSE;
        int i_ret;
        int i_type;
        vlc_value_t val;
331

Laurent Aimar's avatar
Laurent Aimar committed
332
333
        /* Do the read */
        if( p_input->i_state != PAUSE_S  )
334
        {
Laurent Aimar's avatar
Laurent Aimar committed
335
336
337
338
            if( p_input->i_stop <= 0 || p_input->i_time < p_input->i_stop )
                i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux);
            else
                i_ret = 0;  /* EOF */
339

Laurent Aimar's avatar
Laurent Aimar committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
            if( i_ret > 0 )
            {
                /* TODO */
                if( p_input->input.b_title_demux &&
                    p_input->input.p_demux->info.i_update )
                {
                    UpdateFromDemux( p_input );
                    b_force_update = VLC_TRUE;
                }
                else if( !p_input->input.b_title_demux &&
                          p_input->input.p_access &&
                          p_input->input.p_access->info.i_update )
                {
                    UpdateFromAccess( p_input );
                    b_force_update = VLC_TRUE;
                }
            }
            else if( i_ret == 0 )    /* EOF */
            {
                vlc_value_t repeat;
360

Laurent Aimar's avatar
Laurent Aimar committed
361
362
363
364
365
366
367
368
369
                var_Get( p_input, "input-repeat", &repeat );
                if( repeat.i_int == 0 )
                {
                    /* 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->input.b_eof = VLC_TRUE;
                }
                else
370
                {
gbazin's avatar
gbazin committed
371
372
                    msg_Dbg( p_input, "repeating the same input (%d)",
                             repeat.i_int );
Laurent Aimar's avatar
Laurent Aimar committed
373
374
375
376
377
                    if( repeat.i_int > 0 )
                    {
                        repeat.i_int--;
                        var_Set( p_input, "input-repeat", repeat );
                    }
378

Laurent Aimar's avatar
Laurent Aimar committed
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
                    /* Seek to title 0 position 0(start) */
                    val.i_int = 0;
                    input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
                    if( p_input->i_start > 0 )
                    {
                        val.i_time = p_input->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 );
                    }
394
395
                }
            }
Laurent Aimar's avatar
Laurent Aimar committed
396
397
398
399
            else if( i_ret < 0 )
            {
                p_input->b_error = VLC_TRUE;
            }
400
401
402
403
404

            if( i_ret > 0 && p_input->i_slave > 0 )
            {
                SlaveDemux( p_input );
            }
405
        }
Laurent Aimar's avatar
Laurent Aimar committed
406
        else
407
        {
Laurent Aimar's avatar
Laurent Aimar committed
408
409
410
            /* Small wait */
            msleep( 10*1000 );
        }
411

Laurent Aimar's avatar
Laurent Aimar committed
412
413
414
415
416
417
418
419
420
421
        /* Handle control */
        vlc_mutex_lock( &p_input->lock_control );
        ControlReduce( p_input );
        while( !ControlPopNoLock( p_input, &i_type, &val ) )
        {
            msg_Dbg( p_input, "control type=%d", i_type );
            if( Control( p_input, i_type, val ) )
                b_force_update = VLC_TRUE;
        }
        vlc_mutex_unlock( &p_input->lock_control );
422

Laurent Aimar's avatar
Laurent Aimar committed
423
424
425
426
427
428
429
        if( b_force_update ||
            i_intf_update < mdate() )
        {
            vlc_value_t val;
            double f_pos;
            int64_t i_time, i_length;
            /* update input status variables */
gbazin's avatar
gbazin committed
430
431
            if( !demux2_Control( p_input->input.p_demux,
                                 DEMUX_GET_POSITION, &f_pos ) )
Laurent Aimar's avatar
Laurent Aimar committed
432
433
434
435
            {
                val.f_float = (float)f_pos;
                var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
            }
gbazin's avatar
gbazin committed
436
437
            if( !demux2_Control( p_input->input.p_demux,
                                 DEMUX_GET_TIME, &i_time ) )
Laurent Aimar's avatar
Laurent Aimar committed
438
439
440
441
442
            {
                p_input->i_time = i_time;
                val.i_time = i_time;
                var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
            }
gbazin's avatar
gbazin committed
443
444
            if( !demux2_Control( p_input->input.p_demux,
                                 DEMUX_GET_LENGTH, &i_length ) )
Laurent Aimar's avatar
Laurent Aimar committed
445
446
447
448
449
            {
                vlc_value_t old_val;
                var_Get( p_input, "length", &old_val );
                val.i_time = i_length;
                var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
450

Laurent Aimar's avatar
Laurent Aimar committed
451
                if( old_val.i_time != val.i_time )
452
                {
Laurent Aimar's avatar
Laurent Aimar committed
453
                    char psz_buffer[MSTRTIME_MAX_SIZE];
454

Laurent Aimar's avatar
   
Laurent Aimar committed
455
456
457
                    vlc_mutex_lock( &p_input->input.p_item->lock );
                    p_input->input.p_item->i_duration = i_length;
                    vlc_mutex_unlock( &p_input->input.p_item->lock );
Laurent Aimar's avatar
Laurent Aimar committed
458

Laurent Aimar's avatar
   
Laurent Aimar committed
459
460
461
                    input_Control( p_input, INPUT_ADD_INFO,
                                   _("General"), _("Duration"),
                    msecstotimestr( psz_buffer, i_length / 1000 ) );
462
463
                }
            }
Laurent Aimar's avatar
Laurent Aimar committed
464
465
466

            var_SetBool( p_input, "intf-change", VLC_TRUE );
            i_intf_update = mdate() + I64C(150000);
467
        }
Laurent Aimar's avatar
Laurent Aimar committed
468
469
    }

470
    if( !p_input->b_eof && !p_input->b_error && p_input->input.b_eof )
Laurent Aimar's avatar
Laurent Aimar committed
471
    {
472
473
        /* We have finish to demux data but not to play them */
        while( !p_input->b_die )
Sam Hocevar's avatar
   
Sam Hocevar committed
474
        {
475
476
            if( input_EsOutDecodersEmpty( p_input->p_es_out ) )
                break;
477

478
            msg_Dbg( p_input, "waiting decoder fifos to empty" );
479

480
            msleep( INPUT_IDLE_SLEEP );
Sam Hocevar's avatar
   
Sam Hocevar committed
481
        }
482

483
484
        /* We have finished */
        p_input->b_eof = VLC_TRUE;
485
486
    }

487
488
    /* Wait we are asked to die */
    if( !p_input->b_die )
489
    {
490
        Error( p_input );
491
    }
492

493
494
    /* Clean up */
    End( p_input );
Sam Hocevar's avatar
   
Sam Hocevar committed
495

Sam Hocevar's avatar
   
Sam Hocevar committed
496
    return 0;
497
498
}

499
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
500
 * Init: init the input Thread
501
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
502
static int Init( input_thread_t * p_input )
Michel Kaempf's avatar
Michel Kaempf committed
503
{
Laurent Aimar's avatar
Laurent Aimar committed
504
    char *psz;
Laurent Aimar's avatar
   
Laurent Aimar committed
505
    char *psz_subtitle;
Laurent Aimar's avatar
Laurent Aimar committed
506
    vlc_value_t val;
Laurent Aimar's avatar
   
Laurent Aimar committed
507
    double f_fps;
508
509
    vlc_meta_t *p_meta, *p_meta_user;
    int i;
510

Laurent Aimar's avatar
Laurent Aimar committed
511
512
513
    /* Initialize optional stream output. (before access/demuxer) */
    psz = var_GetString( p_input, "sout" );
    if( *psz )
514
    {
Laurent Aimar's avatar
Laurent Aimar committed
515
516
        p_input->p_sout = sout_NewInstance( p_input, psz );
        if( p_input->p_sout == NULL )
517
518
        {
            msg_Err( p_input, "cannot start stream output instance, aborting" );
Laurent Aimar's avatar
Laurent Aimar committed
519
            free( psz );
520
521
            return VLC_EGENERIC;
        }
522
    }
Laurent Aimar's avatar
Laurent Aimar committed
523
    free( psz );
Christophe Massiot's avatar
Christophe Massiot committed
524

Laurent Aimar's avatar
Laurent Aimar committed
525
    /* Create es out */
526
    p_input->p_es_out = input_EsOutNew( p_input );
527
528
    es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
    es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
529

530
    if( InputSourceInit( p_input, &p_input->input,
Laurent Aimar's avatar
   
Laurent Aimar committed
531
                         p_input->input.p_item->psz_uri, NULL ) )
532
    {
533
        goto error;
Laurent Aimar's avatar
Laurent Aimar committed
534
    }
535

536
    /* Create global title (from master) */
Laurent Aimar's avatar
Laurent Aimar committed
537
    p_input->i_title = p_input->input.i_title;
538
    p_input->title   = p_input->input.title;
Laurent Aimar's avatar
Laurent Aimar committed
539
    if( p_input->i_title > 0 )
540
    {
Laurent Aimar's avatar
Laurent Aimar committed
541
542
543
        /* Setup variables */
        input_ControlVarNavigation( p_input );
        input_ControlVarTitle( p_input, 0 );
544
    }
545

Laurent Aimar's avatar
Laurent Aimar committed
546
547
548
549
550
551
552
    /* Global flag */
    p_input->b_can_pace_control = p_input->input.b_can_pace_control;
    p_input->b_can_pause        = p_input->input.b_can_pause;

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

gbazin's avatar
   
gbazin committed
554
555
    /* If the desynchronisation requested by the user is < 0, we need to
     * cache more data. */
gbazin's avatar
   
gbazin committed
556
557
558
    var_Get( p_input, "audio-desync", &val );
    if( val.i_int < 0 )
        p_input->i_pts_delay -= (val.i_int * 1000);
gbazin's avatar
   
gbazin committed
559

560
    /* Load master infos */
Laurent Aimar's avatar
Laurent Aimar committed
561
    /* Init length */
gbazin's avatar
gbazin committed
562
563
    if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH,
                         &val.i_time ) && val.i_time > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
564
565
566
567
568
569
570
571
572
573
574
575
576
577
    {
        var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
        /* TODO update playlist meta data */
    }
    /* Start time*/
    /* Set start time */
    p_input->i_start = (int64_t)var_GetInteger( p_input, "start-time" ) *
                       I64C(1000000);
    p_input->i_stop  = (int64_t)var_GetInteger( p_input, "stop-time" ) *
                       I64C(1000000);

    if( p_input->i_start > 0 )
    {
        if( p_input->i_start >= val.i_time )
578
        {
Laurent Aimar's avatar
Laurent Aimar committed
579
            msg_Warn( p_input, "invalid start-time ignored" );
580
        }
Laurent Aimar's avatar
Laurent Aimar committed
581
        else
582
        {
Laurent Aimar's avatar
Laurent Aimar committed
583
584
585
586
587
588
589
            vlc_value_t s;

            msg_Dbg( p_input, "start-time: %ds",
                     (int)( p_input->i_start / I64C(1000000) ) );

            s.i_time = p_input->i_start;
            input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
590
        }
Laurent Aimar's avatar
Laurent Aimar committed
591
592
593
594
595
    }
    if( p_input->i_stop > 0 && p_input->i_stop <= p_input->i_start )
    {
        msg_Warn( p_input, "invalid stop-time ignored" );
        p_input->i_stop = 0;
596
597
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
598

Laurent Aimar's avatar
   
Laurent Aimar committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
    /* Load subtitles */
    /* Get fps and set it if not already set */
    if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_FPS, &f_fps ) &&
        f_fps > 1.0 )
    {
        vlc_value_t fps;

        if( var_Get( p_input, "sub-fps", &fps ) )
        {
            var_Create( p_input, "sub-fps", VLC_VAR_FLOAT| VLC_VAR_DOINHERIT );
            var_SetFloat( p_input, "sub-fps", f_fps );
        }
    }

    /* Look for and add subtitle files */
    psz_subtitle = var_GetString( p_input, "sub-file" );
    if( *psz_subtitle )
    {
        input_source_t *sub;
        vlc_value_t count;
        vlc_value_t list;

        msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );

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

        /* */
        sub = InputSourceNew( p_input );
        if( !InputSourceInit( p_input, sub, psz_subtitle, "subtitle" ) )
        {
            TAB_APPEND( p_input->i_slave, p_input->slave, sub );

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

                if( count.i_int < list.p_list->i_count )
                {
gbazin's avatar
gbazin committed
639
640
                    input_ControlPush( p_input, INPUT_CONTROL_SET_ES,
                                       &list.p_list->p_values[count.i_int] );
Laurent Aimar's avatar
   
Laurent Aimar committed
641
642
643
644
645
646
647
648
649
650
651
652
653
                }
                var_Change( p_input, "spu-es", VLC_VAR_FREELIST, &list, NULL );
            }
        }
    }

    var_Get( p_input, "sub-autodetect-file", &val );
    if( val.b_bool )
    {
        char *psz_autopath = var_GetString( p_input, "sub-autodetect-path" );
        char **subs = subtitles_Detect( p_input, psz_autopath,
                                        p_input->input.p_item->psz_uri );
        input_source_t *sub;
654

Laurent Aimar's avatar
   
Laurent Aimar committed
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
        for( i = 0; subs[i] != NULL; i++ )
        {
            if( strcmp( psz_subtitle, subs[i] ) )
            {
                sub = InputSourceNew( p_input );
                if( !InputSourceInit( p_input, sub, subs[i], "subtitle" ) )
                {
                    TAB_APPEND( p_input->i_slave, p_input->slave, sub );
                }
            }
            free( subs[i] );
        }
        free( subs );
        free( psz_autopath );
    }
    free( psz_subtitle );
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
705

    /* Look for slave */
    psz = var_GetString( p_input, "input-slave" );
    if( *psz )
    {
        char *psz_delim = strchr( psz, '#' );

        for( ;; )
        {
            input_source_t *slave;

            if( psz_delim )
            {
                *psz_delim++ = '\0';
            }

            if( *psz == '\0' )
            {
                if( psz_delim )
                    continue;
                else
                    break;
            }

            msg_Dbg( p_input, "adding slave '%s'", psz );
            slave = InputSourceNew( p_input );
            if( !InputSourceInit( p_input, slave, psz, NULL ) )
            {
                TAB_APPEND( p_input->i_slave, p_input->slave, slave );
            }
            if( !psz_delim )
                break;
        }
    }
    free( psz );
Laurent Aimar's avatar
Laurent Aimar committed
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721

    /* Set up es_out */
    es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
    val.b_bool =  VLC_FALSE;
    if( p_input->p_sout )
    {
        var_Get( p_input, "sout-all", &val );
    }
    es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
                    val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );

    if( p_input->p_sout )
    {
        if( p_input->p_sout->i_out_pace_nocontrol > 0 )
        {
            p_input->b_out_pace_control = VLC_FALSE;
722
        }
Laurent Aimar's avatar
Laurent Aimar committed
723
724
725
726
727
728
        else
        {
            p_input->b_out_pace_control = VLC_TRUE;
        }
        msg_Dbg( p_input, "starting in %s mode",
                 p_input->b_out_pace_control ? "asynch" : "synch" );
729
730
    }

731
732
    /* Get meta data from users */
    p_meta_user = InputMetaUser( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
733

734
735
736
    /* Get meta data from master input */
    if( demux2_Control( p_input->input.p_demux, DEMUX_GET_META, &p_meta ) )
        p_meta = NULL;
737

738
739
    /* Merge them */
    if( p_meta == NULL )
gbazin's avatar
gbazin committed
740
    {
741
        p_meta = p_meta_user;
gbazin's avatar
gbazin committed
742
    }
743
    else if( p_meta_user )
744
    {
745
746
747
        vlc_meta_Merge( p_meta, p_meta_user );
        vlc_meta_Delete( p_meta_user );
    }
748

749
750
751
752
    /* Get meta data from slave input */
    for( i = 0; i < p_input->i_slave; i++ )
    {
        vlc_meta_t *p_meta_slave;
gbazin's avatar
gbazin committed
753

754
        if( !demux2_Control( p_input->slave[i]->p_demux, DEMUX_GET_META, &p_meta_slave ) )
755
        {
756
            if( p_meta == NULL )
757
            {
758
                p_meta = p_meta_slave;
759
            }
760
761
762
763
764
765
766
767
768
769
            else if( p_meta_slave )
            {
                vlc_meta_Merge( p_meta, p_meta_slave );
                vlc_meta_Delete( p_meta_slave );
            }
        }
    }

    if( p_meta && p_meta->i_meta > 0 )
    {
770
        msg_Dbg( p_input, "meta information:" );
771
772
773
774
775
776
777
778
779
780
781
782
        for( i = 0; i < p_meta->i_meta; i++ )
        {
            msg_Dbg( p_input, "  - '%s' = '%s'",
                    _(p_meta->name[i]), p_meta->value[i] );

            if( !strcmp(p_meta->name[i], VLC_META_TITLE) && p_meta->value[i] )
                input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );

            if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
                input_Control( p_input, INPUT_ADD_INFO, _("General"),
                               _("Author"), p_meta->value[i] );

783
            input_Control( p_input, INPUT_ADD_INFO, _("Meta-information"),
784
                          _(p_meta->name[i]), "%s", p_meta->value[i] );
785
        }
786

gbazin's avatar
gbazin committed
787
        for( i = 0; i < p_meta->i_track; i++ )
788
        {
gbazin's avatar
gbazin committed
789
            vlc_meta_t *tk = p_meta->track[i];
790
791
792
793
794
795
            int j;

            if( tk->i_meta > 0 )
            {
                char *psz_cat = malloc( strlen(_("Stream")) + 10 );

796
797
798
                msg_Dbg( p_input, "  - track[%d]:", i );

                sprintf( psz_cat, "%s %d", _("Stream"), i );
799
800
                for( j = 0; j < tk->i_meta; j++ )
                {
gbazin's avatar
gbazin committed
801
802
                    msg_Dbg( p_input, "     - '%s' = '%s'", _(tk->name[j]),
                             tk->value[j] );
803
804
805

                    input_Control( p_input, INPUT_ADD_INFO, psz_cat,
                                   _(tk->name[j]), "%s", tk->value[j] );
806
807
808
809
                }
            }
        }

810
        if( p_input->p_sout && p_input->p_sout->p_meta == NULL )
811
        {
812
            p_input->p_sout->p_meta = p_meta;
813
814
815
        }
        else
        {
gbazin's avatar
gbazin committed
816
            vlc_meta_Delete( p_meta );
817
        }
818
819
    }

820
821
    msg_Dbg( p_input, "`%s' sucessfully opened",
             p_input->input.p_item->psz_uri );
822

823
824
    /* initialization is complete */
    p_input->i_state = PLAYING_S;
825

826
827
    val.i_int = PLAYING_S;
    var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
gbazin's avatar
gbazin committed
828

829
    return VLC_SUCCESS;
830

831
832
833
error:
    if( p_input->p_es_out )
        input_EsOutDelete( p_input->p_es_out );
Laurent Aimar's avatar
Laurent Aimar committed
834

835
836
    if( p_input->p_sout )
        sout_DeleteInstance( p_input->p_sout );
837

838
839
840
841
842
843
    /* Mark them deleted */
    p_input->input.p_demux = NULL;
    p_input->input.p_stream = NULL;
    p_input->input.p_access = NULL;
    p_input->p_es_out = NULL;
    p_input->p_sout = NULL;
844

845
    return VLC_EGENERIC;
Michel Kaempf's avatar
Michel Kaempf committed
846
847
}

848
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
849
 * Error: RunThread() error loop
850
 *****************************************************************************
851
 * This function is called when an error occured during thread main's loop.
852
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
853
static void Error( input_thread_t *p_input )
Michel Kaempf's avatar
Michel Kaempf committed
854
{
855
    while( !p_input->b_die )
Michel Kaempf's avatar
Michel Kaempf committed
856
    {
857
858
        /* Sleep a while */
        msleep( INPUT_IDLE_SLEEP );
Michel Kaempf's avatar
Michel Kaempf committed
859
860
861
    }
}

862
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
863
 * End: end the input thread
864
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
865
static void End( input_thread_t * p_input )
866
{
Laurent Aimar's avatar
Laurent Aimar committed
867
    vlc_value_t val;
868
    int i;
869

Laurent Aimar's avatar
Laurent Aimar committed
870
871
    msg_Dbg( p_input, "closing `%s'",
             p_input->input.p_item->psz_uri );
872

Laurent Aimar's avatar
Laurent Aimar committed
873
874
875
876
877
878
879
880
    /* We are at the end */
    p_input->i_state = END_S;

    val.i_int = END_S;
    var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );

    /* Clean control variables */
    input_ControlVarClean( p_input );
881

882
883
    /* Clean up master */
    InputSourceClean( p_input, &p_input->input );
Laurent Aimar's avatar
Laurent Aimar committed
884

885
886
887
888
889
890
891
    /* Delete slave */
    for( i = 0; i < p_input->i_slave; i++ )
    {
        InputSourceClean( p_input, p_input->slave[i] );
        free( p_input->slave[i] );
    }
    if( p_input->slave ) free( p_input->slave );
Laurent Aimar's avatar
Laurent Aimar committed
892

893
    /* Unload all modules */
Laurent Aimar's avatar
Laurent Aimar committed
894
895
    if( p_input->p_es_out )
        input_EsOutDelete( p_input->p_es_out );
gbazin's avatar
   
gbazin committed
896

897
    /* Close optional stream output instance */
Laurent Aimar's avatar
Laurent Aimar committed
898
    if( p_input->p_sout )
899
    {
900
901
        vlc_object_t *p_pl =
            vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
902
903
904
905
906
907
        vlc_value_t keep;

        if( var_Get( p_input, "sout-keep", &keep ) >= 0 && keep.b_bool && p_pl )
        {
            /* attach sout to the playlist */
            msg_Warn( p_input, "keeping sout" );
Laurent Aimar's avatar
Laurent Aimar committed
908
909
            vlc_object_detach( p_input->p_sout );
            vlc_object_attach( p_input->p_sout, p_pl );
910
911
912
913
        }
        else
        {
            msg_Warn( p_input, "destroying sout" );
Laurent Aimar's avatar
Laurent Aimar committed
914
            sout_DeleteInstance( p_input->p_sout );
915
916
917
        }
        if( p_pl )
            vlc_object_release( p_pl );
918
919
    }

Laurent Aimar's avatar
Laurent Aimar committed
920
921
922
    /* Tell we're dead */
    p_input->b_dead = VLC_TRUE;
}
923

Laurent Aimar's avatar
Laurent Aimar committed
924
925
926
927
928
929
930
931
932
933
/*****************************************************************************
 * Control
 *****************************************************************************/
static inline int ControlPopNoLock( input_thread_t *p_input,
                                    int *pi_type, vlc_value_t *p_val )
{
    if( p_input->i_control <= 0 )
    {
        return VLC_EGENERIC;
    }
934

Laurent Aimar's avatar
Laurent Aimar committed
935
936
    *pi_type = p_input->control[0].i_type;
    *p_val   = p_input->control[0].val;
Sam Hocevar's avatar
   
Sam Hocevar committed
937

Laurent Aimar's avatar
Laurent Aimar committed
938
939
940
941
    p_input->i_control--;
    if( p_input->i_control > 0 )
    {
        int i;
Christophe Massiot's avatar
Christophe Massiot committed
942

Laurent Aimar's avatar
Laurent Aimar committed
943
944
945
946
947
948
        for( i = 0; i < p_input->i_control; i++ )
        {
            p_input->control[i].i_type = p_input->control[i+1].i_type;
            p_input->control[i].val    = p_input->control[i+1].val;
        }
    }
949

Laurent Aimar's avatar
Laurent Aimar committed
950
951
    return VLC_SUCCESS;
}
952

Laurent Aimar's avatar
Laurent Aimar committed
953
954
955
956
957
958
959
960
961
static void ControlReduce( input_thread_t *p_input )
{
    int i;
    for( i = 1; i < p_input->i_control; i++ )
    {
        const int i_lt = p_input->control[i-1].i_type;
        const int i_ct = p_input->control[i].i_type;

        /* XXX We can't merge INPUT_CONTROL_SET_ES */
gbazin's avatar
gbazin committed
962
963
        msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control,
                 i_lt, i_ct );
Laurent Aimar's avatar
Laurent Aimar committed
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
        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;
            msg_Dbg( p_input, "merged at %d", i );
            /* Remove the i-1 */
            for( j = i; j <  p_input->i_control; j++ )
                p_input->control[j-1] = p_input->control[j];
            p_input->i_control--;
        }
        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
991
}
Laurent Aimar's avatar
Laurent Aimar committed
992

gbazin's avatar
gbazin committed
993
994
static vlc_bool_t Control( input_thread_t *p_input, int i_type,
                           vlc_value_t val )
Laurent Aimar's avatar
Laurent Aimar committed
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
{
    vlc_bool_t b_force_update = VLC_FALSE;

    switch( i_type )
    {
        case INPUT_CONTROL_SET_DIE:
            msg_Dbg( p_input, "control: INPUT_CONTROL_SET_DIE proceed" );
            /* Mark all submodules to die */
            if( p_input->input.p_access )
                p_input->input.p_access->b_die = VLC_TRUE;
            if( p_input->input.p_stream )
                p_input->input.p_stream->b_die = VLC_TRUE;
            p_input->input.p_demux->b_die = VLC_TRUE;

            p_input->b_die = VLC_TRUE;
            break;

        case INPUT_CONTROL_SET_POSITION:
        case INPUT_CONTROL_SET_POSITION_OFFSET:
        {
            double f_pos;
            if( i_type == INPUT_CONTROL_SET_POSITION )
            {
                f_pos = val.f_float;
            }
            else
            {
                /* Should not fail */
gbazin's avatar
gbazin committed
1023
1024
                demux2_Control( p_input->input.p_demux,
                                DEMUX_GET_POSITION, &f_pos );
Laurent Aimar's avatar
Laurent Aimar committed
1025
1026
1027
1028
                f_pos += val.f_float;
            }
            if( f_pos < 0.0 ) f_pos = 0.0;
            if( f_pos > 1.0 ) f_pos = 1.0;
gbazin's avatar
gbazin committed
1029
1030
            if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION,
                                f_pos ) )
Laurent Aimar's avatar
Laurent Aimar committed
1031
            {
gbazin's avatar
gbazin committed
1032
1033
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
                         "%2.1f%% failed", f_pos * 100 );
Laurent Aimar's avatar
Laurent Aimar committed
1034
1035
1036
            }
            else
            {
1037
1038
1039
                if( p_input->i_slave > 0 )
                    SlaveSeek( p_input );

Laurent Aimar's avatar
Laurent Aimar committed
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
                input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
                es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
                b_force_update = VLC_TRUE;
            }
            break;
        }

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

            if( i_type == INPUT_CONTROL_SET_TIME )
            {
                i_time = val.i_time;
            }
            else
            {
                /* Should not fail */
                demux2_Control( p_input->input.p_demux,
                                DEMUX_GET_TIME, &i_time );
                i_time += val.i_time;
            }
            if( i_time < 0 ) i_time = 0;
            i_ret = demux2_Control( p_input->input.p_demux,
                                    DEMUX_SET_TIME, i_time );
            if( i_ret )
            {
                int64_t i_length;
                /* Emulate it with a SET_POS */

                demux2_Control( p_input->input.p_demux,
                                DEMUX_GET_LENGTH, &i_length );
                if( i_length > 0 )
                {
                    double f_pos = (double)i_time / (double)i_length;
                    i_ret = demux2_Control( p_input->input.p_demux,
                                            DEMUX_SET_POSITION, f_pos );
                }
            }
            if( i_ret )
            {
gbazin's avatar
gbazin committed
1083
1084
                msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) "I64Fd
                         " failed", i_time );
Laurent Aimar's avatar
Laurent Aimar committed
1085
1086
1087
            }
            else
            {
1088
1089
1090
                if( p_input->i_slave > 0 )
                    SlaveSeek( p_input );

Laurent Aimar's avatar
Laurent Aimar committed
1091
1092
1093