Coverage Report

Created: 2026-05-16 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/input/input.c
Line
Count
Source
1
/*****************************************************************************
2
 * input.c: input thread
3
 *****************************************************************************
4
 * Copyright (C) 1998-2007 VLC authors and VideoLAN
5
 *
6
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
7
 *          Laurent Aimar <fenrir@via.ecp.fr>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program; if not, write to the Free Software Foundation,
21
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
24
/*****************************************************************************
25
 * Preamble
26
 *****************************************************************************/
27
#ifdef HAVE_CONFIG_H
28
# include "config.h"
29
#endif
30
31
#include <vlc_common.h>
32
#include <vlc_arrays.h>
33
#include <vlc_decoder.h>
34
35
#include <limits.h>
36
#include <assert.h>
37
#include <sys/stat.h>
38
39
#include "input_internal.h"
40
#include "event.h"
41
#include "es_out.h"
42
#include "demux.h"
43
#include "item.h"
44
#include "resource.h"
45
#include "stream.h"
46
#include "stream_output/stream_output.h"
47
#include "mrl_helpers.h"
48
49
#include <vlc_aout.h>
50
#include <vlc_dialog.h>
51
#include <vlc_url.h>
52
#include <vlc_charset.h>
53
#include <vlc_fs.h>
54
#include <vlc_strings.h>
55
#include <vlc_modules.h>
56
#include <vlc_stream.h>
57
#include <vlc_stream_extractor.h>
58
#include <vlc_renderer_discovery.h>
59
#include <vlc_hash.h>
60
61
/*****************************************************************************
62
 * Local prototypes
63
 *****************************************************************************/
64
static  void *Run( void * );
65
static  void *Preparse( void * );
66
67
static void             Destroy ( input_thread_t *p_input );
68
static  int             Init    ( input_thread_t *p_input );
69
static void             End     ( input_thread_t *p_input );
70
static void             MainLoop( input_thread_t *p_input, bool b_interactive );
71
72
static inline int ControlPop( input_thread_t *, int *, input_control_param_t *, vlc_tick_t i_deadline, bool b_postpone_seek );
73
static int ControlPopEarly(input_thread_t *input, int *type,
74
                           input_control_param_t *param);
75
static void       ControlRelease( int i_type, const input_control_param_t *p_param );
76
static bool       ControlIsSeekRequest( int i_type );
77
static bool       Control( input_thread_t *, int, input_control_param_t );
78
static void       ControlPause( input_thread_t *, vlc_tick_t );
79
80
static int  UpdateTitleSeekpointFromDemux( input_thread_t * );
81
static void UpdateGenericFromDemux( input_thread_t * );
82
static void UpdateTitleListfromDemux( input_thread_t * );
83
84
static void MRLSections( const char *, int *, int *, int *, int *);
85
86
static input_source_t *InputSourceNew( const char *psz_mrl );
87
static int InputSourceInit( input_source_t *in, input_thread_t *p_input,
88
                            const char *psz_mrl,
89
                            const char *psz_forced_demux, bool b_in_can_fail );
90
static void InputSourceDestroy( input_source_t * );
91
static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );
92
93
/* TODO */
94
//static void InputGetAttachments( input_thread_t *, input_source_t * );
95
static void SlaveDemux( input_thread_t *p_input );
96
static void SlaveSeek( input_thread_t *p_input );
97
98
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta );
99
static void InputUpdateMeta( input_thread_t *p_input, demux_t *p_demux );
100
static void InputGetExtraFiles( input_thread_t *p_input,
101
                                int *pi_list, char ***pppsz_list,
102
                                const char **psz_access, const char *mrl );
103
104
static void AppendAttachment(input_thread_t* p_input,
105
                              int i_new, input_attachment_t **pp_new);
106
107
0
#define SLAVE_ADD_NOFLAG    0
108
0
#define SLAVE_ADD_FORCED    (1<<0)
109
0
#define SLAVE_ADD_CANFAIL   (1<<1)
110
0
#define SLAVE_ADD_SET_TIME  (1<<2)
111
112
static int input_SlaveSourceAdd( input_thread_t *, enum slave_type,
113
                                 const char *, unsigned );
114
static char *input_SubtitleFile2Uri( input_thread_t *, const char * );
115
static void input_ChangeState( input_thread_t *p_input, int i_state, vlc_tick_t ); /* TODO fix name */
116
117
/**
118
 * Start a input_thread_t created by input_Create.
119
 *
120
 * You must not start an already running input_thread_t.
121
 *
122
 * \param p_input the input thread to start
123
 */
124
int input_Start( input_thread_t *p_input )
125
0
{
126
0
    input_thread_private_t *priv = input_priv(p_input);
127
0
    void *(*func)(void *) = Run;
128
129
0
    if( priv->type == INPUT_TYPE_PREPARSING )
130
0
        func = Preparse;
131
132
0
    assert( !priv->is_running );
133
    /* Create thread and wait for its readiness. */
134
0
    priv->is_running = !vlc_clone( &priv->thread, func, priv );
135
0
    if( !priv->is_running )
136
0
    {
137
0
        msg_Err( p_input, "cannot create input thread" );
138
0
        return VLC_EGENERIC;
139
0
    }
140
0
    return VLC_SUCCESS;
141
0
}
142
143
/**
144
 * Request a running input thread to stop and die
145
 *
146
 * \param p_input the input thread to stop
147
 */
148
void input_Stop( input_thread_t *p_input )
149
0
{
150
0
    input_thread_private_t *sys = input_priv(p_input);
151
152
0
    vlc_mutex_lock( &sys->lock_control );
153
    /* Discard all pending controls */
154
0
    for( size_t i = 0; i < sys->i_control; i++ )
155
0
    {
156
0
        input_control_t *ctrl = &sys->control[i];
157
0
        ControlRelease( ctrl->i_type, &ctrl->param );
158
0
    }
159
0
    sys->i_control = 0;
160
0
    sys->is_stopped = true;
161
0
    vlc_cond_signal( &sys->wait_control );
162
0
    vlc_mutex_unlock( &sys->lock_control );
163
0
    vlc_interrupt_kill( &sys->interrupt );
164
0
}
165
166
/**
167
 * Close an input
168
 *
169
 * It does not call input_Stop itself.
170
 */
171
void input_Close( input_thread_t *p_input )
172
0
{
173
0
    if( input_priv(p_input)->is_running )
174
0
        vlc_join( input_priv(p_input)->thread, NULL );
175
0
    vlc_interrupt_deinit( &input_priv(p_input)->interrupt );
176
0
    Destroy(p_input);
177
0
}
178
179
void input_SetTime( input_thread_t *p_input, vlc_tick_t i_time, bool b_fast )
180
0
{
181
0
    input_control_param_t param;
182
183
0
    param.time.i_val = i_time;
184
0
    param.time.b_fast_seek = b_fast;
185
0
    input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &param );
186
0
}
187
188
void input_SetPosition( input_thread_t *p_input, double f_position, bool b_fast )
189
0
{
190
0
    input_control_param_t param;
191
192
0
    param.pos.f_val = f_position;
193
0
    param.pos.b_fast_seek = b_fast;
194
0
    input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &param );
195
0
}
196
197
/**
198
 * Get the item from an input thread
199
 * FIXME it does not increase ref count of the item.
200
 * if it is used after p_input is destroyed nothing prevent it from
201
 * being freed.
202
 */
203
input_item_t *input_GetItem( input_thread_t *p_input )
204
0
{
205
0
    assert( p_input != NULL );
206
0
    return input_priv(p_input)->p_item;
207
0
}
208
209
#undef input_Create
210
input_thread_t * input_Create( vlc_object_t *p_parent, input_item_t *p_item,
211
                               const struct vlc_input_thread_cfg *cfg )
212
0
{
213
0
    assert(cfg != NULL);
214
215
    /* Allocate descriptor */
216
0
    input_thread_private_t *priv;
217
218
0
    priv = vlc_custom_create( p_parent, sizeof( *priv ), "input" );
219
0
    if( unlikely(priv == NULL) )
220
0
        return NULL;
221
222
0
    priv->master = InputSourceNew( NULL );
223
0
    if( !priv->master )
224
0
    {
225
0
        free( priv );
226
0
        return NULL;
227
0
    }
228
229
0
    input_thread_t *p_input = &priv->input;
230
231
0
    char * psz_name = input_item_GetName( p_item );
232
0
    const char *type_str;
233
0
    switch (cfg->type)
234
0
    {
235
0
        case INPUT_TYPE_PREPARSING:
236
0
            type_str = "preparsing ";
237
0
            break;
238
0
        case INPUT_TYPE_THUMBNAILING:
239
0
            type_str = "thumbnailing ";
240
0
            break;
241
0
        default:
242
0
            type_str = "playback";
243
0
            break;
244
0
    }
245
0
    msg_Dbg( p_input, "Creating an input for %s'%s'", type_str, psz_name);
246
0
    free( psz_name );
247
248
    /* Parse input options */
249
0
    input_item_ApplyOptions( VLC_OBJECT(p_input), p_item );
250
251
    /* Init Common fields */
252
253
0
    switch (cfg->hw_dec)
254
0
    {
255
0
        case INPUT_CFG_HW_DEC_DEFAULT:
256
0
            priv->hw_dec = var_InheritBool(p_input, "hw-dec");
257
0
            break;
258
0
        case INPUT_CFG_HW_DEC_DISABLED:
259
0
            priv->hw_dec = false;
260
0
            break;
261
0
        case INPUT_CFG_HW_DEC_ENABLED:
262
0
            priv->hw_dec = true;
263
0
            break;
264
0
        default: vlc_assert_unreachable();
265
0
    }
266
267
0
    priv->cbs = cfg->cbs;
268
0
    priv->cbs_data = cfg->cbs_data;
269
0
    priv->type = cfg->type;
270
0
    priv->preparse_subitems = cfg->preparsing.subitems;
271
0
    priv->i_start = 0;
272
0
    priv->i_stop  = 0;
273
0
    priv->i_title_offset = input_priv(p_input)->i_seekpoint_offset = 0;
274
0
    priv->i_state = INIT_S;
275
0
    priv->is_running = false;
276
0
    priv->is_stopped = false;
277
0
    priv->b_recording = false;
278
0
    priv->rate = 1.f;
279
0
    TAB_INIT( priv->i_attachment, priv->attachment );
280
0
    priv->p_sout   = NULL;
281
0
    priv->b_out_pace_control = priv->type == INPUT_TYPE_THUMBNAILING;
282
0
    priv->p_renderer = cfg->renderer && priv->type == INPUT_TYPE_PLAYBACK ?
283
0
                vlc_renderer_item_hold( cfg->renderer ) : NULL;
284
0
    priv->prev_frame.enabled = priv->prev_frame.end = false;
285
0
    priv->prev_frame.last_pts = VLC_TICK_INVALID;
286
0
    priv->next_frame_need_data = false;
287
288
0
    priv->viewpoint_changed = false;
289
    /* Fetch the viewpoint from the mediaplayer or the playlist if any */
290
0
    vlc_viewpoint_t *p_viewpoint = var_InheritAddress( p_input, "viewpoint" );
291
0
    if (p_viewpoint != NULL)
292
0
        priv->viewpoint = *p_viewpoint;
293
0
    else
294
0
        vlc_viewpoint_init( &priv->viewpoint );
295
296
0
    input_item_Hold( p_item ); /* Released in Destructor() */
297
0
    priv->p_item = p_item;
298
299
    /* Init Input fields */
300
0
    vlc_mutex_lock( &p_item->lock );
301
302
0
    if( !p_item->p_stats )
303
0
        p_item->p_stats = calloc( 1, sizeof(*p_item->p_stats) );
304
305
0
    if( priv->type != INPUT_TYPE_PLAYBACK )
306
0
    {
307
0
        p_input->obj.logger = NULL;
308
0
        p_input->obj.no_interact = true;
309
0
    }
310
311
    /* Make sure the interaction option is honored */
312
0
    if( !var_InheritBool( p_input, "interact" ) )
313
0
        p_input->obj.no_interact = true;
314
0
    else if( cfg->interact )
315
0
        p_input->obj.no_interact = false;
316
317
0
    vlc_mutex_unlock( &p_item->lock );
318
319
    /* No slave */
320
0
    priv->i_slave = 0;
321
0
    priv->slave   = NULL;
322
323
    /* */
324
0
    if( cfg->resource )
325
0
        priv->p_resource = input_resource_Hold( cfg->resource );
326
0
    else
327
0
        priv->p_resource = input_resource_New( VLC_OBJECT( p_input ) );
328
0
    input_resource_SetInput( priv->p_resource, p_input );
329
330
    /* Init control buffer */
331
0
    vlc_mutex_init( &priv->lock_control );
332
0
    vlc_cond_init( &priv->wait_control );
333
0
    priv->i_control = 0;
334
0
    vlc_interrupt_init(&priv->interrupt);
335
336
    /* Create Object Variables for private use only */
337
338
0
    vlc_object_InitInputConfig(VLC_OBJECT(p_input),
339
0
                               input_priv(p_input)->type != INPUT_TYPE_PREPARSING,
340
0
                               true);
341
342
0
    priv->b_low_delay = var_InheritBool( p_input, "low-delay" );
343
0
    priv->i_jitter_max = VLC_TICK_FROM_MS(var_InheritInteger( p_input, "clock-jitter" ));
344
345
    /* Remove 'Now playing' info as it is probably outdated */
346
0
    input_item_SetNowPlaying( p_item, NULL );
347
0
    input_item_SetESNowPlaying( p_item, NULL );
348
349
    /* */
350
0
    if( priv->type == INPUT_TYPE_PLAYBACK && var_InheritBool( p_input, "stats" ) )
351
0
        priv->stats = input_stats_Create();
352
0
    else
353
0
        priv->stats = NULL;
354
355
0
    priv->p_es_out_display = input_EsOutNew(p_input, priv->master, priv->rate,
356
0
                                            priv->type);
357
0
    if( !priv->p_es_out_display )
358
0
    {
359
0
        Destroy( p_input );
360
0
        return NULL;
361
0
    }
362
0
    priv->p_es_out = NULL;
363
364
0
    return p_input;
365
0
}
366
367
static void Destroy(input_thread_t *input)
368
0
{
369
0
    input_thread_private_t *priv = input_priv(input);
370
371
0
#ifndef NDEBUG
372
0
    char *name = input_item_GetName(priv->p_item);
373
0
    msg_Dbg(input, "destroying input for '%s'", name);
374
0
    free(name);
375
0
#endif
376
377
0
    if (priv->p_renderer != NULL)
378
0
        vlc_renderer_item_release(priv->p_renderer);
379
0
    if (priv->p_es_out_display != NULL)
380
0
        vlc_input_es_out_Delete(priv->p_es_out_display);
381
382
0
    if (priv->p_resource != NULL)
383
0
        input_resource_Release(priv->p_resource);
384
385
0
    input_source_Release(priv->master);
386
0
    input_item_Release(priv->p_item);
387
388
0
    if (priv->stats != NULL)
389
0
        input_stats_Destroy(priv->stats);
390
391
0
    for (size_t i = 0; i < priv->i_control; i++)
392
0
    {
393
0
        input_control_t *ctrl = &priv->control[i];
394
395
0
        ControlRelease(ctrl->i_type, &ctrl->param);
396
0
    }
397
398
0
    vlc_object_delete(VLC_OBJECT(input));
399
0
}
400
401
/*****************************************************************************
402
 * Run: main thread loop
403
 * This is the "normal" thread that spawns the input processing chain,
404
 * reads the stream, cleans up and waits
405
 *****************************************************************************/
406
static void *Run( void *data )
407
0
{
408
0
    input_thread_private_t *priv = data;
409
0
    input_thread_t *p_input = &priv->input;
410
411
0
    vlc_thread_set_name("vlc-input");
412
413
0
    vlc_interrupt_set(&priv->interrupt);
414
415
0
    if( !Init( p_input ) )
416
0
    {
417
0
        MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */
418
419
        /* Clean up */
420
0
        End( p_input );
421
0
    }
422
423
0
    input_SendEventDead( p_input );
424
0
    return NULL;
425
0
}
426
427
static void *Preparse( void *data )
428
0
{
429
0
    input_thread_private_t *priv = data;
430
0
    input_thread_t *p_input = &priv->input;
431
432
0
    vlc_thread_set_name("vlc-preparse");
433
434
0
    vlc_interrupt_set(&priv->interrupt);
435
436
0
    if( !Init( p_input ) )
437
0
    {   /* if the demux is a playlist, call Mainloop that will call
438
         * demux_Demux in order to fetch sub items */
439
0
        if( priv->preparse_subitems
440
0
         && priv->master->p_demux->pf_readdir != NULL )
441
0
            MainLoop( p_input, false );
442
0
        End( p_input );
443
0
    }
444
445
0
    input_SendEventDead( p_input );
446
0
    return NULL;
447
0
}
448
449
bool input_Stopped( input_thread_t *input )
450
0
{
451
0
    input_thread_private_t *sys = input_priv(input);
452
0
    bool ret;
453
454
0
    vlc_mutex_lock( &sys->lock_control );
455
0
    ret = sys->is_stopped;
456
0
    vlc_mutex_unlock( &sys->lock_control );
457
0
    return ret;
458
0
}
459
460
static void StartTitle( input_thread_t * p_input, bool restart )
461
0
{
462
0
    input_thread_private_t *priv = input_priv(p_input);
463
0
    vlc_value_t val;
464
465
    /* Start title/chapter */
466
0
    val.i_int = priv->master->i_title_start - priv->master->i_title_offset;
467
0
    if( val.i_int > 0 && val.i_int < priv->master->i_title )
468
0
        input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE, &val );
469
0
    else if( restart )
470
0
    {
471
0
        val.i_int = 0;
472
0
        input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE, &val );
473
0
    }
474
475
0
    val.i_int = priv->master->i_seekpoint_start -
476
0
                priv->master->i_seekpoint_offset;
477
0
    if( val.i_int > 0 /* TODO: check upper boundary */ )
478
0
        input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val );
479
0
}
480
481
static void ResetPosition( input_thread_t *p_input )
482
0
{
483
0
    input_thread_private_t *priv = input_priv(p_input);
484
0
    StartTitle( p_input, true );
485
486
    /* Seek to start position */
487
0
    if( priv->i_start > 0 )
488
0
        input_SetTime( p_input, 0, false );
489
0
    else
490
0
        input_SetPosition( p_input, 0.0f, false );
491
0
}
492
493
/*****************************************************************************
494
 * Main loop: Fill buffers from access, and demux
495
 *****************************************************************************/
496
497
/**
498
 * MainLoopDemux
499
 * It asks the demuxer to demux some data
500
 */
501
static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed )
502
0
{
503
0
    input_thread_private_t* p_priv = input_priv(p_input);
504
0
    demux_t *p_demux = p_priv->master->p_demux;
505
0
    int i_ret = VLC_DEMUXER_SUCCESS;
506
507
0
    *pb_changed = false;
508
509
0
    if( p_priv->i_stop > 0 )
510
0
    {
511
0
        vlc_tick_t i_time;
512
0
        if( demux_Control( p_demux, DEMUX_GET_TIME, &i_time ) )
513
0
            i_time = VLC_TICK_INVALID;
514
515
0
        if( p_priv->i_stop <= i_time )
516
0
            i_ret = VLC_DEMUXER_EOF;
517
0
    }
518
519
0
    if( i_ret == VLC_DEMUXER_SUCCESS )
520
0
        i_ret = demux_Demux( p_demux );
521
522
0
    i_ret = i_ret > 0 ? VLC_DEMUXER_SUCCESS : ( i_ret < 0 ? VLC_DEMUXER_EGENERIC : VLC_DEMUXER_EOF);
523
524
0
    if( i_ret == VLC_DEMUXER_SUCCESS )
525
0
    {
526
0
        if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_TITLE_LIST ) )
527
0
            UpdateTitleListfromDemux( p_input );
528
529
0
        if( p_priv->master->b_title_demux )
530
0
        {
531
0
            i_ret = UpdateTitleSeekpointFromDemux( p_input );
532
0
            *pb_changed = true;
533
0
        }
534
535
0
        UpdateGenericFromDemux( p_input );
536
0
    }
537
538
    /* If the input is stopped, we don't signal anything to the es_out and
539
     * ignore the previous results from the demux, and we need to stop
540
     * demuxing to let the input close as fast as possible. */
541
0
    if (input_Stopped(p_input))
542
0
        return;
543
544
0
    if( i_ret == VLC_DEMUXER_EOF )
545
0
    {
546
0
        msg_Dbg( p_input, "EOF reached" );
547
0
        p_priv->master->b_eof = true;
548
0
        es_out_Eos(p_priv->p_es_out);
549
0
    }
550
0
    else if( i_ret == VLC_DEMUXER_EGENERIC )
551
0
    {
552
0
        input_ChangeState( p_input, ERROR_S, VLC_TICK_INVALID );
553
0
    }
554
0
    else if( p_priv->i_slave > 0 )
555
0
        SlaveDemux( p_input );
556
0
}
557
558
static vlc_tick_t InputSourceGetLength( input_source_t *source, input_item_t *item,
559
                                        bool *live )
560
0
{
561
0
    vlc_tick_t length;
562
0
    bool ignored;
563
0
    bool *liveptr;
564
0
    if (live == NULL)
565
0
        liveptr = &ignored;
566
0
    else
567
0
    {
568
0
        liveptr = live;
569
0
        *liveptr = false;
570
0
    }
571
572
0
    if( demux_Control( source->p_demux, DEMUX_GET_LENGTH, &length, liveptr ) )
573
0
        length = VLC_TICK_INVALID;
574
0
    if( length == VLC_TICK_INVALID && item != NULL )
575
0
        length = input_item_GetDuration( item );
576
577
0
    return length;
578
0
}
579
580
static void InputSourceStatistics(input_source_t *source, input_item_t *item,
581
                                  struct vlc_input_es_out *out )
582
0
{
583
0
    double f_position = 0.0;
584
0
    vlc_tick_t i_time;
585
0
    vlc_tick_t i_length;
586
0
    vlc_tick_t i_normal_time;
587
588
    /* update input status variables */
589
0
    if( demux_Control( source->p_demux,
590
0
                       DEMUX_GET_POSITION, &f_position ) )
591
0
        f_position = 0.0;
592
593
0
    if( demux_Control( source->p_demux, DEMUX_GET_TIME, &i_time ) )
594
0
        i_time = VLC_TICK_INVALID;
595
596
0
    bool live;
597
0
    i_length = InputSourceGetLength( source, item, &live );
598
599
    /* In case of failure (not implemented or in case of seek), use VLC_TICK_INVALID. */
600
0
    if (demux_Control( source->p_demux, DEMUX_GET_NORMAL_TIME, &i_normal_time ) != VLC_SUCCESS)
601
0
        i_normal_time = VLC_TICK_INVALID;
602
603
0
    es_out_SetTimes( out, f_position, i_time, i_normal_time, i_length,
604
0
                     live );
605
0
}
606
607
/**
608
 * Update timing infos and statistics.
609
 */
610
static void MainLoopStatistics( input_thread_t *p_input )
611
0
{
612
0
    input_thread_private_t *priv = input_priv(p_input);
613
614
0
    InputSourceStatistics( priv->master, priv->p_item, priv->p_es_out );
615
616
0
    for (size_t i = 0; i < priv->i_slave; i++)
617
0
    {
618
0
        input_source_t *in = priv->slave[i];
619
0
        InputSourceStatistics( in, NULL, in->p_slave_es_out );
620
0
    }
621
622
0
    if (priv->stats != NULL)
623
0
    {
624
0
        struct input_stats_t new_stats;
625
0
        input_stats_Compute(priv->stats, &new_stats);
626
627
0
        vlc_mutex_lock(&priv->p_item->lock);
628
0
        *priv->p_item->p_stats = new_stats;
629
0
        vlc_mutex_unlock(&priv->p_item->lock);
630
631
0
        input_SendEventStatistics(p_input, &new_stats);
632
0
    }
633
0
}
634
635
/**
636
 * MainLoop
637
 * The main input loop.
638
 */
639
static void MainLoop( input_thread_t *p_input, bool b_interactive )
640
0
{
641
0
    vlc_tick_t i_intf_update = 0;
642
0
    vlc_tick_t i_inf_update_delay =
643
0
        VLC_TICK_FROM_MS(var_InheritInteger( p_input, "stats-min-report-interval" ));
644
0
    vlc_tick_t i_last_seek_mdate = 0;
645
646
0
    if( b_interactive && var_InheritBool( p_input, "start-paused" ) )
647
0
        ControlPause( p_input, vlc_tick_now() );
648
649
0
    bool eof_signaled = false;
650
651
0
    demux_t *p_demux = input_priv(p_input)->master->p_demux;
652
0
    const bool b_can_demux = p_demux->ops != NULL ?
653
0
        (p_demux->ops->demux.demux != NULL || p_demux->ops->demux.readdir != NULL) :
654
0
        (p_demux->pf_demux != NULL || p_demux->pf_readdir != NULL);
655
656
0
    while( !input_Stopped( p_input ) && input_priv(p_input)->i_state != ERROR_S )
657
0
    {
658
0
        vlc_tick_t i_wakeup = -1;
659
0
        bool b_paused = input_priv(p_input)->i_state == PAUSE_S;
660
        /* FIXME if input_priv(p_input)->i_state == PAUSE_S the access/access_demux
661
         * is paused -> this may cause problem with some of them
662
         * The same problem can be seen when seeking while paused */
663
0
        if( b_paused )
664
0
        {
665
0
            b_paused = !es_out_GetBuffering( input_priv(p_input)->p_es_out )
666
0
                    || input_priv(p_input)->master->b_eof;
667
0
            if( b_paused && input_priv(p_input)->next_frame_need_data )
668
0
                b_paused = false;
669
0
        }
670
671
0
        if( !b_paused )
672
0
        {
673
0
            if( !input_priv(p_input)->master->b_eof )
674
0
            {
675
0
                bool b_force_update = false;
676
677
0
                MainLoopDemux( p_input, &b_force_update );
678
679
0
                if( b_can_demux )
680
0
                    i_wakeup = es_out_GetWakeup( input_priv(p_input)->p_es_out );
681
0
                if( b_force_update )
682
0
                    i_intf_update = 0;
683
684
0
                eof_signaled = false;
685
0
            }
686
0
            else if( !es_out_IsEmpty( input_priv(p_input)->p_es_out ) )
687
0
            {
688
0
                msg_Dbg( p_input, "waiting decoder fifos to empty" );
689
0
                i_wakeup = vlc_tick_now() + INPUT_IDLE_SLEEP;
690
0
            }
691
0
            else
692
0
            {
693
0
                bool eof_handled;
694
0
                if( !eof_signaled )
695
0
                {
696
0
                    eof_handled =
697
0
                        input_SendEvent(p_input, &(struct vlc_input_event) {
698
0
                            .type = INPUT_EVENT_EOF,
699
0
                        });
700
0
                    eof_signaled = true;
701
0
                }
702
0
                else
703
0
                    eof_handled = true;
704
705
0
                if (eof_handled)
706
0
                    i_wakeup = -1;
707
0
                else
708
0
                    break;
709
0
            }
710
711
            /* Update interface and statistics */
712
0
            vlc_tick_t now = vlc_tick_now();
713
0
            if( now >= i_intf_update )
714
0
            {
715
0
                MainLoopStatistics( p_input );
716
0
                i_intf_update = now + i_inf_update_delay;
717
0
            }
718
0
        }
719
720
        /* Handle control */
721
0
        for( ;; )
722
0
        {
723
0
            vlc_tick_t i_deadline = i_wakeup;
724
725
            /* Postpone seeking until ES buffering is complete or at most
726
             * 125 ms. */
727
0
            bool b_postpone = es_out_GetBuffering( input_priv(p_input)->p_es_out )
728
0
                            && !input_priv(p_input)->master->b_eof;
729
0
            if( b_postpone )
730
0
            {
731
0
                vlc_tick_t now = vlc_tick_now();
732
733
                /* Recheck ES buffer level every 20 ms when seeking */
734
0
                if( now < i_last_seek_mdate + VLC_TICK_FROM_MS(125)
735
0
                 && (i_deadline < 0 || i_deadline > now + VLC_TICK_FROM_MS(20)) )
736
0
                    i_deadline = now + VLC_TICK_FROM_MS(20);
737
0
                else
738
0
                    b_postpone = false;
739
0
            }
740
741
0
            int i_type;
742
0
            input_control_param_t param;
743
744
0
            if( ControlPop( p_input, &i_type, &param, i_deadline, b_postpone ) )
745
0
            {
746
0
                if( b_postpone )
747
0
                    continue;
748
0
                break; /* Wake-up time reached */
749
0
            }
750
751
0
            if( Control( p_input, i_type, param ) )
752
0
            {
753
0
                if( ControlIsSeekRequest( i_type ) )
754
0
                    i_last_seek_mdate = vlc_tick_now();
755
0
                i_intf_update = 0;
756
0
            }
757
758
            /* Update the wakeup time */
759
0
            if( input_priv(p_input)->next_frame_need_data )
760
0
                i_wakeup = 0;
761
0
            else if( i_wakeup != 0 )
762
0
            {
763
0
                i_wakeup = es_out_GetWakeup( input_priv(p_input)->p_es_out );
764
0
            }
765
0
        }
766
0
    }
767
0
}
768
769
static int InitSout( input_thread_t * p_input )
770
0
{
771
0
    input_thread_private_t *priv = input_priv(p_input);
772
773
0
    if( priv->type != INPUT_TYPE_PLAYBACK )
774
0
        return VLC_SUCCESS;
775
776
    /* Find a usable sout and attach it to p_input */
777
0
    char *psz = var_GetNonEmptyString( p_input, "sout" );
778
0
    if( priv->p_renderer )
779
0
    {
780
        /* Keep sout if it comes from a renderer and if the user didn't touch
781
         * the sout config */
782
0
        bool keep_sout = psz == NULL;
783
0
        free(psz);
784
785
0
        const char *psz_renderer_sout = vlc_renderer_item_sout( priv->p_renderer );
786
0
        if( asprintf( &psz, "#%s", psz_renderer_sout ) < 0 )
787
0
            return VLC_ENOMEM;
788
0
        if( keep_sout )
789
0
            var_SetBool( p_input, "sout-keep", true );
790
0
    }
791
0
    if( psz && strncasecmp( priv->p_item->psz_uri, "vlc:", 4 ) )
792
0
    {
793
0
        priv->p_sout  = input_resource_RequestSout( priv->p_resource, psz );
794
0
        if( priv->p_sout == NULL )
795
0
        {
796
0
            input_ChangeState( p_input, ERROR_S, VLC_TICK_INVALID );
797
0
            msg_Err( p_input, "cannot start stream output instance, " \
798
0
                              "aborting" );
799
0
            free( psz );
800
0
            return VLC_EGENERIC;
801
0
        }
802
0
    }
803
0
    else
804
0
    {
805
0
        input_resource_TerminateSout( priv->p_resource );
806
0
    }
807
0
    free( psz );
808
809
0
    return VLC_SUCCESS;
810
0
}
811
812
static void InitProperties( input_thread_t *input )
813
0
{
814
0
    input_thread_private_t *priv = input_priv(input);
815
0
    input_source_t *master = priv->master;
816
0
    assert(master);
817
818
0
    int capabilities = 0;
819
820
0
    if( demux_Control( master->p_demux, DEMUX_CAN_SEEK, &master->b_can_seek ) )
821
0
        master->b_can_seek = false;
822
0
    if( master->b_can_seek )
823
0
        capabilities |= VLC_INPUT_CAPABILITIES_SEEKABLE;
824
825
0
    if( master->b_can_pause || !master->b_can_pace_control )
826
0
        capabilities |= VLC_INPUT_CAPABILITIES_PAUSEABLE;
827
0
    if( !master->b_can_pace_control || master->b_can_rate_control )
828
0
        capabilities |= VLC_INPUT_CAPABILITIES_CHANGE_RATE;
829
0
    if( !master->b_rescale_ts && !master->b_can_pace_control && master->b_can_rate_control )
830
0
        capabilities |= VLC_INPUT_CAPABILITIES_REWINDABLE;
831
832
0
    input_SendEventCapabilities( input, capabilities );
833
834
0
    int i_attachment;
835
0
    input_attachment_t **attachment;
836
0
    if( !demux_Control( master->p_demux, DEMUX_GET_ATTACHMENTS,
837
0
                        &attachment, &i_attachment ) )
838
0
    {
839
0
        vlc_mutex_lock( &priv->p_item->lock );
840
0
        AppendAttachment( input, i_attachment, attachment );
841
0
        vlc_mutex_unlock( &priv->p_item->lock );
842
0
    }
843
844
0
    int input_type;
845
0
    if( !demux_Control( master->p_demux, DEMUX_GET_TYPE, &input_type ) )
846
0
    {
847
0
        vlc_mutex_lock( &priv->p_item->lock );
848
0
        priv->p_item->i_type = input_type;
849
0
        vlc_mutex_unlock( &priv->p_item->lock );
850
0
    }
851
0
}
852
853
static void InitTitle( input_thread_t * p_input, bool had_titles )
854
0
{
855
0
    input_thread_private_t *priv = input_priv(p_input);
856
0
    input_source_t *p_master = priv->master;
857
858
0
    if( priv->type != INPUT_TYPE_PLAYBACK )
859
0
        return;
860
861
0
    vlc_mutex_lock( &priv->p_item->lock );
862
0
    priv->i_title_offset = p_master->i_title_offset;
863
0
    priv->i_seekpoint_offset = p_master->i_seekpoint_offset;
864
0
    vlc_mutex_unlock( &priv->p_item->lock );
865
866
    /* Send event only if the count is valid or if titles are gone */
867
0
    if (had_titles || p_master->i_title > 0)
868
0
        input_SendEventTitle( p_input, &(struct vlc_input_event_title) {
869
0
            .action = VLC_INPUT_TITLE_NEW_LIST,
870
0
            .list = {
871
0
                .array = p_master->title,
872
0
                .count = p_master->i_title,
873
0
            },
874
0
        });
875
0
}
876
877
static void FillFileStatsIfAny( input_thread_t * p_input )
878
0
{
879
0
    input_thread_private_t *priv = input_priv( p_input );
880
0
    input_source_t *master = priv->master;
881
0
    stream_t *stream_filters = master->p_demux->s;
882
883
0
    if( stream_filters == NULL )
884
0
        return;
885
886
0
    uint64_t size;
887
888
0
    if( vlc_stream_GetSize(stream_filters, &size) == VLC_SUCCESS )
889
0
        input_item_AddStat( priv->p_item, "size", size );
890
891
0
    uint64_t mtime;
892
0
    if( vlc_stream_GetMTime(stream_filters, &mtime) == VLC_SUCCESS )
893
0
        input_item_AddStat( priv->p_item, "mtime", mtime );
894
0
}
895
896
static void SetStopStart( input_thread_t * p_input )
897
0
{
898
0
    input_thread_private_t *priv = input_priv(p_input);
899
900
    /* Start/stop/run time */
901
0
    priv->i_start = llroundl(CLOCK_FREQ *
902
0
                             (double) var_GetFloat( p_input, "start-time" ));
903
0
    priv->i_stop  = llroundl(CLOCK_FREQ *
904
0
                             (double) var_GetFloat( p_input, "stop-time" ));
905
0
    if( priv->i_stop <= 0 )
906
0
    {
907
0
        priv->i_stop = llroundl(CLOCK_FREQ *
908
0
                                (double) var_GetFloat( p_input, "run-time" ));
909
0
        if( priv->i_stop < 0 )
910
0
        {
911
0
            msg_Warn( p_input, "invalid run-time ignored" );
912
0
            priv->i_stop = 0;
913
0
        }
914
0
        else
915
0
            priv->i_stop += priv->i_start;
916
0
    }
917
918
0
    if( priv->i_start > 0 )
919
0
    {
920
0
        msg_Dbg( p_input, "starting at time: %"PRId64"s",
921
0
                 SEC_FROM_VLC_TICK(priv->i_start) );
922
923
0
        input_SetTime( p_input, 0, false );
924
0
    }
925
0
    if( priv->i_stop > 0 && priv->i_stop <= priv->i_start )
926
0
    {
927
0
        msg_Warn( p_input, "invalid stop-time ignored" );
928
0
        priv->i_stop = 0;
929
0
    }
930
0
}
931
932
static int SlaveCompare(const void *a, const void *b)
933
0
{
934
0
    const input_item_slave_t *p_slave0 = *((const input_item_slave_t **) a);
935
0
    const input_item_slave_t *p_slave1 = *((const input_item_slave_t **) b);
936
937
0
    if( p_slave0 == NULL || p_slave1 == NULL )
938
0
    {
939
        /* Put NULL (or rejected) subs at the end */
940
0
        return p_slave0 == NULL ? 1 : p_slave1 == NULL ? -1 : 0;
941
0
    }
942
943
0
    if( p_slave0->i_priority > p_slave1->i_priority )
944
0
        return -1;
945
946
0
    if( p_slave0->i_priority < p_slave1->i_priority )
947
0
        return 1;
948
949
0
    return 0;
950
0
}
951
952
static bool SlaveExists( input_item_slave_t **pp_slaves, int i_slaves,
953
                         const char *psz_uri)
954
0
{
955
0
    for( int i = 0; i < i_slaves; i++ )
956
0
    {
957
0
        if( pp_slaves[i] != NULL
958
0
         && !strcmp( pp_slaves[i]->psz_uri, psz_uri ) )
959
0
            return true;
960
0
    }
961
0
    return false;
962
0
}
963
964
static void RequestSubRate( input_thread_t *p_input, float f_slave_fps )
965
0
{
966
0
    input_thread_private_t *priv = input_priv(p_input);
967
0
    const float f_fps = input_priv(p_input)->master->f_fps;
968
0
    if( f_fps > 1.f && f_slave_fps > 1.f )
969
0
        priv->slave_subs_rate = f_fps / f_slave_fps;
970
0
    else if ( priv->slave_subs_rate != 0 )
971
0
        priv->slave_subs_rate = 1.f;
972
0
}
973
974
static void SetSubtitlesOptions( input_thread_t *p_input )
975
0
{
976
    /* Get fps and set it if not already set */
977
0
    const float f_fps = input_priv(p_input)->master->f_fps;
978
0
    if( f_fps > 1.f )
979
0
    {
980
0
        var_SetFloat( p_input, "sub-original-fps", f_fps );
981
0
        RequestSubRate( p_input, var_InheritFloat( p_input, "sub-fps" ) );
982
0
    }
983
0
}
984
985
static enum slave_type DeduceSlaveType( input_thread_t *p_input,
986
                                        const char *psz_uri )
987
0
{
988
0
    vlc_url_t parsed_uri;
989
0
    if( vlc_UrlParse( &parsed_uri, psz_uri ) != VLC_SUCCESS ||
990
0
        parsed_uri.psz_path == NULL )
991
0
    {
992
0
        goto fail;
993
0
    }
994
995
0
    enum slave_type i_type;
996
0
    if( !input_item_slave_GetType( parsed_uri.psz_path, &i_type ) )
997
0
        goto fail;
998
999
0
    vlc_UrlClean( &parsed_uri );
1000
0
    return i_type;
1001
1002
0
fail:
1003
0
    msg_Dbg( p_input, "Can't deduce slave type of \"%s\" with file extension.",
1004
0
             psz_uri );
1005
0
    vlc_UrlClean( &parsed_uri );
1006
0
    return SLAVE_TYPE_GENERIC;
1007
0
}
1008
1009
static void GetVarSlaves( input_thread_t *p_input,
1010
                          input_item_slave_t ***ppp_slaves, int *p_slaves )
1011
0
{
1012
0
    char *psz = var_GetNonEmptyString( p_input, "input-slave" );
1013
0
    if( !psz )
1014
0
        return;
1015
1016
0
    input_item_slave_t **pp_slaves = *ppp_slaves;
1017
0
    int i_slaves = *p_slaves;
1018
1019
0
    char *psz_org = psz;
1020
0
    while( psz && *psz )
1021
0
    {
1022
0
        while( *psz == ' ' || *psz == '#' )
1023
0
            psz++;
1024
1025
0
        char *psz_delim = strchr( psz, '#' );
1026
0
        if( psz_delim )
1027
0
            *psz_delim++ = '\0';
1028
1029
0
        if( *psz == 0 )
1030
0
            break;
1031
1032
0
        char *uri = strstr(psz, "://")
1033
0
                                   ? strdup( psz ) : vlc_path2uri( psz, NULL );
1034
0
        psz = psz_delim;
1035
0
        if( uri == NULL )
1036
0
            continue;
1037
1038
0
        const enum slave_type i_type = DeduceSlaveType( p_input, uri );
1039
0
        input_item_slave_t *p_slave =
1040
0
            input_item_slave_New( uri, i_type, SLAVE_PRIORITY_USER );
1041
0
        free( uri );
1042
1043
0
        if( unlikely( p_slave == NULL ) )
1044
0
            break;
1045
0
        TAB_APPEND(i_slaves, pp_slaves, p_slave);
1046
0
    }
1047
0
    free( psz_org );
1048
1049
0
    *ppp_slaves = pp_slaves; /* in case of realloc */
1050
0
    *p_slaves = i_slaves;
1051
0
}
1052
1053
static void LoadSlaves( input_thread_t *p_input )
1054
0
{
1055
0
    input_item_slave_t **pp_slaves;
1056
0
    int i_slaves;
1057
0
    TAB_INIT( i_slaves, pp_slaves );
1058
1059
    /* Look for and add slaves */
1060
1061
0
    char *psz_subtitle = var_GetNonEmptyString( p_input, "sub-file" );
1062
0
    if( psz_subtitle != NULL )
1063
0
    {
1064
0
        msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
1065
0
        char *psz_uri = input_SubtitleFile2Uri( p_input, psz_subtitle );
1066
0
        free( psz_subtitle );
1067
0
        psz_subtitle = NULL;
1068
0
        if( psz_uri != NULL )
1069
0
        {
1070
0
            input_item_slave_t *p_slave =
1071
0
                input_item_slave_New( psz_uri, SLAVE_TYPE_SPU,
1072
0
                                      SLAVE_PRIORITY_USER );
1073
0
            free( psz_uri );
1074
0
            if( p_slave )
1075
0
            {
1076
0
                TAB_APPEND(i_slaves, pp_slaves, p_slave);
1077
0
                psz_subtitle = p_slave->psz_uri;
1078
0
            }
1079
0
        }
1080
0
    }
1081
1082
0
    if( var_GetBool( p_input, "sub-autodetect-file" ) )
1083
0
    {
1084
        /* Add local subtitles */
1085
0
        char *psz_autopath = var_GetNonEmptyString( p_input, "sub-autodetect-path" );
1086
1087
0
        if( subtitles_Detect( p_input, psz_autopath, input_priv(p_input)->p_item->psz_uri,
1088
0
                              &pp_slaves, &i_slaves ) == VLC_SUCCESS )
1089
0
        {
1090
            /* check that we did not add the subtitle through sub-file */
1091
0
            if( psz_subtitle != NULL )
1092
0
            {
1093
0
                for( int i = 1; i < i_slaves; i++ )
1094
0
                {
1095
0
                    input_item_slave_t *p_curr = pp_slaves[i];
1096
0
                    if( p_curr != NULL
1097
0
                     && !strcmp( psz_subtitle, p_curr->psz_uri ) )
1098
0
                    {
1099
                        /* reject current sub */
1100
0
                        input_item_slave_Delete( p_curr );
1101
0
                        pp_slaves[i] = NULL;
1102
0
                    }
1103
0
                }
1104
0
            }
1105
0
        }
1106
0
        free( psz_autopath );
1107
0
    }
1108
1109
    /* Add slaves from the "input-slave" option */
1110
0
    GetVarSlaves( p_input, &pp_slaves, &i_slaves );
1111
1112
    /* Add slaves found by the directory demuxer or via libvlc */
1113
0
    input_item_t *p_item = input_priv(p_input)->p_item;
1114
0
    vlc_mutex_lock( &p_item->lock );
1115
1116
    /* Move item slaves to local pp_slaves */
1117
0
    for( int i = 0; i < p_item->i_slaves; i++ )
1118
0
    {
1119
0
        input_item_slave_t *p_slave = p_item->pp_slaves[i];
1120
0
        if( !SlaveExists( pp_slaves, i_slaves, p_slave->psz_uri ) )
1121
0
            TAB_APPEND(i_slaves, pp_slaves, p_slave);
1122
0
        else
1123
0
            input_item_slave_Delete( p_slave );
1124
0
    }
1125
    /* Slaves that are successfully loaded will be added back to the item */
1126
0
    TAB_CLEAN( p_item->i_slaves, p_item->pp_slaves );
1127
0
    vlc_mutex_unlock( &p_item->lock );
1128
1129
0
    if( i_slaves > 0 ) /* Sort by priority */
1130
0
        qsort( pp_slaves, i_slaves, sizeof (input_item_slave_t*),
1131
0
               SlaveCompare );
1132
1133
    /* add all detected slaves */
1134
0
    static_assert( SLAVE_TYPE_GENERIC <= 1 && SLAVE_TYPE_SPU <= 1,
1135
0
                   "slave type size mismatch");
1136
0
    for( int i = 0; i < i_slaves && pp_slaves[i] != NULL; i++ )
1137
0
    {
1138
0
        input_item_slave_t *p_slave = pp_slaves[i];
1139
        /* Slaves added via options should not fail */
1140
0
        unsigned i_flags = p_slave->i_priority != SLAVE_PRIORITY_USER
1141
0
                           ? SLAVE_ADD_CANFAIL : SLAVE_ADD_NOFLAG;
1142
1143
        /* Force the first subtitle with the highest priority or with the
1144
         * forced flag */
1145
0
        if ( p_slave->b_forced || p_slave->i_priority >= SLAVE_PRIORITY_MATCH_ALL )
1146
0
            i_flags |= SLAVE_ADD_FORCED;
1147
1148
0
        if( input_SlaveSourceAdd( p_input, p_slave->i_type, p_slave->psz_uri,
1149
0
                                  i_flags ) == VLC_SUCCESS )
1150
0
            input_item_AddSlave( input_priv(p_input)->p_item, p_slave );
1151
0
        else
1152
0
            input_item_slave_Delete( p_slave );
1153
0
    }
1154
0
    TAB_CLEAN( i_slaves, pp_slaves );
1155
1156
    /* Load subtitles from attachments */
1157
0
    int i_attachment = 0;
1158
0
    input_attachment_t **pp_attachment = NULL;
1159
1160
0
    vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1161
0
    for( int i = 0; i < input_priv(p_input)->i_attachment; i++ )
1162
0
    {
1163
0
        const input_attachment_t *a = input_priv(p_input)->attachment[i];
1164
0
        if( !strcmp( a->psz_mime, "application/x-srt" ) )
1165
0
            TAB_APPEND( i_attachment, pp_attachment,
1166
0
                        vlc_input_attachment_New( a->psz_name, NULL,
1167
0
                                                  a->psz_description, NULL, 0 ) );
1168
0
    }
1169
0
    vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1170
1171
0
    if( i_attachment > 0 )
1172
0
        var_Create( p_input, "sub-description", VLC_VAR_STRING );
1173
0
    for( int i = 0; i < i_attachment; i++ )
1174
0
    {
1175
0
        input_attachment_t *a = pp_attachment[i];
1176
0
        if( !a )
1177
0
            continue;
1178
0
        char *psz_mrl;
1179
0
        if( a->psz_name[0] &&
1180
0
            asprintf( &psz_mrl, "attachment://%s", a->psz_name ) >= 0 )
1181
0
        {
1182
0
            var_SetString( p_input, "sub-description", a->psz_description ? a->psz_description : "");
1183
1184
            /* Force the first subtitle from attachment if there is no
1185
             * subtitles already forced */
1186
0
            input_SlaveSourceAdd( p_input, SLAVE_TYPE_SPU, psz_mrl,
1187
0
                                  SLAVE_ADD_FORCED );
1188
1189
0
            free( psz_mrl );
1190
            /* Don't update item slaves for attachments */
1191
0
        }
1192
0
        vlc_input_attachment_Release( a );
1193
0
    }
1194
0
    free( pp_attachment );
1195
0
    if( i_attachment > 0 )
1196
0
        var_Destroy( p_input, "sub-description" );
1197
0
}
1198
1199
static void UpdatePtsDelay( input_thread_t *p_input )
1200
0
{
1201
0
    input_thread_private_t *p_sys = input_priv(p_input);
1202
1203
    /* Get max pts delay from input source */
1204
0
    vlc_tick_t i_pts_delay = p_sys->master->i_pts_delay;
1205
0
    for (size_t i = 0; i < p_sys->i_slave; i++)
1206
0
        i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );
1207
1208
0
    if( i_pts_delay < 0 )
1209
0
        i_pts_delay = 0;
1210
1211
    /* Update cr_average depending on the caching */
1212
0
    const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
1213
1214
0
    es_out_SetJitter( input_priv(p_input)->p_es_out, i_pts_delay, 0, i_cr_average );
1215
0
}
1216
1217
static void InitPrograms( input_thread_t * p_input )
1218
0
{
1219
0
    int i_es_out_mode;
1220
0
    int *tab;
1221
0
    size_t count;
1222
1223
0
    TAB_INIT(count, tab);
1224
1225
    /* Compute correct pts_delay */
1226
0
    UpdatePtsDelay( p_input );
1227
1228
    /* Set up es_out */
1229
0
    i_es_out_mode = ES_OUT_MODE_AUTO;
1230
0
    if( input_priv(p_input)->p_sout && !input_priv(p_input)->p_renderer )
1231
0
    {
1232
0
        char *prgms;
1233
1234
0
        if( (prgms = var_GetNonEmptyString( p_input, "programs" )) != NULL )
1235
0
        {
1236
0
            char *buf;
1237
1238
0
            for( const char *prgm = strtok_r( prgms, ",", &buf );
1239
0
                 prgm != NULL;
1240
0
                 prgm = strtok_r( NULL, ",", &buf ) )
1241
0
            {
1242
0
                TAB_APPEND(count, tab, atoi(prgm));
1243
0
            }
1244
1245
0
            if( count > 0 )
1246
0
                i_es_out_mode = ES_OUT_MODE_PARTIAL;
1247
                /* Note : we should remove the "program" callback. */
1248
1249
0
            free( prgms );
1250
0
        }
1251
0
        else if( var_GetBool( p_input, "sout-all" ) )
1252
0
        {
1253
0
            i_es_out_mode = ES_OUT_MODE_ALL;
1254
0
        }
1255
0
    }
1256
0
    es_out_SetMode( input_priv(p_input)->p_es_out, i_es_out_mode );
1257
1258
    /* Inform the demuxer about waited group (needed only for DVB) */
1259
0
    if( i_es_out_mode == ES_OUT_MODE_ALL )
1260
0
    {
1261
0
        demux_Control( input_priv(p_input)->master->p_demux,
1262
0
                       DEMUX_SET_GROUP_ALL );
1263
0
    }
1264
0
    else if( i_es_out_mode == ES_OUT_MODE_PARTIAL )
1265
0
    {
1266
0
        demux_Control( input_priv(p_input)->master->p_demux,
1267
0
                       DEMUX_SET_GROUP_LIST, count, tab );
1268
0
        free(tab);
1269
0
    }
1270
0
    else
1271
0
    {
1272
0
        int program = es_out_GetGroupForced( input_priv(p_input)->p_es_out );
1273
0
        if( program == 0 )
1274
0
            demux_Control( input_priv(p_input)->master->p_demux,
1275
0
                           DEMUX_SET_GROUP_DEFAULT );
1276
0
        else
1277
0
            demux_Control( input_priv(p_input)->master->p_demux,
1278
0
                           DEMUX_SET_GROUP_LIST, (size_t)1,
1279
0
                           (const int *)&program );
1280
0
    }
1281
0
}
1282
1283
static int Init( input_thread_t * p_input )
1284
0
{
1285
0
    input_thread_private_t *priv = input_priv(p_input);
1286
0
    input_source_t *master;
1287
1288
    /* */
1289
0
    input_ChangeState( p_input, OPENING_S, VLC_TICK_INVALID );
1290
0
    input_SendEventCache( p_input, 0.0 );
1291
1292
0
    if( var_Type( vlc_object_parent(p_input), "meta-file" ) )
1293
0
    {
1294
0
        msg_Dbg( p_input, "Input is a meta file: disabling unneeded options" );
1295
0
        var_SetString( p_input, "sout", "" );
1296
0
        var_SetBool( p_input, "sout-all", false );
1297
0
        var_SetString( p_input, "input-slave", "" );
1298
0
        var_SetInteger( p_input, "input-repeat", 0 );
1299
0
        var_SetString( p_input, "sub-file", "" );
1300
0
        var_SetBool( p_input, "sub-autodetect-file", false );
1301
0
    }
1302
1303
0
    if( InitSout( p_input ) )
1304
0
        goto error;
1305
1306
    /* Create es out */
1307
0
    priv->p_es_out = input_EsOutTimeshiftNew(p_input, priv->p_es_out_display, priv->rate);
1308
0
    if (priv->p_es_out == NULL)
1309
0
        goto error;
1310
1311
    /* Handle all early controls */
1312
0
    for (;;)
1313
0
    {
1314
0
        int type;
1315
0
        input_control_param_t param;
1316
0
        if (ControlPopEarly(p_input, &type, &param))
1317
0
            break;
1318
0
        Control(p_input, type, param);
1319
0
    }
1320
1321
    /* */
1322
0
    master = priv->master;
1323
0
    if( master == NULL )
1324
0
        goto error;
1325
0
    int ret = InputSourceInit( master, p_input, priv->p_item->psz_uri,
1326
0
                               NULL, false );
1327
0
    if( ret != VLC_SUCCESS )
1328
0
    {
1329
0
        InputSourceDestroy( master );
1330
0
        goto error;
1331
0
    }
1332
1333
0
    InitProperties( p_input );
1334
1335
0
    InitTitle( p_input, false );
1336
1337
0
    SetStopStart( p_input );
1338
    /* Load master infos */
1339
0
    InputSourceStatistics( master, priv->p_item, priv->p_es_out );
1340
1341
0
    FillFileStatsIfAny( p_input );
1342
1343
0
    if( priv->type != INPUT_TYPE_PREPARSING )
1344
0
    {
1345
0
        StartTitle( p_input, false );
1346
0
        SetSubtitlesOptions( p_input );
1347
0
        LoadSlaves( p_input );
1348
0
        InitPrograms( p_input );
1349
1350
0
        double f_rate = var_GetFloat( p_input, "rate" );
1351
0
        if( f_rate != 0.0 && f_rate != 1.0 )
1352
0
        {
1353
0
            vlc_value_t val = { .f_float = f_rate };
1354
0
            input_ControlPushHelper( p_input, INPUT_CONTROL_SET_RATE, &val );
1355
0
        }
1356
0
    }
1357
1358
0
    if( priv->type == INPUT_TYPE_PLAYBACK && priv->p_sout )
1359
0
    {
1360
0
        priv->b_out_pace_control = sout_StreamIsSynchronous(priv->p_sout);
1361
0
        msg_Dbg( p_input, "starting in %ssync mode",
1362
0
                 priv->b_out_pace_control ? "a" : "" );
1363
0
    }
1364
1365
0
    if (!input_item_IsPreparsed(input_priv(p_input)->p_item))
1366
0
    {
1367
0
        vlc_meta_t *p_meta = vlc_meta_New();
1368
0
        if( p_meta != NULL )
1369
0
        {
1370
            /* Get meta data from users */
1371
0
            InputMetaUser( p_input, p_meta );
1372
1373
            /* Get meta data from master input */
1374
0
            InputSourceMeta( p_input, master, p_meta );
1375
1376
            /* And from slave */
1377
0
            for (size_t i = 0; i < priv->i_slave; i++)
1378
0
                InputSourceMeta( p_input, priv->slave[i], p_meta );
1379
1380
0
            es_out_ControlSetMeta(&priv->p_es_out->out, p_meta);
1381
0
            vlc_meta_Delete( p_meta );
1382
0
        }
1383
0
    }
1384
1385
0
    msg_Dbg( p_input, "`%s' successfully opened",
1386
0
             input_priv(p_input)->p_item->psz_uri );
1387
1388
    /* initialization is complete */
1389
0
    input_ChangeState( p_input, PLAYING_S, vlc_tick_now() );
1390
1391
0
    return VLC_SUCCESS;
1392
1393
0
error:
1394
0
    input_ChangeState( p_input, ERROR_S, VLC_TICK_INVALID );
1395
1396
0
    if( input_priv(p_input)->p_es_out )
1397
0
        vlc_input_es_out_Delete(input_priv(p_input)->p_es_out);
1398
0
    es_out_SetMode(input_priv(p_input)->p_es_out_display, ES_OUT_MODE_END );
1399
0
    if( input_priv(p_input)->p_resource )
1400
0
    {
1401
0
        if( input_priv(p_input)->p_sout )
1402
0
            input_resource_PutSout( input_priv(p_input)->p_resource,
1403
0
                                    input_priv(p_input)->p_sout );
1404
0
        input_resource_SetInput( input_priv(p_input)->p_resource, NULL );
1405
0
        if( input_priv(p_input)->p_resource )
1406
0
        {
1407
0
            input_resource_Release( input_priv(p_input)->p_resource );
1408
0
            input_priv(p_input)->p_resource = NULL;
1409
0
        }
1410
0
    }
1411
1412
    /* Mark them deleted */
1413
0
    input_priv(p_input)->p_es_out = NULL;
1414
0
    input_priv(p_input)->p_sout = NULL;
1415
1416
0
    return VLC_EGENERIC;
1417
0
}
1418
1419
/*****************************************************************************
1420
 * End: end the input thread
1421
 *****************************************************************************/
1422
static void End( input_thread_t * p_input )
1423
0
{
1424
0
    input_thread_private_t *priv = input_priv(p_input);
1425
1426
    /* We are at the end */
1427
0
    input_ChangeState( p_input, END_S, VLC_TICK_INVALID );
1428
1429
    /* Stop es out activity */
1430
0
    es_out_SetMode( priv->p_es_out, ES_OUT_MODE_NONE );
1431
1432
    /* Delete slave */
1433
0
    for (size_t i = 0; i < priv->i_slave; i++)
1434
0
    {
1435
0
        InputSourceDestroy( priv->slave[i] );
1436
0
        input_source_Release( priv->slave[i] );
1437
0
    }
1438
0
    free( priv->slave );
1439
1440
    /* Clean up master */
1441
0
    InputSourceDestroy( priv->master );
1442
0
    priv->i_title_offset = 0;
1443
0
    priv->i_seekpoint_offset = 0;
1444
1445
    /* Unload all modules */
1446
0
    if( priv->p_es_out )
1447
0
    {
1448
0
        vlc_input_es_out_Delete(priv->p_es_out);
1449
0
        priv->p_es_out = NULL;
1450
0
    }
1451
0
    es_out_SetMode( priv->p_es_out_display, ES_OUT_MODE_END );
1452
1453
0
    if( priv->stats != NULL )
1454
0
    {
1455
0
        input_item_t *item = priv->p_item;
1456
        /* make sure we are up to date */
1457
0
        vlc_mutex_lock( &item->lock );
1458
0
        input_stats_Compute( priv->stats, item->p_stats );
1459
0
        vlc_mutex_unlock( &item->lock );
1460
0
    }
1461
1462
0
    vlc_mutex_lock( &priv->p_item->lock );
1463
0
    if( priv->i_attachment > 0 )
1464
0
    {
1465
0
        for( int i = 0; i < priv->i_attachment; i++ )
1466
0
            vlc_input_attachment_Release( priv->attachment[i] );
1467
0
        TAB_CLEAN( priv->i_attachment, priv->attachment );
1468
0
    }
1469
1470
0
    vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1471
1472
    /* */
1473
0
    input_resource_PutSout( input_priv(p_input)->p_resource,
1474
0
                            input_priv(p_input)->p_sout );
1475
0
    input_resource_SetInput( input_priv(p_input)->p_resource, NULL );
1476
0
    if( input_priv(p_input)->p_resource )
1477
0
    {
1478
0
        input_resource_Release( input_priv(p_input)->p_resource );
1479
0
        input_priv(p_input)->p_resource = NULL;
1480
0
    }
1481
0
}
1482
1483
static size_t ControlGetReducedIndexLocked( input_thread_t *p_input,
1484
                                            input_control_t *c )
1485
0
{
1486
0
    input_thread_private_t *sys = input_priv(p_input);
1487
1488
0
    if( sys->i_control == 0 )
1489
0
        return 0;
1490
1491
0
    input_control_t *prev_control = &sys->control[sys->i_control - 1];
1492
0
    const int i_lt = prev_control->i_type;
1493
0
    const int i_ct = c->i_type;
1494
1495
0
    if( i_lt == i_ct )
1496
0
    {
1497
0
        if ( i_ct == INPUT_CONTROL_SET_STATE ||
1498
0
             i_ct == INPUT_CONTROL_SET_RATE ||
1499
0
             i_ct == INPUT_CONTROL_SET_POSITION ||
1500
0
             i_ct == INPUT_CONTROL_SET_TIME ||
1501
0
             i_ct == INPUT_CONTROL_SET_PROGRAM ||
1502
0
             i_ct == INPUT_CONTROL_SET_TITLE ||
1503
0
             i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
1504
0
             i_ct == INPUT_CONTROL_SET_VIEWPOINT )
1505
0
        {
1506
0
            return sys->i_control - 1;
1507
0
        }
1508
0
        else if ( i_ct == INPUT_CONTROL_UPDATE_VIEWPOINT )
1509
0
        {
1510
0
            float yaw1, pitch1, roll1;
1511
0
            float yaw2, pitch2, roll2;
1512
0
            vlc_viewpoint_to_euler(&prev_control->param.viewpoint, &yaw1, &pitch1, &roll1);
1513
0
            vlc_viewpoint_to_euler(&c->param.viewpoint, &yaw2, &pitch2, &roll2);
1514
0
            vlc_viewpoint_from_euler(&c->param.viewpoint,
1515
0
                                     yaw1 + yaw2,
1516
0
                                     pitch1 + pitch2,
1517
0
                                     roll1 + roll2);
1518
0
            c->param.viewpoint.fov += prev_control->param.viewpoint.fov;
1519
0
            return sys->i_control - 1;
1520
0
        }
1521
0
    }
1522
1523
0
    return sys->i_control;
1524
0
}
1525
1526
/*****************************************************************************
1527
 * Control
1528
 *****************************************************************************/
1529
int input_ControlPush( input_thread_t *p_input,
1530
                       int i_type, const input_control_param_t *p_param )
1531
0
{
1532
0
    input_thread_private_t *sys = input_priv(p_input);
1533
1534
0
    vlc_mutex_lock( &sys->lock_control );
1535
0
    input_control_t c = {
1536
0
        .i_type = i_type,
1537
0
    };
1538
0
    if( p_param )
1539
0
        c.param = *p_param;
1540
1541
0
    size_t i_next_control_idx = ControlGetReducedIndexLocked( p_input, &c );
1542
1543
0
    if( sys->is_stopped || i_next_control_idx >= INPUT_CONTROL_FIFO_SIZE )
1544
0
    {
1545
0
        if( sys->is_stopped )
1546
0
            msg_Dbg( p_input, "input control stopped, trashing type=%d",
1547
0
                     i_type );
1548
0
        else
1549
0
            msg_Err( p_input, "input control fifo overflow, trashing type=%d",
1550
0
                     i_type );
1551
0
        if( p_param )
1552
0
            ControlRelease( i_type, p_param );
1553
0
        vlc_mutex_unlock( &sys->lock_control );
1554
0
        return VLC_EGENERIC;
1555
0
    }
1556
1557
0
    sys->control[i_next_control_idx] = c;
1558
0
    sys->i_control = i_next_control_idx + 1;
1559
1560
0
    vlc_cond_signal( &sys->wait_control );
1561
0
    vlc_mutex_unlock( &sys->lock_control );
1562
0
    return VLC_SUCCESS;
1563
0
}
1564
1565
static inline int ControlPop( input_thread_t *p_input,
1566
                              int *pi_type, input_control_param_t *p_param,
1567
                              vlc_tick_t i_deadline, bool b_postpone_seek )
1568
0
{
1569
0
    input_thread_private_t *p_sys = input_priv(p_input);
1570
1571
0
    vlc_mutex_lock( &p_sys->lock_control );
1572
0
    while( p_sys->i_control <= 0 ||
1573
0
           ( b_postpone_seek && ControlIsSeekRequest( p_sys->control[0].i_type ) ) )
1574
0
    {
1575
0
        if( p_sys->is_stopped )
1576
0
        {
1577
0
            vlc_mutex_unlock( &p_sys->lock_control );
1578
0
            return VLC_EGENERIC;
1579
0
        }
1580
1581
0
        if( i_deadline >= 0 )
1582
0
        {
1583
0
            if( vlc_cond_timedwait( &p_sys->wait_control, &p_sys->lock_control,
1584
0
                                    i_deadline ) )
1585
0
            {
1586
0
                vlc_mutex_unlock( &p_sys->lock_control );
1587
0
                return VLC_EGENERIC;
1588
0
            }
1589
0
        }
1590
0
        else
1591
0
            vlc_cond_wait( &p_sys->wait_control, &p_sys->lock_control );
1592
0
    }
1593
1594
    /* */
1595
0
    *pi_type = p_sys->control[0].i_type;
1596
0
    *p_param   = p_sys->control[0].param;
1597
1598
0
    p_sys->i_control --;
1599
0
    if( p_sys->i_control > 0 )
1600
0
        memmove( &p_sys->control[0], &p_sys->control[1],
1601
0
                 sizeof(*p_sys->control) * p_sys->i_control );
1602
0
    vlc_mutex_unlock( &p_sys->lock_control );
1603
1604
0
    return VLC_SUCCESS;
1605
0
}
1606
static bool ControlIsSeekRequest( int i_type )
1607
0
{
1608
0
    switch( i_type )
1609
0
    {
1610
0
    case INPUT_CONTROL_SET_POSITION:
1611
0
    case INPUT_CONTROL_SET_TIME:
1612
0
    case INPUT_CONTROL_SET_TITLE:
1613
0
    case INPUT_CONTROL_SET_TITLE_NEXT:
1614
0
    case INPUT_CONTROL_SET_TITLE_PREV:
1615
0
    case INPUT_CONTROL_SET_SEEKPOINT:
1616
0
    case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
1617
0
    case INPUT_CONTROL_SET_SEEKPOINT_PREV:
1618
0
    case INPUT_CONTROL_NAV_ACTIVATE:
1619
0
    case INPUT_CONTROL_NAV_UP:
1620
0
    case INPUT_CONTROL_NAV_DOWN:
1621
0
    case INPUT_CONTROL_NAV_LEFT:
1622
0
    case INPUT_CONTROL_NAV_RIGHT:
1623
0
    case INPUT_CONTROL_NAV_POPUP:
1624
0
    case INPUT_CONTROL_NAV_MENU:
1625
0
        return true;
1626
0
    default:
1627
0
        return false;
1628
0
    }
1629
0
}
1630
1631
static bool ControlTypeIsEarly(int type)
1632
0
{
1633
    /* These controls can and should be processed before the access/demux
1634
     * is created for optimization purpose. */
1635
0
    switch (type)
1636
0
    {
1637
        /* \warning Make sure the control implementation is not referencing the
1638
         * demux before adding it here. */
1639
0
        case INPUT_CONTROL_SET_PROGRAM:
1640
0
        case INPUT_CONTROL_SET_CATEGORY_DELAY:
1641
0
        case INPUT_CONTROL_SET_ES_CAT_IDS:
1642
0
            return true;
1643
0
        default:
1644
0
            return false;
1645
0
    }
1646
0
}
1647
1648
static int ControlPopEarly(input_thread_t *input, int *type,
1649
                           input_control_param_t *param)
1650
0
{
1651
0
    input_thread_private_t *sys = input_priv(input);
1652
1653
0
    vlc_mutex_lock(&sys->lock_control);
1654
1655
0
    for (size_t i = 0; i < sys->i_control; ++i)
1656
0
    {
1657
0
        if (ControlTypeIsEarly(sys->control[i].i_type))
1658
0
        {
1659
0
            *type = sys->control[i].i_type;
1660
0
            *param = sys->control[i].param;
1661
1662
0
            sys->i_control--;
1663
0
            if (sys->i_control > i)
1664
0
                memmove(&sys->control[i], &sys->control[i+1],
1665
0
                        sizeof(*sys->control) * sys->i_control - i);
1666
0
            vlc_mutex_unlock(&sys->lock_control);
1667
0
            return VLC_SUCCESS;
1668
0
        }
1669
0
    }
1670
1671
0
    vlc_mutex_unlock(&sys->lock_control);
1672
0
    return VLC_EGENERIC;
1673
0
}
1674
1675
static void ControlRelease( int i_type, const input_control_param_t *p_param )
1676
0
{
1677
0
    if( p_param == NULL )
1678
0
        return;
1679
1680
0
    switch( i_type )
1681
0
    {
1682
0
    case INPUT_CONTROL_ADD_SLAVE:
1683
0
        if( p_param->val.p_address )
1684
0
            input_item_slave_Delete( p_param->val.p_address );
1685
0
        break;
1686
0
    case INPUT_CONTROL_SET_RENDERER:
1687
0
        if( p_param->val.p_address )
1688
0
            vlc_renderer_item_release( p_param->val.p_address );
1689
0
        break;
1690
0
    case INPUT_CONTROL_SET_ES:
1691
0
    case INPUT_CONTROL_UNSET_ES:
1692
0
    case INPUT_CONTROL_RESTART_ES:
1693
0
        vlc_es_id_Release( p_param->id );
1694
0
        break;
1695
0
    case INPUT_CONTROL_SET_ES_LIST:
1696
0
    {
1697
0
        for (size_t i = 0; ; i++)
1698
0
        {
1699
0
            vlc_es_id_t *es_id = p_param->list.ids[i];
1700
0
            if (es_id == NULL)
1701
0
                break;
1702
0
            vlc_es_id_Release(es_id);
1703
0
        }
1704
0
        free(p_param->list.ids);
1705
0
        break;
1706
0
    }
1707
0
    case INPUT_CONTROL_SET_ES_CAT_IDS:
1708
0
        free( p_param->cat_ids.str_ids );
1709
0
        break;
1710
0
    case INPUT_CONTROL_SET_RECORD_STATE:
1711
0
        free( p_param->record_state.dir_path );
1712
0
        break;
1713
1714
0
    default:
1715
0
        break;
1716
0
    }
1717
0
}
1718
1719
/* Pause input */
1720
static void ControlPause( input_thread_t *p_input, vlc_tick_t i_control_date )
1721
0
{
1722
0
    int i_state = PAUSE_S;
1723
1724
0
    if( input_priv(p_input)->master->b_can_pause )
1725
0
    {
1726
0
        demux_t *p_demux = input_priv(p_input)->master->p_demux;
1727
1728
0
        if( demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, true ) )
1729
0
        {
1730
0
            msg_Warn( p_input, "cannot set pause state" );
1731
0
            return;
1732
0
        }
1733
0
    }
1734
1735
    /* */
1736
0
    if( es_out_SetPauseState( input_priv(p_input)->p_es_out,
1737
0
                              input_priv(p_input)->master->b_can_pause,
1738
0
                              true, i_control_date ) )
1739
0
    {
1740
0
        msg_Warn( p_input, "cannot set pause state at es_out level" );
1741
0
        return;
1742
0
    }
1743
1744
    /* Switch to new state */
1745
0
    input_ChangeState( p_input, i_state, i_control_date );
1746
0
}
1747
1748
static void ControlUnpause( input_thread_t *p_input, vlc_tick_t i_control_date )
1749
0
{
1750
0
    if( input_priv(p_input)->master->b_can_pause )
1751
0
    {
1752
0
        demux_t *p_demux = input_priv(p_input)->master->p_demux;
1753
1754
0
        if( demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, false ) )
1755
0
        {
1756
0
            msg_Err( p_input, "cannot resume" );
1757
0
            input_ChangeState( p_input, ERROR_S, i_control_date );
1758
0
            return;
1759
0
        }
1760
0
    }
1761
1762
    /* Switch to play */
1763
0
    input_ChangeState( p_input, PLAYING_S, i_control_date );
1764
0
    es_out_SetPauseState( input_priv(p_input)->p_es_out, false, false, i_control_date );
1765
0
}
1766
1767
static void ViewpointApply( input_thread_t *p_input )
1768
0
{
1769
0
    input_thread_private_t *priv = input_priv(p_input);
1770
1771
0
    vlc_viewpoint_clip( &priv->viewpoint );
1772
1773
0
    vout_thread_t **pp_vout;
1774
0
    size_t i_vout;
1775
0
    input_resource_HoldVouts( priv->p_resource, &pp_vout, &i_vout );
1776
1777
0
    for( size_t i = 0; i < i_vout; ++i )
1778
0
    {
1779
0
        var_SetAddress( pp_vout[i], "viewpoint", &priv->viewpoint );
1780
        /* This variable can only be read from callbacks */
1781
0
        var_Change( pp_vout[i], "viewpoint", VLC_VAR_SETVALUE,
1782
0
                    (vlc_value_t) { .p_address = NULL } );
1783
0
        vout_Release(pp_vout[i]);
1784
0
    }
1785
0
    free( pp_vout );
1786
1787
0
    audio_output_t *p_aout = input_resource_HoldAout( priv->p_resource );
1788
0
    if( p_aout )
1789
0
    {
1790
1791
0
        var_SetAddress( p_aout, "viewpoint", &priv->viewpoint );
1792
        /* This variable can only be read from callbacks */
1793
0
        var_Change( p_aout, "viewpoint", VLC_VAR_SETVALUE,
1794
0
                    (vlc_value_t) { .p_address = NULL } );
1795
0
        aout_Release(p_aout);
1796
0
    }
1797
0
}
1798
1799
static void ControlNav( input_thread_t *p_input, int i_type )
1800
0
{
1801
0
    input_thread_private_t *priv = input_priv(p_input);
1802
1803
0
    if( !demux_Control( priv->master->p_demux, i_type
1804
0
                        - INPUT_CONTROL_NAV_ACTIVATE + DEMUX_NAV_ACTIVATE ) )
1805
0
        return; /* The demux handled the navigation control */
1806
1807
0
    input_SendEvent(p_input, &(struct vlc_input_event) {
1808
0
        .type = INPUT_EVENT_NAV_FAILED,
1809
0
        .nav_type = i_type,
1810
0
    });
1811
0
}
1812
1813
static void ControlUpdateRenderer( input_thread_t *p_input, bool b_enable )
1814
0
{
1815
0
    if( b_enable )
1816
0
    {
1817
0
        if( InitSout( p_input ) != VLC_SUCCESS )
1818
0
        {
1819
0
            msg_Err( p_input, "Failed to start sout" );
1820
0
            return;
1821
0
        }
1822
0
    }
1823
0
    else
1824
0
    {
1825
0
        input_resource_PutSout( input_priv(p_input)->p_resource,
1826
0
                                input_priv(p_input)->p_sout );
1827
0
        input_priv(p_input)->p_sout = NULL;
1828
0
    }
1829
0
}
1830
1831
static void ControlInsertDemuxFilter( input_thread_t* p_input, const char* psz_demux_chain )
1832
0
{
1833
0
    input_source_t *p_inputSource = input_priv(p_input)->master;
1834
0
    demux_t *p_filtered_demux = demux_FilterChainNew( p_inputSource->p_demux, psz_demux_chain );
1835
0
    if ( p_filtered_demux != NULL )
1836
0
        p_inputSource->p_demux = p_filtered_demux;
1837
0
    else if ( psz_demux_chain != NULL )
1838
0
        msg_Dbg(p_input, "Failed to create demux filter %s", psz_demux_chain);
1839
0
}
1840
1841
void input_SetProgramId(input_thread_t *input, int group_id)
1842
1843
0
{
1844
0
    input_ControlPushHelper(input, INPUT_CONTROL_SET_PROGRAM,
1845
0
                            &(vlc_value_t) { .i_int = group_id });
1846
0
}
1847
1848
int input_SetEsCatDelay(input_thread_t *input, enum es_format_category_e cat,
1849
                        vlc_tick_t delay)
1850
0
{
1851
0
    const input_control_param_t param = {
1852
0
        .cat_delay = { cat, delay }
1853
0
    };
1854
0
    return input_ControlPush(input, INPUT_CONTROL_SET_CATEGORY_DELAY,
1855
0
                             &param);
1856
0
}
1857
1858
void input_SetEsCatIds(input_thread_t *input, enum es_format_category_e cat,
1859
                       const char *str_ids)
1860
0
{
1861
0
    const input_control_param_t param = {
1862
0
        .cat_ids = { cat, str_ids ? strdup(str_ids) : NULL }
1863
0
    };
1864
0
    input_ControlPush(input, INPUT_CONTROL_SET_ES_CAT_IDS, &param);
1865
0
}
1866
1867
static void ControlSetEsList(input_thread_t *input,
1868
                             enum es_format_category_e cat,
1869
                             vlc_es_id_t **ids)
1870
0
{
1871
0
    input_thread_private_t *priv = input_priv(input);
1872
1873
0
    if (es_out_SetEsList(priv->p_es_out_display, cat, ids) != VLC_SUCCESS)
1874
0
        return;
1875
1876
0
    if (ids[0] != NULL && ids[1] == NULL)
1877
0
    {
1878
        /* Update the only demux touched by this change */
1879
0
        const input_source_t *source = vlc_es_id_GetSource(ids[0]);
1880
0
        assert(source);
1881
0
        demux_Control(source->p_demux, DEMUX_SET_ES,
1882
0
                      vlc_es_id_GetInputId(ids[0]));
1883
0
        return;
1884
0
    }
1885
1886
    /* Send the updated list for each different sources */
1887
0
    size_t count;
1888
0
    for (count = 0; ids[count] != NULL; count++);
1889
0
    int *array = count ? vlc_alloc(count, sizeof(int)) : NULL;
1890
0
    if (!array)
1891
0
        return;
1892
1893
0
    for (size_t i = 0; i < priv->i_slave + 1; ++ i)
1894
0
    {
1895
        /* For master and all slaves */
1896
0
        input_source_t *source = i == 0 ? priv->master : priv->slave[i - 1];
1897
1898
        /* Split the ids array into smaller arrays of ids having the same
1899
         * source. */
1900
0
        size_t set_es_idx = 0;
1901
0
        for (size_t ids_idx = 0; ids_idx < count; ++ids_idx)
1902
0
        {
1903
0
            vlc_es_id_t *id = ids[ids_idx];
1904
0
            if (vlc_es_id_GetSource(id) == source)
1905
0
                array[set_es_idx++] = vlc_es_id_GetInputId(id);
1906
0
        }
1907
1908
        /* Update all demuxers */
1909
0
        if (set_es_idx > 0)
1910
0
        {
1911
0
            if (set_es_idx == 1)
1912
0
                demux_Control(source->p_demux, DEMUX_SET_ES, array[0]);
1913
0
            else
1914
0
                demux_Control(source->p_demux, DEMUX_SET_ES_LIST, set_es_idx,
1915
0
                              array);
1916
0
        }
1917
0
    }
1918
0
    free(array);
1919
0
}
1920
1921
static int ControlSetTime( input_thread_t *p_input, vlc_tick_t val,
1922
                           bool fast_seek )
1923
0
{
1924
0
    input_thread_private_t *priv = input_priv(p_input);
1925
0
    int i_ret;
1926
1927
0
    i_ret = demux_SetTime( priv->master->p_demux, priv->i_start + val,
1928
0
                           !fast_seek );
1929
0
    if( i_ret )
1930
0
    {
1931
0
        vlc_tick_t i_length = InputSourceGetLength( priv->master, priv->p_item, NULL );
1932
        /* Emulate it with a SET_POS */
1933
0
        if( i_length > 0 )
1934
0
        {
1935
0
            double f_pos = (double)(priv->i_start + val) / (double)i_length;
1936
0
            i_ret = demux_SetPosition( priv->master->p_demux, f_pos,
1937
0
                                       !fast_seek );
1938
0
        }
1939
0
    }
1940
1941
0
    if( i_ret == VLC_SUCCESS )
1942
0
    {
1943
0
        if( priv->i_slave > 0 )
1944
0
            SlaveSeek( p_input );
1945
0
        priv->master->b_eof = false;
1946
0
    }
1947
1948
0
    return i_ret;
1949
0
}
1950
1951
static int ControlSetPosition( input_thread_t *p_input, double val,
1952
                               bool fast_seek )
1953
0
{
1954
0
    input_thread_private_t *priv = input_priv(p_input);
1955
1956
0
    vlc_tick_t i_length = InputSourceGetLength(priv->master, priv->p_item, NULL);
1957
0
    if( i_length > 0 )
1958
0
    {
1959
        /* Calculate the updated position according to the current track duration */
1960
0
        if (priv->i_stop != 0)
1961
0
            val = (priv->i_start + val * (priv->i_stop - priv->i_start)) / i_length;
1962
0
        else
1963
0
            val = (priv->i_start + val * (i_length - priv->i_start)) / i_length;
1964
0
    }
1965
1966
0
    int i_ret = demux_SetPosition( priv->master->p_demux, val, !fast_seek );
1967
1968
0
    if( i_ret == VLC_SUCCESS )
1969
0
    {
1970
0
        if( priv->i_slave > 0 )
1971
0
            SlaveSeek( p_input );
1972
0
        priv->master->b_eof = false;
1973
0
    }
1974
1975
0
    return i_ret;
1976
0
}
1977
1978
static void
1979
ResetFramePrevious(input_thread_t *input)
1980
0
{
1981
0
    input_thread_private_t *priv = input_priv(input);
1982
0
    priv->prev_frame.enabled = priv->prev_frame.end = false;
1983
0
}
1984
1985
/* Seek commands sent by the video decoder to reach the previous frame */
1986
static void SeekFramePrevious(input_thread_t *input, vlc_tick_t pts,
1987
                              unsigned frame_rate, unsigned frame_rate_base,
1988
                              int steps, bool failed)
1989
0
{
1990
0
    input_thread_private_t *priv = input_priv(input);
1991
0
    assert(priv->master->b_can_seek);
1992
1993
0
    if (!priv->prev_frame.enabled)
1994
0
        return; /* prev-frame was canceled with a user seek */
1995
1996
0
    assert(!priv->prev_frame.end);
1997
1998
0
    float fps;
1999
0
    if (frame_rate == 0 || frame_rate_base == 0)
2000
0
        fps = 30; /* Assume a good default */
2001
0
    else
2002
0
        fps = ceil(frame_rate / (float) frame_rate_base);
2003
2004
    /* pts is stream based, subtract normal time to seek */
2005
0
    pts -= priv->master->i_normal_time;
2006
    /* Seek a little back */
2007
0
    vlc_tick_t steps_duration = vlc_tick_rate_duration(fps) * steps;
2008
0
    pts -= steps_duration;
2009
2010
0
    if (pts < VLC_TICK_0)
2011
0
        pts = VLC_TICK_0;
2012
2013
0
    if (pts == priv->prev_frame.last_pts && failed)
2014
0
    {
2015
0
        priv->prev_frame.end = pts == VLC_TICK_0;
2016
        /* Either we reached start of file or the decoder asked to seek at the
2017
         * same pts (should not happen, this is  invalid). */
2018
0
        es_out_PrivControl(priv->p_es_out, ES_OUT_PRIV_RESET_PCR_FRAME_PREV,
2019
0
                           0);
2020
0
        input_SendEvent(input, &(struct vlc_input_event) {
2021
0
            .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2022
0
            .frame_previous_status = priv->prev_frame.end ? -EAGAIN : -EINVAL,
2023
0
        });
2024
0
        return;
2025
0
    }
2026
2027
0
    priv->prev_frame.last_pts = pts;
2028
0
    vlc_tick_t buffering_duration = steps_duration;
2029
2030
    /* Add an extra buffering, to add more chance to reach the previous-frame */
2031
0
    buffering_duration += vlc_tick_rate_duration(fps) * 5;
2032
2033
    /* Reset the decoders states and clock sync but not the decoder requesting
2034
     * the seek */
2035
0
    es_out_PrivControl(priv->p_es_out, ES_OUT_PRIV_RESET_PCR_FRAME_PREV,
2036
0
                       buffering_duration);
2037
2038
0
    int ret = ControlSetTime(input, pts, false);
2039
0
    if (ret == VLC_SUCCESS)
2040
0
        return; /* The decoder will send the status */
2041
2042
0
    input_SendEvent(input, &(struct vlc_input_event) {
2043
0
            .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2044
0
            .frame_previous_status = -ENOTSUP,
2045
0
    });
2046
0
}
2047
2048
static bool Control( input_thread_t *p_input,
2049
                     int i_type, input_control_param_t param )
2050
0
{
2051
0
    input_thread_private_t *priv = input_priv(p_input);
2052
0
    const vlc_tick_t i_control_date = vlc_tick_now();
2053
    /* FIXME b_force_update is abused, it should be carefully checked */
2054
0
    bool b_force_update = false;
2055
2056
0
#ifndef NDEBUG
2057
0
    msg_Dbg( p_input, "control type=%d", i_type );
2058
0
#endif
2059
2060
0
    switch( i_type )
2061
0
    {
2062
0
        case INPUT_CONTROL_SET_POSITION:
2063
0
        {
2064
0
            if( priv->b_recording )
2065
0
            {
2066
0
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION ignored while recording" );
2067
0
                break;
2068
0
            }
2069
2070
            /* Reset the decoders states and clock sync (before calling the demuxer */
2071
0
            es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
2072
0
            ResetFramePrevious( p_input );
2073
0
            priv->next_frame_need_data = false;
2074
2075
0
            int i_ret = ControlSetPosition( p_input, param.pos.f_val,
2076
0
                                            param.pos.b_fast_seek );
2077
2078
0
            if( i_ret )
2079
0
                msg_Err( p_input, "INPUT_CONTROL_SET_POSITION "
2080
0
                         "%2.1f%% failed", param.pos.f_val * 100.f );
2081
0
            else
2082
0
                b_force_update = true;
2083
0
            break;
2084
0
        }
2085
2086
0
        case INPUT_CONTROL_SET_TIME:
2087
0
        {
2088
0
            if( priv->b_recording )
2089
0
            {
2090
0
                msg_Err( p_input, "INPUT_CONTROL_SET_TIME ignored while recording" );
2091
0
                break;
2092
0
            }
2093
2094
            /* Reset the decoders states and clock sync (before calling the demuxer */
2095
0
            es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
2096
0
            ResetFramePrevious( p_input );
2097
0
            priv->next_frame_need_data = false;
2098
2099
0
            int i_ret = ControlSetTime( p_input, param.time.i_val,
2100
0
                                        param.time.b_fast_seek );
2101
2102
0
            if( i_ret )
2103
0
                msg_Warn( p_input, "INPUT_CONTROL_SET_TIME @%"PRId64
2104
0
                          " failed or not possible", param.time.i_val );
2105
0
            else
2106
0
                b_force_update = true;
2107
0
            break;
2108
0
        }
2109
2110
0
        case INPUT_CONTROL_SET_STATE:
2111
0
            switch( param.val.i_int )
2112
0
            {
2113
0
                case PLAYING_S:
2114
0
                    if( priv->i_state == PAUSE_S )
2115
0
                    {
2116
0
                        ResetFramePrevious( p_input );
2117
0
                        priv->next_frame_need_data = false;
2118
0
                        ControlUnpause( p_input, i_control_date );
2119
0
                        b_force_update = true;
2120
0
                    }
2121
0
                    break;
2122
0
                case PAUSE_S:
2123
0
                    if( priv->i_state == PLAYING_S )
2124
0
                    {
2125
0
                        ControlPause( p_input, i_control_date );
2126
0
                        b_force_update = true;
2127
0
                    }
2128
0
                    break;
2129
0
                default:
2130
0
                    msg_Err( p_input, "invalid INPUT_CONTROL_SET_STATE" );
2131
0
            }
2132
0
            break;
2133
2134
0
        case INPUT_CONTROL_SET_RATE:
2135
0
        {
2136
            /* Get rate and direction */
2137
0
            float rate = fabsf( param.val.f_float );
2138
0
            int i_rate_sign = param.val.f_float < 0 ? -1 : 1;
2139
2140
            /* Check rate bound */
2141
0
            if( rate > INPUT_RATE_MAX )
2142
0
            {
2143
0
                msg_Info( p_input, "cannot set rate faster" );
2144
0
                rate = INPUT_RATE_MAX;
2145
0
            }
2146
0
            else if( rate < INPUT_RATE_MIN )
2147
0
            {
2148
0
                msg_Info( p_input, "cannot set rate slower" );
2149
0
                rate = INPUT_RATE_MIN;
2150
0
            }
2151
2152
            /* Apply direction */
2153
0
            if( i_rate_sign < 0 )
2154
0
            {
2155
0
                if( priv->master->b_rescale_ts )
2156
0
                {
2157
0
                    msg_Dbg( p_input, "cannot set negative rate" );
2158
0
                    rate = priv->rate;
2159
0
                    assert( rate > 0 );
2160
0
                }
2161
0
                else
2162
0
                {
2163
0
                    rate *= i_rate_sign;
2164
0
                }
2165
0
            }
2166
2167
0
            if( rate != 1.f &&
2168
0
                ( ( !priv->master->b_can_rate_control && !priv->master->b_rescale_ts ) ||
2169
0
                  ( priv->p_sout && !priv->b_out_pace_control ) ) )
2170
0
            {
2171
0
                msg_Dbg( p_input, "cannot change rate" );
2172
0
                rate = 1.f;
2173
0
            }
2174
0
            if( rate != priv->rate &&
2175
0
                !priv->master->b_can_pace_control && priv->master->b_can_rate_control )
2176
0
            {
2177
0
                if( !priv->master->b_rescale_ts )
2178
0
                    es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
2179
2180
0
                if( demux_Control( priv->master->p_demux, DEMUX_SET_RATE,
2181
0
                                   &rate ) )
2182
0
                {
2183
0
                    msg_Warn( p_input, "ACCESS/DEMUX_SET_RATE failed" );
2184
0
                    rate = priv->rate;
2185
0
                }
2186
0
            }
2187
2188
            /* */
2189
0
            if( rate != priv->rate )
2190
0
            {
2191
0
                priv->rate = rate;
2192
0
                input_SendEventRate( p_input, rate );
2193
2194
0
                if( priv->master->b_rescale_ts )
2195
0
                {
2196
0
                    const float rate_source = (priv->master->b_can_pace_control ||
2197
0
                        priv->master->b_can_rate_control ) ? rate : 1.f;
2198
0
                    es_out_SetRate( priv->p_es_out, rate_source, rate );
2199
0
                }
2200
2201
0
                b_force_update = true;
2202
0
            }
2203
0
            break;
2204
0
        }
2205
2206
0
        case INPUT_CONTROL_SET_PROGRAM:
2207
            /* No need to force update, es_out does it if needed */
2208
0
            es_out_Control(&priv->p_es_out->out,
2209
0
                           ES_OUT_SET_GROUP, (int)param.val.i_int);
2210
2211
0
            if( priv->master->p_demux == NULL )
2212
0
                break; /* Possible when called early, the group will be set on
2213
                        * the demux from InitPrograms() */
2214
2215
0
            if( param.val.i_int == 0 )
2216
0
                demux_Control( priv->master->p_demux,
2217
0
                               DEMUX_SET_GROUP_DEFAULT );
2218
0
            else
2219
0
                demux_Control( priv->master->p_demux,
2220
0
                               DEMUX_SET_GROUP_LIST,
2221
0
                               (size_t)1, &(const int){ param.val.i_int });
2222
0
            break;
2223
2224
0
        case INPUT_CONTROL_SET_ES:
2225
0
            if( es_out_SetEs( priv->p_es_out_display, param.id ) == VLC_SUCCESS )
2226
0
            {
2227
0
                const input_source_t *source = vlc_es_id_GetSource( param.id );
2228
0
                assert( source );
2229
0
                demux_Control( source->p_demux, DEMUX_SET_ES,
2230
0
                               vlc_es_id_GetInputId( param.id ) );
2231
0
            }
2232
0
            break;
2233
0
        case INPUT_CONTROL_SET_ES_LIST:
2234
0
            ControlSetEsList( p_input, param.list.cat, param.list.ids );
2235
0
            break;
2236
0
        case INPUT_CONTROL_UNSET_ES:
2237
0
            es_out_UnsetEs( priv->p_es_out_display, param.id );
2238
0
            break;
2239
0
        case INPUT_CONTROL_RESTART_ES:
2240
0
            es_out_RestartEs( priv->p_es_out_display, param.id );
2241
0
            break;
2242
0
        case INPUT_CONTROL_SET_ES_CAT_IDS:
2243
0
            es_out_SetEsCatIds( priv->p_es_out_display, param.cat_ids.cat,
2244
0
                                param.cat_ids.str_ids );
2245
0
            break;
2246
2247
0
        case INPUT_CONTROL_SET_VIEWPOINT:
2248
0
        case INPUT_CONTROL_SET_INITIAL_VIEWPOINT:
2249
0
        case INPUT_CONTROL_UPDATE_VIEWPOINT:
2250
0
            if ( i_type == INPUT_CONTROL_SET_INITIAL_VIEWPOINT )
2251
0
            {
2252
2253
                /* Set the initial viewpoint if it had not been changed by the
2254
                 * user. */
2255
0
                if( !priv->viewpoint_changed )
2256
0
                    priv->viewpoint = param.viewpoint;
2257
                /* Update viewpoints of aout and every vouts in all cases. */
2258
0
            }
2259
0
            else if ( i_type == INPUT_CONTROL_SET_VIEWPOINT)
2260
0
            {
2261
0
                priv->viewpoint_changed = true;
2262
0
                priv->viewpoint = param.viewpoint;
2263
0
            }
2264
0
            else
2265
0
            {
2266
0
                priv->viewpoint_changed = true;
2267
0
                float previous[3], update[3];
2268
0
                vlc_viewpoint_to_euler(&priv->viewpoint, &previous[0],
2269
0
                                       &previous[1], &previous[2]);
2270
0
                vlc_viewpoint_to_euler(&param.viewpoint, &update[0],
2271
0
                                       &update[1], &update[2]);
2272
2273
                /* Clip the pitch to avoid glitches at singularity points. */
2274
0
                vlc_viewpoint_from_euler(&priv->viewpoint,
2275
0
                        previous[0] + update[0],
2276
0
                        VLC_CLIP(previous[1] + update[1], -85.f, 85.f),
2277
0
                        previous[2] + update[2]);
2278
0
                priv->viewpoint.fov += param.viewpoint.fov;
2279
0
            }
2280
2281
0
            ViewpointApply( p_input );
2282
0
            break;
2283
2284
0
        case INPUT_CONTROL_SET_CATEGORY_DELAY:
2285
0
            assert(param.cat_delay.cat == AUDIO_ES
2286
0
                || param.cat_delay.cat == SPU_ES
2287
0
                || param.cat_delay.cat == VIDEO_ES);
2288
0
            es_out_SetDelay(priv->p_es_out_display,
2289
0
                            param.cat_delay.cat, param.cat_delay.delay);
2290
0
            break;
2291
0
        case INPUT_CONTROL_SET_ES_DELAY:
2292
0
            assert(param.es_delay.id);
2293
0
            es_out_SetEsDelay(priv->p_es_out_display, param.es_delay.id,
2294
0
                              param.es_delay.delay);
2295
0
            break;
2296
2297
0
        case INPUT_CONTROL_SET_TITLE:
2298
0
        case INPUT_CONTROL_SET_TITLE_NEXT:
2299
0
        case INPUT_CONTROL_SET_TITLE_PREV:
2300
0
        {
2301
0
            if( priv->b_recording )
2302
0
            {
2303
0
                msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
2304
0
                break;
2305
0
            }
2306
0
            if( priv->master->i_title <= 0 )
2307
0
                break;
2308
2309
0
            int i_title = demux_GetTitle( priv->master->p_demux );
2310
0
            if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
2311
0
                i_title--;
2312
0
            else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
2313
0
                i_title++;
2314
0
            else
2315
0
                i_title = param.val.i_int;
2316
0
            if( i_title < 0 || i_title >= priv->master->i_title )
2317
0
                break;
2318
2319
0
            es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
2320
0
            ResetFramePrevious( p_input );
2321
0
            priv->next_frame_need_data = false;
2322
0
            demux_Control(priv->master->p_demux,
2323
0
                          DEMUX_SET_TITLE, i_title);
2324
0
            break;
2325
0
        }
2326
0
        case INPUT_CONTROL_SET_SEEKPOINT:
2327
0
        case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
2328
0
        case INPUT_CONTROL_SET_SEEKPOINT_PREV:
2329
0
        {
2330
0
            if( priv->b_recording )
2331
0
            {
2332
0
                msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
2333
0
                break;
2334
0
            }
2335
0
            if( priv->master->i_title <= 0 )
2336
0
                break;
2337
2338
0
            demux_t *p_demux = priv->master->p_demux;
2339
2340
0
            int i_title = demux_GetTitle( p_demux );
2341
0
            int i_seekpoint = demux_GetSeekpoint( p_demux );
2342
2343
0
            if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
2344
0
            {
2345
0
                vlc_tick_t i_seekpoint_time = priv->master->title[i_title]->seekpoint[i_seekpoint]->i_time_offset;
2346
0
                vlc_tick_t i_input_time = var_GetInteger( p_input, "time" );
2347
0
                if( i_seekpoint_time >= 0 && i_input_time >= 0 )
2348
0
                {
2349
0
                    if( i_input_time < i_seekpoint_time + VLC_TICK_FROM_SEC(3) )
2350
0
                        i_seekpoint--;
2351
0
                }
2352
0
                else
2353
0
                    i_seekpoint--;
2354
0
            }
2355
0
            else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
2356
0
                i_seekpoint++;
2357
0
            else
2358
0
                i_seekpoint = param.val.i_int;
2359
0
            if( i_seekpoint < 0
2360
0
             || i_seekpoint >= priv->master->title[i_title]->i_seekpoint )
2361
0
                break;
2362
2363
0
            es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
2364
0
            ResetFramePrevious( p_input );
2365
0
            priv->next_frame_need_data = false;
2366
0
            demux_Control( priv->master->p_demux,
2367
0
                           DEMUX_SET_SEEKPOINT, i_seekpoint );
2368
0
            input_SendEventSeekpoint( p_input, i_title, i_seekpoint );
2369
0
            if( priv->i_slave > 0 )
2370
0
                SlaveSeek( p_input );
2371
0
            break;
2372
0
        }
2373
2374
0
        case INPUT_CONTROL_RESET_POSITION:
2375
0
            ResetPosition( p_input );
2376
0
            break;
2377
2378
0
        case INPUT_CONTROL_ADD_SLAVE:
2379
0
            if( param.val.p_address )
2380
0
            {
2381
0
                input_item_slave_t *p_item_slave  = param.val.p_address;
2382
0
                unsigned i_flags = SLAVE_ADD_CANFAIL | SLAVE_ADD_SET_TIME;
2383
0
                if( p_item_slave->b_forced )
2384
0
                    i_flags |= SLAVE_ADD_FORCED;
2385
2386
0
                if( input_SlaveSourceAdd( p_input, p_item_slave->i_type,
2387
0
                                          p_item_slave->psz_uri, i_flags )
2388
0
                                          == VLC_SUCCESS )
2389
0
                {
2390
                    /* Update item slaves */
2391
0
                    input_item_AddSlave( priv->p_item, p_item_slave );
2392
                    /* The slave is now owned by the item */
2393
0
                    param.val.p_address = NULL;
2394
0
                }
2395
0
            }
2396
0
            break;
2397
0
        case INPUT_CONTROL_SET_SUBS_FPS:
2398
0
            RequestSubRate( p_input, param.val.f_float );
2399
0
            input_SendEventSubsFPS( p_input, param.val.f_float );
2400
0
            break;
2401
2402
0
        case INPUT_CONTROL_SET_RECORD_STATE:
2403
0
        {
2404
0
            bool enabled = param.record_state.enabled;
2405
0
            const char *dir_path = param.record_state.dir_path;
2406
0
            if( !!priv->b_recording != enabled )
2407
0
            {
2408
0
                if( priv->master->b_can_stream_record )
2409
0
                {
2410
0
                    if( demux_Control( priv->master->p_demux,
2411
0
                                       DEMUX_SET_RECORD_STATE, enabled, dir_path ) )
2412
0
                        enabled = false;
2413
0
                }
2414
0
                else
2415
0
                {
2416
0
                    if( es_out_SetRecordState( priv->p_es_out_display, enabled, dir_path ) )
2417
0
                        enabled = false;
2418
0
                }
2419
0
                priv->b_recording = enabled;
2420
2421
0
                input_SendEventRecord( p_input, enabled );
2422
2423
0
                b_force_update = true;
2424
0
            }
2425
0
            break;
2426
0
        }
2427
2428
0
        case INPUT_CONTROL_SET_FRAME_NEXT:
2429
0
            if (!priv->master->b_can_pause)
2430
0
            {
2431
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2432
0
                    .type = INPUT_EVENT_FRAME_NEXT_STATUS,
2433
0
                    .frame_next_status = -ENOTSUP,
2434
0
                });
2435
0
                break;
2436
0
            }
2437
0
            if( priv->i_state == PAUSE_S )
2438
0
            {
2439
0
                ResetFramePrevious( p_input );
2440
0
                es_out_SetFrameNext( priv->p_es_out );
2441
0
            }
2442
0
            else if( priv->i_state == PLAYING_S )
2443
0
            {
2444
0
                ControlPause( p_input, i_control_date );
2445
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2446
0
                    .type = INPUT_EVENT_FRAME_NEXT_STATUS,
2447
0
                    .frame_next_status = -EAGAIN,
2448
0
                });
2449
0
            }
2450
0
            else
2451
0
            {
2452
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2453
0
                    .type = INPUT_EVENT_FRAME_NEXT_STATUS,
2454
0
                    .frame_next_status = -EINVAL,
2455
0
                });
2456
0
            }
2457
0
            b_force_update = true;
2458
0
            break;
2459
0
        case INPUT_CONTROL_SET_FRAME_PREVIOUS:
2460
0
            if (!priv->master->b_can_seek || !priv->master->b_can_pause
2461
0
             || !priv->master->b_can_pace_control)
2462
0
            {
2463
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2464
0
                    .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2465
0
                    .frame_previous_status = -ENOTSUP,
2466
0
                });
2467
0
                break;
2468
0
            }
2469
0
            if (priv->prev_frame.end)
2470
0
            {
2471
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2472
0
                    .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2473
0
                    .frame_previous_status = -EAGAIN,
2474
0
                });
2475
0
                break;
2476
0
            }
2477
0
            if( priv->i_state == PAUSE_S )
2478
0
            {
2479
0
                priv->prev_frame.last_pts = VLC_TICK_INVALID;
2480
0
                priv->prev_frame.enabled = true;
2481
0
                es_out_SetFramePrevious( priv->p_es_out );
2482
0
            }
2483
0
            else if( priv->i_state == PLAYING_S )
2484
0
            {
2485
0
                ControlPause( p_input, i_control_date );
2486
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2487
0
                    .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2488
0
                    .frame_next_status = -EAGAIN,
2489
0
                });
2490
0
            }
2491
0
            else
2492
0
            {
2493
0
                msg_Err( p_input, "invalid state for frame prev" );
2494
0
                input_SendEvent(p_input, &(struct vlc_input_event) {
2495
0
                    .type = INPUT_EVENT_FRAME_PREVIOUS_STATUS,
2496
0
                    .frame_previous_status = -EINVAL,
2497
0
                });
2498
0
            }
2499
0
            b_force_update = true;
2500
0
            break;
2501
0
        case INPUT_CONTROL_NEED_DATA_FRAME_NEXT:
2502
0
            priv->next_frame_need_data = param.val.b_bool;
2503
0
            break;
2504
0
        case INPUT_CONTROL_SEEK_FRAME_PREVIOUS:
2505
0
            SeekFramePrevious(p_input, param.frame_previous_seek.pts,
2506
0
                              param.frame_previous_seek.frame_rate,
2507
0
                              param.frame_previous_seek.frame_rate_base,
2508
0
                              param.frame_previous_seek.steps,
2509
0
                              param.frame_previous_seek.failed);
2510
0
            break;
2511
0
        case INPUT_CONTROL_SET_RENDERER:
2512
0
        {
2513
0
            vlc_renderer_item_t *p_item = param.val.p_address;
2514
0
            input_thread_private_t *p_priv = input_priv( p_input );
2515
            // We do not support switching from a renderer to another for now
2516
0
            if ( p_item == NULL && p_priv->p_renderer == NULL )
2517
0
                break;
2518
2519
0
            vlc_es_id_t **context;
2520
0
            if( es_out_StopAllEs( priv->p_es_out_display, &context ) != VLC_SUCCESS )
2521
0
                break;
2522
2523
0
            if ( p_priv->p_renderer )
2524
0
            {
2525
0
                ControlUpdateRenderer( p_input, false );
2526
0
                demux_FilterDisable( p_priv->master->p_demux,
2527
0
                        vlc_renderer_item_demux_filter( p_priv->p_renderer ) );
2528
0
                vlc_renderer_item_release( p_priv->p_renderer );
2529
0
                p_priv->p_renderer = NULL;
2530
0
            }
2531
0
            if( p_item != NULL )
2532
0
            {
2533
0
                p_priv->p_renderer = vlc_renderer_item_hold( p_item );
2534
0
                ControlUpdateRenderer( p_input, true );
2535
0
                if( !demux_FilterEnable( p_priv->master->p_demux,
2536
0
                                vlc_renderer_item_demux_filter( p_priv->p_renderer ) ) )
2537
0
                {
2538
0
                    ControlInsertDemuxFilter( p_input,
2539
0
                                        vlc_renderer_item_demux_filter( p_item ) );
2540
0
                }
2541
0
            }
2542
0
            es_out_StartAllEs( priv->p_es_out_display, context );
2543
0
            break;
2544
0
        }
2545
0
        case INPUT_CONTROL_SET_VBI_PAGE:
2546
0
            es_out_SetVbiPage( priv->p_es_out_display, param.vbi_page.id,
2547
0
                               param.vbi_page.page );
2548
0
            break;
2549
0
        case INPUT_CONTROL_SET_VBI_TRANSPARENCY:
2550
0
            es_out_SetVbiTransparency( priv->p_es_out_display,
2551
0
                                       param.vbi_transparency.id,
2552
0
                                       param.vbi_transparency.enabled );
2553
0
            break;
2554
2555
0
        case INPUT_CONTROL_NAV_ACTIVATE:
2556
0
        case INPUT_CONTROL_NAV_UP:
2557
0
        case INPUT_CONTROL_NAV_DOWN:
2558
0
        case INPUT_CONTROL_NAV_LEFT:
2559
0
        case INPUT_CONTROL_NAV_RIGHT:
2560
0
        case INPUT_CONTROL_NAV_POPUP:
2561
0
        case INPUT_CONTROL_NAV_MENU:
2562
0
            ControlNav( p_input, i_type );
2563
0
            break;
2564
2565
0
        default:
2566
0
            msg_Err( p_input, "not yet implemented" );
2567
0
            break;
2568
0
    }
2569
2570
0
    ControlRelease( i_type, &param );
2571
0
    return b_force_update;
2572
0
}
2573
2574
/*****************************************************************************
2575
 * UpdateTitleSeekpoint
2576
 *****************************************************************************/
2577
static int UpdateTitleSeekpoint( input_thread_t *p_input,
2578
                                 int i_title, int i_seekpoint )
2579
0
{
2580
0
    int i_title_end = input_priv(p_input)->master->i_title_end -
2581
0
                        input_priv(p_input)->master->i_title_offset;
2582
0
    int i_seekpoint_end = input_priv(p_input)->master->i_seekpoint_end -
2583
0
                            input_priv(p_input)->master->i_seekpoint_offset;
2584
2585
0
    if( i_title_end >= 0 && i_seekpoint_end >= 0 )
2586
0
    {
2587
0
        if( i_title > i_title_end ||
2588
0
            ( i_title == i_title_end && i_seekpoint > i_seekpoint_end ) )
2589
0
            return VLC_DEMUXER_EOF;
2590
0
    }
2591
0
    else if( i_seekpoint_end >= 0 )
2592
0
    {
2593
0
        if( i_seekpoint > i_seekpoint_end )
2594
0
            return VLC_DEMUXER_EOF;
2595
0
    }
2596
0
    else if( i_title_end >= 0 )
2597
0
    {
2598
0
        if( i_title > i_title_end )
2599
0
            return VLC_DEMUXER_EOF;
2600
0
    }
2601
0
    return VLC_DEMUXER_SUCCESS;
2602
0
}
2603
/*****************************************************************************
2604
 * Update*FromDemux:
2605
 *****************************************************************************/
2606
static int UpdateTitleSeekpointFromDemux( input_thread_t *p_input )
2607
0
{
2608
0
    demux_t *p_demux = input_priv(p_input)->master->p_demux;
2609
2610
    /* TODO event-like */
2611
0
    if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_TITLE ) )
2612
0
        input_SendEventTitle( p_input, &(struct vlc_input_event_title) {
2613
0
            .action = VLC_INPUT_TITLE_SELECTED,
2614
0
            .selected_idx = demux_GetTitle( p_demux ),
2615
0
        });
2616
2617
0
    if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_SEEKPOINT ) )
2618
0
        input_SendEventSeekpoint( p_input, demux_GetTitle( p_demux ),
2619
0
                                  demux_GetSeekpoint( p_demux ) );
2620
2621
0
    return UpdateTitleSeekpoint( p_input,
2622
0
                                 demux_GetTitle( p_demux ),
2623
0
                                 demux_GetSeekpoint( p_demux ) );
2624
0
}
2625
2626
static void UpdateGenericFromDemux( input_thread_t *p_input )
2627
0
{
2628
0
    demux_t *p_demux = input_priv(p_input)->master->p_demux;
2629
2630
0
    if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_META ) )
2631
0
        InputUpdateMeta( p_input, p_demux );
2632
2633
0
    {
2634
0
        double quality;
2635
0
        double strength;
2636
2637
0
        if( !demux_Control( p_demux, DEMUX_GET_SIGNAL, &quality, &strength ) )
2638
0
            input_SendEventSignal( p_input, quality, strength );
2639
0
    }
2640
0
}
2641
2642
static void UpdateTitleListfromDemux( input_thread_t *p_input )
2643
0
{
2644
0
    input_thread_private_t *priv = input_priv(p_input);
2645
0
    input_source_t *in = priv->master;
2646
2647
    /* Delete the preexisting titles */
2648
0
    bool had_titles = in->i_title > 0;
2649
0
    if( had_titles )
2650
0
    {
2651
0
        for( int i = 0; i < in->i_title; i++ )
2652
0
            vlc_input_title_Delete( in->title[i] );
2653
0
    }
2654
0
    TAB_CLEAN( in->i_title, in->title );
2655
2656
    /* Get the new title list */
2657
0
    if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2658
0
                       &in->title, &in->i_title,
2659
0
                       &in->i_title_offset, &in->i_seekpoint_offset ) )
2660
0
        TAB_INIT( in->i_title, in->title );
2661
0
    else
2662
0
        in->b_title_demux = true;
2663
2664
0
    InitTitle( p_input, had_titles );
2665
0
}
2666
2667
static int
2668
InputStreamHandleAnchor( input_thread_t *p_input, input_source_t *source,
2669
                         stream_t **stream, char const *anchor )
2670
0
{
2671
0
    struct mrl_info mrli;
2672
0
    mrl_info_Init( &mrli );
2673
0
    if( mrl_FragmentSplit( &mrli, anchor ) )
2674
0
    {
2675
0
        mrl_info_Clean( &mrli );
2676
0
        return VLC_EGENERIC;
2677
0
    }
2678
2679
0
    if( stream_extractor_AttachParsed( stream, &mrli ) )
2680
0
    {
2681
0
        msg_Err( p_input, "unable to attach stream-extractors for %s",
2682
0
            (*stream)->psz_url );
2683
0
        mrl_info_Clean( &mrli );
2684
0
        return VLC_EGENERIC;
2685
0
    }
2686
2687
0
    if( vlc_stream_directory_Attach( stream, NULL,
2688
0
                                    (const char **) mrli.volumes.pp_elems,
2689
0
                                    mrli.volumes.i_count ) )
2690
0
        msg_Dbg( p_input, "attachment of directory-extractor failed for %s",
2691
0
            (*stream)->psz_url );
2692
2693
0
    MRLSections( mrli.extra ? mrli.extra : "",
2694
0
        &source->i_title_start, &source->i_title_end,
2695
0
        &source->i_seekpoint_start, &source->i_seekpoint_end );
2696
2697
0
    mrl_info_Clean( &mrli );
2698
2699
0
    return VLC_SUCCESS;
2700
0
}
2701
2702
static demux_t *InputDemuxNew( input_thread_t *p_input, es_out_t *p_es_out,
2703
                               input_source_t *p_source, const char *url,
2704
                               const char *psz_demux, const char *psz_anchor )
2705
0
{
2706
0
    input_thread_private_t *priv = input_priv(p_input );
2707
0
    vlc_object_t *obj = VLC_OBJECT(p_input);
2708
2709
    /* create the underlying access stream */
2710
0
    bool preparsing = priv->type == INPUT_TYPE_PREPARSING;
2711
0
    stream_t *p_stream = stream_AccessNew( obj, p_input, p_es_out,
2712
0
                                           preparsing, url );
2713
0
    if( p_stream == NULL )
2714
0
        return NULL;
2715
2716
0
    p_stream = stream_FilterAutoNew( p_stream );
2717
2718
0
    if(( p_stream->ops != NULL && (p_stream->ops->stream.read == NULL &&
2719
0
                    p_stream->ops->stream.block == NULL && p_stream->ops->stream.readdir == NULL))
2720
0
                || ( p_stream->ops == NULL && (p_stream->pf_read == NULL && p_stream->pf_block == NULL &&
2721
0
                        p_stream->pf_readdir == NULL)) )
2722
0
    {   /* Combined access/demux, no stream filtering */
2723
0
        MRLSections( psz_anchor,
2724
0
                     &p_source->i_title_start, &p_source->i_title_end,
2725
0
                    &p_source->i_seekpoint_start, &p_source->i_seekpoint_end );
2726
0
        return p_stream;
2727
0
    }
2728
2729
    /* attach explicit stream filters to stream */
2730
0
    char *psz_filters = var_InheritString( p_input, "stream-filter" );
2731
0
    if( psz_filters )
2732
0
    {
2733
0
        p_stream = stream_FilterChainNew( p_stream, psz_filters );
2734
0
        free( psz_filters );
2735
0
    }
2736
2737
    /* handle anchors */
2738
0
    if( InputStreamHandleAnchor( p_input, p_source, &p_stream, psz_anchor ) )
2739
0
        goto error;
2740
2741
    /* attach conditional record stream-filter */
2742
0
    if( var_InheritBool( p_input, "input-record-native" ) )
2743
0
        p_stream = stream_FilterChainNew( p_stream, "record" );
2744
2745
    /* create a regular demux with the access stream created */
2746
0
    demux_t *demux = demux_NewAdvanced( obj, p_input, psz_demux, url, p_stream,
2747
0
                                        p_es_out, preparsing );
2748
0
    if( demux != NULL )
2749
0
        return demux;
2750
2751
0
error:
2752
0
    vlc_stream_Delete( p_stream );
2753
0
    return NULL;
2754
0
}
2755
2756
static void input_SplitMRL( const char **, const char **, const char **,
2757
                            const char **, char * );
2758
2759
static input_source_t *InputSourceNew( const char *psz_mrl )
2760
0
{
2761
0
    input_source_t *in = calloc(1, sizeof(*in) );
2762
0
    if( unlikely(in == NULL) )
2763
0
        return NULL;
2764
2765
0
    vlc_atomic_rc_init( &in->rc );
2766
2767
0
    if( psz_mrl )
2768
0
    {
2769
        /* Use the MD5 sum of the complete source URL as an identifier. */
2770
0
        vlc_hash_md5_t md5;
2771
2772
0
        in->str_id = malloc(VLC_HASH_MD5_DIGEST_HEX_SIZE);
2773
0
        if( !in->str_id )
2774
0
        {
2775
0
            free( in );
2776
0
            return NULL;
2777
0
        }
2778
2779
0
        vlc_hash_md5_Init( &md5 );
2780
0
        vlc_hash_md5_Update( &md5, psz_mrl, strlen( psz_mrl ) );
2781
0
        vlc_hash_FinishHex( &md5, in->str_id );
2782
0
    }
2783
2784
0
    in->i_normal_time = VLC_TICK_0;
2785
2786
0
    return in;
2787
0
}
2788
2789
static int InputSourceInit( input_source_t *in, input_thread_t *p_input,
2790
                            const char *psz_mrl,
2791
                            const char *psz_forced_demux, bool b_in_can_fail )
2792
0
{
2793
0
    input_thread_private_t *priv = input_priv(p_input);
2794
0
    const char *psz_access, *psz_demux, *psz_path, *psz_anchor = NULL;
2795
0
    const bool master = priv->master == in;
2796
2797
0
    assert( psz_mrl );
2798
0
    char *psz_dup = strdup( psz_mrl );
2799
0
    char *psz_demux_var = NULL;
2800
2801
0
    if( psz_dup == NULL )
2802
0
        return VLC_ENOMEM;
2803
2804
    /* Split uri */
2805
0
    input_SplitMRL( &psz_access, &psz_demux, &psz_path, &psz_anchor, psz_dup );
2806
2807
0
    if( psz_demux == NULL || psz_demux[0] == '\0' )
2808
0
        psz_demux = psz_demux_var = var_InheritString( p_input, "demux" );
2809
2810
0
    if( psz_forced_demux != NULL )
2811
0
        psz_demux = psz_forced_demux;
2812
2813
0
    if( psz_demux == NULL )
2814
0
        psz_demux = "any";
2815
2816
0
    msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
2817
0
             psz_mrl, psz_access, psz_demux, psz_path );
2818
2819
0
    if( master /* XXX ugly */)
2820
0
    {   /* On master stream only, use input-list */
2821
0
        char *str = var_InheritString( p_input, "input-list" );
2822
0
        if( str != NULL )
2823
0
        {
2824
0
            char *list;
2825
2826
0
            var_Create( p_input, "concat-list", VLC_VAR_STRING );
2827
0
            if( likely(asprintf( &list, "%s://%s,%s", psz_access, psz_path,
2828
0
                                 str ) >= 0) )
2829
0
            {
2830
0
                 var_SetString( p_input, "concat-list", list );
2831
0
                 free( list );
2832
0
            }
2833
0
            free( str );
2834
0
            psz_access = "concat";
2835
0
        }
2836
0
    }
2837
2838
0
    char *psz_newanchor = NULL;
2839
0
    if( strcasecmp( psz_access, "concat" ) )
2840
0
    {   /* Autodetect extra files if none specified */
2841
0
        int count;
2842
0
        char **tab;
2843
2844
0
        TAB_INIT( count, tab );
2845
0
        InputGetExtraFiles( p_input, &count, &tab, &psz_access, psz_mrl );
2846
2847
0
        for( int i = 0; i < count; i++ )
2848
0
        {
2849
0
            char *psz = mrl_AppendAnchorFragment( psz_newanchor ? psz_newanchor : psz_anchor, tab[i] );
2850
0
            if( psz )
2851
0
            {
2852
0
                 free( psz_newanchor );
2853
0
                 psz_newanchor = psz;
2854
0
            }
2855
0
            free( tab[i] );
2856
0
        }
2857
0
        TAB_CLEAN( count, tab );
2858
0
    }
2859
2860
0
    char *url;
2861
0
    if( likely(asprintf( &url, "%s://%s", psz_access, psz_path ) >= 0) )
2862
0
    {
2863
0
        struct vlc_input_es_out *es_out;
2864
0
        if (!master)
2865
0
        {
2866
0
            in->p_slave_es_out
2867
0
                = es_out
2868
0
                = input_EsOutSourceNew(priv->p_es_out, in);
2869
0
        }
2870
0
        else
2871
0
            es_out = priv->p_es_out;
2872
2873
0
        if( es_out )
2874
0
            in->p_demux = InputDemuxNew( p_input, &es_out->out, in, url,
2875
0
                                         psz_demux, psz_newanchor ? psz_newanchor : psz_anchor );
2876
0
        free( url );
2877
0
    }
2878
0
    else
2879
0
        in->p_demux = NULL;
2880
2881
0
    free( psz_newanchor );
2882
0
    free( psz_demux_var );
2883
0
    free( psz_dup );
2884
2885
0
    if( in->p_demux == NULL )
2886
0
    {
2887
0
        if( !b_in_can_fail && !input_Stopped( p_input ) )
2888
0
            vlc_dialog_display_error( p_input, _("Your media can't be opened"),
2889
0
                                      _("VLC is unable to open the MRL '%s'."
2890
0
                                      " Check the log for details."), psz_mrl );
2891
0
        if( in->p_slave_es_out )
2892
0
        {
2893
0
            vlc_input_es_out_Delete(in->p_slave_es_out);
2894
0
            in->p_slave_es_out = NULL;
2895
0
        }
2896
0
        return VLC_EGENERIC;
2897
0
    }
2898
2899
0
    char *psz_demux_chain = NULL;
2900
0
    if( priv->p_renderer )
2901
0
    {
2902
0
        const char* psz_renderer_demux = vlc_renderer_item_demux_filter(
2903
0
                    priv->p_renderer );
2904
0
        if( psz_renderer_demux )
2905
0
            psz_demux_chain = strdup( psz_renderer_demux );
2906
0
    }
2907
0
    if( !psz_demux_chain )
2908
0
        psz_demux_chain = var_GetNonEmptyString(p_input, "demux-filter");
2909
0
    if( psz_demux_chain != NULL ) /* add the chain of demux filters */
2910
0
    {
2911
0
        in->p_demux = demux_FilterChainNew( in->p_demux, psz_demux_chain );
2912
0
        free( psz_demux_chain );
2913
2914
0
        if( in->p_demux == NULL )
2915
0
        {
2916
0
            msg_Err(p_input, "Failed to create demux filter");
2917
0
            if( in->p_slave_es_out )
2918
0
            {
2919
0
                vlc_input_es_out_Delete(in->p_slave_es_out);
2920
0
                in->p_slave_es_out = NULL;
2921
0
            }
2922
0
            return VLC_EGENERIC;
2923
0
        }
2924
0
    }
2925
2926
    /* Get infos from (access_)demux */
2927
0
    if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
2928
0
                       &in->b_can_pace_control ) )
2929
0
        in->b_can_pace_control = false;
2930
2931
0
    const bool b_can_demux = in->p_demux->ops != NULL ?
2932
0
        in->p_demux->ops->demux.demux != NULL : in->p_demux->pf_demux != NULL;
2933
2934
    /* Threaded and directory demuxers do not have pace control */
2935
0
    assert( b_can_demux || !in->b_can_pace_control );
2936
2937
0
    if( !in->b_can_pace_control )
2938
0
    {
2939
0
        if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_RATE,
2940
0
                           &in->b_can_rate_control ) )
2941
0
        {
2942
0
            in->b_can_rate_control = false;
2943
0
            in->b_rescale_ts = true;
2944
0
        }
2945
0
        else
2946
0
            in->b_rescale_ts = !in->b_can_rate_control;
2947
0
    }
2948
0
    else
2949
0
    {
2950
0
        in->b_can_rate_control = true;
2951
0
        in->b_rescale_ts = true;
2952
0
    }
2953
2954
    /* Set record capabilities */
2955
0
    if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
2956
0
        in->b_can_stream_record = false;
2957
0
    if( !var_GetBool( p_input, "input-record-native" ) )
2958
0
        in->b_can_stream_record = false;
2959
2960
0
    demux_Control( in->p_demux, DEMUX_CAN_PAUSE, &in->b_can_pause );
2961
2962
    /* get attachment
2963
     * FIXME improve for preparsing: move it after GET_META and check psz_arturl */
2964
0
    if( input_priv(p_input)->type == INPUT_TYPE_PLAYBACK )
2965
0
    {
2966
0
        if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2967
0
                           &in->title, &in->i_title,
2968
0
                           &in->i_title_offset, &in->i_seekpoint_offset ))
2969
0
        {
2970
0
            TAB_INIT( in->i_title, in->title );
2971
0
        }
2972
0
        else
2973
0
        {
2974
0
            in->b_title_demux = true;
2975
0
        }
2976
2977
0
        if ( demux_Control( in->p_demux, DEMUX_GET_PTS_DELAY, &in->i_pts_delay )
2978
0
             != VLC_SUCCESS )
2979
0
            in->i_pts_delay = 0;
2980
0
        if( in->i_pts_delay > INPUT_PTS_DELAY_MAX )
2981
0
            in->i_pts_delay = INPUT_PTS_DELAY_MAX;
2982
0
        else if( in->i_pts_delay < 0 )
2983
0
            in->i_pts_delay = 0;
2984
0
    }
2985
2986
0
    if( demux_Control( in->p_demux, DEMUX_GET_FPS, &in->f_fps ) )
2987
0
        in->f_fps = 0.f;
2988
2989
0
    if( var_GetInteger( p_input, "clock-synchro" ) != -1 )
2990
0
        in->b_can_pace_control = !var_GetInteger( p_input, "clock-synchro" );
2991
2992
0
    return VLC_SUCCESS;
2993
0
}
2994
2995
/*****************************************************************************
2996
 * InputSourceDestroy:
2997
 *****************************************************************************/
2998
static void InputSourceDestroy( input_source_t *in )
2999
0
{
3000
0
    int i;
3001
3002
0
    if( in->p_demux )
3003
0
        demux_Delete( in->p_demux );
3004
0
    if( in->p_slave_es_out )
3005
0
        vlc_input_es_out_Delete(in->p_slave_es_out);
3006
3007
0
    if( in->i_title > 0 )
3008
0
    {
3009
0
        for( i = 0; i < in->i_title; i++ )
3010
0
            vlc_input_title_Delete( in->title[i] );
3011
0
    }
3012
0
    TAB_CLEAN( in->i_title, in->title );
3013
0
}
3014
3015
/*****************************************************************************
3016
 * InputSourceMeta:
3017
 *****************************************************************************/
3018
static void InputSourceMeta( input_thread_t *p_input,
3019
                             input_source_t *p_source, vlc_meta_t *p_meta )
3020
0
{
3021
0
    demux_t *p_demux = p_source->p_demux;
3022
3023
    /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
3024
     * is a bad idea */
3025
3026
0
    bool has_meta = false;
3027
3028
    /* Read demux meta */
3029
0
    if( !demux_Control( p_demux, DEMUX_GET_META, p_meta ) )
3030
0
        has_meta = true;
3031
3032
0
    bool has_unsupported;
3033
0
    if( demux_Control( p_demux, DEMUX_HAS_UNSUPPORTED_META, &has_unsupported ) )
3034
0
        has_unsupported = true;
3035
3036
    /* If the demux report unsupported meta data, or if we don't have meta data
3037
     * try an external "meta reader" */
3038
0
    if( has_meta && !has_unsupported )
3039
0
        return;
3040
3041
0
    demux_meta_t *p_demux_meta =
3042
0
        vlc_custom_create( p_input, sizeof( *p_demux_meta ), "demux meta" );
3043
0
    if( unlikely(p_demux_meta == NULL) )
3044
0
        return;
3045
0
    p_demux_meta->p_item = input_priv(p_input)->p_item;
3046
3047
0
    module_t *p_id3 = module_need( p_demux_meta, "meta reader", NULL, false );
3048
0
    if( p_id3 )
3049
0
    {
3050
0
        if( p_demux_meta->p_meta )
3051
0
        {
3052
0
            vlc_meta_Merge( p_meta, p_demux_meta->p_meta );
3053
0
            vlc_meta_Delete( p_demux_meta->p_meta );
3054
0
        }
3055
3056
0
        if( p_demux_meta->i_attachments > 0 )
3057
0
        {
3058
0
            vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
3059
0
            AppendAttachment( p_input, p_demux_meta->i_attachments,
3060
0
                              p_demux_meta->attachments );
3061
0
            vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
3062
0
        }
3063
0
        module_unneed( p_demux_meta, p_id3 );
3064
0
    }
3065
0
    vlc_object_delete(p_demux_meta);
3066
0
}
3067
3068
3069
static void SlaveDemux( input_thread_t *p_input )
3070
0
{
3071
0
    input_thread_private_t *priv = input_priv(p_input);
3072
0
    vlc_tick_t i_time;
3073
3074
0
    if( demux_Control( input_priv(p_input)->master->p_demux, DEMUX_GET_TIME, &i_time ) )
3075
0
    {
3076
0
        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
3077
0
        return;
3078
0
    }
3079
3080
0
    for (size_t i = 0; i < input_priv(p_input)->i_slave; i++)
3081
0
    {
3082
0
        input_source_t *in = input_priv(p_input)->slave[i];
3083
0
        int i_ret;
3084
3085
0
        if( in->b_eof )
3086
0
            continue;
3087
3088
0
        if( priv->slave_subs_rate != in->sub_rate )
3089
0
        {
3090
0
            if( in->b_slave_sub && in->b_can_rate_control )
3091
0
            {
3092
0
                if( in->sub_rate != 0 ) /* Don't reset when it's the first time */
3093
0
                    es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
3094
0
                float new_rate = priv->slave_subs_rate;
3095
0
                demux_Control( in->p_demux, DEMUX_SET_RATE, &new_rate );
3096
0
            }
3097
0
            in->sub_rate = priv->slave_subs_rate;
3098
0
        }
3099
3100
3101
        /* Call demux_Demux until we have read enough data */
3102
0
        if( demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
3103
0
        {
3104
0
            for( ;; )
3105
0
            {
3106
0
                vlc_tick_t i_stime;
3107
0
                if( demux_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
3108
0
                {
3109
0
                    msg_Err(p_input, "slave[%zu] doesn't like "
3110
0
                            "DEMUX_GET_TIME -> EOF", i);
3111
0
                    i_ret = 0;
3112
0
                    break;
3113
0
                }
3114
3115
0
                if( i_stime >= i_time )
3116
0
                {
3117
0
                    i_ret = 1;
3118
0
                    break;
3119
0
                }
3120
3121
0
                if( ( i_ret = demux_Demux( in->p_demux ) ) <= 0 )
3122
0
                    break;
3123
0
            }
3124
0
        }
3125
0
        else
3126
0
        {
3127
0
            i_ret = demux_Demux( in->p_demux );
3128
0
        }
3129
3130
0
        if( i_ret <= 0 )
3131
0
        {
3132
0
            msg_Dbg(p_input, "slave %zu EOF", i);
3133
0
            in->b_eof = true;
3134
0
        }
3135
0
    }
3136
0
}
3137
3138
static void SlaveSeek( input_thread_t *p_input )
3139
0
{
3140
0
    vlc_tick_t i_time;
3141
3142
0
    if( demux_Control( input_priv(p_input)->master->p_demux, DEMUX_GET_TIME, &i_time ) )
3143
0
    {
3144
0
        msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
3145
0
        return;
3146
0
    }
3147
3148
0
    for (size_t i = 0; i < input_priv(p_input)->i_slave; i++)
3149
0
    {
3150
0
        input_source_t *in = input_priv(p_input)->slave[i];
3151
3152
0
        if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time, true ) )
3153
0
        {
3154
0
            if( !in->b_eof )
3155
0
                msg_Err(p_input, "seek failed for slave %zu -> EOF", i);
3156
0
            in->b_eof = true;
3157
0
        }
3158
0
        else
3159
0
        {
3160
0
            in->b_eof = false;
3161
0
        }
3162
0
    }
3163
0
}
3164
3165
/*****************************************************************************
3166
 * InputMetaUser:
3167
 *****************************************************************************/
3168
static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta )
3169
0
{
3170
0
    static const struct { int i_meta; const char *psz_name; } p_list[] = {
3171
0
        { vlc_meta_Title,       "meta-title" },
3172
0
        { vlc_meta_Artist,      "meta-artist" },
3173
0
        { vlc_meta_Genre,       "meta-genre" },
3174
0
        { vlc_meta_Copyright,   "meta-copyright" },
3175
0
        { vlc_meta_Description, "meta-description" },
3176
0
        { vlc_meta_Date,        "meta-date" },
3177
0
        { vlc_meta_URL,         "meta-url" },
3178
0
        { 0, NULL }
3179
0
    };
3180
3181
    /* Get meta information from user */
3182
0
    for( int i = 0; p_list[i].psz_name; i++ )
3183
0
    {
3184
0
        char *psz_string = var_GetNonEmptyString( p_input, p_list[i].psz_name );
3185
0
        if( !psz_string )
3186
0
            continue;
3187
3188
0
        EnsureUTF8( psz_string );
3189
0
        vlc_meta_Set( p_meta, p_list[i].i_meta, psz_string );
3190
0
        free( psz_string );
3191
0
    }
3192
0
}
3193
3194
static void AppendAttachment( input_thread_t *p_input, int i_new,
3195
                              input_attachment_t **pp_new )
3196
0
{
3197
0
    input_thread_private_t *priv = input_priv( p_input );
3198
0
    int i_attachment = priv->i_attachment;
3199
0
    int i;
3200
3201
0
    if ( i_attachment + i_new == 0 )
3202
0
    {
3203
0
        free( pp_new );
3204
0
        return;
3205
0
    }
3206
3207
0
    input_attachment_t **pp_att = realloc( priv->attachment,
3208
0
                    sizeof(*pp_att) * ( i_attachment + i_new ) );
3209
0
    if( likely(pp_att) )
3210
0
    {
3211
0
        priv->attachment = pp_att;
3212
0
        for( i = 0; i < i_new; i++ )
3213
0
        {
3214
0
            pp_att[i_attachment++] = pp_new[i];
3215
0
        }
3216
        /* */
3217
0
        priv->i_attachment = i_attachment;
3218
3219
0
        input_SendEvent(p_input, &(struct vlc_input_event) {
3220
0
            .type = INPUT_EVENT_ATTACHMENTS,
3221
0
            .attachments = { .array = pp_new, .count = i_new },
3222
0
        });
3223
3224
0
        free( pp_new );
3225
0
        return;
3226
0
    }
3227
3228
    /* on alloc errors */
3229
0
    for( i = 0; i < i_new; i++ )
3230
0
        vlc_input_attachment_Release( pp_new[i] );
3231
0
    free( pp_new );
3232
0
}
3233
3234
/*****************************************************************************
3235
 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
3236
 * arturl and locking issue.
3237
 *****************************************************************************/
3238
static void InputUpdateMeta( input_thread_t *p_input, demux_t *p_demux )
3239
0
{
3240
0
    vlc_meta_t *p_meta = vlc_meta_New();
3241
0
    if( unlikely(p_meta == NULL) )
3242
0
        return;
3243
3244
0
    demux_Control( p_demux, DEMUX_GET_META, p_meta );
3245
3246
    /* If metadata changed, then the attachments might have changed.
3247
       We need to update them in case they contain album art. */
3248
0
    input_attachment_t **attachment;
3249
0
    int i_attachment;
3250
3251
0
    if( !demux_Control( p_demux, DEMUX_GET_ATTACHMENTS,
3252
0
                        &attachment, &i_attachment ) )
3253
0
    {
3254
0
        input_thread_private_t *priv = input_priv(p_input);
3255
0
        vlc_mutex_lock( &priv->p_item->lock );
3256
0
        int nb_new = 0;
3257
0
        for ( int i = 0; i < i_attachment; ++i )
3258
0
        {
3259
0
            bool is_new = true;
3260
0
            for( int j = 0; j < priv->i_attachment; ++j )
3261
0
            {
3262
0
                if( priv->attachment[j] == attachment[i] )
3263
0
                {
3264
0
                    vlc_input_attachment_Release( attachment[i] );
3265
0
                    is_new = false;
3266
0
                    break;
3267
0
                }
3268
0
            }
3269
0
            if( is_new )
3270
0
                attachment[nb_new++] = attachment[i];
3271
0
        }
3272
0
        AppendAttachment( p_input, nb_new, attachment );
3273
0
        vlc_mutex_unlock( &priv->p_item->lock );
3274
0
    }
3275
3276
0
    es_out_ControlSetMeta(&input_priv(p_input)->p_es_out->out, p_meta);
3277
0
    vlc_meta_Delete( p_meta );
3278
0
}
3279
3280
/*****************************************************************************
3281
 * InputGetExtraFiles
3282
 *  Autodetect extra input list
3283
 *****************************************************************************/
3284
static void InputGetExtraFilesPattern( input_thread_t *p_input,
3285
                                       int *pi_list, char ***pppsz_list,
3286
                                       const char *uri,
3287
                                       const char *psz_match,
3288
                                       const char *psz_format,
3289
                                       int i_start, int i_stop )
3290
0
{
3291
0
    int i_list;
3292
0
    char **ppsz_list;
3293
0
    TAB_INIT( i_list, ppsz_list );
3294
3295
    /* Remove the extension */
3296
0
    size_t end = strlen(uri) - strlen(psz_match);
3297
0
    if (unlikely(end > INT_MAX))
3298
0
        goto exit;
3299
3300
    /* Try to list files */
3301
0
    for( int i = i_start; i <= i_stop; i++ )
3302
0
    {
3303
0
        char *url;
3304
0
        if( asprintf( &url, psz_format, (int)end, uri, i ) < 0 )
3305
0
            break;
3306
3307
0
        char *filepath = vlc_uri2path(url);
3308
3309
0
        struct stat st;
3310
0
        if( filepath == NULL ||
3311
0
            vlc_stat( filepath, &st ) || !S_ISREG( st.st_mode ) || !st.st_size )
3312
0
        {
3313
0
            free( filepath );
3314
0
            free( url );
3315
0
            break;
3316
0
        }
3317
3318
0
        msg_Dbg( p_input, "Detected extra file `%s'", filepath );
3319
0
        free( filepath );
3320
0
        TAB_APPEND( i_list, ppsz_list, url );
3321
0
    }
3322
0
exit:
3323
0
    *pi_list = i_list;
3324
0
    *pppsz_list = ppsz_list;
3325
0
}
3326
3327
static void InputGetExtraFiles( input_thread_t *p_input,
3328
                                int *pi_list, char ***pppsz_list,
3329
                                const char **ppsz_access, const char *mrl )
3330
0
{
3331
0
    static const struct pattern
3332
0
    {
3333
0
        const char *psz_access_force;
3334
0
        const char *psz_match;
3335
0
        const char *psz_format;
3336
0
        int i_start;
3337
0
        int i_stop;
3338
0
    } patterns[] = {
3339
        /* XXX the order is important */
3340
0
        { "concat", ".001", "%.*s.%.3d", 2, 999 },
3341
0
        { NULL, ".part1.rar","%.*s.part%.1d.rar", 2, 9 },
3342
0
        { NULL, ".part01.rar","%.*s.part%.2d.rar", 2, 99, },
3343
0
        { NULL, ".part001.rar", "%.*s.part%.3d.rar", 2, 999 },
3344
0
        { NULL, ".rar", "%.*s.r%.2d", 0, 99 },
3345
0
        { "concat", ".mts", "%.*s.mts%d", 1, 999 },
3346
0
    };
3347
3348
0
    assert(mrl != NULL);
3349
0
    TAB_INIT( *pi_list, *pppsz_list );
3350
3351
0
    if( **ppsz_access && strcmp( *ppsz_access, "file" ) )
3352
0
        return;
3353
3354
    /* reject files with anchor data from stream extractor */
3355
0
    if( strpbrk( mrl, "#?" ) )
3356
0
        return;
3357
3358
0
    const size_t i_path = strlen(mrl);
3359
3360
0
    for( size_t i = 0; i < ARRAY_SIZE( patterns ); ++i )
3361
0
    {
3362
0
        const struct pattern* pat = &patterns[i];
3363
0
        const size_t i_ext = strlen( pat->psz_match );
3364
3365
0
        if( i_path < i_ext )
3366
0
            continue;
3367
3368
0
        if( !strcmp( &mrl[i_path-i_ext], pat->psz_match ) )
3369
0
        {
3370
0
            InputGetExtraFilesPattern( p_input, pi_list, pppsz_list, mrl,
3371
0
                pat->psz_match, pat->psz_format, pat->i_start, pat->i_stop );
3372
3373
0
            if( *pi_list > 0 && pat->psz_access_force )
3374
0
                *ppsz_access = pat->psz_access_force;
3375
0
            return;
3376
0
        }
3377
0
    }
3378
0
}
3379
3380
/* */
3381
static void input_ChangeState( input_thread_t *p_input, int i_state,
3382
                               vlc_tick_t state_date )
3383
0
{
3384
0
    if( input_priv(p_input)->i_state == i_state )
3385
0
        return;
3386
3387
0
    input_priv(p_input)->i_state = i_state;
3388
0
    if (i_state == END_S || i_state == ERROR_S)
3389
0
        input_SendEventCapabilities( p_input, 0 );
3390
0
    input_SendEventState( p_input, i_state, state_date );
3391
0
}
3392
3393
3394
/*****************************************************************************
3395
 * MRLSplit: parse the access, demux and url part of the
3396
 *           Media Resource Locator.
3397
 *****************************************************************************/
3398
static void input_SplitMRL( const char **access, const char **demux,
3399
                            const char **path, const char **anchor, char *buf )
3400
0
{
3401
0
    char *p;
3402
3403
    /* Separate <path> from <access>[/<demux>]:// */
3404
0
    p = strchr( buf, ':');
3405
0
    if( p != NULL )
3406
0
    {
3407
0
        *(p++) = '\0'; /* skips ':' */
3408
0
        *path = p + (strncmp(p, "//", 2) ? 0 : 2); /* skips "//" */
3409
3410
        /* Remove HTML anchor if present (not supported).
3411
         * The hash symbol itself should be URI-encoded. */
3412
0
        p = strchr( p, '#' );
3413
0
        if( p != NULL )
3414
0
        {
3415
0
            *(p++) = '\0';
3416
0
            *anchor = p;
3417
0
        }
3418
0
        else
3419
0
            *anchor = "";
3420
0
    }
3421
0
    else
3422
0
    {
3423
0
#ifndef NDEBUG
3424
0
        fprintf( stderr, "%s(\"%s\") probably not a valid URI!\n", __func__,
3425
0
                 buf );
3426
0
#endif
3427
        /* Note: this is a valid non const pointer to "": */
3428
0
        *path = buf + strlen( buf );
3429
0
    }
3430
3431
    /* Separate access from demux */
3432
0
    p = strchr( buf, '/' );
3433
0
    if( p != NULL )
3434
0
    {
3435
0
        *(p++) = '\0';
3436
0
        if( p[0] == '$' )
3437
0
            p++;
3438
0
        *demux = p;
3439
0
    }
3440
0
    else
3441
0
        *demux = "";
3442
3443
    /* We really don't want module name substitution here! */
3444
0
    p = buf;
3445
0
    if( p[0] == '$' )
3446
0
        p++;
3447
0
    *access = p;
3448
0
}
3449
3450
static const char *MRLSeekPoint( const char *str, int *title, int *chapter )
3451
0
{
3452
0
    char *end;
3453
0
    unsigned long u;
3454
3455
    /* Look for the title */
3456
0
    u = strtoul( str, &end, 0 );
3457
0
    *title = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
3458
0
    str = end;
3459
3460
    /* Look for the chapter */
3461
0
    if( *str == ':' )
3462
0
    {
3463
0
        str++;
3464
0
        u = strtoul( str, &end, 0 );
3465
0
        *chapter = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
3466
0
        str = end;
3467
0
    }
3468
0
    else
3469
0
        *chapter = -1;
3470
3471
0
    return str;
3472
0
}
3473
3474
3475
/*****************************************************************************
3476
 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
3477
 *
3478
 * Syntax:
3479
 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
3480
 *****************************************************************************/
3481
static void MRLSections( const char *p,
3482
                         int *pi_title_start, int *pi_title_end,
3483
                         int *pi_chapter_start, int *pi_chapter_end )
3484
0
{
3485
0
    *pi_title_start = *pi_title_end = *pi_chapter_start = *pi_chapter_end = -1;
3486
3487
0
    int title_start, chapter_start, title_end, chapter_end;
3488
3489
0
    if( !p )
3490
0
        return;
3491
3492
0
    if( *p != '-' )
3493
0
        p = MRLSeekPoint( p, &title_start, &chapter_start );
3494
0
    else
3495
0
        title_start = chapter_start = -1;
3496
3497
0
    if( *p == '-' )
3498
0
        p = MRLSeekPoint( p + 1, &title_end, &chapter_end );
3499
0
    else
3500
0
        title_end = chapter_end = -1;
3501
3502
0
    if( *p ) /* syntax error */
3503
0
        return;
3504
3505
0
    *pi_title_start = title_start;
3506
0
    *pi_title_end = title_end;
3507
0
    *pi_chapter_start = chapter_start;
3508
0
    *pi_chapter_end = chapter_end;
3509
0
}
3510
3511
static int input_SlaveSourceAdd( input_thread_t *p_input,
3512
                                 enum slave_type i_type, const char *psz_uri,
3513
                                 unsigned i_flags )
3514
0
{
3515
0
    input_thread_private_t *priv = input_priv(p_input);
3516
0
    const char *psz_forced_demux;
3517
0
    const bool b_can_fail = i_flags & SLAVE_ADD_CANFAIL;
3518
0
    const bool b_forced = i_flags & SLAVE_ADD_FORCED;
3519
0
    const bool b_set_time = i_flags & SLAVE_ADD_SET_TIME;
3520
3521
0
    switch( i_type )
3522
0
    {
3523
0
    case SLAVE_TYPE_SPU:
3524
0
        psz_forced_demux = "subtitle";
3525
0
        break;
3526
0
    case SLAVE_TYPE_GENERIC:
3527
0
        psz_forced_demux = NULL;
3528
0
        break;
3529
0
    default:
3530
0
        vlc_assert_unreachable();
3531
0
    }
3532
3533
0
    msg_Dbg( p_input, "loading %s slave: %s (forced: %d)",
3534
0
             i_type == SLAVE_TYPE_SPU ? "spu" : "generic", psz_uri, b_forced );
3535
3536
0
    input_source_t *p_source = InputSourceNew( psz_uri );
3537
0
    if( !p_source )
3538
0
        return VLC_EGENERIC;
3539
3540
0
    if( b_forced )
3541
0
        p_source->autoselected = true;
3542
3543
0
    int ret = InputSourceInit( p_source, p_input, psz_uri,
3544
0
                               psz_forced_demux,
3545
0
                               b_can_fail || psz_forced_demux );
3546
3547
0
    if( psz_forced_demux && ret != VLC_SUCCESS )
3548
0
        ret = InputSourceInit( p_source, p_input, psz_uri, NULL, b_can_fail );
3549
3550
0
    if( ret != VLC_SUCCESS )
3551
0
    {
3552
0
        msg_Warn( p_input, "failed to add %s as slave", psz_uri );
3553
0
        goto error;
3554
0
    }
3555
3556
0
    if( i_type == SLAVE_TYPE_GENERIC )
3557
0
    {
3558
0
        if( b_set_time )
3559
0
        {
3560
0
            vlc_tick_t i_time;
3561
3562
            /* Set position */
3563
0
            if( demux_Control( priv->master->p_demux, DEMUX_GET_TIME, &i_time ) )
3564
0
            {
3565
0
                msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
3566
0
                goto error;
3567
0
            }
3568
3569
0
            if( demux_Control( p_source->p_demux,
3570
0
                               DEMUX_SET_TIME, i_time, true ) )
3571
0
            {
3572
0
                msg_Err( p_input, "seek failed for new slave" );
3573
0
                goto error;
3574
0
            }
3575
0
        }
3576
3577
        /* Get meta (access and demux) */
3578
0
        InputUpdateMeta( p_input, p_source->p_demux );
3579
0
    }
3580
0
    else
3581
0
        p_source->b_slave_sub = true;
3582
3583
0
    TAB_APPEND( priv->i_slave, priv->slave, p_source );
3584
3585
0
    return VLC_SUCCESS;
3586
3587
0
error:
3588
0
    InputSourceDestroy( p_source );
3589
0
    input_source_Release( p_source );
3590
0
    return VLC_EGENERIC;
3591
0
}
3592
3593
static char *input_SubtitleFile2Uri( input_thread_t *p_input,
3594
                                     const char *psz_subtitle )
3595
0
{
3596
    /* if we are provided a subtitle.sub file,
3597
     * see if we don't have a subtitle.idx and use it instead */
3598
0
    char *psz_idxpath = NULL;
3599
0
    char *psz_extension = strrchr( psz_subtitle, '.');
3600
0
    if( psz_extension && strcmp( psz_extension, ".sub" ) == 0 )
3601
0
    {
3602
0
        psz_idxpath = strdup( psz_subtitle );
3603
0
        if( psz_idxpath )
3604
0
        {
3605
0
            struct stat st;
3606
3607
0
            psz_extension = psz_extension - psz_subtitle + psz_idxpath;
3608
0
            strcpy( psz_extension, ".idx" );
3609
3610
0
            if( !vlc_stat( psz_idxpath, &st ) && S_ISREG( st.st_mode ) )
3611
0
            {
3612
0
                msg_Dbg( p_input, "using %s as subtitle file instead of %s",
3613
0
                         psz_idxpath, psz_subtitle );
3614
0
                psz_subtitle = psz_idxpath;
3615
0
            }
3616
0
        }
3617
0
    }
3618
3619
0
    char *psz_uri = vlc_path2uri( psz_subtitle, NULL );
3620
0
    free( psz_idxpath );
3621
0
    return psz_uri;
3622
0
}
3623
3624
int input_GetAttachments(input_thread_t *input,
3625
                         input_attachment_t ***attachments)
3626
0
{
3627
0
    input_thread_private_t *priv = input_priv(input);
3628
3629
0
    vlc_mutex_lock(&priv->p_item->lock);
3630
0
    int attachments_count = priv->i_attachment;
3631
0
    if (attachments_count <= 0)
3632
0
    {
3633
0
        vlc_mutex_unlock(&priv->p_item->lock);
3634
0
        *attachments = NULL;
3635
0
        return 0;
3636
0
    }
3637
3638
0
    *attachments = vlc_alloc(attachments_count, sizeof(input_attachment_t*));
3639
0
    if (!*attachments)
3640
0
    {
3641
0
        vlc_mutex_unlock(&priv->p_item->lock);
3642
0
        return -1;
3643
0
    }
3644
3645
0
    for (int i = 0; i < attachments_count; i++)
3646
0
        (*attachments)[i] = vlc_input_attachment_Hold(priv->attachment[i]);
3647
3648
0
    vlc_mutex_unlock(&priv->p_item->lock);
3649
0
    return attachments_count;
3650
0
}
3651
3652
input_attachment_t *input_GetAttachment(input_thread_t *input, const char *name)
3653
0
{
3654
0
    input_thread_private_t *priv = input_priv(input);
3655
3656
0
    vlc_mutex_lock(&priv->p_item->lock);
3657
0
    for (int i = 0; i < priv->i_attachment; i++)
3658
0
    {
3659
0
        if (!strcmp( priv->attachment[i]->psz_name, name))
3660
0
        {
3661
0
            input_attachment_t *attachment =
3662
0
                vlc_input_attachment_Hold(priv->attachment[i] );
3663
0
            vlc_mutex_unlock( &priv->p_item->lock );
3664
0
            return attachment;
3665
0
        }
3666
0
    }
3667
0
    vlc_mutex_unlock( &priv->p_item->lock );
3668
0
    return NULL;
3669
0
}
3670
3671
bool input_CanPaceControl(input_thread_t *input)
3672
0
{
3673
0
    input_thread_private_t *priv = input_priv(input);
3674
0
    return priv->master->b_can_pace_control;
3675
0
}
3676
3677
vlc_tick_t input_GetItemDuration(input_thread_t *input, vlc_tick_t duration)
3678
0
{
3679
0
    input_thread_private_t *priv = input_priv(input);
3680
3681
0
    if( priv->i_stop == 0 ) /* consider `duration` as stop time, if stop-time not set */
3682
0
        duration -= priv->i_start;
3683
0
    else /* calculate duration based on start-time and stop-time */
3684
0
        duration = priv->i_stop - priv->i_start;
3685
3686
0
    return duration;
3687
0
}