gui: macos: use float for rate
[vlc.git] / src / input / input.c
blob08f55c047038ac306c77ff91ee51748314d60f2a
1 /*****************************************************************************
2 * input.c: input thread
3 *****************************************************************************
4 * Copyright (C) 1998-2007 VLC authors and VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
7 * Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
33 #include <limits.h>
34 #include <assert.h>
35 #include <sys/stat.h>
37 #include "input_internal.h"
38 #include "event.h"
39 #include "es_out.h"
40 #include "demux.h"
41 #include "item.h"
42 #include "resource.h"
43 #include "stream.h"
45 #include <vlc_aout.h>
46 #include <vlc_sout.h>
47 #include <vlc_dialog.h>
48 #include <vlc_url.h>
49 #include <vlc_charset.h>
50 #include <vlc_fs.h>
51 #include <vlc_strings.h>
52 #include <vlc_modules.h>
53 #include <vlc_stream.h>
54 #include <vlc_stream_extractor.h>
55 #include <vlc_renderer_discovery.h>
57 /*****************************************************************************
58 * Local prototypes
59 *****************************************************************************/
60 static void *Run( void * );
61 static void *Preparse( void * );
63 static input_thread_t * Create ( vlc_object_t *, input_thread_events_cb, void *,
64 input_item_t *, const char *, bool, bool,
65 input_resource_t *, vlc_renderer_item_t * );
66 static int Init ( input_thread_t *p_input );
67 static void End ( input_thread_t *p_input );
68 static void MainLoop( input_thread_t *p_input, bool b_interactive );
70 static inline int ControlPop( input_thread_t *, int *, input_control_param_t *, vlc_tick_t i_deadline, bool b_postpone_seek );
71 static void ControlRelease( int i_type, const input_control_param_t *p_param );
72 static bool ControlIsSeekRequest( int i_type );
73 static bool Control( input_thread_t *, int, input_control_param_t );
74 static void ControlPause( input_thread_t *, vlc_tick_t );
76 static int UpdateTitleSeekpointFromDemux( input_thread_t * );
77 static void UpdateGenericFromDemux( input_thread_t * );
78 static void UpdateTitleListfromDemux( input_thread_t * );
80 static void MRLSections( const char *, int *, int *, int *, int *);
82 static input_source_t *InputSourceNew( input_thread_t *, const char *,
83 const char *psz_forced_demux,
84 bool b_in_can_fail );
85 static void InputSourceDestroy( input_source_t * );
86 static void InputSourceMeta( input_thread_t *, input_source_t *, vlc_meta_t * );
88 /* TODO */
89 //static void InputGetAttachments( input_thread_t *, input_source_t * );
90 static void SlaveDemux( input_thread_t *p_input );
91 static void SlaveSeek( input_thread_t *p_input );
93 static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta );
94 static void InputUpdateMeta( input_thread_t *p_input, demux_t *p_demux );
95 static void InputGetExtraFiles( input_thread_t *p_input,
96 int *pi_list, char ***pppsz_list,
97 const char **psz_access, const char *psz_path );
99 static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
100 const demux_t ***ppp_attachment_demux,
101 int i_new, input_attachment_t **pp_new, const demux_t *p_demux );
103 #define SLAVE_ADD_NOFLAG 0
104 #define SLAVE_ADD_FORCED (1<<0)
105 #define SLAVE_ADD_CANFAIL (1<<1)
106 #define SLAVE_ADD_SET_TIME (1<<2)
108 static int input_SlaveSourceAdd( input_thread_t *, enum slave_type,
109 const char *, unsigned );
110 static char *input_SubtitleFile2Uri( input_thread_t *, const char * );
111 static void input_ChangeState( input_thread_t *p_input, int i_state ); /* TODO fix name */
113 #undef input_Create
115 * Create a new input_thread_t.
117 * You need to call input_Start on it when you are done
118 * adding callback on the variables/events you want to monitor.
120 * \param p_parent a vlc_object
121 * \param p_item an input item
122 * \param psz_log an optional prefix for this input logs
123 * \param p_resource an optional input ressource
124 * \return a pointer to the spawned input thread
126 input_thread_t *input_Create( vlc_object_t *p_parent,
127 input_thread_events_cb events_cb, void *events_data,
128 input_item_t *p_item,
129 const char *psz_log, input_resource_t *p_resource,
130 vlc_renderer_item_t *p_renderer )
132 return Create( p_parent, events_cb, events_data, p_item, psz_log, false,
133 false, p_resource, p_renderer );
136 #undef input_Read
138 * Initialize an input thread and run it until it stops by itself.
140 * \param p_parent a vlc_object
141 * \param p_item an input item
142 * \return an error code, VLC_SUCCESS on success
144 int input_Read( vlc_object_t *p_parent, input_item_t *p_item,
145 input_thread_events_cb events_cb, void *events_data )
147 input_thread_t *p_input = Create( p_parent, events_cb, events_data, p_item,
148 NULL, false, false, NULL, NULL );
149 if( !p_input )
150 return VLC_EGENERIC;
152 if( !Init( p_input ) )
154 MainLoop( p_input, false );
155 End( p_input );
158 vlc_object_release( p_input );
159 return VLC_SUCCESS;
162 input_thread_t *input_CreatePreparser( vlc_object_t *parent,
163 input_thread_events_cb events_cb,
164 void *events_data, input_item_t *item )
166 return Create( parent, events_cb, events_data, item, NULL, true, false, NULL, NULL );
169 input_thread_t *input_CreateThumbnailer(vlc_object_t *obj,
170 input_thread_events_cb events_cb,
171 void *events_data, input_item_t *item)
173 return Create( obj, events_cb, events_data, item, NULL, false, true, NULL, NULL );
177 * Start a input_thread_t created by input_Create.
179 * You must not start an already running input_thread_t.
181 * \param the input thread to start
183 int input_Start( input_thread_t *p_input )
185 input_thread_private_t *priv = input_priv(p_input);
186 void *(*func)(void *) = Run;
188 if( priv->b_preparsing )
189 func = Preparse;
191 assert( !priv->is_running );
192 /* Create thread and wait for its readiness. */
193 priv->is_running = !vlc_clone( &priv->thread, func, priv,
194 VLC_THREAD_PRIORITY_INPUT );
195 if( !priv->is_running )
197 msg_Err( p_input, "cannot create input thread" );
198 return VLC_EGENERIC;
200 return VLC_SUCCESS;
204 * Request a running input thread to stop and die
206 * \param p_input the input thread to stop
208 void input_Stop( input_thread_t *p_input )
210 input_thread_private_t *sys = input_priv(p_input);
212 vlc_mutex_lock( &sys->lock_control );
213 /* Discard all pending controls */
214 for( size_t i = 0; i < sys->i_control; i++ )
216 input_control_t *ctrl = &sys->control[i];
217 ControlRelease( ctrl->i_type, &ctrl->param );
219 sys->i_control = 0;
220 sys->is_stopped = true;
221 vlc_cond_signal( &sys->wait_control );
222 vlc_mutex_unlock( &sys->lock_control );
223 vlc_interrupt_kill( &sys->interrupt );
227 * Close an input
229 * It does not call input_Stop itself.
231 void input_Close( input_thread_t *p_input )
233 if( input_priv(p_input)->is_running )
234 vlc_join( input_priv(p_input)->thread, NULL );
235 vlc_interrupt_deinit( &input_priv(p_input)->interrupt );
236 vlc_object_release( p_input );
239 void input_SetTime( input_thread_t *p_input, vlc_tick_t i_time, bool b_fast )
241 input_control_param_t param;
243 param.time.i_val = i_time;
244 param.time.b_fast_seek = b_fast;
245 input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &param );
248 void input_SetPosition( input_thread_t *p_input, float f_position, bool b_fast )
250 input_control_param_t param;
252 param.pos.f_val = f_position;
253 param.pos.b_fast_seek = b_fast;
254 input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &param );
258 * Input destructor (called when the object's refcount reaches 0).
260 static void input_Destructor( vlc_object_t *obj )
262 input_thread_t *p_input = (input_thread_t *)obj;
263 input_thread_private_t *priv = input_priv(p_input);
264 #ifndef NDEBUG
265 char * psz_name = input_item_GetName( priv->p_item );
266 msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
267 free( psz_name );
268 #endif
270 if( priv->p_renderer )
271 vlc_renderer_item_release( priv->p_renderer );
272 if( priv->p_es_out_display )
273 es_out_Delete( priv->p_es_out_display );
275 if( priv->p_resource )
276 input_resource_Release( priv->p_resource );
277 if( priv->p_resource_private )
278 input_resource_Release( priv->p_resource_private );
280 input_item_Release( priv->p_item );
282 if( priv->stats != NULL )
283 input_stats_Destroy( priv->stats );
285 for( size_t i = 0; i < priv->i_control; i++ )
287 input_control_t *p_ctrl = &priv->control[i];
288 ControlRelease( p_ctrl->i_type, &p_ctrl->param );
291 vlc_cond_destroy( &priv->wait_control );
292 vlc_mutex_destroy( &priv->lock_control );
296 * Get the item from an input thread
297 * FIXME it does not increase ref count of the item.
298 * if it is used after p_input is destroyed nothing prevent it from
299 * being freed.
301 input_item_t *input_GetItem( input_thread_t *p_input )
303 assert( p_input != NULL );
304 return input_priv(p_input)->p_item;
307 /*****************************************************************************
308 * This function creates a new input, and returns a pointer
309 * to its description. On error, it returns NULL.
311 * XXX Do not forget to update vlc_input.h if you add new variables.
312 *****************************************************************************/
313 static input_thread_t *Create( vlc_object_t *p_parent,
314 input_thread_events_cb events_cb, void *events_data,
315 input_item_t *p_item, const char *psz_header,
316 bool b_preparsing, bool b_thumbnailing,
317 input_resource_t *p_resource,
318 vlc_renderer_item_t *p_renderer )
320 /* Allocate descriptor */
321 input_thread_private_t *priv;
323 priv = vlc_custom_create( p_parent, sizeof( *priv ), "input" );
324 if( unlikely(priv == NULL) )
325 return NULL;
327 input_thread_t *p_input = &priv->input;
329 char * psz_name = input_item_GetName( p_item );
330 msg_Dbg( p_input, "Creating an input for %s'%s'",
331 b_preparsing ? "preparsing " : "", psz_name);
332 free( psz_name );
334 /* Parse input options */
335 input_item_ApplyOptions( VLC_OBJECT(p_input), p_item );
337 p_input->obj.header = psz_header ? strdup( psz_header ) : NULL;
339 /* Init Common fields */
340 priv->events_cb = events_cb;
341 priv->events_data = events_data;
342 priv->b_preparsing = b_preparsing;
343 priv->b_can_pace_control = true;
344 priv->i_start = 0;
345 priv->i_time = 0;
346 priv->i_stop = 0;
347 priv->i_title = 0;
348 priv->title = NULL;
349 priv->i_title_offset = input_priv(p_input)->i_seekpoint_offset = 0;
350 priv->i_state = INIT_S;
351 priv->is_running = false;
352 priv->is_stopped = false;
353 priv->b_recording = false;
354 priv->b_thumbnailing = b_thumbnailing;
355 priv->rate = 1.f;
356 memset( &priv->bookmark, 0, sizeof(priv->bookmark) );
357 TAB_INIT( priv->i_bookmark, priv->pp_bookmark );
358 TAB_INIT( priv->i_attachment, priv->attachment );
359 priv->attachment_demux = NULL;
360 priv->p_sout = NULL;
361 priv->b_out_pace_control = b_thumbnailing;
362 priv->p_renderer = p_renderer && b_preparsing == false ?
363 vlc_renderer_item_hold( p_renderer ) : NULL;
365 priv->viewpoint_changed = false;
366 /* Fetch the viewpoint from the mediaplayer or the playlist if any */
367 vlc_viewpoint_t *p_viewpoint = var_InheritAddress( p_input, "viewpoint" );
368 if (p_viewpoint != NULL)
369 priv->viewpoint = *p_viewpoint;
370 else
371 vlc_viewpoint_init( &priv->viewpoint );
373 priv->i_last_es_cat = UNKNOWN_ES;
375 input_item_Hold( p_item ); /* Released in Destructor() */
376 priv->p_item = p_item;
378 /* Init Input fields */
379 priv->master = NULL;
380 vlc_mutex_lock( &p_item->lock );
382 if( !p_item->p_stats )
383 p_item->p_stats = calloc( 1, sizeof(*p_item->p_stats) );
385 /* setup the preparse depth of the item
386 * if we are preparsing, use the i_preparse_depth of the parent item */
387 if( !priv->b_preparsing )
389 char *psz_rec = var_InheritString( p_parent, "recursive" );
391 if( psz_rec != NULL )
393 if ( !strcasecmp( psz_rec, "none" ) )
394 p_item->i_preparse_depth = 0;
395 else if ( !strcasecmp( psz_rec, "collapse" ) )
396 p_item->i_preparse_depth = 1;
397 else
398 p_item->i_preparse_depth = -1; /* default is expand */
399 free (psz_rec);
400 } else
401 p_item->i_preparse_depth = -1;
403 else
404 p_input->obj.flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT;
406 /* Make sure the interaction option is honored */
407 if( !var_InheritBool( p_input, "interact" ) )
408 p_input->obj.flags |= OBJECT_FLAGS_NOINTERACT;
409 else if( p_item->b_preparse_interact )
411 /* If true, this item was asked explicitly to interact with the user
412 * (via libvlc_MetadataRequest). Sub items created from this input won't
413 * have this flag and won't interact with the user */
414 p_input->obj.flags &= ~OBJECT_FLAGS_NOINTERACT;
417 vlc_mutex_unlock( &p_item->lock );
419 /* No slave */
420 priv->i_slave = 0;
421 priv->slave = NULL;
423 /* */
424 if( p_resource )
426 priv->p_resource_private = NULL;
427 priv->p_resource = input_resource_Hold( p_resource );
429 else
431 priv->p_resource_private = input_resource_New( VLC_OBJECT( p_input ) );
432 priv->p_resource = input_resource_Hold( priv->p_resource_private );
434 input_resource_SetInput( priv->p_resource, p_input );
436 /* Init control buffer */
437 vlc_mutex_init( &priv->lock_control );
438 vlc_cond_init( &priv->wait_control );
439 priv->i_control = 0;
440 vlc_interrupt_init(&priv->interrupt);
442 /* Create Object Variables for private use only */
443 input_ConfigVarInit( p_input );
445 priv->i_audio_delay =
446 VLC_TICK_FROM_MS( var_GetInteger( p_input, "audio-desync" ) );
447 priv->i_spu_delay = 0;
449 /* */
450 if( !priv->b_preparsing )
452 char *psz_bookmarks = var_GetNonEmptyString( p_input, "bookmarks" );
453 if( psz_bookmarks )
455 /* FIXME: have a common cfg parsing routine used by sout and others */
456 char *psz_parser, *psz_start, *psz_end;
457 psz_parser = psz_bookmarks;
458 while( (psz_start = strchr( psz_parser, '{' ) ) )
460 seekpoint_t *p_seekpoint;
461 char backup;
462 psz_start++;
463 psz_end = strchr( psz_start, '}' );
464 if( !psz_end ) break;
465 psz_parser = psz_end + 1;
466 backup = *psz_parser;
467 *psz_parser = 0;
468 *psz_end = ',';
470 p_seekpoint = vlc_seekpoint_New();
472 if( unlikely( p_seekpoint == NULL ) )
473 break;
475 while( (psz_end = strchr( psz_start, ',' ) ) )
477 *psz_end = 0;
478 if( !strncmp( psz_start, "name=", 5 ) )
480 free( p_seekpoint->psz_name );
482 p_seekpoint->psz_name = strdup(psz_start + 5);
484 else if( !strncmp( psz_start, "time=", 5 ) )
486 p_seekpoint->i_time_offset = vlc_tick_from_sec(atof(psz_start + 5));
488 psz_start = psz_end + 1;
490 msg_Dbg( p_input, "adding bookmark: %s, time=%"PRId64,
491 p_seekpoint->psz_name,
492 p_seekpoint->i_time_offset );
493 input_Control( p_input, INPUT_ADD_BOOKMARK, p_seekpoint );
494 vlc_seekpoint_Delete( p_seekpoint );
495 *psz_parser = backup;
497 free( psz_bookmarks );
501 /* Remove 'Now playing' info as it is probably outdated */
502 input_item_SetNowPlaying( p_item, NULL );
503 input_item_SetESNowPlaying( p_item, NULL );
505 /* */
506 if( !priv->b_preparsing && var_InheritBool( p_input, "stats" ) )
507 priv->stats = input_stats_Create();
508 else
509 priv->stats = NULL;
511 priv->p_es_out_display = input_EsOutNew( p_input, priv->rate );
512 priv->p_es_out = NULL;
514 /* Set the destructor when we are sure we are initialized */
515 vlc_object_set_destructor( p_input, input_Destructor );
517 return p_input;
520 /*****************************************************************************
521 * Run: main thread loop
522 * This is the "normal" thread that spawns the input processing chain,
523 * reads the stream, cleans up and waits
524 *****************************************************************************/
525 static void *Run( void *data )
527 input_thread_private_t *priv = data;
528 input_thread_t *p_input = &priv->input;
530 vlc_interrupt_set(&priv->interrupt);
532 if( !Init( p_input ) )
534 if( priv->b_can_pace_control && priv->b_out_pace_control )
536 /* We don't want a high input priority here or we'll
537 * end-up sucking up all the CPU time */
538 vlc_set_priority( priv->thread, VLC_THREAD_PRIORITY_LOW );
541 MainLoop( p_input, true ); /* FIXME it can be wrong (like with VLM) */
543 /* Clean up */
544 End( p_input );
547 input_SendEventDead( p_input );
548 return NULL;
551 static void *Preparse( void *data )
553 input_thread_private_t *priv = data;
554 input_thread_t *p_input = &priv->input;
556 vlc_interrupt_set(&priv->interrupt);
558 if( !Init( p_input ) )
559 { /* if the demux is a playlist, call Mainloop that will call
560 * demux_Demux in order to fetch sub items */
561 bool b_is_playlist = false;
563 if ( input_item_ShouldPreparseSubItems( priv->p_item )
564 && demux_Control( priv->master->p_demux, DEMUX_IS_PLAYLIST,
565 &b_is_playlist ) )
566 b_is_playlist = false;
567 if( b_is_playlist )
568 MainLoop( p_input, false );
569 End( p_input );
572 input_SendEventDead( p_input );
573 return NULL;
576 bool input_Stopped( input_thread_t *input )
578 input_thread_private_t *sys = input_priv(input);
579 bool ret;
581 vlc_mutex_lock( &sys->lock_control );
582 ret = sys->is_stopped;
583 vlc_mutex_unlock( &sys->lock_control );
584 return ret;
587 /*****************************************************************************
588 * Main loop: Fill buffers from access, and demux
589 *****************************************************************************/
592 * MainLoopDemux
593 * It asks the demuxer to demux some data
595 static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed )
597 input_thread_private_t* p_priv = input_priv(p_input);
598 demux_t *p_demux = p_priv->master->p_demux;
599 int i_ret = VLC_DEMUXER_SUCCESS;
601 *pb_changed = false;
603 if( p_priv->i_stop > 0 )
605 if( demux_Control( p_demux, DEMUX_GET_TIME, &p_priv->i_time ) )
606 p_priv->i_time = 0;
608 if( p_priv->i_stop <= p_priv->i_time )
609 i_ret = VLC_DEMUXER_EOF;
612 if( i_ret == VLC_DEMUXER_SUCCESS )
613 i_ret = demux_Demux( p_demux );
615 i_ret = i_ret > 0 ? VLC_DEMUXER_SUCCESS : ( i_ret < 0 ? VLC_DEMUXER_EGENERIC : VLC_DEMUXER_EOF);
617 if( i_ret == VLC_DEMUXER_SUCCESS )
619 if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_TITLE_LIST ) )
620 UpdateTitleListfromDemux( p_input );
622 if( p_priv->master->b_title_demux )
624 i_ret = UpdateTitleSeekpointFromDemux( p_input );
625 *pb_changed = true;
628 UpdateGenericFromDemux( p_input );
631 if( i_ret == VLC_DEMUXER_EOF )
633 msg_Dbg( p_input, "EOF reached" );
634 p_priv->master->b_eof = true;
635 es_out_Eos(p_priv->p_es_out);
637 else if( i_ret == VLC_DEMUXER_EGENERIC )
639 input_ChangeState( p_input, ERROR_S );
641 else if( p_priv->i_slave > 0 )
642 SlaveDemux( p_input );
645 static int MainLoopTryRepeat( input_thread_t *p_input )
647 int i_repeat = var_GetInteger( p_input, "input-repeat" );
648 if( i_repeat <= 0 )
649 return VLC_EGENERIC;
651 vlc_value_t val;
653 msg_Dbg( p_input, "repeating the same input (%d)", i_repeat );
654 if( i_repeat > 0 )
656 i_repeat--;
657 var_SetInteger( p_input, "input-repeat", i_repeat );
660 input_thread_private_t *priv = input_priv(p_input);
661 /* Seek to start title/seekpoint */
662 val.i_int = priv->master->i_title_start - priv->master->i_title_offset;
663 if( val.i_int < 0 || val.i_int >= priv->master->i_title )
664 val.i_int = 0;
665 input_ControlPushHelper( p_input,
666 INPUT_CONTROL_SET_TITLE, &val );
668 val.i_int = priv->master->i_seekpoint_start -
669 priv->master->i_seekpoint_offset;
670 if( val.i_int > 0 /* TODO: check upper boundary */ )
671 input_ControlPushHelper( p_input,
672 INPUT_CONTROL_SET_SEEKPOINT, &val );
674 /* Seek to start position */
675 if( priv->i_start > 0 )
676 input_SetTime( p_input, priv->i_start, false );
677 else
678 input_SetPosition( p_input, 0.0f, false );
680 return VLC_SUCCESS;
684 * Update timing infos and statistics.
686 static void MainLoopStatistics( input_thread_t *p_input )
688 input_thread_private_t *priv = input_priv(p_input);
689 double f_position = 0.0;
690 vlc_tick_t i_time = 0;
691 vlc_tick_t i_length = 0;
693 /* update input status variables */
694 if( demux_Control( priv->master->p_demux,
695 DEMUX_GET_POSITION, &f_position ) )
696 f_position = 0.0;
698 if( demux_Control( priv->master->p_demux, DEMUX_GET_TIME, &i_time ) )
699 i_time = 0;
700 input_priv(p_input)->i_time = i_time;
702 if( demux_Control( priv->master->p_demux, DEMUX_GET_LENGTH, &i_length ) )
703 i_length = 0;
705 es_out_SetTimes( priv->p_es_out, f_position, i_time, i_length );
707 struct input_stats_t new_stats;
708 if( priv->stats != NULL )
709 input_stats_Compute( priv->stats, &new_stats );
711 /* update current bookmark */
712 vlc_mutex_lock( &priv->p_item->lock );
713 priv->bookmark.i_time_offset = i_time;
714 if( priv->stats != NULL )
715 *priv->p_item->p_stats = new_stats;
716 vlc_mutex_unlock( &priv->p_item->lock );
718 input_SendEventStatistics( p_input, &new_stats );
722 * MainLoop
723 * The main input loop.
725 static void MainLoop( input_thread_t *p_input, bool b_interactive )
727 vlc_tick_t i_intf_update = 0;
728 vlc_tick_t i_last_seek_mdate = 0;
730 if( b_interactive && var_InheritBool( p_input, "start-paused" ) )
731 ControlPause( p_input, vlc_tick_now() );
733 bool b_pause_after_eof = b_interactive &&
734 var_InheritBool( p_input, "play-and-pause" );
735 bool b_paused_at_eof = false;
737 demux_t *p_demux = input_priv(p_input)->master->p_demux;
738 const bool b_can_demux = p_demux->pf_demux != NULL;
740 while( !input_Stopped( p_input ) && input_priv(p_input)->i_state != ERROR_S )
742 vlc_tick_t i_wakeup = -1;
743 bool b_paused = input_priv(p_input)->i_state == PAUSE_S;
744 /* FIXME if input_priv(p_input)->i_state == PAUSE_S the access/access_demux
745 * is paused -> this may cause problem with some of them
746 * The same problem can be seen when seeking while paused */
747 if( b_paused )
748 b_paused = !es_out_GetBuffering( input_priv(p_input)->p_es_out )
749 || input_priv(p_input)->master->b_eof;
751 if( !b_paused )
753 if( !input_priv(p_input)->master->b_eof )
755 bool b_force_update = false;
757 MainLoopDemux( p_input, &b_force_update );
759 if( b_can_demux )
760 i_wakeup = es_out_GetWakeup( input_priv(p_input)->p_es_out );
761 if( b_force_update )
762 i_intf_update = 0;
764 b_paused_at_eof = false;
766 else if( !es_out_GetEmpty( input_priv(p_input)->p_es_out ) )
768 msg_Dbg( p_input, "waiting decoder fifos to empty" );
769 i_wakeup = vlc_tick_now() + INPUT_IDLE_SLEEP;
771 /* Pause after eof only if the input is pausable.
772 * This way we won't trigger timeshifting for nothing */
773 else if( b_pause_after_eof && input_priv(p_input)->b_can_pause )
775 if( b_paused_at_eof )
776 break;
778 input_control_param_t param;
779 param.val.i_int = PAUSE_S;
781 msg_Dbg( p_input, "pausing at EOF (pause after each)");
782 Control( p_input, INPUT_CONTROL_SET_STATE, param );
784 b_paused = true;
785 b_paused_at_eof = true;
787 else
789 if( MainLoopTryRepeat( p_input ) )
790 break;
793 /* Update interface and statistics */
794 vlc_tick_t now = vlc_tick_now();
795 if( now >= i_intf_update )
797 MainLoopStatistics( p_input );
798 i_intf_update = now + VLC_TICK_FROM_MS(250);
802 /* Handle control */
803 for( ;; )
805 vlc_tick_t i_deadline = i_wakeup;
807 /* Postpone seeking until ES buffering is complete or at most
808 * 125 ms. */
809 bool b_postpone = es_out_GetBuffering( input_priv(p_input)->p_es_out )
810 && !input_priv(p_input)->master->b_eof;
811 if( b_postpone )
813 vlc_tick_t now = vlc_tick_now();
815 /* Recheck ES buffer level every 20 ms when seeking */
816 if( now < i_last_seek_mdate + VLC_TICK_FROM_MS(125)
817 && (i_deadline < 0 || i_deadline > now + VLC_TICK_FROM_MS(20)) )
818 i_deadline = now + VLC_TICK_FROM_MS(20);
819 else
820 b_postpone = false;
823 int i_type;
824 input_control_param_t param;
826 if( ControlPop( p_input, &i_type, &param, i_deadline, b_postpone ) )
828 if( b_postpone )
829 continue;
830 break; /* Wake-up time reached */
833 #ifndef NDEBUG
834 msg_Dbg( p_input, "control type=%d", i_type );
835 #endif
836 if( Control( p_input, i_type, param ) )
838 if( ControlIsSeekRequest( i_type ) )
839 i_last_seek_mdate = vlc_tick_now();
840 i_intf_update = 0;
843 /* Update the wakeup time */
844 if( i_wakeup != 0 )
845 i_wakeup = es_out_GetWakeup( input_priv(p_input)->p_es_out );
850 #ifdef ENABLE_SOUT
851 static int InitSout( input_thread_t * p_input )
853 input_thread_private_t *priv = input_priv(p_input);
855 if( priv->b_preparsing )
856 return VLC_SUCCESS;
858 /* Find a usable sout and attach it to p_input */
859 char *psz = var_GetNonEmptyString( p_input, "sout" );
860 if( priv->p_renderer )
862 /* Keep sout if it comes from a renderer and if the user didn't touch
863 * the sout config */
864 bool keep_sout = psz == NULL;
865 free(psz);
867 const char *psz_renderer_sout = vlc_renderer_item_sout( priv->p_renderer );
868 if( asprintf( &psz, "#%s", psz_renderer_sout ) < 0 )
869 return VLC_ENOMEM;
870 if( keep_sout )
871 var_SetBool( p_input, "sout-keep", true );
873 if( psz && strncasecmp( priv->p_item->psz_uri, "vlc:", 4 ) )
875 priv->p_sout = input_resource_RequestSout( priv->p_resource, NULL, psz );
876 if( priv->p_sout == NULL )
878 input_ChangeState( p_input, ERROR_S );
879 msg_Err( p_input, "cannot start stream output instance, " \
880 "aborting" );
881 free( psz );
882 return VLC_EGENERIC;
885 else
887 input_resource_RequestSout( priv->p_resource, NULL, NULL );
889 free( psz );
891 return VLC_SUCCESS;
893 #endif
895 static void InitTitle( input_thread_t * p_input, bool had_titles )
897 input_thread_private_t *priv = input_priv(p_input);
898 input_source_t *p_master = priv->master;
900 if( priv->b_preparsing )
901 return;
903 vlc_mutex_lock( &priv->p_item->lock );
904 /* Create global title (from master) */
905 priv->i_title = p_master->i_title;
906 priv->title = p_master->title;
907 priv->i_title_offset = p_master->i_title_offset;
908 priv->i_seekpoint_offset = p_master->i_seekpoint_offset;
910 /* Global flag */
911 priv->b_can_pace_control = p_master->b_can_pace_control;
912 priv->b_can_pause = p_master->b_can_pause;
913 priv->b_can_rate_control = p_master->b_can_rate_control;
914 vlc_mutex_unlock( &priv->p_item->lock );
916 /* Send event only if the count is valid or if titles are gone */
917 if (had_titles || p_master->i_title > 0)
918 input_SendEventTitle( p_input, &(struct vlc_input_event_title) {
919 .action = VLC_INPUT_TITLE_NEW_LIST,
920 .list = {
921 .array = p_master->title,
922 .count = p_master->i_title,
927 static void StartTitle( input_thread_t * p_input )
929 input_thread_private_t *priv = input_priv(p_input);
930 vlc_value_t val;
932 /* Start title/chapter */
933 val.i_int = priv->master->i_title_start - priv->master->i_title_offset;
934 if( val.i_int > 0 && val.i_int < priv->master->i_title )
935 input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE, &val );
937 val.i_int = priv->master->i_seekpoint_start -
938 priv->master->i_seekpoint_offset;
939 if( val.i_int > 0 /* TODO: check upper boundary */ )
940 input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val );
942 /* Start/stop/run time */
943 priv->i_start = llroundf((float)CLOCK_FREQ
944 * var_GetFloat( p_input, "start-time" ));
945 priv->i_stop = llroundf((float)CLOCK_FREQ
946 * var_GetFloat( p_input, "stop-time" ));
947 if( priv->i_stop <= 0 )
949 priv->i_stop = llroundf((float)CLOCK_FREQ
950 * var_GetFloat( p_input, "run-time" ));
951 if( priv->i_stop < 0 )
953 msg_Warn( p_input, "invalid run-time ignored" );
954 priv->i_stop = 0;
956 else
957 priv->i_stop += priv->i_start;
960 if( priv->i_start > 0 )
962 msg_Dbg( p_input, "starting at time: %"PRId64"s",
963 SEC_FROM_VLC_TICK(priv->i_start) );
965 input_SetTime( p_input, priv->i_start, false );
967 if( priv->i_stop > 0 && priv->i_stop <= priv->i_start )
969 msg_Warn( p_input, "invalid stop-time ignored" );
970 priv->i_stop = 0;
974 static int SlaveCompare(const void *a, const void *b)
976 const input_item_slave_t *p_slave0 = *((const input_item_slave_t **) a);
977 const input_item_slave_t *p_slave1 = *((const input_item_slave_t **) b);
979 if( p_slave0 == NULL || p_slave1 == NULL )
981 /* Put NULL (or rejected) subs at the end */
982 return p_slave0 == NULL ? 1 : p_slave1 == NULL ? -1 : 0;
985 if( p_slave0->i_priority > p_slave1->i_priority )
986 return -1;
988 if( p_slave0->i_priority < p_slave1->i_priority )
989 return 1;
991 return 0;
994 static bool SlaveExists( input_item_slave_t **pp_slaves, int i_slaves,
995 const char *psz_uri)
997 for( int i = 0; i < i_slaves; i++ )
999 if( pp_slaves[i] != NULL
1000 && !strcmp( pp_slaves[i]->psz_uri, psz_uri ) )
1001 return true;
1003 return false;
1006 static void RequestSubRate( input_thread_t *p_input, float f_slave_fps )
1008 input_thread_private_t *priv = input_priv(p_input);
1009 const float f_fps = input_priv(p_input)->master->f_fps;
1010 if( f_fps > 1.f && f_slave_fps > 1.f )
1011 priv->slave_subs_rate = f_fps / f_slave_fps;
1012 else if ( priv->slave_subs_rate != 0 )
1013 priv->slave_subs_rate = 1.f;
1016 static void SetSubtitlesOptions( input_thread_t *p_input )
1018 input_thread_private_t *priv = input_priv(p_input);
1020 /* Get fps and set it if not already set */
1021 const float f_fps = input_priv(p_input)->master->f_fps;
1022 if( f_fps > 1.f )
1024 var_SetFloat( p_input, "sub-original-fps", f_fps );
1025 RequestSubRate( p_input, var_InheritFloat( p_input, "sub-fps" ) );
1028 int64_t sub_delay = var_InheritInteger( p_input, "sub-delay" );
1029 if( sub_delay != 0 )
1031 priv->i_spu_delay = vlc_tick_from_samples(sub_delay, 10);
1032 input_SendEventSubtitleDelay( p_input, priv->i_spu_delay );
1033 /* UpdatePtsDelay will be called next by InitPrograms */
1037 static void GetVarSlaves( input_thread_t *p_input,
1038 input_item_slave_t ***ppp_slaves, int *p_slaves )
1040 char *psz = var_GetNonEmptyString( p_input, "input-slave" );
1041 if( !psz )
1042 return;
1044 input_item_slave_t **pp_slaves = *ppp_slaves;
1045 int i_slaves = *p_slaves;
1047 char *psz_org = psz;
1048 while( psz && *psz )
1050 while( *psz == ' ' || *psz == '#' )
1051 psz++;
1053 char *psz_delim = strchr( psz, '#' );
1054 if( psz_delim )
1055 *psz_delim++ = '\0';
1057 if( *psz == 0 )
1058 break;
1060 char *uri = strstr(psz, "://")
1061 ? strdup( psz ) : vlc_path2uri( psz, NULL );
1062 psz = psz_delim;
1063 if( uri == NULL )
1064 continue;
1066 input_item_slave_t *p_slave =
1067 input_item_slave_New( uri, SLAVE_TYPE_AUDIO, SLAVE_PRIORITY_USER );
1068 free( uri );
1070 if( unlikely( p_slave == NULL ) )
1071 break;
1072 TAB_APPEND(i_slaves, pp_slaves, p_slave);
1074 free( psz_org );
1076 *ppp_slaves = pp_slaves; /* in case of realloc */
1077 *p_slaves = i_slaves;
1080 static void LoadSlaves( input_thread_t *p_input )
1082 input_item_slave_t **pp_slaves;
1083 int i_slaves;
1084 TAB_INIT( i_slaves, pp_slaves );
1086 /* Look for and add slaves */
1088 char *psz_subtitle = var_GetNonEmptyString( p_input, "sub-file" );
1089 if( psz_subtitle != NULL )
1091 msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle );
1092 char *psz_uri = input_SubtitleFile2Uri( p_input, psz_subtitle );
1093 free( psz_subtitle );
1094 psz_subtitle = NULL;
1095 if( psz_uri != NULL )
1097 input_item_slave_t *p_slave =
1098 input_item_slave_New( psz_uri, SLAVE_TYPE_SPU,
1099 SLAVE_PRIORITY_USER );
1100 free( psz_uri );
1101 if( p_slave )
1103 TAB_APPEND(i_slaves, pp_slaves, p_slave);
1104 psz_subtitle = p_slave->psz_uri;
1109 if( var_GetBool( p_input, "sub-autodetect-file" ) )
1111 /* Add local subtitles */
1112 char *psz_autopath = var_GetNonEmptyString( p_input, "sub-autodetect-path" );
1114 if( subtitles_Detect( p_input, psz_autopath, input_priv(p_input)->p_item->psz_uri,
1115 &pp_slaves, &i_slaves ) == VLC_SUCCESS )
1117 /* check that we did not add the subtitle through sub-file */
1118 if( psz_subtitle != NULL )
1120 for( int i = 1; i < i_slaves; i++ )
1122 input_item_slave_t *p_curr = pp_slaves[i];
1123 if( p_curr != NULL
1124 && !strcmp( psz_subtitle, p_curr->psz_uri ) )
1126 /* reject current sub */
1127 input_item_slave_Delete( p_curr );
1128 pp_slaves[i] = NULL;
1133 free( psz_autopath );
1136 /* Add slaves found by the directory demuxer or via libvlc */
1137 input_item_t *p_item = input_priv(p_input)->p_item;
1138 vlc_mutex_lock( &p_item->lock );
1140 /* Move item slaves to local pp_slaves */
1141 for( int i = 0; i < p_item->i_slaves; i++ )
1143 input_item_slave_t *p_slave = p_item->pp_slaves[i];
1144 if( !SlaveExists( pp_slaves, i_slaves, p_slave->psz_uri ) )
1145 TAB_APPEND(i_slaves, pp_slaves, p_slave);
1146 else
1147 input_item_slave_Delete( p_slave );
1149 /* Slaves that are successfully loaded will be added back to the item */
1150 TAB_CLEAN( p_item->i_slaves, p_item->pp_slaves );
1151 vlc_mutex_unlock( &p_item->lock );
1153 /* Add slaves from the "input-slave" option */
1154 GetVarSlaves( p_input, &pp_slaves, &i_slaves );
1156 if( i_slaves > 0 )
1157 qsort( pp_slaves, i_slaves, sizeof (input_item_slave_t*),
1158 SlaveCompare );
1160 /* add all detected slaves */
1161 bool p_forced[2] = { false, false };
1162 static_assert( SLAVE_TYPE_AUDIO <= 1 && SLAVE_TYPE_SPU <= 1,
1163 "slave type size mismatch");
1164 for( int i = 0; i < i_slaves && pp_slaves[i] != NULL; i++ )
1166 input_item_slave_t *p_slave = pp_slaves[i];
1167 /* Slaves added via options should not fail */
1168 unsigned i_flags = p_slave->i_priority != SLAVE_PRIORITY_USER
1169 ? SLAVE_ADD_CANFAIL : SLAVE_ADD_NOFLAG;
1170 bool b_forced = false;
1172 /* Force the first subtitle with the highest priority or with the
1173 * forced flag */
1174 if( !p_forced[p_slave->i_type]
1175 && ( p_slave->b_forced || p_slave->i_priority == SLAVE_PRIORITY_USER ) )
1177 i_flags |= SLAVE_ADD_FORCED;
1178 b_forced = true;
1181 if( input_SlaveSourceAdd( p_input, p_slave->i_type, p_slave->psz_uri,
1182 i_flags ) == VLC_SUCCESS )
1184 input_item_AddSlave( input_priv(p_input)->p_item, p_slave );
1185 if( b_forced )
1186 p_forced[p_slave->i_type] = true;
1188 else
1189 input_item_slave_Delete( p_slave );
1191 TAB_CLEAN( i_slaves, pp_slaves );
1193 /* Load subtitles from attachments */
1194 int i_attachment = 0;
1195 input_attachment_t **pp_attachment = NULL;
1197 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1198 for( int i = 0; i < input_priv(p_input)->i_attachment; i++ )
1200 const input_attachment_t *a = input_priv(p_input)->attachment[i];
1201 if( !strcmp( a->psz_mime, "application/x-srt" ) )
1202 TAB_APPEND( i_attachment, pp_attachment,
1203 vlc_input_attachment_New( a->psz_name, NULL,
1204 a->psz_description, NULL, 0 ) );
1206 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1208 if( i_attachment > 0 )
1209 var_Create( p_input, "sub-description", VLC_VAR_STRING );
1210 for( int i = 0; i < i_attachment; i++ )
1212 input_attachment_t *a = pp_attachment[i];
1213 if( !a )
1214 continue;
1215 char *psz_mrl;
1216 if( a->psz_name[0] &&
1217 asprintf( &psz_mrl, "attachment://%s", a->psz_name ) >= 0 )
1219 var_SetString( p_input, "sub-description", a->psz_description ? a->psz_description : "");
1221 /* Force the first subtitle from attachment if there is no
1222 * subtitles already forced */
1223 if( input_SlaveSourceAdd( p_input, SLAVE_TYPE_SPU, psz_mrl,
1224 p_forced[ SLAVE_TYPE_SPU ] ?
1225 SLAVE_ADD_NOFLAG : SLAVE_ADD_FORCED ) == VLC_SUCCESS )
1226 p_forced[ SLAVE_TYPE_SPU ] = true;
1228 free( psz_mrl );
1229 /* Don't update item slaves for attachements */
1231 vlc_input_attachment_Delete( a );
1233 free( pp_attachment );
1234 if( i_attachment > 0 )
1235 var_Destroy( p_input, "sub-description" );
1238 static void UpdatePtsDelay( input_thread_t *p_input )
1240 input_thread_private_t *p_sys = input_priv(p_input);
1242 /* Get max pts delay from input source */
1243 vlc_tick_t i_pts_delay = p_sys->master->i_pts_delay;
1244 for( int i = 0; i < p_sys->i_slave; i++ )
1245 i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );
1247 if( i_pts_delay < 0 )
1248 i_pts_delay = 0;
1250 /* Take care of audio/spu delay */
1251 const vlc_tick_t i_extra_delay = __MIN( p_sys->i_audio_delay, p_sys->i_spu_delay );
1252 if( i_extra_delay < 0 )
1253 i_pts_delay -= i_extra_delay;
1255 /* Update cr_average depending on the caching */
1256 const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
1258 /* */
1259 es_out_SetDelay( input_priv(p_input)->p_es_out_display, AUDIO_ES,
1260 p_sys->i_audio_delay );
1261 es_out_SetDelay( input_priv(p_input)->p_es_out_display, SPU_ES,
1262 p_sys->i_spu_delay );
1263 es_out_SetJitter( input_priv(p_input)->p_es_out, i_pts_delay, 0, i_cr_average );
1266 static void InitPrograms( input_thread_t * p_input )
1268 int i_es_out_mode;
1269 int *tab;
1270 size_t count;
1272 /* Compute correct pts_delay */
1273 UpdatePtsDelay( p_input );
1275 /* Set up es_out */
1276 i_es_out_mode = ES_OUT_MODE_AUTO;
1277 if( input_priv(p_input)->p_sout && !input_priv(p_input)->p_renderer )
1279 char *prgms;
1281 if( (prgms = var_GetNonEmptyString( p_input, "programs" )) != NULL )
1283 char *buf;
1285 TAB_INIT(count, tab);
1286 for( const char *prgm = strtok_r( prgms, ",", &buf );
1287 prgm != NULL;
1288 prgm = strtok_r( NULL, ",", &buf ) )
1290 TAB_APPEND(count, tab, atoi(prgm));
1293 if( count > 0 )
1294 i_es_out_mode = ES_OUT_MODE_PARTIAL;
1295 /* Note : we should remove the "program" callback. */
1297 free( prgms );
1299 else if( var_GetBool( p_input, "sout-all" ) )
1301 i_es_out_mode = ES_OUT_MODE_ALL;
1304 es_out_SetMode( input_priv(p_input)->p_es_out, i_es_out_mode );
1306 /* Inform the demuxer about waited group (needed only for DVB) */
1307 if( i_es_out_mode == ES_OUT_MODE_ALL )
1309 demux_Control( input_priv(p_input)->master->p_demux,
1310 DEMUX_SET_GROUP_ALL );
1312 else if( i_es_out_mode == ES_OUT_MODE_PARTIAL )
1314 demux_Control( input_priv(p_input)->master->p_demux,
1315 DEMUX_SET_GROUP_LIST, count, tab );
1316 free(tab);
1318 else
1320 int program = es_out_GetGroupForced( input_priv(p_input)->p_es_out );
1321 if( program == 0 )
1322 demux_Control( input_priv(p_input)->master->p_demux,
1323 DEMUX_SET_GROUP_DEFAULT );
1324 else
1325 demux_Control( input_priv(p_input)->master->p_demux,
1326 DEMUX_SET_GROUP_LIST, (size_t)1,
1327 (const int *)&program );
1331 static int Init( input_thread_t * p_input )
1333 input_thread_private_t *priv = input_priv(p_input);
1334 input_source_t *master;
1336 /* */
1337 input_ChangeState( p_input, OPENING_S );
1338 input_SendEventCache( p_input, 0.0 );
1340 if( var_Type( p_input->obj.parent, "meta-file" ) )
1342 msg_Dbg( p_input, "Input is a meta file: disabling unneeded options" );
1343 var_SetString( p_input, "sout", "" );
1344 var_SetBool( p_input, "sout-all", false );
1345 var_SetString( p_input, "input-slave", "" );
1346 var_SetInteger( p_input, "input-repeat", 0 );
1347 var_SetString( p_input, "sub-file", "" );
1348 var_SetBool( p_input, "sub-autodetect-file", false );
1351 #ifdef ENABLE_SOUT
1352 if( InitSout( p_input ) )
1353 goto error;
1354 #endif
1356 /* Create es out */
1357 priv->p_es_out = input_EsOutTimeshiftNew( p_input, priv->p_es_out_display, priv->rate );
1358 if( priv->p_es_out == NULL )
1359 goto error;
1361 /* */
1362 master = InputSourceNew( p_input, priv->p_item->psz_uri, NULL, false );
1363 if( master == NULL )
1364 goto error;
1365 priv->master = master;
1367 InitTitle( p_input, false );
1369 /* Load master infos */
1370 /* Init length */
1371 vlc_tick_t i_length;
1372 if( demux_Control( master->p_demux, DEMUX_GET_LENGTH, &i_length ) )
1373 i_length = 0;
1374 if( i_length <= 0 )
1375 i_length = input_item_GetDuration( priv->p_item );
1376 input_SendEventLength( p_input, i_length );
1378 input_SendEventPosition( p_input, 0.0, 0 );
1380 if( !priv->b_preparsing )
1382 StartTitle( p_input );
1383 SetSubtitlesOptions( p_input );
1384 LoadSlaves( p_input );
1385 InitPrograms( p_input );
1387 double f_rate = var_GetFloat( p_input, "rate" );
1388 if( f_rate != 0.0 && f_rate != 1.0 )
1390 vlc_value_t val = { .f_float = f_rate };
1391 input_ControlPushHelper( p_input, INPUT_CONTROL_SET_RATE, &val );
1395 if( !priv->b_preparsing && priv->p_sout )
1397 priv->b_out_pace_control = priv->p_sout->i_out_pace_nocontrol > 0;
1399 msg_Dbg( p_input, "starting in %ssync mode",
1400 priv->b_out_pace_control ? "a" : "" );
1403 vlc_meta_t *p_meta = vlc_meta_New();
1404 if( p_meta != NULL )
1406 /* Get meta data from users */
1407 InputMetaUser( p_input, p_meta );
1409 /* Get meta data from master input */
1410 InputSourceMeta( p_input, master, p_meta );
1412 /* And from slave */
1413 for( int i = 0; i < priv->i_slave; i++ )
1414 InputSourceMeta( p_input, priv->slave[i], p_meta );
1416 es_out_ControlSetMeta( priv->p_es_out, p_meta );
1417 vlc_meta_Delete( p_meta );
1420 msg_Dbg( p_input, "`%s' successfully opened",
1421 input_priv(p_input)->p_item->psz_uri );
1423 /* initialization is complete */
1424 input_ChangeState( p_input, PLAYING_S );
1426 return VLC_SUCCESS;
1428 error:
1429 input_ChangeState( p_input, ERROR_S );
1431 if( input_priv(p_input)->p_es_out )
1432 es_out_Delete( input_priv(p_input)->p_es_out );
1433 es_out_SetMode( input_priv(p_input)->p_es_out_display, ES_OUT_MODE_END );
1434 if( input_priv(p_input)->p_resource )
1436 if( input_priv(p_input)->p_sout )
1437 input_resource_RequestSout( input_priv(p_input)->p_resource,
1438 input_priv(p_input)->p_sout, NULL );
1439 input_resource_SetInput( input_priv(p_input)->p_resource, NULL );
1440 if( input_priv(p_input)->p_resource_private )
1441 input_resource_Terminate( input_priv(p_input)->p_resource_private );
1444 /* Mark them deleted */
1445 input_priv(p_input)->p_es_out = NULL;
1446 input_priv(p_input)->p_sout = NULL;
1448 return VLC_EGENERIC;
1451 /*****************************************************************************
1452 * End: end the input thread
1453 *****************************************************************************/
1454 static void End( input_thread_t * p_input )
1456 input_thread_private_t *priv = input_priv(p_input);
1458 /* We are at the end */
1459 input_ChangeState( p_input, END_S );
1461 /* Stop es out activity */
1462 es_out_SetMode( priv->p_es_out, ES_OUT_MODE_NONE );
1464 /* Delete slave */
1465 for( int i = 0; i < priv->i_slave; i++ )
1466 InputSourceDestroy( priv->slave[i] );
1467 free( priv->slave );
1469 /* Clean up master */
1470 InputSourceDestroy( priv->master );
1471 priv->i_title = 0;
1472 priv->title = NULL;
1473 priv->i_title_offset = 0;
1474 priv->i_seekpoint_offset = 0;
1476 /* Unload all modules */
1477 if( priv->p_es_out )
1478 es_out_Delete( priv->p_es_out );
1479 es_out_SetMode( priv->p_es_out_display, ES_OUT_MODE_END );
1481 if( priv->stats != NULL )
1483 input_item_t *item = priv->p_item;
1484 /* make sure we are up to date */
1485 vlc_mutex_lock( &item->lock );
1486 input_stats_Compute( priv->stats, item->p_stats );
1487 vlc_mutex_unlock( &item->lock );
1490 vlc_mutex_lock( &priv->p_item->lock );
1491 if( priv->i_attachment > 0 )
1493 for( int i = 0; i < priv->i_attachment; i++ )
1494 vlc_input_attachment_Delete( priv->attachment[i] );
1495 TAB_CLEAN( priv->i_attachment, priv->attachment );
1496 free( priv->attachment_demux);
1497 priv->attachment_demux = NULL;
1500 /* clean bookmarks */
1501 for( int i = 0; i < priv->i_bookmark; ++i )
1502 vlc_seekpoint_Delete( priv->pp_bookmark[i] );
1503 TAB_CLEAN( priv->i_bookmark, priv->pp_bookmark );
1505 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1507 /* */
1508 input_resource_RequestSout( input_priv(p_input)->p_resource,
1509 input_priv(p_input)->p_sout, NULL );
1510 input_resource_SetInput( input_priv(p_input)->p_resource, NULL );
1511 if( input_priv(p_input)->p_resource_private )
1512 input_resource_Terminate( input_priv(p_input)->p_resource_private );
1515 /*****************************************************************************
1516 * Control
1517 *****************************************************************************/
1518 void input_ControlPush( input_thread_t *p_input,
1519 int i_type, const input_control_param_t *p_param )
1521 input_thread_private_t *sys = input_priv(p_input);
1523 vlc_mutex_lock( &sys->lock_control );
1524 if( sys->is_stopped || sys->i_control >= INPUT_CONTROL_FIFO_SIZE )
1526 if( sys->is_stopped )
1527 msg_Dbg( p_input, "input control stopped, trashing type=%d",
1528 i_type );
1529 else
1530 msg_Err( p_input, "input control fifo overflow, trashing type=%d",
1531 i_type );
1532 if( p_param )
1533 ControlRelease( i_type, p_param );
1535 else
1537 input_control_t c;
1538 c.i_type = i_type;
1539 if( p_param )
1540 c.param = *p_param;
1541 else
1542 memset( &c.param, 0, sizeof(c.param) );
1544 sys->control[sys->i_control++] = c;
1546 vlc_cond_signal( &sys->wait_control );
1548 vlc_mutex_unlock( &sys->lock_control );
1551 static size_t ControlGetReducedIndexLocked( input_thread_t *p_input )
1553 const int i_lt = input_priv(p_input)->control[0].i_type;
1554 size_t i;
1555 for( i = 1; i < input_priv(p_input)->i_control; i++ )
1557 const int i_ct = input_priv(p_input)->control[i].i_type;
1559 if( i_lt == i_ct &&
1560 ( i_ct == INPUT_CONTROL_SET_STATE ||
1561 i_ct == INPUT_CONTROL_SET_RATE ||
1562 i_ct == INPUT_CONTROL_SET_POSITION ||
1563 i_ct == INPUT_CONTROL_SET_TIME ||
1564 i_ct == INPUT_CONTROL_SET_PROGRAM ||
1565 i_ct == INPUT_CONTROL_SET_TITLE ||
1566 i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
1567 i_ct == INPUT_CONTROL_SET_BOOKMARK ) )
1569 continue;
1571 else
1573 /* TODO but that's not that important
1574 - merge SET_X with SET_X_CMD
1575 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1576 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1579 break;
1582 return i - 1;
1586 static inline int ControlPop( input_thread_t *p_input,
1587 int *pi_type, input_control_param_t *p_param,
1588 vlc_tick_t i_deadline, bool b_postpone_seek )
1590 input_thread_private_t *p_sys = input_priv(p_input);
1592 vlc_mutex_lock( &p_sys->lock_control );
1593 while( p_sys->i_control <= 0 ||
1594 ( b_postpone_seek && ControlIsSeekRequest( p_sys->control[0].i_type ) ) )
1596 if( p_sys->is_stopped )
1598 vlc_mutex_unlock( &p_sys->lock_control );
1599 return VLC_EGENERIC;
1602 if( i_deadline >= 0 )
1604 if( vlc_cond_timedwait( &p_sys->wait_control, &p_sys->lock_control,
1605 i_deadline ) )
1607 vlc_mutex_unlock( &p_sys->lock_control );
1608 return VLC_EGENERIC;
1611 else
1612 vlc_cond_wait( &p_sys->wait_control, &p_sys->lock_control );
1615 /* */
1616 const size_t i_index = ControlGetReducedIndexLocked( p_input );
1618 for( size_t i = 0; i < i_index; ++i )
1620 /* Release Reduced controls */
1621 ControlRelease( p_sys->control[i].i_type, &p_sys->control[i].param );
1624 /* */
1625 *pi_type = p_sys->control[i_index].i_type;
1626 *p_param = p_sys->control[i_index].param;
1628 p_sys->i_control -= i_index + 1;
1629 if( p_sys->i_control > 0 )
1630 memmove( &p_sys->control[0], &p_sys->control[i_index+1],
1631 sizeof(*p_sys->control) * p_sys->i_control );
1632 vlc_mutex_unlock( &p_sys->lock_control );
1634 return VLC_SUCCESS;
1636 static bool ControlIsSeekRequest( int i_type )
1638 switch( i_type )
1640 case INPUT_CONTROL_SET_POSITION:
1641 case INPUT_CONTROL_JUMP_POSITION:
1642 case INPUT_CONTROL_SET_TIME:
1643 case INPUT_CONTROL_JUMP_TIME:
1644 case INPUT_CONTROL_SET_TITLE:
1645 case INPUT_CONTROL_SET_TITLE_NEXT:
1646 case INPUT_CONTROL_SET_TITLE_PREV:
1647 case INPUT_CONTROL_SET_SEEKPOINT:
1648 case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
1649 case INPUT_CONTROL_SET_SEEKPOINT_PREV:
1650 case INPUT_CONTROL_SET_BOOKMARK:
1651 case INPUT_CONTROL_NAV_ACTIVATE:
1652 case INPUT_CONTROL_NAV_UP:
1653 case INPUT_CONTROL_NAV_DOWN:
1654 case INPUT_CONTROL_NAV_LEFT:
1655 case INPUT_CONTROL_NAV_RIGHT:
1656 case INPUT_CONTROL_NAV_POPUP:
1657 case INPUT_CONTROL_NAV_MENU:
1658 return true;
1659 default:
1660 return false;
1664 static void ControlRelease( int i_type, const input_control_param_t *p_param )
1666 if( p_param == NULL )
1667 return;
1669 switch( i_type )
1671 case INPUT_CONTROL_ADD_SLAVE:
1672 if( p_param->val.p_address )
1673 input_item_slave_Delete( p_param->val.p_address );
1674 break;
1675 case INPUT_CONTROL_SET_RENDERER:
1676 if( p_param->val.p_address )
1677 vlc_renderer_item_release( p_param->val.p_address );
1678 break;
1679 case INPUT_CONTROL_SET_ES:
1680 case INPUT_CONTROL_UNSET_ES:
1681 case INPUT_CONTROL_RESTART_ES:
1682 vlc_es_id_Release( p_param->id );
1683 break;
1685 default:
1686 break;
1690 /* Pause input */
1691 static void ControlPause( input_thread_t *p_input, vlc_tick_t i_control_date )
1693 int i_state = PAUSE_S;
1695 if( input_priv(p_input)->b_can_pause )
1697 demux_t *p_demux = input_priv(p_input)->master->p_demux;
1699 if( demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, true ) )
1701 msg_Warn( p_input, "cannot set pause state" );
1702 return;
1706 /* */
1707 if( es_out_SetPauseState( input_priv(p_input)->p_es_out, input_priv(p_input)->b_can_pause,
1708 true, i_control_date ) )
1710 msg_Warn( p_input, "cannot set pause state at es_out level" );
1711 return;
1714 /* Switch to new state */
1715 input_ChangeState( p_input, i_state );
1718 static void ControlUnpause( input_thread_t *p_input, vlc_tick_t i_control_date )
1720 if( input_priv(p_input)->b_can_pause )
1722 demux_t *p_demux = input_priv(p_input)->master->p_demux;
1724 if( demux_Control( p_demux, DEMUX_SET_PAUSE_STATE, false ) )
1726 msg_Err( p_input, "cannot resume" );
1727 input_ChangeState( p_input, ERROR_S );
1728 return;
1732 /* Switch to play */
1733 input_ChangeState( p_input, PLAYING_S );
1734 es_out_SetPauseState( input_priv(p_input)->p_es_out, false, false, i_control_date );
1737 static void ViewpointApply( input_thread_t *p_input )
1739 input_thread_private_t *priv = input_priv(p_input);
1741 vlc_viewpoint_clip( &priv->viewpoint );
1743 vout_thread_t **pp_vout;
1744 size_t i_vout;
1745 input_resource_HoldVouts( priv->p_resource, &pp_vout, &i_vout );
1747 for( size_t i = 0; i < i_vout; ++i )
1749 var_SetAddress( pp_vout[i], "viewpoint", &priv->viewpoint );
1750 /* This variable can only be read from callbacks */
1751 var_Change( pp_vout[i], "viewpoint", VLC_VAR_SETVALUE,
1752 (vlc_value_t) { .p_address = NULL } );
1753 vlc_object_release( pp_vout[i] );
1755 free( pp_vout );
1757 audio_output_t *p_aout = input_resource_HoldAout( priv->p_resource );
1758 if( p_aout )
1761 var_SetAddress( p_aout, "viewpoint", &priv->viewpoint );
1762 /* This variable can only be read from callbacks */
1763 var_Change( p_aout, "viewpoint", VLC_VAR_SETVALUE,
1764 (vlc_value_t) { .p_address = NULL } );
1765 vlc_object_release( p_aout );
1769 static void ControlNav( input_thread_t *p_input, int i_type )
1771 input_thread_private_t *priv = input_priv(p_input);
1773 if( !demux_Control( priv->master->p_demux, i_type
1774 - INPUT_CONTROL_NAV_ACTIVATE + DEMUX_NAV_ACTIVATE ) )
1775 return; /* The demux handled the navigation control */
1777 /* Handle Up/Down/Left/Right if the demux can't navigate */
1778 vlc_viewpoint_t vp = {0};
1779 int vol_direction = 0;
1780 int seek_direction = 0;
1781 switch( i_type )
1783 case INPUT_CONTROL_NAV_UP:
1784 vol_direction = 1;
1785 vp.pitch = -1.f;
1786 break;
1787 case INPUT_CONTROL_NAV_DOWN:
1788 vol_direction = -1;
1789 vp.pitch = 1.f;
1790 break;
1791 case INPUT_CONTROL_NAV_LEFT:
1792 seek_direction = -1;
1793 vp.yaw = -1.f;
1794 break;
1795 case INPUT_CONTROL_NAV_RIGHT:
1796 seek_direction = 1;
1797 vp.yaw = 1.f;
1798 break;
1799 case INPUT_CONTROL_NAV_ACTIVATE:
1800 case INPUT_CONTROL_NAV_POPUP:
1801 case INPUT_CONTROL_NAV_MENU:
1802 return;
1803 default:
1804 vlc_assert_unreachable();
1807 /* Try to change the viewpoint if possible */
1808 vout_thread_t **pp_vout;
1809 size_t i_vout;
1810 bool b_viewpoint_ch = false;
1811 input_resource_HoldVouts( priv->p_resource, &pp_vout, &i_vout );
1812 for( size_t i = 0; i < i_vout; ++i )
1814 if( !b_viewpoint_ch
1815 && var_GetBool( pp_vout[i], "viewpoint-changeable" ) )
1816 b_viewpoint_ch = true;
1817 vlc_object_release( pp_vout[i] );
1819 free( pp_vout );
1821 if( b_viewpoint_ch )
1823 priv->viewpoint_changed = true;
1824 priv->viewpoint.yaw += vp.yaw;
1825 priv->viewpoint.pitch += vp.pitch;
1826 priv->viewpoint.roll += vp.roll;
1827 priv->viewpoint.fov += vp.fov;
1828 ViewpointApply( p_input );
1829 return;
1832 /* Seek or change volume if the input doesn't have navigation or viewpoint */
1833 if( seek_direction != 0 )
1835 vlc_tick_t it = vlc_tick_from_sec( seek_direction * var_InheritInteger( p_input, "short-jump-size" ) );
1836 Control( p_input, INPUT_CONTROL_JUMP_TIME, (input_control_param_t) {
1837 .time.b_fast_seek = false,
1838 .time.i_val = it
1841 else
1843 assert( vol_direction != 0 );
1844 audio_output_t *p_aout = input_resource_HoldAout( priv->p_resource );
1845 if( p_aout )
1847 aout_VolumeUpdate( p_aout, vol_direction, NULL );
1848 vlc_object_release( p_aout );
1853 #ifdef ENABLE_SOUT
1854 static void ControlUpdateRenderer( input_thread_t *p_input, bool b_enable )
1856 if( b_enable )
1858 if( InitSout( p_input ) != VLC_SUCCESS )
1860 msg_Err( p_input, "Failed to start sout" );
1861 return;
1864 else
1866 input_resource_RequestSout( input_priv(p_input)->p_resource,
1867 input_priv(p_input)->p_sout, NULL );
1868 input_priv(p_input)->p_sout = NULL;
1871 #endif
1873 static void ControlInsertDemuxFilter( input_thread_t* p_input, const char* psz_demux_chain )
1875 input_source_t *p_inputSource = input_priv(p_input)->master;
1876 demux_t *p_filtered_demux = demux_FilterChainNew( p_inputSource->p_demux, psz_demux_chain );
1877 if ( p_filtered_demux != NULL )
1878 p_inputSource->p_demux = p_filtered_demux;
1879 else if ( psz_demux_chain != NULL )
1880 msg_Dbg(p_input, "Failed to create demux filter %s", psz_demux_chain);
1884 static bool Control( input_thread_t *p_input,
1885 int i_type, input_control_param_t param )
1887 input_thread_private_t *priv = input_priv(p_input);
1888 const vlc_tick_t i_control_date = vlc_tick_now();
1889 /* FIXME b_force_update is abused, it should be carefully checked */
1890 bool b_force_update = false;
1891 vlc_value_t val;
1893 switch( i_type )
1895 case INPUT_CONTROL_SET_POSITION:
1896 case INPUT_CONTROL_JUMP_POSITION:
1898 const bool absolute = i_type == INPUT_CONTROL_SET_POSITION;
1899 if( priv->b_recording )
1901 msg_Err( p_input, "INPUT_CONTROL_SET_POSITION ignored while recording" );
1902 break;
1905 /* Reset the decoders states and clock sync (before calling the demuxer */
1906 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
1907 if( demux_SetPosition( priv->master->p_demux, (double)param.pos.f_val,
1908 !param.pos.b_fast_seek, absolute ) )
1910 msg_Err( p_input, "INPUT_CONTROL_SET_POSITION "
1911 "%s%2.1f%% failed",
1912 absolute ? "@" : param.pos.f_val >= 0 ? "+" : "",
1913 param.pos.f_val * 100.f );
1915 else
1917 if( priv->i_slave > 0 )
1918 SlaveSeek( p_input );
1919 priv->master->b_eof = false;
1921 b_force_update = true;
1923 break;
1926 case INPUT_CONTROL_SET_TIME:
1927 case INPUT_CONTROL_JUMP_TIME:
1929 const bool absolute = i_type == INPUT_CONTROL_SET_TIME;
1930 int i_ret;
1932 if( priv->b_recording )
1934 msg_Err( p_input, "INPUT_CONTROL_SET_TIME ignored while recording" );
1935 break;
1938 /* Reset the decoders states and clock sync (before calling the demuxer */
1939 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
1941 i_ret = demux_SetTime( priv->master->p_demux, param.time.i_val,
1942 !param.time.b_fast_seek, absolute );
1943 if( i_ret )
1945 vlc_tick_t i_length;
1947 /* Emulate it with a SET_POS */
1948 if( !demux_Control( priv->master->p_demux,
1949 DEMUX_GET_LENGTH, &i_length ) && i_length > 0 )
1951 double f_pos = (double)param.time.i_val / (double)i_length;
1952 i_ret = demux_SetPosition( priv->master->p_demux, f_pos,
1953 !param.time.b_fast_seek,
1954 absolute );
1957 if( i_ret )
1959 msg_Warn( p_input, "INPUT_CONTROL_SET_TIME %s%"PRId64
1960 " failed or not possible",
1961 absolute ? "@" : param.time.i_val >= 0 ? "+" : "",
1962 param.time.i_val );
1964 else
1966 if( priv->i_slave > 0 )
1967 SlaveSeek( p_input );
1968 priv->master->b_eof = false;
1970 b_force_update = true;
1972 break;
1975 case INPUT_CONTROL_SET_STATE:
1976 switch( param.val.i_int )
1978 case PLAYING_S:
1979 if( priv->i_state == PAUSE_S )
1981 ControlUnpause( p_input, i_control_date );
1982 b_force_update = true;
1984 break;
1985 case PAUSE_S:
1986 if( priv->i_state == PLAYING_S )
1988 ControlPause( p_input, i_control_date );
1989 b_force_update = true;
1991 break;
1992 default:
1993 msg_Err( p_input, "invalid INPUT_CONTROL_SET_STATE" );
1995 break;
1997 case INPUT_CONTROL_SET_RATE:
1999 /* Get rate and direction */
2000 float rate = fabsf( param.val.f_float );
2001 int i_rate_sign = rate < 0 ? -1 : 1;
2003 /* Check rate bound */
2004 if( rate > INPUT_RATE_DEFAULT / INPUT_RATE_MIN )
2006 msg_Info( p_input, "cannot set rate faster" );
2007 rate = INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
2009 else if( rate < INPUT_RATE_DEFAULT / INPUT_RATE_MAX )
2011 msg_Info( p_input, "cannot set rate slower" );
2012 rate = INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
2015 /* Apply direction */
2016 if( i_rate_sign < 0 )
2018 if( priv->master->b_rescale_ts )
2020 msg_Dbg( p_input, "cannot set negative rate" );
2021 rate = priv->rate;
2022 assert( rate > 0 );
2024 else
2026 rate *= i_rate_sign;
2030 if( rate != 1.f &&
2031 ( ( !priv->b_can_rate_control && !priv->master->b_rescale_ts ) ||
2032 ( priv->p_sout && !priv->b_out_pace_control ) ) )
2034 msg_Dbg( p_input, "cannot change rate" );
2035 rate = 1.f;
2037 if( rate != priv->rate &&
2038 !priv->b_can_pace_control && priv->b_can_rate_control )
2040 if( !priv->master->b_rescale_ts )
2041 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
2043 if( demux_Control( priv->master->p_demux, DEMUX_SET_RATE,
2044 &rate ) )
2046 msg_Warn( p_input, "ACCESS/DEMUX_SET_RATE failed" );
2047 rate = priv->rate;
2051 /* */
2052 if( rate != priv->rate )
2054 priv->rate = rate;
2055 input_SendEventRate( p_input, rate );
2057 if( priv->master->b_rescale_ts )
2059 const float rate_source = (priv->b_can_pace_control || priv->b_can_rate_control ) ? rate : 1.f;
2060 es_out_SetRate( priv->p_es_out, rate_source, rate );
2063 b_force_update = true;
2065 break;
2068 case INPUT_CONTROL_SET_PROGRAM:
2069 /* No need to force update, es_out does it if needed */
2070 es_out_Control( priv->p_es_out,
2071 ES_OUT_SET_GROUP, (int)param.val.i_int );
2073 if( param.val.i_int == 0 )
2074 demux_Control( priv->master->p_demux,
2075 DEMUX_SET_GROUP_DEFAULT );
2076 else
2077 demux_Control( priv->master->p_demux,
2078 DEMUX_SET_GROUP_LIST,
2079 (size_t)1, &(const int){ param.val.i_int });
2080 break;
2082 case INPUT_CONTROL_SET_ES_BY_ID:
2083 /* No need to force update, es_out does it if needed */
2084 es_out_Control( priv->p_es_out_display,
2085 ES_OUT_SET_ES_BY_ID, (int)param.val.i_int, true );
2087 demux_Control( priv->master->p_demux, DEMUX_SET_ES,
2088 (int)param.val.i_int );
2089 break;
2091 case INPUT_CONTROL_RESTART_ES_BY_ID:
2092 es_out_Control( priv->p_es_out_display,
2093 ES_OUT_RESTART_ES_BY_ID, (int)param.val.i_int );
2094 break;
2096 case INPUT_CONTROL_SET_ES:
2097 if( es_out_Control( input_priv(p_input)->p_es_out_display,
2098 ES_OUT_SET_ES, param.id ) == VLC_SUCCESS )
2099 demux_Control( input_priv(p_input)->master->p_demux, DEMUX_SET_ES,
2100 vlc_es_id_GetInputId( param.id ) );
2101 break;
2102 case INPUT_CONTROL_UNSET_ES:
2103 es_out_Control( input_priv(p_input)->p_es_out_display,
2104 ES_OUT_UNSET_ES, param.id );
2105 break;
2106 case INPUT_CONTROL_RESTART_ES:
2107 es_out_Control( input_priv(p_input)->p_es_out_display,
2108 ES_OUT_RESTART_ES, param.id );
2109 break;
2111 case INPUT_CONTROL_SET_VIEWPOINT:
2112 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT:
2113 case INPUT_CONTROL_UPDATE_VIEWPOINT:
2114 if ( i_type == INPUT_CONTROL_SET_INITIAL_VIEWPOINT )
2117 /* Set the initial viewpoint if it had not been changed by the
2118 * user. */
2119 if( !priv->viewpoint_changed )
2120 priv->viewpoint = param.viewpoint;
2121 /* Update viewpoints of aout and every vouts in all cases. */
2123 else if ( i_type == INPUT_CONTROL_SET_VIEWPOINT)
2125 priv->viewpoint_changed = true;
2126 priv->viewpoint = param.viewpoint;
2128 else
2130 priv->viewpoint_changed = true;
2131 priv->viewpoint.yaw += param.viewpoint.yaw;
2132 priv->viewpoint.pitch += param.viewpoint.pitch;
2133 priv->viewpoint.roll += param.viewpoint.roll;
2134 priv->viewpoint.fov += param.viewpoint.fov;
2137 ViewpointApply( p_input );
2138 break;
2140 case INPUT_CONTROL_SET_AUDIO_DELAY:
2141 if( param.delay.b_absolute )
2142 priv->i_audio_delay = param.delay.i_val;
2143 else
2144 priv->i_audio_delay += param.delay.i_val;
2145 input_SendEventAudioDelay( p_input, priv->i_audio_delay );
2146 UpdatePtsDelay( p_input );
2147 break;
2149 case INPUT_CONTROL_SET_SPU_DELAY:
2150 if( param.delay.b_absolute )
2151 priv->i_spu_delay = param.delay.i_val;
2152 else
2153 priv->i_spu_delay += param.delay.i_val;
2154 input_SendEventSubtitleDelay( p_input, priv->i_spu_delay );
2155 UpdatePtsDelay( p_input );
2156 break;
2158 case INPUT_CONTROL_SET_TITLE:
2159 case INPUT_CONTROL_SET_TITLE_NEXT:
2160 case INPUT_CONTROL_SET_TITLE_PREV:
2162 if( priv->b_recording )
2164 msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
2165 break;
2167 if( priv->master->i_title <= 0 )
2168 break;
2170 int i_title = demux_GetTitle( priv->master->p_demux );
2171 if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
2172 i_title--;
2173 else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
2174 i_title++;
2175 else
2176 i_title = param.val.i_int;
2177 if( i_title < 0 || i_title >= priv->master->i_title )
2178 break;
2180 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
2181 demux_Control( priv->master->p_demux,
2182 DEMUX_SET_TITLE, i_title );
2183 input_SendEventTitle( p_input, &(struct vlc_input_event_title) {
2184 .action = VLC_INPUT_TITLE_SELECTED,
2185 .selected_idx = i_title,
2187 break;
2189 case INPUT_CONTROL_SET_SEEKPOINT:
2190 case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
2191 case INPUT_CONTROL_SET_SEEKPOINT_PREV:
2193 if( priv->b_recording )
2195 msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
2196 break;
2198 if( priv->master->i_title <= 0 )
2199 break;
2201 demux_t *p_demux = priv->master->p_demux;
2203 int i_title = demux_GetTitle( p_demux );
2204 int i_seekpoint = demux_GetSeekpoint( p_demux );
2206 if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
2208 vlc_tick_t i_seekpoint_time = priv->master->title[i_title]->seekpoint[i_seekpoint]->i_time_offset;
2209 vlc_tick_t i_input_time = var_GetInteger( p_input, "time" );
2210 if( i_seekpoint_time >= 0 && i_input_time >= 0 )
2212 if( i_input_time < i_seekpoint_time + VLC_TICK_FROM_SEC(3) )
2213 i_seekpoint--;
2215 else
2216 i_seekpoint--;
2218 else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
2219 i_seekpoint++;
2220 else
2221 i_seekpoint = param.val.i_int;
2222 if( i_seekpoint < 0
2223 || i_seekpoint >= priv->master->title[i_title]->i_seekpoint )
2224 break;
2226 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
2227 demux_Control( priv->master->p_demux,
2228 DEMUX_SET_SEEKPOINT, i_seekpoint );
2229 input_SendEventSeekpoint( p_input, i_title, i_seekpoint );
2230 break;
2233 case INPUT_CONTROL_ADD_SLAVE:
2234 if( param.val.p_address )
2236 input_item_slave_t *p_item_slave = param.val.p_address;
2237 unsigned i_flags = SLAVE_ADD_CANFAIL | SLAVE_ADD_SET_TIME;
2238 if( p_item_slave->b_forced )
2239 i_flags |= SLAVE_ADD_FORCED;
2241 if( input_SlaveSourceAdd( p_input, p_item_slave->i_type,
2242 p_item_slave->psz_uri, i_flags )
2243 == VLC_SUCCESS )
2245 /* Update item slaves */
2246 input_item_AddSlave( priv->p_item, p_item_slave );
2247 /* The slave is now owned by the item */
2248 param.val.p_address = NULL;
2251 break;
2252 case INPUT_CONTROL_SET_SUBS_FPS:
2253 RequestSubRate( p_input, param.val.f_float );
2254 input_SendEventSubsFPS( p_input, param.val.f_float );
2255 break;
2257 case INPUT_CONTROL_SET_RECORD_STATE:
2258 val = param.val;
2259 if( !!priv->b_recording != !!val.b_bool )
2261 if( priv->master->b_can_stream_record )
2263 if( demux_Control( priv->master->p_demux,
2264 DEMUX_SET_RECORD_STATE, val.b_bool ) )
2265 val.b_bool = false;
2267 else
2269 if( es_out_SetRecordState( priv->p_es_out_display, val.b_bool ) )
2270 val.b_bool = false;
2272 priv->b_recording = val.b_bool;
2274 input_SendEventRecord( p_input, val.b_bool );
2276 b_force_update = true;
2278 break;
2280 case INPUT_CONTROL_SET_FRAME_NEXT:
2281 if( priv->i_state == PAUSE_S )
2283 es_out_SetFrameNext( priv->p_es_out );
2285 else if( priv->i_state == PLAYING_S )
2287 ControlPause( p_input, i_control_date );
2289 else
2291 msg_Err( p_input, "invalid state for frame next" );
2293 b_force_update = true;
2294 break;
2296 case INPUT_CONTROL_SET_BOOKMARK:
2298 vlc_tick_t time_offset = -1;
2299 int bookmark = param.val.i_int;
2301 vlc_mutex_lock( &priv->p_item->lock );
2302 if( bookmark >= 0 && bookmark < priv->i_bookmark )
2304 const seekpoint_t *p_bookmark = priv->pp_bookmark[bookmark];
2305 time_offset = p_bookmark->i_time_offset;
2307 vlc_mutex_unlock( &priv->p_item->lock );
2309 if( time_offset < 0 )
2311 msg_Err( p_input, "invalid bookmark %d", bookmark );
2312 break;
2315 b_force_update =
2316 Control( p_input, INPUT_CONTROL_SET_TIME,
2317 (input_control_param_t) { .time.i_val = time_offset,
2318 .time.b_fast_seek = false } );
2319 break;
2321 case INPUT_CONTROL_SET_RENDERER:
2323 #ifdef ENABLE_SOUT
2324 vlc_renderer_item_t *p_item = param.val.p_address;
2325 input_thread_private_t *p_priv = input_priv( p_input );
2326 // We do not support switching from a renderer to another for now
2327 if ( p_item == NULL && p_priv->p_renderer == NULL )
2328 break;
2330 void *context;
2331 if( es_out_Control( priv->p_es_out_display,
2332 ES_OUT_STOP_ALL_ES, &context ) != VLC_SUCCESS )
2333 break;
2335 if ( p_priv->p_renderer )
2337 ControlUpdateRenderer( p_input, false );
2338 demux_FilterDisable( p_priv->master->p_demux,
2339 vlc_renderer_item_demux_filter( p_priv->p_renderer ) );
2340 vlc_renderer_item_release( p_priv->p_renderer );
2341 p_priv->p_renderer = NULL;
2343 if( p_item != NULL )
2345 p_priv->p_renderer = vlc_renderer_item_hold( p_item );
2346 ControlUpdateRenderer( p_input, true );
2347 if( !demux_FilterEnable( p_priv->master->p_demux,
2348 vlc_renderer_item_demux_filter( p_priv->p_renderer ) ) )
2350 ControlInsertDemuxFilter( p_input,
2351 vlc_renderer_item_demux_filter( p_item ) );
2353 input_resource_TerminateVout( p_priv->p_resource );
2355 es_out_Control( priv->p_es_out_display, ES_OUT_START_ALL_ES,
2356 context );
2357 #endif
2358 break;
2360 case INPUT_CONTROL_SET_VBI_PAGE:
2361 es_out_Control( priv->p_es_out_display, ES_OUT_SET_VBI_PAGE,
2362 param.vbi_page.id, param.vbi_page.page );
2363 break;
2364 case INPUT_CONTROL_SET_VBI_TRANSPARENCY:
2365 es_out_Control( priv->p_es_out_display, ES_OUT_SET_VBI_TRANSPARENCY,
2366 param.vbi_transparency.id,
2367 param.vbi_transparency.enabled );
2368 break;
2370 case INPUT_CONTROL_NAV_ACTIVATE:
2371 case INPUT_CONTROL_NAV_UP:
2372 case INPUT_CONTROL_NAV_DOWN:
2373 case INPUT_CONTROL_NAV_LEFT:
2374 case INPUT_CONTROL_NAV_RIGHT:
2375 case INPUT_CONTROL_NAV_POPUP:
2376 case INPUT_CONTROL_NAV_MENU:
2377 ControlNav( p_input, i_type );
2378 break;
2380 default:
2381 msg_Err( p_input, "not yet implemented" );
2382 break;
2385 ControlRelease( i_type, &param );
2386 return b_force_update;
2389 /*****************************************************************************
2390 * UpdateTitleSeekpoint
2391 *****************************************************************************/
2392 static int UpdateTitleSeekpoint( input_thread_t *p_input,
2393 int i_title, int i_seekpoint )
2395 int i_title_end = input_priv(p_input)->master->i_title_end -
2396 input_priv(p_input)->master->i_title_offset;
2397 int i_seekpoint_end = input_priv(p_input)->master->i_seekpoint_end -
2398 input_priv(p_input)->master->i_seekpoint_offset;
2400 if( i_title_end >= 0 && i_seekpoint_end >= 0 )
2402 if( i_title > i_title_end ||
2403 ( i_title == i_title_end && i_seekpoint > i_seekpoint_end ) )
2404 return VLC_DEMUXER_EOF;
2406 else if( i_seekpoint_end >= 0 )
2408 if( i_seekpoint > i_seekpoint_end )
2409 return VLC_DEMUXER_EOF;
2411 else if( i_title_end >= 0 )
2413 if( i_title > i_title_end )
2414 return VLC_DEMUXER_EOF;
2416 return VLC_DEMUXER_SUCCESS;
2418 /*****************************************************************************
2419 * Update*FromDemux:
2420 *****************************************************************************/
2421 static int UpdateTitleSeekpointFromDemux( input_thread_t *p_input )
2423 demux_t *p_demux = input_priv(p_input)->master->p_demux;
2425 /* TODO event-like */
2426 if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_TITLE ) )
2427 input_SendEventTitle( p_input, &(struct vlc_input_event_title) {
2428 .action = VLC_INPUT_TITLE_SELECTED,
2429 .selected_idx = demux_GetTitle( p_demux ),
2432 if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_SEEKPOINT ) )
2433 input_SendEventSeekpoint( p_input, demux_GetTitle( p_demux ),
2434 demux_GetSeekpoint( p_demux ) );
2436 return UpdateTitleSeekpoint( p_input,
2437 demux_GetTitle( p_demux ),
2438 demux_GetSeekpoint( p_demux ) );
2441 static void UpdateGenericFromDemux( input_thread_t *p_input )
2443 demux_t *p_demux = input_priv(p_input)->master->p_demux;
2445 if( demux_TestAndClearFlags( p_demux, INPUT_UPDATE_META ) )
2446 InputUpdateMeta( p_input, p_demux );
2449 double quality;
2450 double strength;
2452 if( !demux_Control( p_demux, DEMUX_GET_SIGNAL, &quality, &strength ) )
2453 input_SendEventSignal( p_input, quality, strength );
2457 static void UpdateTitleListfromDemux( input_thread_t *p_input )
2459 input_thread_private_t *priv = input_priv(p_input);
2460 input_source_t *in = priv->master;
2462 /* Delete the preexisting titles */
2463 bool had_titles = false;
2464 if( in->i_title > 0 )
2466 had_titles = true;
2467 for( int i = 0; i < in->i_title; i++ )
2468 vlc_input_title_Delete( in->title[i] );
2469 TAB_CLEAN( in->i_title, in->title );
2470 priv->i_title = 0;
2471 priv->title = NULL;
2472 in->b_title_demux = false;
2475 /* Get the new title list */
2476 if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2477 &in->title, &in->i_title,
2478 &in->i_title_offset, &in->i_seekpoint_offset ) )
2479 TAB_INIT( in->i_title, in->title );
2480 else
2481 in->b_title_demux = true;
2483 InitTitle( p_input, had_titles );
2486 static int
2487 InputStreamHandleAnchor( input_thread_t *p_input, input_source_t *source,
2488 stream_t **stream, char const *anchor )
2490 char const* extra;
2491 if( stream_extractor_AttachParsed( stream, anchor, &extra ) )
2493 msg_Err( p_input, "unable to attach stream-extractors for %s",
2494 (*stream)->psz_url );
2496 return VLC_EGENERIC;
2499 if( vlc_stream_directory_Attach( stream, NULL ) )
2500 msg_Dbg( p_input, "attachment of directory-extractor failed for %s",
2501 (*stream)->psz_url );
2503 MRLSections( extra ? extra : "",
2504 &source->i_title_start, &source->i_title_end,
2505 &source->i_seekpoint_start, &source->i_seekpoint_end );
2507 return VLC_SUCCESS;
2510 static demux_t *InputDemuxNew( input_thread_t *p_input,
2511 input_source_t *p_source, const char *url,
2512 const char *psz_demux, const char *psz_anchor )
2514 input_thread_private_t *priv = input_priv(p_input );
2515 vlc_object_t *obj = VLC_OBJECT(p_input);
2517 /* create the underlying access stream */
2518 stream_t *p_stream = stream_AccessNew( obj, p_input, priv->p_es_out,
2519 priv->b_preparsing, url );
2520 if( p_stream == NULL )
2521 return NULL;
2523 p_stream = stream_FilterAutoNew( p_stream );
2525 if( p_stream->pf_read == NULL && p_stream->pf_block == NULL
2526 && p_stream->pf_readdir == NULL )
2527 { /* Combined access/demux, no stream filtering */
2528 MRLSections( psz_anchor,
2529 &p_source->i_title_start, &p_source->i_title_end,
2530 &p_source->i_seekpoint_start, &p_source->i_seekpoint_end );
2531 return p_stream;
2534 /* attach explicit stream filters to stream */
2535 char *psz_filters = var_InheritString( p_input, "stream-filter" );
2536 if( psz_filters )
2538 p_stream = stream_FilterChainNew( p_stream, psz_filters );
2539 free( psz_filters );
2542 /* handle anchors */
2543 if( InputStreamHandleAnchor( p_input, p_source, &p_stream, psz_anchor ) )
2544 goto error;
2546 /* attach conditional record stream-filter */
2547 if( var_InheritBool( p_input, "input-record-native" ) )
2548 p_stream = stream_FilterChainNew( p_stream, "record" );
2550 /* create a regular demux with the access stream created */
2551 demux_t *demux = demux_NewAdvanced( obj, p_input, psz_demux, url, p_stream,
2552 priv->p_es_out, priv->b_preparsing );
2553 if( demux != NULL )
2554 return demux;
2556 error:
2557 vlc_stream_Delete( p_stream );
2558 return NULL;
2561 static void input_SplitMRL( const char **, const char **, const char **,
2562 const char **, char * );
2564 /*****************************************************************************
2565 * InputSourceNew:
2566 *****************************************************************************/
2567 static input_source_t *InputSourceNew( input_thread_t *p_input,
2568 const char *psz_mrl,
2569 const char *psz_forced_demux,
2570 bool b_in_can_fail )
2572 input_thread_private_t *priv = input_priv(p_input);
2573 input_source_t *in = calloc(1, sizeof(*in) );
2574 if( unlikely(in == NULL) )
2575 return NULL;
2577 const char *psz_access, *psz_demux, *psz_path, *psz_anchor = NULL;
2579 assert( psz_mrl );
2580 char *psz_dup = strdup( psz_mrl );
2581 char *psz_demux_var = NULL;
2583 if( psz_dup == NULL )
2585 free( in );
2586 return NULL;
2589 /* Split uri */
2590 input_SplitMRL( &psz_access, &psz_demux, &psz_path, &psz_anchor, psz_dup );
2592 if( psz_demux == NULL || psz_demux[0] == '\0' )
2593 psz_demux = psz_demux_var = var_InheritString( p_input, "demux" );
2595 if( psz_forced_demux != NULL )
2596 psz_demux = psz_forced_demux;
2598 if( psz_demux == NULL )
2599 psz_demux = "any";
2601 msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
2602 psz_mrl, psz_access, psz_demux, psz_path );
2604 if( input_priv(p_input)->master == NULL /* XXX ugly */)
2605 { /* On master stream only, use input-list */
2606 char *str = var_InheritString( p_input, "input-list" );
2607 if( str != NULL )
2609 char *list;
2611 var_Create( p_input, "concat-list", VLC_VAR_STRING );
2612 if( likely(asprintf( &list, "%s://%s,%s", psz_access, psz_path,
2613 str ) >= 0) )
2615 var_SetString( p_input, "concat-list", list );
2616 free( list );
2618 free( str );
2619 psz_access = "concat";
2623 if( strcasecmp( psz_access, "concat" ) )
2624 { /* Autodetect extra files if none specified */
2625 int count;
2626 char **tab;
2628 TAB_INIT( count, tab );
2629 InputGetExtraFiles( p_input, &count, &tab, &psz_access, psz_path );
2630 if( count > 0 )
2632 char *list = NULL;
2634 for( int i = 0; i < count; i++ )
2636 char *str;
2637 if( asprintf( &str, "%s,%s", list ? list : psz_mrl,
2638 tab[i] ) < 0 )
2639 break;
2641 free( tab[i] );
2642 free( list );
2643 list = str;
2646 var_Create( p_input, "concat-list", VLC_VAR_STRING );
2647 if( likely(list != NULL) )
2649 var_SetString( p_input, "concat-list", list );
2650 free( list );
2653 TAB_CLEAN( count, tab );
2656 char *url;
2657 if( likely(asprintf( &url, "%s://%s", psz_access, psz_path ) >= 0) )
2659 in->p_demux = InputDemuxNew( p_input, in, url, psz_demux, psz_anchor );
2660 free( url );
2662 else
2663 in->p_demux = NULL;
2665 free( psz_demux_var );
2666 free( psz_dup );
2668 if( in->p_demux == NULL )
2670 if( !b_in_can_fail && !input_Stopped( p_input ) )
2671 vlc_dialog_display_error( p_input, _("Your input can't be opened"),
2672 _("VLC is unable to open the MRL '%s'."
2673 " Check the log for details."), psz_mrl );
2674 free( in );
2675 return NULL;
2678 char *psz_demux_chain = NULL;
2679 if( priv->p_renderer )
2681 const char* psz_renderer_demux = vlc_renderer_item_demux_filter(
2682 priv->p_renderer );
2683 if( psz_renderer_demux )
2684 psz_demux_chain = strdup( psz_renderer_demux );
2686 if( !psz_demux_chain )
2687 psz_demux_chain = var_GetNonEmptyString(p_input, "demux-filter");
2688 if( psz_demux_chain != NULL ) /* add the chain of demux filters */
2690 in->p_demux = demux_FilterChainNew( in->p_demux, psz_demux_chain );
2691 free( psz_demux_chain );
2693 if( in->p_demux == NULL )
2695 msg_Err(p_input, "Failed to create demux filter");
2696 free( in );
2697 return NULL;
2701 /* Get infos from (access_)demux */
2702 int capabilites = 0;
2703 bool b_can_seek;
2704 if( demux_Control( in->p_demux, DEMUX_CAN_SEEK, &b_can_seek ) )
2705 b_can_seek = false;
2706 if( b_can_seek )
2707 capabilites |= VLC_INPUT_CAPABILITIES_SEEKABLE;
2709 if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
2710 &in->b_can_pace_control ) )
2711 in->b_can_pace_control = false;
2713 assert( in->p_demux->pf_demux != NULL || !in->b_can_pace_control );
2715 if( !in->b_can_pace_control )
2717 if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_RATE,
2718 &in->b_can_rate_control ) )
2720 in->b_can_rate_control = false;
2721 in->b_rescale_ts = true;
2723 else
2724 in->b_rescale_ts = !in->b_can_rate_control;
2726 else
2728 in->b_can_rate_control = true;
2729 in->b_rescale_ts = true;
2732 demux_Control( in->p_demux, DEMUX_CAN_PAUSE, &in->b_can_pause );
2734 if( in->b_can_pause || !in->b_can_pace_control )
2735 capabilites |= VLC_INPUT_CAPABILITIES_PAUSEABLE;
2736 if( !in->b_can_pace_control || in->b_can_rate_control )
2737 capabilites |= VLC_INPUT_CAPABILITIES_CHANGE_RATE;
2738 if( !in->b_rescale_ts && !in->b_can_pace_control && in->b_can_rate_control )
2739 capabilites |= VLC_INPUT_CAPABILITIES_REWINDABLE;
2741 /* Set record capabilities */
2742 if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
2743 in->b_can_stream_record = false;
2744 #ifdef ENABLE_SOUT
2745 if( !var_GetBool( p_input, "input-record-native" ) )
2746 in->b_can_stream_record = false;
2747 capabilites |= VLC_INPUT_CAPABILITIES_RECORDABLE;
2748 #else
2749 if( in->b_can_stream_record )
2750 capabilites |= VLC_INPUT_CAPABILITIES_RECORDABLE;
2751 #endif
2753 input_SendEventCapabilities( p_input, capabilites );
2755 /* get attachment
2756 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2757 if( !input_priv(p_input)->b_preparsing )
2759 if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
2760 &in->title, &in->i_title,
2761 &in->i_title_offset, &in->i_seekpoint_offset ))
2763 TAB_INIT( in->i_title, in->title );
2765 else
2767 in->b_title_demux = true;
2770 int i_attachment;
2771 input_attachment_t **attachment;
2772 if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS,
2773 &attachment, &i_attachment ) )
2775 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
2776 AppendAttachment( &input_priv(p_input)->i_attachment, &input_priv(p_input)->attachment, &input_priv(p_input)->attachment_demux,
2777 i_attachment, attachment, in->p_demux );
2778 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
2781 demux_Control( in->p_demux, DEMUX_GET_PTS_DELAY, &in->i_pts_delay );
2782 if( in->i_pts_delay > INPUT_PTS_DELAY_MAX )
2783 in->i_pts_delay = INPUT_PTS_DELAY_MAX;
2784 else if( in->i_pts_delay < 0 )
2785 in->i_pts_delay = 0;
2788 if( demux_Control( in->p_demux, DEMUX_GET_FPS, &in->f_fps ) )
2789 in->f_fps = 0.f;
2791 if( var_GetInteger( p_input, "clock-synchro" ) != -1 )
2792 in->b_can_pace_control = !var_GetInteger( p_input, "clock-synchro" );
2794 return in;
2797 /*****************************************************************************
2798 * InputSourceDestroy:
2799 *****************************************************************************/
2800 static void InputSourceDestroy( input_source_t *in )
2802 int i;
2804 if( in->p_demux )
2805 demux_Delete( in->p_demux );
2807 if( in->i_title > 0 )
2809 for( i = 0; i < in->i_title; i++ )
2810 vlc_input_title_Delete( in->title[i] );
2811 TAB_CLEAN( in->i_title, in->title );
2814 free( in );
2817 /*****************************************************************************
2818 * InputSourceMeta:
2819 *****************************************************************************/
2820 static void InputSourceMeta( input_thread_t *p_input,
2821 input_source_t *p_source, vlc_meta_t *p_meta )
2823 demux_t *p_demux = p_source->p_demux;
2825 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2826 * is a bad idea */
2828 bool has_meta = false;
2830 /* Read demux meta */
2831 if( !demux_Control( p_demux, DEMUX_GET_META, p_meta ) )
2832 has_meta = true;
2834 bool has_unsupported;
2835 if( demux_Control( p_demux, DEMUX_HAS_UNSUPPORTED_META, &has_unsupported ) )
2836 has_unsupported = true;
2838 /* If the demux report unsupported meta data, or if we don't have meta data
2839 * try an external "meta reader" */
2840 if( has_meta && !has_unsupported )
2841 return;
2843 demux_meta_t *p_demux_meta =
2844 vlc_custom_create( p_input, sizeof( *p_demux_meta ), "demux meta" );
2845 if( unlikely(p_demux_meta == NULL) )
2846 return;
2847 p_demux_meta->p_item = input_priv(p_input)->p_item;
2849 module_t *p_id3 = module_need( p_demux_meta, "meta reader", NULL, false );
2850 if( p_id3 )
2852 if( p_demux_meta->p_meta )
2854 vlc_meta_Merge( p_meta, p_demux_meta->p_meta );
2855 vlc_meta_Delete( p_demux_meta->p_meta );
2858 if( p_demux_meta->i_attachments > 0 )
2860 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
2861 AppendAttachment( &input_priv(p_input)->i_attachment, &input_priv(p_input)->attachment, &input_priv(p_input)->attachment_demux,
2862 p_demux_meta->i_attachments, p_demux_meta->attachments, p_demux);
2863 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
2865 module_unneed( p_demux, p_id3 );
2867 vlc_object_release( p_demux_meta );
2871 static void SlaveDemux( input_thread_t *p_input )
2873 input_thread_private_t *priv = input_priv(p_input);
2874 vlc_tick_t i_time;
2875 int i;
2877 if( demux_Control( input_priv(p_input)->master->p_demux, DEMUX_GET_TIME, &i_time ) )
2879 msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
2880 return;
2883 for( i = 0; i < input_priv(p_input)->i_slave; i++ )
2885 input_source_t *in = input_priv(p_input)->slave[i];
2886 int i_ret;
2888 if( in->b_eof )
2889 continue;
2891 if( priv->slave_subs_rate != in->sub_rate )
2893 if( in->b_slave_sub && in->b_can_rate_control )
2895 if( in->sub_rate != 0 ) /* Don't reset when it's the first time */
2896 es_out_Control( priv->p_es_out, ES_OUT_RESET_PCR );
2897 float new_rate = priv->slave_subs_rate;
2898 demux_Control( in->p_demux, DEMUX_SET_RATE, &new_rate );
2900 in->sub_rate = priv->slave_subs_rate;
2904 /* Call demux_Demux until we have read enough data */
2905 if( demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
2907 for( ;; )
2909 vlc_tick_t i_stime;
2910 if( demux_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
2912 msg_Err( p_input, "slave[%d] doesn't like "
2913 "DEMUX_GET_TIME -> EOF", i );
2914 i_ret = 0;
2915 break;
2918 if( i_stime >= i_time )
2920 i_ret = 1;
2921 break;
2924 if( ( i_ret = demux_Demux( in->p_demux ) ) <= 0 )
2925 break;
2928 else
2930 i_ret = demux_Demux( in->p_demux );
2933 if( i_ret <= 0 )
2935 msg_Dbg( p_input, "slave %d EOF", i );
2936 in->b_eof = true;
2941 static void SlaveSeek( input_thread_t *p_input )
2943 vlc_tick_t i_time;
2944 int i;
2946 if( demux_Control( input_priv(p_input)->master->p_demux, DEMUX_GET_TIME, &i_time ) )
2948 msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
2949 return;
2952 for( i = 0; i < input_priv(p_input)->i_slave; i++ )
2954 input_source_t *in = input_priv(p_input)->slave[i];
2956 if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time, true ) )
2958 if( !in->b_eof )
2959 msg_Err( p_input, "seek failed for slave %d -> EOF", i );
2960 in->b_eof = true;
2962 else
2964 in->b_eof = false;
2969 /*****************************************************************************
2970 * InputMetaUser:
2971 *****************************************************************************/
2972 static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta )
2974 static const struct { int i_meta; const char *psz_name; } p_list[] = {
2975 { vlc_meta_Title, "meta-title" },
2976 { vlc_meta_Artist, "meta-artist" },
2977 { vlc_meta_Genre, "meta-genre" },
2978 { vlc_meta_Copyright, "meta-copyright" },
2979 { vlc_meta_Description, "meta-description" },
2980 { vlc_meta_Date, "meta-date" },
2981 { vlc_meta_URL, "meta-url" },
2982 { 0, NULL }
2985 /* Get meta information from user */
2986 for( int i = 0; p_list[i].psz_name; i++ )
2988 char *psz_string = var_GetNonEmptyString( p_input, p_list[i].psz_name );
2989 if( !psz_string )
2990 continue;
2992 EnsureUTF8( psz_string );
2993 vlc_meta_Set( p_meta, p_list[i].i_meta, psz_string );
2994 free( psz_string );
2998 static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment,
2999 const demux_t ***ppp_attachment_demux,
3000 int i_new, input_attachment_t **pp_new, const demux_t *p_demux )
3002 int i_attachment = *pi_attachment;
3003 int i;
3005 input_attachment_t **pp_att = realloc( *ppp_attachment,
3006 sizeof(*pp_att) * ( i_attachment + i_new ) );
3007 if( likely(pp_att) )
3009 *ppp_attachment = pp_att;
3010 const demux_t **pp_attdmx = realloc( *ppp_attachment_demux,
3011 sizeof(*pp_attdmx) * ( i_attachment + i_new ) );
3012 if( likely(pp_attdmx) )
3014 *ppp_attachment_demux = pp_attdmx;
3016 for( i = 0; i < i_new; i++ )
3018 pp_att[i_attachment] = pp_new[i];
3019 pp_attdmx[i_attachment++] = p_demux;
3021 /* */
3022 *pi_attachment = i_attachment;
3023 free( pp_new );
3024 return;
3028 /* on alloc errors */
3029 for( i = 0; i < i_new; i++ )
3030 vlc_input_attachment_Delete( pp_new[i] );
3031 free( pp_new );
3034 /*****************************************************************************
3035 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
3036 * arturl and locking issue.
3037 *****************************************************************************/
3038 static void InputUpdateMeta( input_thread_t *p_input, demux_t *p_demux )
3040 vlc_meta_t *p_meta = vlc_meta_New();
3041 if( unlikely(p_meta == NULL) )
3042 return;
3044 demux_Control( p_demux, DEMUX_GET_META, p_meta );
3046 /* If metadata changed, then the attachments might have changed.
3047 We need to update them in case they contain album art. */
3048 input_attachment_t **attachment;
3049 int i_attachment;
3051 if( !demux_Control( p_demux, DEMUX_GET_ATTACHMENTS,
3052 &attachment, &i_attachment ) )
3054 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
3055 if( input_priv(p_input)->i_attachment > 0 )
3057 int j = 0;
3058 for( int i = 0; i < input_priv(p_input)->i_attachment; i++ )
3060 if( input_priv(p_input)->attachment_demux[i] == p_demux )
3061 vlc_input_attachment_Delete( input_priv(p_input)->attachment[i] );
3062 else
3064 input_priv(p_input)->attachment[j] = input_priv(p_input)->attachment[i];
3065 input_priv(p_input)->attachment_demux[j] = input_priv(p_input)->attachment_demux[i];
3066 j++;
3069 input_priv(p_input)->i_attachment = j;
3071 AppendAttachment( &input_priv(p_input)->i_attachment, &input_priv(p_input)->attachment, &input_priv(p_input)->attachment_demux,
3072 i_attachment, attachment, p_demux );
3073 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
3076 es_out_ControlSetMeta( input_priv(p_input)->p_es_out, p_meta );
3077 vlc_meta_Delete( p_meta );
3080 /*****************************************************************************
3081 * InputGetExtraFiles
3082 * Autodetect extra input list
3083 *****************************************************************************/
3084 static void InputGetExtraFilesPattern( input_thread_t *p_input,
3085 int *pi_list, char ***pppsz_list,
3086 const char *psz_path,
3087 const char *psz_match,
3088 const char *psz_format,
3089 int i_start, int i_stop )
3091 int i_list;
3092 char **ppsz_list;
3093 TAB_INIT( i_list, ppsz_list );
3095 char *psz_base = strdup( psz_path );
3096 if( !psz_base )
3097 goto exit;
3099 /* Remove the extension */
3100 char *psz_end = &psz_base[strlen(psz_base)-strlen(psz_match)];
3101 assert( psz_end >= psz_base);
3102 *psz_end = '\0';
3104 /* Try to list files */
3105 for( int i = i_start; i <= i_stop; i++ )
3107 char *psz_probe;
3108 if( asprintf( &psz_probe, psz_format, psz_base, i ) < 0 )
3109 break;
3111 char *filepath = get_path( psz_probe );
3113 struct stat st;
3114 if( filepath == NULL ||
3115 vlc_stat( filepath, &st ) || !S_ISREG( st.st_mode ) || !st.st_size )
3117 free( filepath );
3118 free( psz_probe );
3119 break;
3122 msg_Dbg( p_input, "Detected extra file `%s'", filepath );
3124 char* psz_uri = vlc_path2uri( filepath, NULL );
3125 if( psz_uri )
3126 TAB_APPEND( i_list, ppsz_list, psz_uri );
3128 free( filepath );
3129 free( psz_probe );
3131 free( psz_base );
3132 exit:
3133 *pi_list = i_list;
3134 *pppsz_list = ppsz_list;
3137 static void InputGetExtraFiles( input_thread_t *p_input,
3138 int *pi_list, char ***pppsz_list,
3139 const char **ppsz_access, const char *psz_path )
3141 static const struct pattern
3143 const char *psz_access_force;
3144 const char *psz_match;
3145 const char *psz_format;
3146 int i_start;
3147 int i_stop;
3148 } patterns[] = {
3149 /* XXX the order is important */
3150 { "concat", ".001", "%s.%.3d", 2, 999 },
3151 { NULL, ".part1.rar","%s.part%.1d.rar", 2, 9 },
3152 { NULL, ".part01.rar","%s.part%.2d.rar", 2, 99, },
3153 { NULL, ".part001.rar", "%s.part%.3d.rar", 2, 999 },
3154 { NULL, ".rar", "%s.r%.2d", 0, 99 },
3155 { "concat", ".mts", "%s.mts%d", 1, 999 },
3158 TAB_INIT( *pi_list, *pppsz_list );
3160 if( ( **ppsz_access && strcmp( *ppsz_access, "file" ) ) || !psz_path )
3161 return;
3163 const size_t i_path = strlen(psz_path);
3165 for( size_t i = 0; i < ARRAY_SIZE( patterns ); ++i )
3167 const struct pattern* pat = &patterns[i];
3168 const size_t i_ext = strlen( pat->psz_match );
3170 if( i_path < i_ext )
3171 continue;
3173 if( !strcmp( &psz_path[i_path-i_ext], pat->psz_match ) )
3175 InputGetExtraFilesPattern( p_input, pi_list, pppsz_list, psz_path,
3176 pat->psz_match, pat->psz_format, pat->i_start, pat->i_stop );
3178 if( *pi_list > 0 && pat->psz_access_force )
3179 *ppsz_access = pat->psz_access_force;
3180 return;
3185 /* */
3186 static void input_ChangeState( input_thread_t *p_input, int i_state )
3188 if( input_priv(p_input)->i_state == i_state )
3189 return;
3191 input_priv(p_input)->i_state = i_state;
3192 if( i_state == ERROR_S )
3193 input_item_SetErrorWhenReading( input_priv(p_input)->p_item, true );
3194 input_SendEventState( p_input, i_state );
3198 /*****************************************************************************
3199 * MRLSplit: parse the access, demux and url part of the
3200 * Media Resource Locator.
3201 *****************************************************************************/
3202 static void input_SplitMRL( const char **access, const char **demux,
3203 const char **path, const char **anchor, char *buf )
3205 char *p;
3207 /* Separate <path> from <access>[/<demux>]:// */
3208 p = strstr( buf, "://" );
3209 if( p != NULL )
3211 *p = '\0';
3212 p += 3; /* skips "://" */
3213 *path = p;
3215 /* Remove HTML anchor if present (not supported).
3216 * The hash symbol itself should be URI-encoded. */
3217 p = strchr( p, '#' );
3218 if( p != NULL )
3220 *(p++) = '\0';
3221 *anchor = p;
3223 else
3224 *anchor = "";
3226 else
3228 #ifndef NDEBUG
3229 fprintf( stderr, "%s(\"%s\") probably not a valid URI!\n", __func__,
3230 buf );
3231 #endif
3232 /* Note: this is a valid non const pointer to "": */
3233 *path = buf + strlen( buf );
3236 /* Separate access from demux */
3237 p = strchr( buf, '/' );
3238 if( p != NULL )
3240 *(p++) = '\0';
3241 if( p[0] == '$' )
3242 p++;
3243 *demux = p;
3245 else
3246 *demux = "";
3248 /* We really don't want module name substitution here! */
3249 p = buf;
3250 if( p[0] == '$' )
3251 p++;
3252 *access = p;
3255 static const char *MRLSeekPoint( const char *str, int *title, int *chapter )
3257 char *end;
3258 unsigned long u;
3260 /* Look for the title */
3261 u = strtoul( str, &end, 0 );
3262 *title = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
3263 str = end;
3265 /* Look for the chapter */
3266 if( *str == ':' )
3268 str++;
3269 u = strtoul( str, &end, 0 );
3270 *chapter = (str == end || u > (unsigned long)INT_MAX) ? -1 : (int)u;
3271 str = end;
3273 else
3274 *chapter = -1;
3276 return str;
3280 /*****************************************************************************
3281 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
3283 * Syntax:
3284 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
3285 *****************************************************************************/
3286 static void MRLSections( const char *p,
3287 int *pi_title_start, int *pi_title_end,
3288 int *pi_chapter_start, int *pi_chapter_end )
3290 *pi_title_start = *pi_title_end = *pi_chapter_start = *pi_chapter_end = -1;
3292 int title_start, chapter_start, title_end, chapter_end;
3294 if( !p )
3295 return;
3297 if( *p != '-' )
3298 p = MRLSeekPoint( p, &title_start, &chapter_start );
3299 else
3300 title_start = chapter_start = -1;
3302 if( *p == '-' )
3303 p = MRLSeekPoint( p + 1, &title_end, &chapter_end );
3304 else
3305 title_end = chapter_end = -1;
3307 if( *p ) /* syntax error */
3308 return;
3310 *pi_title_start = title_start;
3311 *pi_title_end = title_end;
3312 *pi_chapter_start = chapter_start;
3313 *pi_chapter_end = chapter_end;
3316 static int input_SlaveSourceAdd( input_thread_t *p_input,
3317 enum slave_type i_type, const char *psz_uri,
3318 unsigned i_flags )
3320 input_thread_private_t *priv = input_priv(p_input);
3321 const char *psz_forced_demux;
3322 const bool b_can_fail = i_flags & SLAVE_ADD_CANFAIL;
3323 const bool b_forced = i_flags & SLAVE_ADD_FORCED;
3324 const bool b_set_time = i_flags & SLAVE_ADD_SET_TIME;
3325 enum es_format_category_e i_cat;
3327 switch( i_type )
3329 case SLAVE_TYPE_SPU:
3330 psz_forced_demux = "subtitle";
3331 i_cat = SPU_ES;
3332 break;
3333 case SLAVE_TYPE_AUDIO:
3334 psz_forced_demux = NULL;
3335 i_cat = AUDIO_ES;
3336 break;
3337 default:
3338 vlc_assert_unreachable();
3341 msg_Dbg( p_input, "loading %s slave: %s (forced: %d)",
3342 i_cat == SPU_ES ? "spu" : "audio", psz_uri, b_forced );
3344 priv->i_last_es_cat = UNKNOWN_ES;
3346 input_source_t *p_source = InputSourceNew( p_input, psz_uri,
3347 psz_forced_demux,
3348 b_can_fail || psz_forced_demux );
3350 if( psz_forced_demux && p_source == NULL )
3351 p_source = InputSourceNew( p_input, psz_uri, NULL, b_can_fail );
3353 if( p_source == NULL )
3355 msg_Warn( p_input, "failed to add %s as slave", psz_uri );
3356 return VLC_EGENERIC;
3359 if( i_type == SLAVE_TYPE_AUDIO )
3361 if( b_set_time )
3363 vlc_tick_t i_time;
3365 /* Set position */
3366 if( demux_Control( priv->master->p_demux, DEMUX_GET_TIME, &i_time ) )
3368 msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
3369 InputSourceDestroy( p_source );
3370 return VLC_EGENERIC;
3373 if( demux_Control( p_source->p_demux,
3374 DEMUX_SET_TIME, i_time, true ) )
3376 msg_Err( p_input, "seek failed for new slave" );
3377 InputSourceDestroy( p_source );
3378 return VLC_EGENERIC;
3382 /* Get meta (access and demux) */
3383 InputUpdateMeta( p_input, p_source->p_demux );
3385 else
3386 p_source->b_slave_sub = true;
3388 TAB_APPEND( priv->i_slave, priv->slave, p_source );
3390 if( !b_forced || priv->i_last_es_cat != i_cat )
3391 return VLC_SUCCESS;
3393 assert( priv->i_last_es_id != -1 );
3395 es_out_Control( priv->p_es_out_display, ES_OUT_SET_ES_DEFAULT_BY_ID,
3396 priv->i_last_es_id );
3397 es_out_Control( priv->p_es_out_display, ES_OUT_SET_ES_BY_ID,
3398 priv->i_last_es_id, false );
3400 return VLC_SUCCESS;
3403 static char *input_SubtitleFile2Uri( input_thread_t *p_input,
3404 const char *psz_subtitle )
3406 /* if we are provided a subtitle.sub file,
3407 * see if we don't have a subtitle.idx and use it instead */
3408 char *psz_idxpath = NULL;
3409 char *psz_extension = strrchr( psz_subtitle, '.');
3410 if( psz_extension && strcmp( psz_extension, ".sub" ) == 0 )
3412 psz_idxpath = strdup( psz_subtitle );
3413 if( psz_idxpath )
3415 struct stat st;
3417 psz_extension = psz_extension - psz_subtitle + psz_idxpath;
3418 strcpy( psz_extension, ".idx" );
3420 if( !vlc_stat( psz_idxpath, &st ) && S_ISREG( st.st_mode ) )
3422 msg_Dbg( p_input, "using %s as subtitle file instead of %s",
3423 psz_idxpath, psz_subtitle );
3424 psz_subtitle = psz_idxpath;
3429 char *psz_uri = vlc_path2uri( psz_subtitle, NULL );
3430 free( psz_idxpath );
3431 return psz_uri;
3434 /**/
3435 /* TODO FIXME nearly the same logic that snapshot code */
3436 char *input_CreateFilename(input_thread_t *input, input_item_t *item,
3437 const char *dir, const char *filenamefmt,
3438 const char *ext)
3440 char *path;
3441 char *filename = str_format(input, item, filenamefmt);
3442 if (unlikely(filename == NULL))
3443 return NULL;
3445 filename_sanitize(filename);
3447 if (((ext != NULL)
3448 ? asprintf(&path, "%s"DIR_SEP"%s.%s", dir, filename, ext)
3449 : asprintf(&path, "%s"DIR_SEP"%s", dir, filename)) < 0)
3450 path = NULL;
3452 free(filename);
3453 return path;
3456 int input_GetAttachments(input_thread_t *input,
3457 input_attachment_t ***attachments)
3459 input_thread_private_t *priv = input_priv(input);
3461 vlc_mutex_lock(&priv->p_item->lock);
3462 int attachments_count = priv->i_attachment;
3463 if (attachments_count <= 0)
3465 vlc_mutex_unlock(&priv->p_item->lock);
3466 *attachments = NULL;
3467 return 0;
3470 *attachments = vlc_alloc(attachments_count, sizeof(input_attachment_t*));
3471 if (!*attachments)
3472 return -1;
3474 for (int i = 0; i < attachments_count; i++)
3475 (*attachments)[i] = vlc_input_attachment_Duplicate(priv->attachment[i]);
3477 vlc_mutex_unlock(&priv->p_item->lock);
3478 return attachments_count;
3481 input_attachment_t *input_GetAttachment(input_thread_t *input, const char *name)
3483 input_thread_private_t *priv = input_priv(input);
3485 vlc_mutex_lock(&priv->p_item->lock);
3486 for (int i = 0; i < priv->i_attachment; i++)
3488 if (!strcmp( priv->attachment[i]->psz_name, name))
3490 input_attachment_t *attachment =
3491 vlc_input_attachment_Duplicate(priv->attachment[i] );
3492 vlc_mutex_unlock( &priv->p_item->lock );
3493 return attachment;
3496 vlc_mutex_unlock( &priv->p_item->lock );
3497 return NULL;