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