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