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 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_decoder.h>
38 #include "input_internal.h"
48 #include <vlc_dialog.h>
50 #include <vlc_charset.h>
52 #include <vlc_strings.h>
53 #include <vlc_modules.h>
54 #include <vlc_stream.h>
55 #include <vlc_stream_extractor.h>
56 #include <vlc_renderer_discovery.h>
58 /*****************************************************************************
60 *****************************************************************************/
61 enum input_create_option
{
62 INPUT_CREATE_OPTION_NONE
,
63 INPUT_CREATE_OPTION_PREPARSING
,
64 INPUT_CREATE_OPTION_THUMBNAILING
,
67 static void *Run( void * );
68 static void *Preparse( void * );
70 static input_thread_t
* Create ( vlc_object_t
*, input_thread_events_cb
, void *,
71 input_item_t
*, enum input_create_option option
,
72 input_resource_t
*, vlc_renderer_item_t
* );
73 static void Destroy ( input_thread_t
*p_input
);
74 static int Init ( input_thread_t
*p_input
);
75 static void End ( input_thread_t
*p_input
);
76 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
);
78 static inline int ControlPop( input_thread_t
*, int *, input_control_param_t
*, vlc_tick_t i_deadline
, bool b_postpone_seek
);
79 static void ControlRelease( int i_type
, const input_control_param_t
*p_param
);
80 static bool ControlIsSeekRequest( int i_type
);
81 static bool Control( input_thread_t
*, int, input_control_param_t
);
82 static void ControlPause( input_thread_t
*, vlc_tick_t
);
84 static int UpdateTitleSeekpointFromDemux( input_thread_t
* );
85 static void UpdateGenericFromDemux( input_thread_t
* );
86 static void UpdateTitleListfromDemux( input_thread_t
* );
88 static void MRLSections( const char *, int *, int *, int *, int *);
90 static input_source_t
*InputSourceNew( input_thread_t
*, const char *,
91 const char *psz_forced_demux
,
93 static void InputSourceDestroy( input_source_t
* );
94 static void InputSourceMeta( input_thread_t
*, input_source_t
*, vlc_meta_t
* );
97 //static void InputGetAttachments( input_thread_t *, input_source_t * );
98 static void SlaveDemux( input_thread_t
*p_input
);
99 static void SlaveSeek( input_thread_t
*p_input
);
101 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
);
102 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
);
103 static void InputGetExtraFiles( input_thread_t
*p_input
,
104 int *pi_list
, char ***pppsz_list
,
105 const char **psz_access
, const char *psz_path
);
107 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
,
108 const demux_t
***ppp_attachment_demux
,
109 int i_new
, input_attachment_t
**pp_new
, const demux_t
*p_demux
);
111 #define SLAVE_ADD_NOFLAG 0
112 #define SLAVE_ADD_FORCED (1<<0)
113 #define SLAVE_ADD_CANFAIL (1<<1)
114 #define SLAVE_ADD_SET_TIME (1<<2)
116 static int input_SlaveSourceAdd( input_thread_t
*, enum slave_type
,
117 const char *, unsigned );
118 static char *input_SubtitleFile2Uri( input_thread_t
*, const char * );
119 static void input_ChangeState( input_thread_t
*p_input
, int i_state
, vlc_tick_t
); /* TODO fix name */
123 * Create a new input_thread_t.
125 * You need to call input_Start on it when you are done
126 * adding callback on the variables/events you want to monitor.
128 * \param p_parent a vlc_object
129 * \param p_item an input item
130 * \param p_resource an optional input ressource
131 * \return a pointer to the spawned input thread
133 input_thread_t
*input_Create( vlc_object_t
*p_parent
,
134 input_thread_events_cb events_cb
, void *events_data
,
135 input_item_t
*p_item
,
136 input_resource_t
*p_resource
,
137 vlc_renderer_item_t
*p_renderer
)
139 return Create( p_parent
, events_cb
, events_data
, p_item
,
140 INPUT_CREATE_OPTION_NONE
, p_resource
, p_renderer
);
143 input_thread_t
*input_CreatePreparser( vlc_object_t
*parent
,
144 input_thread_events_cb events_cb
,
145 void *events_data
, input_item_t
*item
)
147 return Create( parent
, events_cb
, events_data
, item
,
148 INPUT_CREATE_OPTION_PREPARSING
, NULL
, NULL
);
151 input_thread_t
*input_CreateThumbnailer(vlc_object_t
*obj
,
152 input_thread_events_cb events_cb
,
153 void *events_data
, input_item_t
*item
)
155 return Create( obj
, events_cb
, events_data
, item
,
156 INPUT_CREATE_OPTION_THUMBNAILING
, NULL
, NULL
);
160 * Start a input_thread_t created by input_Create.
162 * You must not start an already running input_thread_t.
164 * \param the input thread to start
166 int input_Start( input_thread_t
*p_input
)
168 input_thread_private_t
*priv
= input_priv(p_input
);
169 void *(*func
)(void *) = Run
;
171 if( priv
->b_preparsing
)
174 assert( !priv
->is_running
);
175 /* Create thread and wait for its readiness. */
176 priv
->is_running
= !vlc_clone( &priv
->thread
, func
, priv
,
177 VLC_THREAD_PRIORITY_INPUT
);
178 if( !priv
->is_running
)
180 msg_Err( p_input
, "cannot create input thread" );
187 * Request a running input thread to stop and die
189 * \param p_input the input thread to stop
191 void input_Stop( input_thread_t
*p_input
)
193 input_thread_private_t
*sys
= input_priv(p_input
);
195 vlc_mutex_lock( &sys
->lock_control
);
196 /* Discard all pending controls */
197 for( size_t i
= 0; i
< sys
->i_control
; i
++ )
199 input_control_t
*ctrl
= &sys
->control
[i
];
200 ControlRelease( ctrl
->i_type
, &ctrl
->param
);
203 sys
->is_stopped
= true;
204 vlc_cond_signal( &sys
->wait_control
);
205 vlc_mutex_unlock( &sys
->lock_control
);
206 vlc_interrupt_kill( &sys
->interrupt
);
212 * It does not call input_Stop itself.
214 void input_Close( input_thread_t
*p_input
)
216 if( input_priv(p_input
)->is_running
)
217 vlc_join( input_priv(p_input
)->thread
, NULL
);
218 vlc_interrupt_deinit( &input_priv(p_input
)->interrupt
);
222 void input_SetTime( input_thread_t
*p_input
, vlc_tick_t i_time
, bool b_fast
)
224 input_control_param_t param
;
226 param
.time
.i_val
= i_time
;
227 param
.time
.b_fast_seek
= b_fast
;
228 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, ¶m
);
231 void input_SetPosition( input_thread_t
*p_input
, float f_position
, bool b_fast
)
233 input_control_param_t param
;
235 param
.pos
.f_val
= f_position
;
236 param
.pos
.b_fast_seek
= b_fast
;
237 input_ControlPush( p_input
, INPUT_CONTROL_SET_POSITION
, ¶m
);
241 * Get the item from an input thread
242 * FIXME it does not increase ref count of the item.
243 * if it is used after p_input is destroyed nothing prevent it from
246 input_item_t
*input_GetItem( input_thread_t
*p_input
)
248 assert( p_input
!= NULL
);
249 return input_priv(p_input
)->p_item
;
252 /*****************************************************************************
253 * This function creates a new input, and returns a pointer
254 * to its description. On error, it returns NULL.
256 * XXX Do not forget to update vlc_input.h if you add new variables.
257 *****************************************************************************/
258 static input_thread_t
*Create( vlc_object_t
*p_parent
,
259 input_thread_events_cb events_cb
, void *events_data
,
260 input_item_t
*p_item
,
261 enum input_create_option option
,
262 input_resource_t
*p_resource
,
263 vlc_renderer_item_t
*p_renderer
)
265 /* Allocate descriptor */
266 input_thread_private_t
*priv
;
268 priv
= vlc_custom_create( p_parent
, sizeof( *priv
), "input" );
269 if( unlikely(priv
== NULL
) )
272 input_thread_t
*p_input
= &priv
->input
;
274 char * psz_name
= input_item_GetName( p_item
);
275 const char *option_str
;
278 case INPUT_CREATE_OPTION_PREPARSING
:
279 option_str
= "preparsing ";
281 case INPUT_CREATE_OPTION_THUMBNAILING
:
282 option_str
= "thumbnailing ";
288 msg_Dbg( p_input
, "Creating an input for %s'%s'", option_str
, psz_name
);
291 /* Parse input options */
292 input_item_ApplyOptions( VLC_OBJECT(p_input
), p_item
);
294 /* Init Common fields */
295 priv
->events_cb
= events_cb
;
296 priv
->events_data
= events_data
;
297 priv
->b_preparsing
= option
== INPUT_CREATE_OPTION_PREPARSING
;
298 priv
->b_thumbnailing
= option
== INPUT_CREATE_OPTION_THUMBNAILING
;
299 priv
->b_can_pace_control
= true;
302 priv
->i_title_offset
= input_priv(p_input
)->i_seekpoint_offset
= 0;
303 priv
->i_state
= INIT_S
;
304 priv
->is_running
= false;
305 priv
->is_stopped
= false;
306 priv
->b_recording
= false;
308 priv
->normal_time
= VLC_TICK_0
;
309 TAB_INIT( priv
->i_attachment
, priv
->attachment
);
310 priv
->attachment_demux
= NULL
;
312 priv
->b_out_pace_control
= priv
->b_thumbnailing
;
313 priv
->p_renderer
= p_renderer
&& priv
->b_preparsing
== false ?
314 vlc_renderer_item_hold( p_renderer
) : NULL
;
316 priv
->viewpoint_changed
= false;
317 /* Fetch the viewpoint from the mediaplayer or the playlist if any */
318 vlc_viewpoint_t
*p_viewpoint
= var_InheritAddress( p_input
, "viewpoint" );
319 if (p_viewpoint
!= NULL
)
320 priv
->viewpoint
= *p_viewpoint
;
322 vlc_viewpoint_init( &priv
->viewpoint
);
324 priv
->i_last_es_cat
= UNKNOWN_ES
;
326 input_item_Hold( p_item
); /* Released in Destructor() */
327 priv
->p_item
= p_item
;
329 /* Init Input fields */
331 vlc_mutex_lock( &p_item
->lock
);
333 if( !p_item
->p_stats
)
334 p_item
->p_stats
= calloc( 1, sizeof(*p_item
->p_stats
) );
336 /* setup the preparse depth of the item
337 * if we are preparsing, use the i_preparse_depth of the parent item */
338 if( priv
->b_preparsing
|| priv
->b_thumbnailing
)
340 p_input
->obj
.logger
= NULL
;
341 p_input
->obj
.no_interact
= true;
345 char *psz_rec
= var_InheritString( p_parent
, "recursive" );
347 if( psz_rec
!= NULL
)
349 if ( !strcasecmp( psz_rec
, "none" ) )
350 p_item
->i_preparse_depth
= 0;
351 else if ( !strcasecmp( psz_rec
, "collapse" ) )
352 p_item
->i_preparse_depth
= 1;
354 p_item
->i_preparse_depth
= -1; /* default is expand */
357 p_item
->i_preparse_depth
= -1;
360 /* Make sure the interaction option is honored */
361 if( !var_InheritBool( p_input
, "interact" ) )
362 p_input
->obj
.no_interact
= true;
363 else if( p_item
->b_preparse_interact
)
365 /* If true, this item was asked explicitly to interact with the user
366 * (via libvlc_MetadataRequest). Sub items created from this input won't
367 * have this flag and won't interact with the user */
368 p_input
->obj
.no_interact
= false;
371 vlc_mutex_unlock( &p_item
->lock
);
379 priv
->p_resource
= input_resource_Hold( p_resource
);
381 priv
->p_resource
= input_resource_New( VLC_OBJECT( p_input
) );
382 input_resource_SetInput( priv
->p_resource
, p_input
);
384 /* Init control buffer */
385 vlc_mutex_init( &priv
->lock_control
);
386 vlc_cond_init( &priv
->wait_control
);
388 vlc_interrupt_init(&priv
->interrupt
);
390 /* Create Object Variables for private use only */
391 input_ConfigVarInit( p_input
);
393 /* Remove 'Now playing' info as it is probably outdated */
394 input_item_SetNowPlaying( p_item
, NULL
);
395 input_item_SetESNowPlaying( p_item
, NULL
);
398 if( !priv
->b_preparsing
&& var_InheritBool( p_input
, "stats" ) )
399 priv
->stats
= input_stats_Create();
403 priv
->p_es_out_display
= input_EsOutNew( p_input
, priv
->rate
);
404 priv
->p_es_out
= NULL
;
409 static void Destroy(input_thread_t
*input
)
411 input_thread_private_t
*priv
= input_priv(input
);
414 char *name
= input_item_GetName(priv
->p_item
);
415 msg_Dbg(input
, "destroying input for '%s'", name
);
419 if (priv
->p_renderer
!= NULL
)
420 vlc_renderer_item_release(priv
->p_renderer
);
421 if (priv
->p_es_out_display
!= NULL
)
422 es_out_Delete(priv
->p_es_out_display
);
424 if (priv
->p_resource
!= NULL
)
425 input_resource_Release(priv
->p_resource
);
427 input_item_Release(priv
->p_item
);
429 if (priv
->stats
!= NULL
)
430 input_stats_Destroy(priv
->stats
);
432 for (size_t i
= 0; i
< priv
->i_control
; i
++)
434 input_control_t
*ctrl
= &priv
->control
[i
];
436 ControlRelease(ctrl
->i_type
, &ctrl
->param
);
439 vlc_cond_destroy(&priv
->wait_control
);
440 vlc_mutex_destroy(&priv
->lock_control
);
441 vlc_object_delete(VLC_OBJECT(input
));
444 /*****************************************************************************
445 * Run: main thread loop
446 * This is the "normal" thread that spawns the input processing chain,
447 * reads the stream, cleans up and waits
448 *****************************************************************************/
449 static void *Run( void *data
)
451 input_thread_private_t
*priv
= data
;
452 input_thread_t
*p_input
= &priv
->input
;
454 vlc_interrupt_set(&priv
->interrupt
);
456 if( !Init( p_input
) )
458 if( priv
->b_can_pace_control
&& priv
->b_out_pace_control
)
460 /* We don't want a high input priority here or we'll
461 * end-up sucking up all the CPU time */
462 vlc_set_priority( priv
->thread
, VLC_THREAD_PRIORITY_LOW
);
465 MainLoop( p_input
, true ); /* FIXME it can be wrong (like with VLM) */
471 input_SendEventDead( p_input
);
475 static void *Preparse( void *data
)
477 input_thread_private_t
*priv
= data
;
478 input_thread_t
*p_input
= &priv
->input
;
480 vlc_interrupt_set(&priv
->interrupt
);
482 if( !Init( p_input
) )
483 { /* if the demux is a playlist, call Mainloop that will call
484 * demux_Demux in order to fetch sub items */
485 bool b_is_playlist
= false;
487 if ( input_item_ShouldPreparseSubItems( priv
->p_item
)
488 && demux_Control( priv
->master
->p_demux
, DEMUX_IS_PLAYLIST
,
490 b_is_playlist
= false;
492 MainLoop( p_input
, false );
496 input_SendEventDead( p_input
);
500 bool input_Stopped( input_thread_t
*input
)
502 input_thread_private_t
*sys
= input_priv(input
);
505 vlc_mutex_lock( &sys
->lock_control
);
506 ret
= sys
->is_stopped
;
507 vlc_mutex_unlock( &sys
->lock_control
);
511 /*****************************************************************************
512 * Main loop: Fill buffers from access, and demux
513 *****************************************************************************/
517 * It asks the demuxer to demux some data
519 static void MainLoopDemux( input_thread_t
*p_input
, bool *pb_changed
)
521 input_thread_private_t
* p_priv
= input_priv(p_input
);
522 demux_t
*p_demux
= p_priv
->master
->p_demux
;
523 int i_ret
= VLC_DEMUXER_SUCCESS
;
527 if( p_priv
->i_stop
> 0 )
530 if( demux_Control( p_demux
, DEMUX_GET_TIME
, &i_time
) )
531 i_time
= VLC_TICK_INVALID
;
533 if( p_priv
->i_stop
<= i_time
)
534 i_ret
= VLC_DEMUXER_EOF
;
537 if( i_ret
== VLC_DEMUXER_SUCCESS
)
538 i_ret
= demux_Demux( p_demux
);
540 i_ret
= i_ret
> 0 ? VLC_DEMUXER_SUCCESS
: ( i_ret
< 0 ? VLC_DEMUXER_EGENERIC
: VLC_DEMUXER_EOF
);
542 if( i_ret
== VLC_DEMUXER_SUCCESS
)
544 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE_LIST
) )
545 UpdateTitleListfromDemux( p_input
);
547 if( p_priv
->master
->b_title_demux
)
549 i_ret
= UpdateTitleSeekpointFromDemux( p_input
);
553 UpdateGenericFromDemux( p_input
);
556 if( i_ret
== VLC_DEMUXER_EOF
)
558 msg_Dbg( p_input
, "EOF reached" );
559 p_priv
->master
->b_eof
= true;
560 es_out_Eos(p_priv
->p_es_out
);
562 else if( i_ret
== VLC_DEMUXER_EGENERIC
)
564 input_ChangeState( p_input
, ERROR_S
, VLC_TICK_INVALID
);
566 else if( p_priv
->i_slave
> 0 )
567 SlaveDemux( p_input
);
570 static int MainLoopTryRepeat( input_thread_t
*p_input
)
572 int i_repeat
= var_GetInteger( p_input
, "input-repeat" );
578 msg_Dbg( p_input
, "repeating the same input (%d)", i_repeat
);
582 var_SetInteger( p_input
, "input-repeat", i_repeat
);
585 input_thread_private_t
*priv
= input_priv(p_input
);
586 /* Seek to start title/seekpoint */
587 val
.i_int
= priv
->master
->i_title_start
- priv
->master
->i_title_offset
;
588 if( val
.i_int
< 0 || val
.i_int
>= priv
->master
->i_title
)
590 input_ControlPushHelper( p_input
,
591 INPUT_CONTROL_SET_TITLE
, &val
);
593 val
.i_int
= priv
->master
->i_seekpoint_start
-
594 priv
->master
->i_seekpoint_offset
;
595 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
596 input_ControlPushHelper( p_input
,
597 INPUT_CONTROL_SET_SEEKPOINT
, &val
);
599 /* Seek to start position */
600 if( priv
->i_start
> 0 )
601 input_SetTime( p_input
, priv
->i_start
, false );
603 input_SetPosition( p_input
, 0.0f
, false );
609 * Update timing infos and statistics.
611 static void MainLoopStatistics( input_thread_t
*p_input
)
613 input_thread_private_t
*priv
= input_priv(p_input
);
614 double f_position
= 0.0;
618 /* update input status variables */
619 if( demux_Control( priv
->master
->p_demux
,
620 DEMUX_GET_POSITION
, &f_position
) )
623 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
624 i_time
= VLC_TICK_INVALID
;
626 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
627 i_length
= VLC_TICK_INVALID
;
629 /* In case of failure (not implemented or in case of seek), use the last
630 * normal_time value (that is VLC_TICK_0 by default). */
631 demux_Control( priv
->master
->p_demux
, DEMUX_GET_NORMAL_TIME
, &priv
->normal_time
);
633 es_out_SetTimes( priv
->p_es_out
, f_position
, i_time
, priv
->normal_time
,
636 struct input_stats_t new_stats
;
637 if( priv
->stats
!= NULL
)
638 input_stats_Compute( priv
->stats
, &new_stats
);
640 vlc_mutex_lock( &priv
->p_item
->lock
);
641 if( priv
->stats
!= NULL
)
642 *priv
->p_item
->p_stats
= new_stats
;
643 vlc_mutex_unlock( &priv
->p_item
->lock
);
645 input_SendEventStatistics( p_input
, &new_stats
);
650 * The main input loop.
652 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
)
654 vlc_tick_t i_intf_update
= 0;
655 vlc_tick_t i_last_seek_mdate
= 0;
657 if( b_interactive
&& var_InheritBool( p_input
, "start-paused" ) )
658 ControlPause( p_input
, vlc_tick_now() );
660 bool b_pause_after_eof
= b_interactive
&&
661 var_InheritBool( p_input
, "play-and-pause" );
662 bool b_paused_at_eof
= false;
664 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
665 const bool b_can_demux
= p_demux
->pf_demux
!= NULL
;
667 while( !input_Stopped( p_input
) && input_priv(p_input
)->i_state
!= ERROR_S
)
669 vlc_tick_t i_wakeup
= -1;
670 bool b_paused
= input_priv(p_input
)->i_state
== PAUSE_S
;
671 /* FIXME if input_priv(p_input)->i_state == PAUSE_S the access/access_demux
672 * is paused -> this may cause problem with some of them
673 * The same problem can be seen when seeking while paused */
675 b_paused
= !es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
676 || input_priv(p_input
)->master
->b_eof
;
680 if( !input_priv(p_input
)->master
->b_eof
)
682 bool b_force_update
= false;
684 MainLoopDemux( p_input
, &b_force_update
);
687 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
691 b_paused_at_eof
= false;
693 else if( !es_out_GetEmpty( input_priv(p_input
)->p_es_out
) )
695 msg_Dbg( p_input
, "waiting decoder fifos to empty" );
696 i_wakeup
= vlc_tick_now() + INPUT_IDLE_SLEEP
;
698 /* Pause after eof only if the input is pausable.
699 * This way we won't trigger timeshifting for nothing */
700 else if( b_pause_after_eof
&& input_priv(p_input
)->b_can_pause
)
702 if( b_paused_at_eof
)
705 input_control_param_t param
;
706 param
.val
.i_int
= PAUSE_S
;
708 msg_Dbg( p_input
, "pausing at EOF (pause after each)");
709 Control( p_input
, INPUT_CONTROL_SET_STATE
, param
);
712 b_paused_at_eof
= true;
716 if( MainLoopTryRepeat( p_input
) )
720 /* Update interface and statistics */
721 vlc_tick_t now
= vlc_tick_now();
722 if( now
>= i_intf_update
)
724 MainLoopStatistics( p_input
);
725 i_intf_update
= now
+ VLC_TICK_FROM_MS(250);
732 vlc_tick_t i_deadline
= i_wakeup
;
734 /* Postpone seeking until ES buffering is complete or at most
736 bool b_postpone
= es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
737 && !input_priv(p_input
)->master
->b_eof
;
740 vlc_tick_t now
= vlc_tick_now();
742 /* Recheck ES buffer level every 20 ms when seeking */
743 if( now
< i_last_seek_mdate
+ VLC_TICK_FROM_MS(125)
744 && (i_deadline
< 0 || i_deadline
> now
+ VLC_TICK_FROM_MS(20)) )
745 i_deadline
= now
+ VLC_TICK_FROM_MS(20);
751 input_control_param_t param
;
753 if( ControlPop( p_input
, &i_type
, ¶m
, i_deadline
, b_postpone
) )
757 break; /* Wake-up time reached */
761 msg_Dbg( p_input
, "control type=%d", i_type
);
763 if( Control( p_input
, i_type
, param
) )
765 if( ControlIsSeekRequest( i_type
) )
766 i_last_seek_mdate
= vlc_tick_now();
770 /* Update the wakeup time */
772 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
778 static int InitSout( input_thread_t
* p_input
)
780 input_thread_private_t
*priv
= input_priv(p_input
);
782 if( priv
->b_preparsing
)
785 /* Find a usable sout and attach it to p_input */
786 char *psz
= var_GetNonEmptyString( p_input
, "sout" );
787 if( priv
->p_renderer
)
789 /* Keep sout if it comes from a renderer and if the user didn't touch
791 bool keep_sout
= psz
== NULL
;
794 const char *psz_renderer_sout
= vlc_renderer_item_sout( priv
->p_renderer
);
795 if( asprintf( &psz
, "#%s", psz_renderer_sout
) < 0 )
798 var_SetBool( p_input
, "sout-keep", true );
800 if( psz
&& strncasecmp( priv
->p_item
->psz_uri
, "vlc:", 4 ) )
802 priv
->p_sout
= input_resource_RequestSout( priv
->p_resource
, NULL
, psz
);
803 if( priv
->p_sout
== NULL
)
805 input_ChangeState( p_input
, ERROR_S
, VLC_TICK_INVALID
);
806 msg_Err( p_input
, "cannot start stream output instance, " \
814 input_resource_RequestSout( priv
->p_resource
, NULL
, NULL
);
822 static void InitTitle( input_thread_t
* p_input
, bool had_titles
)
824 input_thread_private_t
*priv
= input_priv(p_input
);
825 input_source_t
*p_master
= priv
->master
;
827 if( priv
->b_preparsing
)
830 vlc_mutex_lock( &priv
->p_item
->lock
);
831 priv
->i_title_offset
= p_master
->i_title_offset
;
832 priv
->i_seekpoint_offset
= p_master
->i_seekpoint_offset
;
835 priv
->b_can_pace_control
= p_master
->b_can_pace_control
;
836 priv
->b_can_pause
= p_master
->b_can_pause
;
837 priv
->b_can_rate_control
= p_master
->b_can_rate_control
;
838 vlc_mutex_unlock( &priv
->p_item
->lock
);
840 /* Send event only if the count is valid or if titles are gone */
841 if (had_titles
|| p_master
->i_title
> 0)
842 input_SendEventTitle( p_input
, &(struct vlc_input_event_title
) {
843 .action
= VLC_INPUT_TITLE_NEW_LIST
,
845 .array
= p_master
->title
,
846 .count
= p_master
->i_title
,
851 static void StartTitle( input_thread_t
* p_input
)
853 input_thread_private_t
*priv
= input_priv(p_input
);
856 /* Start title/chapter */
857 val
.i_int
= priv
->master
->i_title_start
- priv
->master
->i_title_offset
;
858 if( val
.i_int
> 0 && val
.i_int
< priv
->master
->i_title
)
859 input_ControlPushHelper( p_input
, INPUT_CONTROL_SET_TITLE
, &val
);
861 val
.i_int
= priv
->master
->i_seekpoint_start
-
862 priv
->master
->i_seekpoint_offset
;
863 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
864 input_ControlPushHelper( p_input
, INPUT_CONTROL_SET_SEEKPOINT
, &val
);
866 /* Start/stop/run time */
867 priv
->i_start
= llroundf((float)CLOCK_FREQ
868 * var_GetFloat( p_input
, "start-time" ));
869 priv
->i_stop
= llroundf((float)CLOCK_FREQ
870 * var_GetFloat( p_input
, "stop-time" ));
871 if( priv
->i_stop
<= 0 )
873 priv
->i_stop
= llroundf((float)CLOCK_FREQ
874 * var_GetFloat( p_input
, "run-time" ));
875 if( priv
->i_stop
< 0 )
877 msg_Warn( p_input
, "invalid run-time ignored" );
881 priv
->i_stop
+= priv
->i_start
;
884 if( priv
->i_start
> 0 )
886 msg_Dbg( p_input
, "starting at time: %"PRId64
"s",
887 SEC_FROM_VLC_TICK(priv
->i_start
) );
889 input_SetTime( p_input
, priv
->i_start
, false );
891 if( priv
->i_stop
> 0 && priv
->i_stop
<= priv
->i_start
)
893 msg_Warn( p_input
, "invalid stop-time ignored" );
898 static int SlaveCompare(const void *a
, const void *b
)
900 const input_item_slave_t
*p_slave0
= *((const input_item_slave_t
**) a
);
901 const input_item_slave_t
*p_slave1
= *((const input_item_slave_t
**) b
);
903 if( p_slave0
== NULL
|| p_slave1
== NULL
)
905 /* Put NULL (or rejected) subs at the end */
906 return p_slave0
== NULL
? 1 : p_slave1
== NULL
? -1 : 0;
909 if( p_slave0
->i_priority
> p_slave1
->i_priority
)
912 if( p_slave0
->i_priority
< p_slave1
->i_priority
)
918 static bool SlaveExists( input_item_slave_t
**pp_slaves
, int i_slaves
,
921 for( int i
= 0; i
< i_slaves
; i
++ )
923 if( pp_slaves
[i
] != NULL
924 && !strcmp( pp_slaves
[i
]->psz_uri
, psz_uri
) )
930 static void RequestSubRate( input_thread_t
*p_input
, float f_slave_fps
)
932 input_thread_private_t
*priv
= input_priv(p_input
);
933 const float f_fps
= input_priv(p_input
)->master
->f_fps
;
934 if( f_fps
> 1.f
&& f_slave_fps
> 1.f
)
935 priv
->slave_subs_rate
= f_fps
/ f_slave_fps
;
936 else if ( priv
->slave_subs_rate
!= 0 )
937 priv
->slave_subs_rate
= 1.f
;
940 static void SetSubtitlesOptions( input_thread_t
*p_input
)
942 /* Get fps and set it if not already set */
943 const float f_fps
= input_priv(p_input
)->master
->f_fps
;
946 var_SetFloat( p_input
, "sub-original-fps", f_fps
);
947 RequestSubRate( p_input
, var_InheritFloat( p_input
, "sub-fps" ) );
951 static void GetVarSlaves( input_thread_t
*p_input
,
952 input_item_slave_t
***ppp_slaves
, int *p_slaves
)
954 char *psz
= var_GetNonEmptyString( p_input
, "input-slave" );
958 input_item_slave_t
**pp_slaves
= *ppp_slaves
;
959 int i_slaves
= *p_slaves
;
964 while( *psz
== ' ' || *psz
== '#' )
967 char *psz_delim
= strchr( psz
, '#' );
974 char *uri
= strstr(psz
, "://")
975 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
980 input_item_slave_t
*p_slave
=
981 input_item_slave_New( uri
, SLAVE_TYPE_AUDIO
, SLAVE_PRIORITY_USER
);
984 if( unlikely( p_slave
== NULL
) )
986 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
990 *ppp_slaves
= pp_slaves
; /* in case of realloc */
991 *p_slaves
= i_slaves
;
994 static void LoadSlaves( input_thread_t
*p_input
)
996 input_item_slave_t
**pp_slaves
;
998 TAB_INIT( i_slaves
, pp_slaves
);
1000 /* Look for and add slaves */
1002 char *psz_subtitle
= var_GetNonEmptyString( p_input
, "sub-file" );
1003 if( psz_subtitle
!= NULL
)
1005 msg_Dbg( p_input
, "forced subtitle: %s", psz_subtitle
);
1006 char *psz_uri
= input_SubtitleFile2Uri( p_input
, psz_subtitle
);
1007 free( psz_subtitle
);
1008 psz_subtitle
= NULL
;
1009 if( psz_uri
!= NULL
)
1011 input_item_slave_t
*p_slave
=
1012 input_item_slave_New( psz_uri
, SLAVE_TYPE_SPU
,
1013 SLAVE_PRIORITY_USER
);
1017 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1018 psz_subtitle
= p_slave
->psz_uri
;
1023 if( var_GetBool( p_input
, "sub-autodetect-file" ) )
1025 /* Add local subtitles */
1026 char *psz_autopath
= var_GetNonEmptyString( p_input
, "sub-autodetect-path" );
1028 if( subtitles_Detect( p_input
, psz_autopath
, input_priv(p_input
)->p_item
->psz_uri
,
1029 &pp_slaves
, &i_slaves
) == VLC_SUCCESS
)
1031 /* check that we did not add the subtitle through sub-file */
1032 if( psz_subtitle
!= NULL
)
1034 for( int i
= 1; i
< i_slaves
; i
++ )
1036 input_item_slave_t
*p_curr
= pp_slaves
[i
];
1038 && !strcmp( psz_subtitle
, p_curr
->psz_uri
) )
1040 /* reject current sub */
1041 input_item_slave_Delete( p_curr
);
1042 pp_slaves
[i
] = NULL
;
1047 free( psz_autopath
);
1050 /* Add slaves found by the directory demuxer or via libvlc */
1051 input_item_t
*p_item
= input_priv(p_input
)->p_item
;
1052 vlc_mutex_lock( &p_item
->lock
);
1054 /* Move item slaves to local pp_slaves */
1055 for( int i
= 0; i
< p_item
->i_slaves
; i
++ )
1057 input_item_slave_t
*p_slave
= p_item
->pp_slaves
[i
];
1058 if( !SlaveExists( pp_slaves
, i_slaves
, p_slave
->psz_uri
) )
1059 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1061 input_item_slave_Delete( p_slave
);
1063 /* Slaves that are successfully loaded will be added back to the item */
1064 TAB_CLEAN( p_item
->i_slaves
, p_item
->pp_slaves
);
1065 vlc_mutex_unlock( &p_item
->lock
);
1067 /* Add slaves from the "input-slave" option */
1068 GetVarSlaves( p_input
, &pp_slaves
, &i_slaves
);
1071 qsort( pp_slaves
, i_slaves
, sizeof (input_item_slave_t
*),
1074 /* add all detected slaves */
1075 bool p_forced
[2] = { false, false };
1076 static_assert( SLAVE_TYPE_AUDIO
<= 1 && SLAVE_TYPE_SPU
<= 1,
1077 "slave type size mismatch");
1078 for( int i
= 0; i
< i_slaves
&& pp_slaves
[i
] != NULL
; i
++ )
1080 input_item_slave_t
*p_slave
= pp_slaves
[i
];
1081 /* Slaves added via options should not fail */
1082 unsigned i_flags
= p_slave
->i_priority
!= SLAVE_PRIORITY_USER
1083 ? SLAVE_ADD_CANFAIL
: SLAVE_ADD_NOFLAG
;
1084 bool b_forced
= false;
1086 /* Force the first subtitle with the highest priority or with the
1088 if( !p_forced
[p_slave
->i_type
]
1089 && ( p_slave
->b_forced
|| p_slave
->i_priority
== SLAVE_PRIORITY_USER
) )
1091 i_flags
|= SLAVE_ADD_FORCED
;
1095 if( input_SlaveSourceAdd( p_input
, p_slave
->i_type
, p_slave
->psz_uri
,
1096 i_flags
) == VLC_SUCCESS
)
1098 input_item_AddSlave( input_priv(p_input
)->p_item
, p_slave
);
1100 p_forced
[p_slave
->i_type
] = true;
1103 input_item_slave_Delete( p_slave
);
1105 TAB_CLEAN( i_slaves
, pp_slaves
);
1107 /* Load subtitles from attachments */
1108 int i_attachment
= 0;
1109 input_attachment_t
**pp_attachment
= NULL
;
1111 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
1112 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
1114 const input_attachment_t
*a
= input_priv(p_input
)->attachment
[i
];
1115 if( !strcmp( a
->psz_mime
, "application/x-srt" ) )
1116 TAB_APPEND( i_attachment
, pp_attachment
,
1117 vlc_input_attachment_New( a
->psz_name
, NULL
,
1118 a
->psz_description
, NULL
, 0 ) );
1120 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1122 if( i_attachment
> 0 )
1123 var_Create( p_input
, "sub-description", VLC_VAR_STRING
);
1124 for( int i
= 0; i
< i_attachment
; i
++ )
1126 input_attachment_t
*a
= pp_attachment
[i
];
1130 if( a
->psz_name
[0] &&
1131 asprintf( &psz_mrl
, "attachment://%s", a
->psz_name
) >= 0 )
1133 var_SetString( p_input
, "sub-description", a
->psz_description
? a
->psz_description
: "");
1135 /* Force the first subtitle from attachment if there is no
1136 * subtitles already forced */
1137 if( input_SlaveSourceAdd( p_input
, SLAVE_TYPE_SPU
, psz_mrl
,
1138 p_forced
[ SLAVE_TYPE_SPU
] ?
1139 SLAVE_ADD_NOFLAG
: SLAVE_ADD_FORCED
) == VLC_SUCCESS
)
1140 p_forced
[ SLAVE_TYPE_SPU
] = true;
1143 /* Don't update item slaves for attachements */
1145 vlc_input_attachment_Delete( a
);
1147 free( pp_attachment
);
1148 if( i_attachment
> 0 )
1149 var_Destroy( p_input
, "sub-description" );
1152 static void UpdatePtsDelay( input_thread_t
*p_input
)
1154 input_thread_private_t
*p_sys
= input_priv(p_input
);
1156 /* Get max pts delay from input source */
1157 vlc_tick_t i_pts_delay
= p_sys
->master
->i_pts_delay
;
1158 for( int i
= 0; i
< p_sys
->i_slave
; i
++ )
1159 i_pts_delay
= __MAX( i_pts_delay
, p_sys
->slave
[i
]->i_pts_delay
);
1161 if( i_pts_delay
< 0 )
1164 /* Update cr_average depending on the caching */
1165 const int i_cr_average
= var_GetInteger( p_input
, "cr-average" ) * i_pts_delay
/ DEFAULT_PTS_DELAY
;
1167 es_out_SetJitter( input_priv(p_input
)->p_es_out
, i_pts_delay
, 0, i_cr_average
);
1170 static void InitPrograms( input_thread_t
* p_input
)
1176 /* Compute correct pts_delay */
1177 UpdatePtsDelay( p_input
);
1180 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1181 if( input_priv(p_input
)->p_sout
&& !input_priv(p_input
)->p_renderer
)
1185 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1189 TAB_INIT(count
, tab
);
1190 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1192 prgm
= strtok_r( NULL
, ",", &buf
) )
1194 TAB_APPEND(count
, tab
, atoi(prgm
));
1198 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1199 /* Note : we should remove the "program" callback. */
1203 else if( var_GetBool( p_input
, "sout-all" ) )
1205 i_es_out_mode
= ES_OUT_MODE_ALL
;
1208 es_out_SetMode( input_priv(p_input
)->p_es_out
, i_es_out_mode
);
1210 /* Inform the demuxer about waited group (needed only for DVB) */
1211 if( i_es_out_mode
== ES_OUT_MODE_ALL
)
1213 demux_Control( input_priv(p_input
)->master
->p_demux
,
1214 DEMUX_SET_GROUP_ALL
);
1216 else if( i_es_out_mode
== ES_OUT_MODE_PARTIAL
)
1218 demux_Control( input_priv(p_input
)->master
->p_demux
,
1219 DEMUX_SET_GROUP_LIST
, count
, tab
);
1224 int program
= es_out_GetGroupForced( input_priv(p_input
)->p_es_out
);
1226 demux_Control( input_priv(p_input
)->master
->p_demux
,
1227 DEMUX_SET_GROUP_DEFAULT
);
1229 demux_Control( input_priv(p_input
)->master
->p_demux
,
1230 DEMUX_SET_GROUP_LIST
, (size_t)1,
1231 (const int *)&program
);
1235 static int Init( input_thread_t
* p_input
)
1237 input_thread_private_t
*priv
= input_priv(p_input
);
1238 input_source_t
*master
;
1241 input_ChangeState( p_input
, OPENING_S
, VLC_TICK_INVALID
);
1242 input_SendEventCache( p_input
, 0.0 );
1244 if( var_Type( vlc_object_parent(p_input
), "meta-file" ) )
1246 msg_Dbg( p_input
, "Input is a meta file: disabling unneeded options" );
1247 var_SetString( p_input
, "sout", "" );
1248 var_SetBool( p_input
, "sout-all", false );
1249 var_SetString( p_input
, "input-slave", "" );
1250 var_SetInteger( p_input
, "input-repeat", 0 );
1251 var_SetString( p_input
, "sub-file", "" );
1252 var_SetBool( p_input
, "sub-autodetect-file", false );
1256 if( InitSout( p_input
) )
1261 priv
->p_es_out
= input_EsOutTimeshiftNew( p_input
, priv
->p_es_out_display
, priv
->rate
);
1262 if( priv
->p_es_out
== NULL
)
1266 master
= InputSourceNew( p_input
, priv
->p_item
->psz_uri
, NULL
, false );
1267 if( master
== NULL
)
1269 priv
->master
= master
;
1271 InitTitle( p_input
, false );
1273 /* Load master infos */
1275 vlc_tick_t i_length
;
1276 if( demux_Control( master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
1277 i_length
= VLC_TICK_INVALID
;
1278 if( i_length
== VLC_TICK_INVALID
)
1279 i_length
= input_item_GetDuration( priv
->p_item
);
1281 input_SendEventTimes( p_input
, 0.0, VLC_TICK_INVALID
, priv
->normal_time
,
1284 if( !priv
->b_preparsing
)
1286 StartTitle( p_input
);
1287 SetSubtitlesOptions( p_input
);
1288 LoadSlaves( p_input
);
1289 InitPrograms( p_input
);
1291 double f_rate
= var_GetFloat( p_input
, "rate" );
1292 if( f_rate
!= 0.0 && f_rate
!= 1.0 )
1294 vlc_value_t val
= { .f_float
= f_rate
};
1295 input_ControlPushHelper( p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1299 if( !priv
->b_preparsing
&& priv
->p_sout
)
1301 priv
->b_out_pace_control
= priv
->p_sout
->i_out_pace_nocontrol
> 0;
1303 msg_Dbg( p_input
, "starting in %ssync mode",
1304 priv
->b_out_pace_control
? "a" : "" );
1307 vlc_meta_t
*p_meta
= vlc_meta_New();
1308 if( p_meta
!= NULL
)
1310 /* Get meta data from users */
1311 InputMetaUser( p_input
, p_meta
);
1313 /* Get meta data from master input */
1314 InputSourceMeta( p_input
, master
, p_meta
);
1316 /* And from slave */
1317 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1318 InputSourceMeta( p_input
, priv
->slave
[i
], p_meta
);
1320 es_out_ControlSetMeta( priv
->p_es_out
, p_meta
);
1321 vlc_meta_Delete( p_meta
);
1324 msg_Dbg( p_input
, "`%s' successfully opened",
1325 input_priv(p_input
)->p_item
->psz_uri
);
1327 /* initialization is complete */
1328 input_ChangeState( p_input
, PLAYING_S
, vlc_tick_now() );
1333 input_ChangeState( p_input
, ERROR_S
, VLC_TICK_INVALID
);
1335 if( input_priv(p_input
)->p_es_out
)
1336 es_out_Delete( input_priv(p_input
)->p_es_out
);
1337 es_out_SetMode( input_priv(p_input
)->p_es_out_display
, ES_OUT_MODE_END
);
1338 if( input_priv(p_input
)->p_resource
)
1340 if( input_priv(p_input
)->p_sout
)
1341 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1342 input_priv(p_input
)->p_sout
, NULL
);
1343 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1344 if( input_priv(p_input
)->p_resource
)
1346 input_resource_Release( input_priv(p_input
)->p_resource
);
1347 input_priv(p_input
)->p_resource
= NULL
;
1351 /* Mark them deleted */
1352 input_priv(p_input
)->p_es_out
= NULL
;
1353 input_priv(p_input
)->p_sout
= NULL
;
1355 return VLC_EGENERIC
;
1358 /*****************************************************************************
1359 * End: end the input thread
1360 *****************************************************************************/
1361 static void End( input_thread_t
* p_input
)
1363 input_thread_private_t
*priv
= input_priv(p_input
);
1365 /* We are at the end */
1366 input_ChangeState( p_input
, END_S
, VLC_TICK_INVALID
);
1368 /* Stop es out activity */
1369 es_out_SetMode( priv
->p_es_out
, ES_OUT_MODE_NONE
);
1372 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1373 InputSourceDestroy( priv
->slave
[i
] );
1374 free( priv
->slave
);
1376 /* Clean up master */
1377 InputSourceDestroy( priv
->master
);
1378 priv
->i_title_offset
= 0;
1379 priv
->i_seekpoint_offset
= 0;
1381 /* Unload all modules */
1382 if( priv
->p_es_out
)
1383 es_out_Delete( priv
->p_es_out
);
1384 es_out_SetMode( priv
->p_es_out_display
, ES_OUT_MODE_END
);
1386 if( priv
->stats
!= NULL
)
1388 input_item_t
*item
= priv
->p_item
;
1389 /* make sure we are up to date */
1390 vlc_mutex_lock( &item
->lock
);
1391 input_stats_Compute( priv
->stats
, item
->p_stats
);
1392 vlc_mutex_unlock( &item
->lock
);
1395 vlc_mutex_lock( &priv
->p_item
->lock
);
1396 if( priv
->i_attachment
> 0 )
1398 for( int i
= 0; i
< priv
->i_attachment
; i
++ )
1399 vlc_input_attachment_Delete( priv
->attachment
[i
] );
1400 TAB_CLEAN( priv
->i_attachment
, priv
->attachment
);
1401 free( priv
->attachment_demux
);
1402 priv
->attachment_demux
= NULL
;
1405 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1408 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1409 input_priv(p_input
)->p_sout
, NULL
);
1410 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1411 if( input_priv(p_input
)->p_resource
)
1413 input_resource_Release( input_priv(p_input
)->p_resource
);
1414 input_priv(p_input
)->p_resource
= NULL
;
1418 /*****************************************************************************
1420 *****************************************************************************/
1421 int input_ControlPush( input_thread_t
*p_input
,
1422 int i_type
, const input_control_param_t
*p_param
)
1424 input_thread_private_t
*sys
= input_priv(p_input
);
1426 vlc_mutex_lock( &sys
->lock_control
);
1427 if( sys
->is_stopped
|| sys
->i_control
>= INPUT_CONTROL_FIFO_SIZE
)
1429 if( sys
->is_stopped
)
1430 msg_Dbg( p_input
, "input control stopped, trashing type=%d",
1433 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1436 ControlRelease( i_type
, p_param
);
1437 vlc_mutex_unlock( &sys
->lock_control
);
1438 return VLC_EGENERIC
;
1447 memset( &c
.param
, 0, sizeof(c
.param
) );
1449 sys
->control
[sys
->i_control
++] = c
;
1451 vlc_cond_signal( &sys
->wait_control
);
1452 vlc_mutex_unlock( &sys
->lock_control
);
1457 static size_t ControlGetReducedIndexLocked( input_thread_t
*p_input
)
1459 const int i_lt
= input_priv(p_input
)->control
[0].i_type
;
1461 for( i
= 1; i
< input_priv(p_input
)->i_control
; i
++ )
1463 const int i_ct
= input_priv(p_input
)->control
[i
].i_type
;
1466 ( i_ct
== INPUT_CONTROL_SET_STATE
||
1467 i_ct
== INPUT_CONTROL_SET_RATE
||
1468 i_ct
== INPUT_CONTROL_SET_POSITION
||
1469 i_ct
== INPUT_CONTROL_SET_TIME
||
1470 i_ct
== INPUT_CONTROL_SET_PROGRAM
||
1471 i_ct
== INPUT_CONTROL_SET_TITLE
||
1472 i_ct
== INPUT_CONTROL_SET_SEEKPOINT
) )
1478 /* TODO but that's not that important
1479 - merge SET_X with SET_X_CMD
1480 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1481 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1491 static inline int ControlPop( input_thread_t
*p_input
,
1492 int *pi_type
, input_control_param_t
*p_param
,
1493 vlc_tick_t i_deadline
, bool b_postpone_seek
)
1495 input_thread_private_t
*p_sys
= input_priv(p_input
);
1497 vlc_mutex_lock( &p_sys
->lock_control
);
1498 while( p_sys
->i_control
<= 0 ||
1499 ( b_postpone_seek
&& ControlIsSeekRequest( p_sys
->control
[0].i_type
) ) )
1501 if( p_sys
->is_stopped
)
1503 vlc_mutex_unlock( &p_sys
->lock_control
);
1504 return VLC_EGENERIC
;
1507 if( i_deadline
>= 0 )
1509 if( vlc_cond_timedwait( &p_sys
->wait_control
, &p_sys
->lock_control
,
1512 vlc_mutex_unlock( &p_sys
->lock_control
);
1513 return VLC_EGENERIC
;
1517 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
1521 const size_t i_index
= ControlGetReducedIndexLocked( p_input
);
1523 for( size_t i
= 0; i
< i_index
; ++i
)
1525 /* Release Reduced controls */
1526 ControlRelease( p_sys
->control
[i
].i_type
, &p_sys
->control
[i
].param
);
1530 *pi_type
= p_sys
->control
[i_index
].i_type
;
1531 *p_param
= p_sys
->control
[i_index
].param
;
1533 p_sys
->i_control
-= i_index
+ 1;
1534 if( p_sys
->i_control
> 0 )
1535 memmove( &p_sys
->control
[0], &p_sys
->control
[i_index
+1],
1536 sizeof(*p_sys
->control
) * p_sys
->i_control
);
1537 vlc_mutex_unlock( &p_sys
->lock_control
);
1541 static bool ControlIsSeekRequest( int i_type
)
1545 case INPUT_CONTROL_SET_POSITION
:
1546 case INPUT_CONTROL_JUMP_POSITION
:
1547 case INPUT_CONTROL_SET_TIME
:
1548 case INPUT_CONTROL_JUMP_TIME
:
1549 case INPUT_CONTROL_SET_TITLE
:
1550 case INPUT_CONTROL_SET_TITLE_NEXT
:
1551 case INPUT_CONTROL_SET_TITLE_PREV
:
1552 case INPUT_CONTROL_SET_SEEKPOINT
:
1553 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1554 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1555 case INPUT_CONTROL_NAV_ACTIVATE
:
1556 case INPUT_CONTROL_NAV_UP
:
1557 case INPUT_CONTROL_NAV_DOWN
:
1558 case INPUT_CONTROL_NAV_LEFT
:
1559 case INPUT_CONTROL_NAV_RIGHT
:
1560 case INPUT_CONTROL_NAV_POPUP
:
1561 case INPUT_CONTROL_NAV_MENU
:
1568 static void ControlRelease( int i_type
, const input_control_param_t
*p_param
)
1570 if( p_param
== NULL
)
1575 case INPUT_CONTROL_ADD_SLAVE
:
1576 if( p_param
->val
.p_address
)
1577 input_item_slave_Delete( p_param
->val
.p_address
);
1579 case INPUT_CONTROL_SET_RENDERER
:
1580 if( p_param
->val
.p_address
)
1581 vlc_renderer_item_release( p_param
->val
.p_address
);
1583 case INPUT_CONTROL_SET_ES
:
1584 case INPUT_CONTROL_UNSET_ES
:
1585 case INPUT_CONTROL_RESTART_ES
:
1586 vlc_es_id_Release( p_param
->id
);
1588 case INPUT_CONTROL_SET_ES_LIST
:
1590 for (size_t i
= 0; ; i
++)
1592 vlc_es_id_t
*es_id
= p_param
->list
.ids
[i
];
1595 vlc_es_id_Release(es_id
);
1597 free(p_param
->list
.ids
);
1607 static void ControlPause( input_thread_t
*p_input
, vlc_tick_t i_control_date
)
1609 int i_state
= PAUSE_S
;
1611 if( input_priv(p_input
)->b_can_pause
)
1613 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1615 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, true ) )
1617 msg_Warn( p_input
, "cannot set pause state" );
1623 if( es_out_SetPauseState( input_priv(p_input
)->p_es_out
, input_priv(p_input
)->b_can_pause
,
1624 true, i_control_date
) )
1626 msg_Warn( p_input
, "cannot set pause state at es_out level" );
1630 /* Switch to new state */
1631 input_ChangeState( p_input
, i_state
, i_control_date
);
1634 static void ControlUnpause( input_thread_t
*p_input
, vlc_tick_t i_control_date
)
1636 if( input_priv(p_input
)->b_can_pause
)
1638 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1640 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, false ) )
1642 msg_Err( p_input
, "cannot resume" );
1643 input_ChangeState( p_input
, ERROR_S
, i_control_date
);
1648 /* Switch to play */
1649 input_ChangeState( p_input
, PLAYING_S
, i_control_date
);
1650 es_out_SetPauseState( input_priv(p_input
)->p_es_out
, false, false, i_control_date
);
1653 static void ViewpointApply( input_thread_t
*p_input
)
1655 input_thread_private_t
*priv
= input_priv(p_input
);
1657 vlc_viewpoint_clip( &priv
->viewpoint
);
1659 vout_thread_t
**pp_vout
;
1661 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1663 for( size_t i
= 0; i
< i_vout
; ++i
)
1665 var_SetAddress( pp_vout
[i
], "viewpoint", &priv
->viewpoint
);
1666 /* This variable can only be read from callbacks */
1667 var_Change( pp_vout
[i
], "viewpoint", VLC_VAR_SETVALUE
,
1668 (vlc_value_t
) { .p_address
= NULL
} );
1669 vout_Release(pp_vout
[i
]);
1673 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1677 var_SetAddress( p_aout
, "viewpoint", &priv
->viewpoint
);
1678 /* This variable can only be read from callbacks */
1679 var_Change( p_aout
, "viewpoint", VLC_VAR_SETVALUE
,
1680 (vlc_value_t
) { .p_address
= NULL
} );
1681 aout_Release(p_aout
);
1685 static void ControlNav( input_thread_t
*p_input
, int i_type
)
1687 input_thread_private_t
*priv
= input_priv(p_input
);
1689 if( !demux_Control( priv
->master
->p_demux
, i_type
1690 - INPUT_CONTROL_NAV_ACTIVATE
+ DEMUX_NAV_ACTIVATE
) )
1691 return; /* The demux handled the navigation control */
1693 /* Handle Up/Down/Left/Right if the demux can't navigate */
1694 vlc_viewpoint_t vp
= {0};
1695 int vol_direction
= 0;
1696 int seek_direction
= 0;
1699 case INPUT_CONTROL_NAV_UP
:
1703 case INPUT_CONTROL_NAV_DOWN
:
1707 case INPUT_CONTROL_NAV_LEFT
:
1708 seek_direction
= -1;
1711 case INPUT_CONTROL_NAV_RIGHT
:
1715 case INPUT_CONTROL_NAV_ACTIVATE
:
1716 case INPUT_CONTROL_NAV_POPUP
:
1717 case INPUT_CONTROL_NAV_MENU
:
1720 vlc_assert_unreachable();
1723 /* Try to change the viewpoint if possible */
1724 vout_thread_t
**pp_vout
;
1726 bool b_viewpoint_ch
= false;
1727 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1728 for( size_t i
= 0; i
< i_vout
; ++i
)
1731 && var_GetBool( pp_vout
[i
], "viewpoint-changeable" ) )
1732 b_viewpoint_ch
= true;
1733 vout_Release(pp_vout
[i
]);
1737 if( b_viewpoint_ch
)
1739 priv
->viewpoint_changed
= true;
1740 priv
->viewpoint
.yaw
+= vp
.yaw
;
1741 priv
->viewpoint
.pitch
+= vp
.pitch
;
1742 priv
->viewpoint
.roll
+= vp
.roll
;
1743 priv
->viewpoint
.fov
+= vp
.fov
;
1744 ViewpointApply( p_input
);
1748 /* Seek or change volume if the input doesn't have navigation or viewpoint */
1749 if( seek_direction
!= 0 )
1751 vlc_tick_t it
= vlc_tick_from_sec( seek_direction
* var_InheritInteger( p_input
, "short-jump-size" ) );
1752 Control( p_input
, INPUT_CONTROL_JUMP_TIME
, (input_control_param_t
) {
1753 .time
.b_fast_seek
= false,
1759 assert( vol_direction
!= 0 );
1760 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1763 aout_VolumeUpdate( p_aout
, vol_direction
, NULL
);
1764 aout_Release(p_aout
);
1770 static void ControlUpdateRenderer( input_thread_t
*p_input
, bool b_enable
)
1774 if( InitSout( p_input
) != VLC_SUCCESS
)
1776 msg_Err( p_input
, "Failed to start sout" );
1782 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1783 input_priv(p_input
)->p_sout
, NULL
);
1784 input_priv(p_input
)->p_sout
= NULL
;
1789 static void ControlInsertDemuxFilter( input_thread_t
* p_input
, const char* psz_demux_chain
)
1791 input_source_t
*p_inputSource
= input_priv(p_input
)->master
;
1792 demux_t
*p_filtered_demux
= demux_FilterChainNew( p_inputSource
->p_demux
, psz_demux_chain
);
1793 if ( p_filtered_demux
!= NULL
)
1794 p_inputSource
->p_demux
= p_filtered_demux
;
1795 else if ( psz_demux_chain
!= NULL
)
1796 msg_Dbg(p_input
, "Failed to create demux filter %s", psz_demux_chain
);
1799 void input_ControlSync(input_thread_t
*p_input
, int i_type
,
1800 const input_control_param_t
* param
)
1802 assert( !input_priv(p_input
)->is_running
);
1803 Control( p_input
, i_type
, *param
);
1806 static bool Control( input_thread_t
*p_input
,
1807 int i_type
, input_control_param_t param
)
1809 input_thread_private_t
*priv
= input_priv(p_input
);
1810 const vlc_tick_t i_control_date
= vlc_tick_now();
1811 /* FIXME b_force_update is abused, it should be carefully checked */
1812 bool b_force_update
= false;
1817 case INPUT_CONTROL_SET_POSITION
:
1818 case INPUT_CONTROL_JUMP_POSITION
:
1820 const bool absolute
= i_type
== INPUT_CONTROL_SET_POSITION
;
1821 if( priv
->b_recording
)
1823 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION ignored while recording" );
1827 /* Reset the decoders states and clock sync (before calling the demuxer */
1828 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
1829 if( demux_SetPosition( priv
->master
->p_demux
, (double)param
.pos
.f_val
,
1830 !param
.pos
.b_fast_seek
, absolute
) )
1832 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION "
1834 absolute
? "@" : param
.pos
.f_val
>= 0 ? "+" : "",
1835 param
.pos
.f_val
* 100.f
);
1839 if( priv
->i_slave
> 0 )
1840 SlaveSeek( p_input
);
1841 priv
->master
->b_eof
= false;
1843 b_force_update
= true;
1848 case INPUT_CONTROL_SET_TIME
:
1849 case INPUT_CONTROL_JUMP_TIME
:
1851 const bool absolute
= i_type
== INPUT_CONTROL_SET_TIME
;
1854 if( priv
->b_recording
)
1856 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME ignored while recording" );
1860 /* Reset the decoders states and clock sync (before calling the demuxer */
1861 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
1863 i_ret
= demux_SetTime( priv
->master
->p_demux
, param
.time
.i_val
,
1864 !param
.time
.b_fast_seek
, absolute
);
1867 vlc_tick_t i_length
;
1869 /* Emulate it with a SET_POS */
1870 if( !demux_Control( priv
->master
->p_demux
,
1871 DEMUX_GET_LENGTH
, &i_length
) && i_length
> 0 )
1873 double f_pos
= (double)param
.time
.i_val
/ (double)i_length
;
1874 i_ret
= demux_SetPosition( priv
->master
->p_demux
, f_pos
,
1875 !param
.time
.b_fast_seek
,
1881 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME %s%"PRId64
1882 " failed or not possible",
1883 absolute
? "@" : param
.time
.i_val
>= 0 ? "+" : "",
1888 if( priv
->i_slave
> 0 )
1889 SlaveSeek( p_input
);
1890 priv
->master
->b_eof
= false;
1892 b_force_update
= true;
1897 case INPUT_CONTROL_SET_STATE
:
1898 switch( param
.val
.i_int
)
1901 if( priv
->i_state
== PAUSE_S
)
1903 ControlUnpause( p_input
, i_control_date
);
1904 b_force_update
= true;
1908 if( priv
->i_state
== PLAYING_S
)
1910 ControlPause( p_input
, i_control_date
);
1911 b_force_update
= true;
1915 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
1919 case INPUT_CONTROL_SET_RATE
:
1921 /* Get rate and direction */
1922 float rate
= fabsf( param
.val
.f_float
);
1923 int i_rate_sign
= param
.val
.f_float
< 0 ? -1 : 1;
1925 /* Check rate bound */
1926 if( rate
> INPUT_RATE_MAX
)
1928 msg_Info( p_input
, "cannot set rate faster" );
1929 rate
= INPUT_RATE_MAX
;
1931 else if( rate
< INPUT_RATE_MIN
)
1933 msg_Info( p_input
, "cannot set rate slower" );
1934 rate
= INPUT_RATE_MIN
;
1937 /* Apply direction */
1938 if( i_rate_sign
< 0 )
1940 if( priv
->master
->b_rescale_ts
)
1942 msg_Dbg( p_input
, "cannot set negative rate" );
1948 rate
*= i_rate_sign
;
1953 ( ( !priv
->b_can_rate_control
&& !priv
->master
->b_rescale_ts
) ||
1954 ( priv
->p_sout
&& !priv
->b_out_pace_control
) ) )
1956 msg_Dbg( p_input
, "cannot change rate" );
1959 if( rate
!= priv
->rate
&&
1960 !priv
->b_can_pace_control
&& priv
->b_can_rate_control
)
1962 if( !priv
->master
->b_rescale_ts
)
1963 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
1965 if( demux_Control( priv
->master
->p_demux
, DEMUX_SET_RATE
,
1968 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
1974 if( rate
!= priv
->rate
)
1977 input_SendEventRate( p_input
, rate
);
1979 if( priv
->master
->b_rescale_ts
)
1981 const float rate_source
= (priv
->b_can_pace_control
|| priv
->b_can_rate_control
) ? rate
: 1.f
;
1982 es_out_SetRate( priv
->p_es_out
, rate_source
, rate
);
1985 b_force_update
= true;
1990 case INPUT_CONTROL_SET_PROGRAM
:
1991 /* No need to force update, es_out does it if needed */
1992 es_out_Control( priv
->p_es_out
,
1993 ES_OUT_SET_GROUP
, (int)param
.val
.i_int
);
1995 if( param
.val
.i_int
== 0 )
1996 demux_Control( priv
->master
->p_demux
,
1997 DEMUX_SET_GROUP_DEFAULT
);
1999 demux_Control( priv
->master
->p_demux
,
2000 DEMUX_SET_GROUP_LIST
,
2001 (size_t)1, &(const int){ param
.val
.i_int
});
2004 case INPUT_CONTROL_SET_ES_BY_ID
:
2005 /* No need to force update, es_out does it if needed */
2006 es_out_Control( priv
->p_es_out_display
,
2007 ES_OUT_SET_ES_BY_ID
, (int)param
.val
.i_int
, true );
2009 demux_Control( priv
->master
->p_demux
, DEMUX_SET_ES
,
2010 (int)param
.val
.i_int
);
2013 case INPUT_CONTROL_RESTART_ES_BY_ID
:
2014 es_out_Control( priv
->p_es_out_display
,
2015 ES_OUT_RESTART_ES_BY_ID
, (int)param
.val
.i_int
);
2018 case INPUT_CONTROL_SET_ES
:
2019 if( es_out_Control( input_priv(p_input
)->p_es_out_display
,
2020 ES_OUT_SET_ES
, vlc_es_id_get_out( param
.id
) )
2022 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_ES
,
2023 vlc_es_id_GetInputId( param
.id
) );
2025 case INPUT_CONTROL_SET_ES_LIST
:
2027 if( es_out_Control( input_priv(p_input
)->p_es_out_display
,
2028 ES_OUT_SET_ES_LIST
, param
.list
.cat
,
2029 param
.list
.ids
) == VLC_SUCCESS
)
2031 if( param
.list
.ids
[0] != NULL
&& param
.list
.ids
[1] == NULL
)
2032 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_ES
,
2033 vlc_es_id_GetInputId( param
.list
.ids
[0] ) );
2036 /* Send an array of int id from the array of es_id to the
2039 for (count
= 0; param
.list
.ids
[count
] != NULL
; count
++);
2041 int *ids
= count
? vlc_alloc(count
, sizeof(int)) : NULL
;
2042 if (count
== 0 || ids
)
2044 for (size_t i
= 0; i
< count
; ++i
)
2045 ids
[i
] = vlc_es_id_GetInputId(param
.list
.ids
[i
]);
2046 demux_Control(input_priv(p_input
)->master
->p_demux
,
2047 DEMUX_SET_ES_LIST
, count
, ids
);
2054 case INPUT_CONTROL_UNSET_ES
:
2055 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2056 ES_OUT_UNSET_ES
, vlc_es_id_get_out(param
.id
) );
2058 case INPUT_CONTROL_RESTART_ES
:
2059 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2060 ES_OUT_RESTART_ES
, vlc_es_id_get_out( param
.id
) );
2063 case INPUT_CONTROL_SET_VIEWPOINT
:
2064 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT
:
2065 case INPUT_CONTROL_UPDATE_VIEWPOINT
:
2066 if ( i_type
== INPUT_CONTROL_SET_INITIAL_VIEWPOINT
)
2069 /* Set the initial viewpoint if it had not been changed by the
2071 if( !priv
->viewpoint_changed
)
2072 priv
->viewpoint
= param
.viewpoint
;
2073 /* Update viewpoints of aout and every vouts in all cases. */
2075 else if ( i_type
== INPUT_CONTROL_SET_VIEWPOINT
)
2077 priv
->viewpoint_changed
= true;
2078 priv
->viewpoint
= param
.viewpoint
;
2082 priv
->viewpoint_changed
= true;
2083 priv
->viewpoint
.yaw
+= param
.viewpoint
.yaw
;
2084 priv
->viewpoint
.pitch
+= param
.viewpoint
.pitch
;
2085 priv
->viewpoint
.roll
+= param
.viewpoint
.roll
;
2086 priv
->viewpoint
.fov
+= param
.viewpoint
.fov
;
2089 ViewpointApply( p_input
);
2092 case INPUT_CONTROL_SET_CATEGORY_DELAY
:
2093 assert(param
.cat_delay
.cat
== AUDIO_ES
2094 || param
.cat_delay
.cat
== SPU_ES
);
2095 es_out_SetDelay(priv
->p_es_out_display
,
2096 param
.cat_delay
.cat
, param
.cat_delay
.delay
);
2098 case INPUT_CONTROL_SET_ES_DELAY
:
2099 assert(param
.es_delay
.id
);
2100 es_out_SetEsDelay(priv
->p_es_out_display
,
2101 vlc_es_id_get_out(param
.es_delay
.id
),
2102 param
.es_delay
.delay
);
2105 case INPUT_CONTROL_SET_TITLE
:
2106 case INPUT_CONTROL_SET_TITLE_NEXT
:
2107 case INPUT_CONTROL_SET_TITLE_PREV
:
2109 if( priv
->b_recording
)
2111 msg_Err( p_input
, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
2114 if( priv
->master
->i_title
<= 0 )
2117 int i_title
= demux_GetTitle( priv
->master
->p_demux
);
2118 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
2120 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
2123 i_title
= param
.val
.i_int
;
2124 if( i_title
< 0 || i_title
>= priv
->master
->i_title
)
2127 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
2128 demux_Control( priv
->master
->p_demux
,
2129 DEMUX_SET_TITLE
, i_title
);
2132 case INPUT_CONTROL_SET_SEEKPOINT
:
2133 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
2134 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
2136 if( priv
->b_recording
)
2138 msg_Err( p_input
, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
2141 if( priv
->master
->i_title
<= 0 )
2144 demux_t
*p_demux
= priv
->master
->p_demux
;
2146 int i_title
= demux_GetTitle( p_demux
);
2147 int i_seekpoint
= demux_GetSeekpoint( p_demux
);
2149 if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_PREV
)
2151 vlc_tick_t i_seekpoint_time
= priv
->master
->title
[i_title
]->seekpoint
[i_seekpoint
]->i_time_offset
;
2152 vlc_tick_t i_input_time
= var_GetInteger( p_input
, "time" );
2153 if( i_seekpoint_time
>= 0 && i_input_time
>= 0 )
2155 if( i_input_time
< i_seekpoint_time
+ VLC_TICK_FROM_SEC(3) )
2161 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
2164 i_seekpoint
= param
.val
.i_int
;
2166 || i_seekpoint
>= priv
->master
->title
[i_title
]->i_seekpoint
)
2169 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
2170 demux_Control( priv
->master
->p_demux
,
2171 DEMUX_SET_SEEKPOINT
, i_seekpoint
);
2172 input_SendEventSeekpoint( p_input
, i_title
, i_seekpoint
);
2176 case INPUT_CONTROL_ADD_SLAVE
:
2177 if( param
.val
.p_address
)
2179 input_item_slave_t
*p_item_slave
= param
.val
.p_address
;
2180 unsigned i_flags
= SLAVE_ADD_CANFAIL
| SLAVE_ADD_SET_TIME
;
2181 if( p_item_slave
->b_forced
)
2182 i_flags
|= SLAVE_ADD_FORCED
;
2184 if( input_SlaveSourceAdd( p_input
, p_item_slave
->i_type
,
2185 p_item_slave
->psz_uri
, i_flags
)
2188 /* Update item slaves */
2189 input_item_AddSlave( priv
->p_item
, p_item_slave
);
2190 /* The slave is now owned by the item */
2191 param
.val
.p_address
= NULL
;
2195 case INPUT_CONTROL_SET_SUBS_FPS
:
2196 RequestSubRate( p_input
, param
.val
.f_float
);
2197 input_SendEventSubsFPS( p_input
, param
.val
.f_float
);
2200 case INPUT_CONTROL_SET_RECORD_STATE
:
2202 if( !!priv
->b_recording
!= !!val
.b_bool
)
2204 if( priv
->master
->b_can_stream_record
)
2206 if( demux_Control( priv
->master
->p_demux
,
2207 DEMUX_SET_RECORD_STATE
, val
.b_bool
) )
2212 if( es_out_SetRecordState( priv
->p_es_out_display
, val
.b_bool
) )
2215 priv
->b_recording
= val
.b_bool
;
2217 input_SendEventRecord( p_input
, val
.b_bool
);
2219 b_force_update
= true;
2223 case INPUT_CONTROL_SET_FRAME_NEXT
:
2224 if( priv
->i_state
== PAUSE_S
)
2226 es_out_SetFrameNext( priv
->p_es_out
);
2228 else if( priv
->i_state
== PLAYING_S
)
2230 ControlPause( p_input
, i_control_date
);
2234 msg_Err( p_input
, "invalid state for frame next" );
2236 b_force_update
= true;
2239 case INPUT_CONTROL_SET_RENDERER
:
2242 vlc_renderer_item_t
*p_item
= param
.val
.p_address
;
2243 input_thread_private_t
*p_priv
= input_priv( p_input
);
2244 // We do not support switching from a renderer to another for now
2245 if ( p_item
== NULL
&& p_priv
->p_renderer
== NULL
)
2249 if( es_out_Control( priv
->p_es_out_display
,
2250 ES_OUT_STOP_ALL_ES
, &context
) != VLC_SUCCESS
)
2253 if ( p_priv
->p_renderer
)
2255 ControlUpdateRenderer( p_input
, false );
2256 demux_FilterDisable( p_priv
->master
->p_demux
,
2257 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) );
2258 vlc_renderer_item_release( p_priv
->p_renderer
);
2259 p_priv
->p_renderer
= NULL
;
2261 if( p_item
!= NULL
)
2263 p_priv
->p_renderer
= vlc_renderer_item_hold( p_item
);
2264 ControlUpdateRenderer( p_input
, true );
2265 if( !demux_FilterEnable( p_priv
->master
->p_demux
,
2266 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) ) )
2268 ControlInsertDemuxFilter( p_input
,
2269 vlc_renderer_item_demux_filter( p_item
) );
2272 es_out_Control( priv
->p_es_out_display
, ES_OUT_START_ALL_ES
,
2277 case INPUT_CONTROL_SET_VBI_PAGE
:
2278 es_out_Control( priv
->p_es_out_display
, ES_OUT_SET_VBI_PAGE
,
2279 param
.vbi_page
.id
, param
.vbi_page
.page
);
2281 case INPUT_CONTROL_SET_VBI_TRANSPARENCY
:
2282 es_out_Control( priv
->p_es_out_display
, ES_OUT_SET_VBI_TRANSPARENCY
,
2283 param
.vbi_transparency
.id
,
2284 param
.vbi_transparency
.enabled
);
2286 case INPUT_CONTROL_SET_ES_AUTOSELECT
:
2287 es_out_Control( priv
->p_es_out_display
, ES_OUT_SET_AUTOSELECT
,
2288 param
.es_autoselect
.cat
, param
.es_autoselect
.enabled
);
2291 case INPUT_CONTROL_NAV_ACTIVATE
:
2292 case INPUT_CONTROL_NAV_UP
:
2293 case INPUT_CONTROL_NAV_DOWN
:
2294 case INPUT_CONTROL_NAV_LEFT
:
2295 case INPUT_CONTROL_NAV_RIGHT
:
2296 case INPUT_CONTROL_NAV_POPUP
:
2297 case INPUT_CONTROL_NAV_MENU
:
2298 ControlNav( p_input
, i_type
);
2302 msg_Err( p_input
, "not yet implemented" );
2306 ControlRelease( i_type
, ¶m
);
2307 return b_force_update
;
2310 /*****************************************************************************
2311 * UpdateTitleSeekpoint
2312 *****************************************************************************/
2313 static int UpdateTitleSeekpoint( input_thread_t
*p_input
,
2314 int i_title
, int i_seekpoint
)
2316 int i_title_end
= input_priv(p_input
)->master
->i_title_end
-
2317 input_priv(p_input
)->master
->i_title_offset
;
2318 int i_seekpoint_end
= input_priv(p_input
)->master
->i_seekpoint_end
-
2319 input_priv(p_input
)->master
->i_seekpoint_offset
;
2321 if( i_title_end
>= 0 && i_seekpoint_end
>= 0 )
2323 if( i_title
> i_title_end
||
2324 ( i_title
== i_title_end
&& i_seekpoint
> i_seekpoint_end
) )
2325 return VLC_DEMUXER_EOF
;
2327 else if( i_seekpoint_end
>= 0 )
2329 if( i_seekpoint
> i_seekpoint_end
)
2330 return VLC_DEMUXER_EOF
;
2332 else if( i_title_end
>= 0 )
2334 if( i_title
> i_title_end
)
2335 return VLC_DEMUXER_EOF
;
2337 return VLC_DEMUXER_SUCCESS
;
2339 /*****************************************************************************
2341 *****************************************************************************/
2342 static int UpdateTitleSeekpointFromDemux( input_thread_t
*p_input
)
2344 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2346 /* TODO event-like */
2347 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE
) )
2348 input_SendEventTitle( p_input
, &(struct vlc_input_event_title
) {
2349 .action
= VLC_INPUT_TITLE_SELECTED
,
2350 .selected_idx
= demux_GetTitle( p_demux
),
2353 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_SEEKPOINT
) )
2354 input_SendEventSeekpoint( p_input
, demux_GetTitle( p_demux
),
2355 demux_GetSeekpoint( p_demux
) );
2357 return UpdateTitleSeekpoint( p_input
,
2358 demux_GetTitle( p_demux
),
2359 demux_GetSeekpoint( p_demux
) );
2362 static void UpdateGenericFromDemux( input_thread_t
*p_input
)
2364 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2366 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_META
) )
2367 InputUpdateMeta( p_input
, p_demux
);
2373 if( !demux_Control( p_demux
, DEMUX_GET_SIGNAL
, &quality
, &strength
) )
2374 input_SendEventSignal( p_input
, quality
, strength
);
2378 static void UpdateTitleListfromDemux( input_thread_t
*p_input
)
2380 input_thread_private_t
*priv
= input_priv(p_input
);
2381 input_source_t
*in
= priv
->master
;
2383 /* Delete the preexisting titles */
2384 bool had_titles
= in
->i_title
> 0;
2386 /* Get the new title list */
2387 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2388 &in
->title
, &in
->i_title
,
2389 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2390 TAB_INIT( in
->i_title
, in
->title
);
2392 in
->b_title_demux
= true;
2394 InitTitle( p_input
, had_titles
);
2398 InputStreamHandleAnchor( input_thread_t
*p_input
, input_source_t
*source
,
2399 stream_t
**stream
, char const *anchor
)
2402 if( stream_extractor_AttachParsed( stream
, anchor
, &extra
) )
2404 msg_Err( p_input
, "unable to attach stream-extractors for %s",
2405 (*stream
)->psz_url
);
2407 return VLC_EGENERIC
;
2410 if( vlc_stream_directory_Attach( stream
, NULL
) )
2411 msg_Dbg( p_input
, "attachment of directory-extractor failed for %s",
2412 (*stream
)->psz_url
);
2414 MRLSections( extra
? extra
: "",
2415 &source
->i_title_start
, &source
->i_title_end
,
2416 &source
->i_seekpoint_start
, &source
->i_seekpoint_end
);
2421 static demux_t
*InputDemuxNew( input_thread_t
*p_input
,
2422 input_source_t
*p_source
, const char *url
,
2423 const char *psz_demux
, const char *psz_anchor
)
2425 input_thread_private_t
*priv
= input_priv(p_input
);
2426 vlc_object_t
*obj
= VLC_OBJECT(p_input
);
2428 /* create the underlying access stream */
2429 stream_t
*p_stream
= stream_AccessNew( obj
, p_input
, priv
->p_es_out
,
2430 priv
->b_preparsing
, url
);
2431 if( p_stream
== NULL
)
2434 p_stream
= stream_FilterAutoNew( p_stream
);
2436 if( p_stream
->pf_read
== NULL
&& p_stream
->pf_block
== NULL
2437 && p_stream
->pf_readdir
== NULL
)
2438 { /* Combined access/demux, no stream filtering */
2439 MRLSections( psz_anchor
,
2440 &p_source
->i_title_start
, &p_source
->i_title_end
,
2441 &p_source
->i_seekpoint_start
, &p_source
->i_seekpoint_end
);
2445 /* attach explicit stream filters to stream */
2446 char *psz_filters
= var_InheritString( p_input
, "stream-filter" );
2449 p_stream
= stream_FilterChainNew( p_stream
, psz_filters
);
2450 free( psz_filters
);
2453 /* handle anchors */
2454 if( InputStreamHandleAnchor( p_input
, p_source
, &p_stream
, psz_anchor
) )
2457 /* attach conditional record stream-filter */
2458 if( var_InheritBool( p_input
, "input-record-native" ) )
2459 p_stream
= stream_FilterChainNew( p_stream
, "record" );
2461 /* create a regular demux with the access stream created */
2462 demux_t
*demux
= demux_NewAdvanced( obj
, p_input
, psz_demux
, url
, p_stream
,
2463 priv
->p_es_out
, priv
->b_preparsing
);
2468 vlc_stream_Delete( p_stream
);
2472 static void input_SplitMRL( const char **, const char **, const char **,
2473 const char **, char * );
2475 /*****************************************************************************
2477 *****************************************************************************/
2478 static input_source_t
*InputSourceNew( input_thread_t
*p_input
,
2479 const char *psz_mrl
,
2480 const char *psz_forced_demux
,
2481 bool b_in_can_fail
)
2483 input_thread_private_t
*priv
= input_priv(p_input
);
2484 input_source_t
*in
= calloc(1, sizeof(*in
) );
2485 if( unlikely(in
== NULL
) )
2488 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2491 char *psz_dup
= strdup( psz_mrl
);
2492 char *psz_demux_var
= NULL
;
2494 if( psz_dup
== NULL
)
2501 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, &psz_anchor
, psz_dup
);
2503 if( psz_demux
== NULL
|| psz_demux
[0] == '\0' )
2504 psz_demux
= psz_demux_var
= var_InheritString( p_input
, "demux" );
2506 if( psz_forced_demux
!= NULL
)
2507 psz_demux
= psz_forced_demux
;
2509 if( psz_demux
== NULL
)
2512 msg_Dbg( p_input
, "`%s' gives access `%s' demux `%s' path `%s'",
2513 psz_mrl
, psz_access
, psz_demux
, psz_path
);
2515 if( input_priv(p_input
)->master
== NULL
/* XXX ugly */)
2516 { /* On master stream only, use input-list */
2517 char *str
= var_InheritString( p_input
, "input-list" );
2522 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2523 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2526 var_SetString( p_input
, "concat-list", list
);
2530 psz_access
= "concat";
2534 if( strcasecmp( psz_access
, "concat" ) )
2535 { /* Autodetect extra files if none specified */
2539 TAB_INIT( count
, tab
);
2540 InputGetExtraFiles( p_input
, &count
, &tab
, &psz_access
, psz_path
);
2545 for( int i
= 0; i
< count
; i
++ )
2548 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2557 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2558 if( likely(list
!= NULL
) )
2560 var_SetString( p_input
, "concat-list", list
);
2564 TAB_CLEAN( count
, tab
);
2568 if( likely(asprintf( &url
, "%s://%s", psz_access
, psz_path
) >= 0) )
2570 in
->p_demux
= InputDemuxNew( p_input
, in
, url
, psz_demux
, psz_anchor
);
2576 free( psz_demux_var
);
2579 if( in
->p_demux
== NULL
)
2581 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2582 vlc_dialog_display_error( p_input
, _("Your input can't be opened"),
2583 _("VLC is unable to open the MRL '%s'."
2584 " Check the log for details."), psz_mrl
);
2589 char *psz_demux_chain
= NULL
;
2590 if( priv
->p_renderer
)
2592 const char* psz_renderer_demux
= vlc_renderer_item_demux_filter(
2594 if( psz_renderer_demux
)
2595 psz_demux_chain
= strdup( psz_renderer_demux
);
2597 if( !psz_demux_chain
)
2598 psz_demux_chain
= var_GetNonEmptyString(p_input
, "demux-filter");
2599 if( psz_demux_chain
!= NULL
) /* add the chain of demux filters */
2601 in
->p_demux
= demux_FilterChainNew( in
->p_demux
, psz_demux_chain
);
2602 free( psz_demux_chain
);
2604 if( in
->p_demux
== NULL
)
2606 msg_Err(p_input
, "Failed to create demux filter");
2612 /* Get infos from (access_)demux */
2613 int capabilites
= 0;
2615 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &b_can_seek
) )
2618 capabilites
|= VLC_INPUT_CAPABILITIES_SEEKABLE
;
2620 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_PACE
,
2621 &in
->b_can_pace_control
) )
2622 in
->b_can_pace_control
= false;
2624 assert( in
->p_demux
->pf_demux
!= NULL
|| !in
->b_can_pace_control
);
2626 if( !in
->b_can_pace_control
)
2628 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_RATE
,
2629 &in
->b_can_rate_control
) )
2631 in
->b_can_rate_control
= false;
2632 in
->b_rescale_ts
= true;
2635 in
->b_rescale_ts
= !in
->b_can_rate_control
;
2639 in
->b_can_rate_control
= true;
2640 in
->b_rescale_ts
= true;
2643 demux_Control( in
->p_demux
, DEMUX_CAN_PAUSE
, &in
->b_can_pause
);
2645 if( in
->b_can_pause
|| !in
->b_can_pace_control
)
2646 capabilites
|= VLC_INPUT_CAPABILITIES_PAUSEABLE
;
2647 if( !in
->b_can_pace_control
|| in
->b_can_rate_control
)
2648 capabilites
|= VLC_INPUT_CAPABILITIES_CHANGE_RATE
;
2649 if( !in
->b_rescale_ts
&& !in
->b_can_pace_control
&& in
->b_can_rate_control
)
2650 capabilites
|= VLC_INPUT_CAPABILITIES_REWINDABLE
;
2652 /* Set record capabilities */
2653 if( demux_Control( in
->p_demux
, DEMUX_CAN_RECORD
, &in
->b_can_stream_record
) )
2654 in
->b_can_stream_record
= false;
2656 if( !var_GetBool( p_input
, "input-record-native" ) )
2657 in
->b_can_stream_record
= false;
2658 capabilites
|= VLC_INPUT_CAPABILITIES_RECORDABLE
;
2660 if( in
->b_can_stream_record
)
2661 capabilites
|= VLC_INPUT_CAPABILITIES_RECORDABLE
;
2664 input_SendEventCapabilities( p_input
, capabilites
);
2667 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2668 if( !input_priv(p_input
)->b_preparsing
)
2670 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2671 &in
->title
, &in
->i_title
,
2672 &in
->i_title_offset
, &in
->i_seekpoint_offset
))
2674 TAB_INIT( in
->i_title
, in
->title
);
2678 in
->b_title_demux
= true;
2682 input_attachment_t
**attachment
;
2683 if( !demux_Control( in
->p_demux
, DEMUX_GET_ATTACHMENTS
,
2684 &attachment
, &i_attachment
) )
2686 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2687 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2688 i_attachment
, attachment
, in
->p_demux
);
2689 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2692 demux_Control( in
->p_demux
, DEMUX_GET_PTS_DELAY
, &in
->i_pts_delay
);
2693 if( in
->i_pts_delay
> INPUT_PTS_DELAY_MAX
)
2694 in
->i_pts_delay
= INPUT_PTS_DELAY_MAX
;
2695 else if( in
->i_pts_delay
< 0 )
2696 in
->i_pts_delay
= 0;
2699 if( demux_Control( in
->p_demux
, DEMUX_GET_FPS
, &in
->f_fps
) )
2702 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2703 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2708 /*****************************************************************************
2709 * InputSourceDestroy:
2710 *****************************************************************************/
2711 static void InputSourceDestroy( input_source_t
*in
)
2716 demux_Delete( in
->p_demux
);
2718 if( in
->i_title
> 0 )
2720 for( i
= 0; i
< in
->i_title
; i
++ )
2721 vlc_input_title_Delete( in
->title
[i
] );
2722 TAB_CLEAN( in
->i_title
, in
->title
);
2728 /*****************************************************************************
2730 *****************************************************************************/
2731 static void InputSourceMeta( input_thread_t
*p_input
,
2732 input_source_t
*p_source
, vlc_meta_t
*p_meta
)
2734 demux_t
*p_demux
= p_source
->p_demux
;
2736 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2739 bool has_meta
= false;
2741 /* Read demux meta */
2742 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
2745 bool has_unsupported
;
2746 if( demux_Control( p_demux
, DEMUX_HAS_UNSUPPORTED_META
, &has_unsupported
) )
2747 has_unsupported
= true;
2749 /* If the demux report unsupported meta data, or if we don't have meta data
2750 * try an external "meta reader" */
2751 if( has_meta
&& !has_unsupported
)
2754 demux_meta_t
*p_demux_meta
=
2755 vlc_custom_create( p_input
, sizeof( *p_demux_meta
), "demux meta" );
2756 if( unlikely(p_demux_meta
== NULL
) )
2758 p_demux_meta
->p_item
= input_priv(p_input
)->p_item
;
2760 module_t
*p_id3
= module_need( p_demux_meta
, "meta reader", NULL
, false );
2763 if( p_demux_meta
->p_meta
)
2765 vlc_meta_Merge( p_meta
, p_demux_meta
->p_meta
);
2766 vlc_meta_Delete( p_demux_meta
->p_meta
);
2769 if( p_demux_meta
->i_attachments
> 0 )
2771 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2772 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2773 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
, p_demux
);
2774 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2776 module_unneed( p_demux
, p_id3
);
2778 vlc_object_delete(p_demux_meta
);
2782 static void SlaveDemux( input_thread_t
*p_input
)
2784 input_thread_private_t
*priv
= input_priv(p_input
);
2788 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2790 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2794 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2796 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2802 if( priv
->slave_subs_rate
!= in
->sub_rate
)
2804 if( in
->b_slave_sub
&& in
->b_can_rate_control
)
2806 if( in
->sub_rate
!= 0 ) /* Don't reset when it's the first time */
2807 es_out_Control( priv
->p_es_out
, ES_OUT_RESET_PCR
);
2808 float new_rate
= priv
->slave_subs_rate
;
2809 demux_Control( in
->p_demux
, DEMUX_SET_RATE
, &new_rate
);
2811 in
->sub_rate
= priv
->slave_subs_rate
;
2815 /* Call demux_Demux until we have read enough data */
2816 if( demux_Control( in
->p_demux
, DEMUX_SET_NEXT_DEMUX_TIME
, i_time
) )
2821 if( demux_Control( in
->p_demux
, DEMUX_GET_TIME
, &i_stime
) )
2823 msg_Err( p_input
, "slave[%d] doesn't like "
2824 "DEMUX_GET_TIME -> EOF", i
);
2829 if( i_stime
>= i_time
)
2835 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2841 i_ret
= demux_Demux( in
->p_demux
);
2846 msg_Dbg( p_input
, "slave %d EOF", i
);
2852 static void SlaveSeek( input_thread_t
*p_input
)
2857 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2859 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2863 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2865 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2867 if( demux_Control( in
->p_demux
, DEMUX_SET_TIME
, i_time
, true ) )
2870 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2880 /*****************************************************************************
2882 *****************************************************************************/
2883 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
)
2885 static const struct { int i_meta
; const char *psz_name
; } p_list
[] = {
2886 { vlc_meta_Title
, "meta-title" },
2887 { vlc_meta_Artist
, "meta-artist" },
2888 { vlc_meta_Genre
, "meta-genre" },
2889 { vlc_meta_Copyright
, "meta-copyright" },
2890 { vlc_meta_Description
, "meta-description" },
2891 { vlc_meta_Date
, "meta-date" },
2892 { vlc_meta_URL
, "meta-url" },
2896 /* Get meta information from user */
2897 for( int i
= 0; p_list
[i
].psz_name
; i
++ )
2899 char *psz_string
= var_GetNonEmptyString( p_input
, p_list
[i
].psz_name
);
2903 EnsureUTF8( psz_string
);
2904 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, psz_string
);
2909 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
,
2910 const demux_t
***ppp_attachment_demux
,
2911 int i_new
, input_attachment_t
**pp_new
, const demux_t
*p_demux
)
2913 int i_attachment
= *pi_attachment
;
2916 input_attachment_t
**pp_att
= realloc( *ppp_attachment
,
2917 sizeof(*pp_att
) * ( i_attachment
+ i_new
) );
2918 if( likely(pp_att
) )
2920 *ppp_attachment
= pp_att
;
2921 const demux_t
**pp_attdmx
= realloc( *ppp_attachment_demux
,
2922 sizeof(*pp_attdmx
) * ( i_attachment
+ i_new
) );
2923 if( likely(pp_attdmx
) )
2925 *ppp_attachment_demux
= pp_attdmx
;
2927 for( i
= 0; i
< i_new
; i
++ )
2929 pp_att
[i_attachment
] = pp_new
[i
];
2930 pp_attdmx
[i_attachment
++] = p_demux
;
2933 *pi_attachment
= i_attachment
;
2939 /* on alloc errors */
2940 for( i
= 0; i
< i_new
; i
++ )
2941 vlc_input_attachment_Delete( pp_new
[i
] );
2945 /*****************************************************************************
2946 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
2947 * arturl and locking issue.
2948 *****************************************************************************/
2949 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
)
2951 vlc_meta_t
*p_meta
= vlc_meta_New();
2952 if( unlikely(p_meta
== NULL
) )
2955 demux_Control( p_demux
, DEMUX_GET_META
, p_meta
);
2957 /* If metadata changed, then the attachments might have changed.
2958 We need to update them in case they contain album art. */
2959 input_attachment_t
**attachment
;
2962 if( !demux_Control( p_demux
, DEMUX_GET_ATTACHMENTS
,
2963 &attachment
, &i_attachment
) )
2965 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2966 if( input_priv(p_input
)->i_attachment
> 0 )
2969 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
2971 if( input_priv(p_input
)->attachment_demux
[i
] == p_demux
)
2972 vlc_input_attachment_Delete( input_priv(p_input
)->attachment
[i
] );
2975 input_priv(p_input
)->attachment
[j
] = input_priv(p_input
)->attachment
[i
];
2976 input_priv(p_input
)->attachment_demux
[j
] = input_priv(p_input
)->attachment_demux
[i
];
2980 input_priv(p_input
)->i_attachment
= j
;
2982 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2983 i_attachment
, attachment
, p_demux
);
2984 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2987 es_out_ControlSetMeta( input_priv(p_input
)->p_es_out
, p_meta
);
2988 vlc_meta_Delete( p_meta
);
2991 /*****************************************************************************
2992 * InputGetExtraFiles
2993 * Autodetect extra input list
2994 *****************************************************************************/
2995 static void InputGetExtraFilesPattern( input_thread_t
*p_input
,
2996 int *pi_list
, char ***pppsz_list
,
2997 const char *psz_path
,
2998 const char *psz_match
,
2999 const char *psz_format
,
3000 int i_start
, int i_stop
)
3004 TAB_INIT( i_list
, ppsz_list
);
3006 char *psz_base
= strdup( psz_path
);
3010 /* Remove the extension */
3011 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
3012 assert( psz_end
>= psz_base
);
3015 /* Try to list files */
3016 for( int i
= i_start
; i
<= i_stop
; i
++ )
3019 if( asprintf( &psz_probe
, psz_format
, psz_base
, i
) < 0 )
3022 char *filepath
= get_path( psz_probe
);
3025 if( filepath
== NULL
||
3026 vlc_stat( filepath
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
3033 msg_Dbg( p_input
, "Detected extra file `%s'", filepath
);
3035 char* psz_uri
= vlc_path2uri( filepath
, NULL
);
3037 TAB_APPEND( i_list
, ppsz_list
, psz_uri
);
3045 *pppsz_list
= ppsz_list
;
3048 static void InputGetExtraFiles( input_thread_t
*p_input
,
3049 int *pi_list
, char ***pppsz_list
,
3050 const char **ppsz_access
, const char *psz_path
)
3052 static const struct pattern
3054 const char *psz_access_force
;
3055 const char *psz_match
;
3056 const char *psz_format
;
3060 /* XXX the order is important */
3061 { "concat", ".001", "%s.%.3d", 2, 999 },
3062 { NULL
, ".part1.rar","%s.part%.1d.rar", 2, 9 },
3063 { NULL
, ".part01.rar","%s.part%.2d.rar", 2, 99, },
3064 { NULL
, ".part001.rar", "%s.part%.3d.rar", 2, 999 },
3065 { NULL
, ".rar", "%s.r%.2d", 0, 99 },
3066 { "concat", ".mts", "%s.mts%d", 1, 999 },
3069 TAB_INIT( *pi_list
, *pppsz_list
);
3071 if( ( **ppsz_access
&& strcmp( *ppsz_access
, "file" ) ) || !psz_path
)
3074 const size_t i_path
= strlen(psz_path
);
3076 for( size_t i
= 0; i
< ARRAY_SIZE( patterns
); ++i
)
3078 const struct pattern
* pat
= &patterns
[i
];
3079 const size_t i_ext
= strlen( pat
->psz_match
);
3081 if( i_path
< i_ext
)
3084 if( !strcmp( &psz_path
[i_path
-i_ext
], pat
->psz_match
) )
3086 InputGetExtraFilesPattern( p_input
, pi_list
, pppsz_list
, psz_path
,
3087 pat
->psz_match
, pat
->psz_format
, pat
->i_start
, pat
->i_stop
);
3089 if( *pi_list
> 0 && pat
->psz_access_force
)
3090 *ppsz_access
= pat
->psz_access_force
;
3097 static void input_ChangeState( input_thread_t
*p_input
, int i_state
,
3098 vlc_tick_t state_date
)
3100 if( input_priv(p_input
)->i_state
== i_state
)
3103 input_priv(p_input
)->i_state
= i_state
;
3104 if( i_state
== ERROR_S
)
3105 input_item_SetErrorWhenReading( input_priv(p_input
)->p_item
, true );
3106 input_SendEventState( p_input
, i_state
, state_date
);
3110 /*****************************************************************************
3111 * MRLSplit: parse the access, demux and url part of the
3112 * Media Resource Locator.
3113 *****************************************************************************/
3114 static void input_SplitMRL( const char **access
, const char **demux
,
3115 const char **path
, const char **anchor
, char *buf
)
3119 /* Separate <path> from <access>[/<demux>]:// */
3120 p
= strstr( buf
, "://" );
3124 p
+= 3; /* skips "://" */
3127 /* Remove HTML anchor if present (not supported).
3128 * The hash symbol itself should be URI-encoded. */
3129 p
= strchr( p
, '#' );
3141 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
3144 /* Note: this is a valid non const pointer to "": */
3145 *path
= buf
+ strlen( buf
);
3148 /* Separate access from demux */
3149 p
= strchr( buf
, '/' );
3160 /* We really don't want module name substitution here! */
3167 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
3172 /* Look for the title */
3173 u
= strtoul( str
, &end
, 0 );
3174 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3177 /* Look for the chapter */
3181 u
= strtoul( str
, &end
, 0 );
3182 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3192 /*****************************************************************************
3193 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
3196 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
3197 *****************************************************************************/
3198 static void MRLSections( const char *p
,
3199 int *pi_title_start
, int *pi_title_end
,
3200 int *pi_chapter_start
, int *pi_chapter_end
)
3202 *pi_title_start
= *pi_title_end
= *pi_chapter_start
= *pi_chapter_end
= -1;
3204 int title_start
, chapter_start
, title_end
, chapter_end
;
3210 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
3212 title_start
= chapter_start
= -1;
3215 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
3217 title_end
= chapter_end
= -1;
3219 if( *p
) /* syntax error */
3222 *pi_title_start
= title_start
;
3223 *pi_title_end
= title_end
;
3224 *pi_chapter_start
= chapter_start
;
3225 *pi_chapter_end
= chapter_end
;
3228 static int input_SlaveSourceAdd( input_thread_t
*p_input
,
3229 enum slave_type i_type
, const char *psz_uri
,
3232 input_thread_private_t
*priv
= input_priv(p_input
);
3233 const char *psz_forced_demux
;
3234 const bool b_can_fail
= i_flags
& SLAVE_ADD_CANFAIL
;
3235 const bool b_forced
= i_flags
& SLAVE_ADD_FORCED
;
3236 const bool b_set_time
= i_flags
& SLAVE_ADD_SET_TIME
;
3237 enum es_format_category_e i_cat
;
3241 case SLAVE_TYPE_SPU
:
3242 psz_forced_demux
= "subtitle";
3245 case SLAVE_TYPE_AUDIO
:
3246 psz_forced_demux
= NULL
;
3250 vlc_assert_unreachable();
3253 msg_Dbg( p_input
, "loading %s slave: %s (forced: %d)",
3254 i_cat
== SPU_ES
? "spu" : "audio", psz_uri
, b_forced
);
3256 priv
->i_last_es_cat
= UNKNOWN_ES
;
3258 input_source_t
*p_source
= InputSourceNew( p_input
, psz_uri
,
3260 b_can_fail
|| psz_forced_demux
);
3262 if( psz_forced_demux
&& p_source
== NULL
)
3263 p_source
= InputSourceNew( p_input
, psz_uri
, NULL
, b_can_fail
);
3265 if( p_source
== NULL
)
3267 msg_Warn( p_input
, "failed to add %s as slave", psz_uri
);
3268 return VLC_EGENERIC
;
3271 if( i_type
== SLAVE_TYPE_AUDIO
)
3278 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
3280 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
3281 InputSourceDestroy( p_source
);
3282 return VLC_EGENERIC
;
3285 if( demux_Control( p_source
->p_demux
,
3286 DEMUX_SET_TIME
, i_time
, true ) )
3288 msg_Err( p_input
, "seek failed for new slave" );
3289 InputSourceDestroy( p_source
);
3290 return VLC_EGENERIC
;
3294 /* Get meta (access and demux) */
3295 InputUpdateMeta( p_input
, p_source
->p_demux
);
3298 p_source
->b_slave_sub
= true;
3300 TAB_APPEND( priv
->i_slave
, priv
->slave
, p_source
);
3302 if( !b_forced
|| priv
->i_last_es_cat
!= i_cat
)
3305 assert( priv
->i_last_es_id
!= -1 );
3307 es_out_Control( priv
->p_es_out_display
, ES_OUT_SET_ES_DEFAULT_BY_ID
,
3308 priv
->i_last_es_id
);
3309 es_out_Control( priv
->p_es_out_display
, ES_OUT_SET_ES_BY_ID
,
3310 priv
->i_last_es_id
, false );
3315 static char *input_SubtitleFile2Uri( input_thread_t
*p_input
,
3316 const char *psz_subtitle
)
3318 /* if we are provided a subtitle.sub file,
3319 * see if we don't have a subtitle.idx and use it instead */
3320 char *psz_idxpath
= NULL
;
3321 char *psz_extension
= strrchr( psz_subtitle
, '.');
3322 if( psz_extension
&& strcmp( psz_extension
, ".sub" ) == 0 )
3324 psz_idxpath
= strdup( psz_subtitle
);
3329 psz_extension
= psz_extension
- psz_subtitle
+ psz_idxpath
;
3330 strcpy( psz_extension
, ".idx" );
3332 if( !vlc_stat( psz_idxpath
, &st
) && S_ISREG( st
.st_mode
) )
3334 msg_Dbg( p_input
, "using %s as subtitle file instead of %s",
3335 psz_idxpath
, psz_subtitle
);
3336 psz_subtitle
= psz_idxpath
;
3341 char *psz_uri
= vlc_path2uri( psz_subtitle
, NULL
);
3342 free( psz_idxpath
);
3346 int input_GetAttachments(input_thread_t
*input
,
3347 input_attachment_t
***attachments
)
3349 input_thread_private_t
*priv
= input_priv(input
);
3351 vlc_mutex_lock(&priv
->p_item
->lock
);
3352 int attachments_count
= priv
->i_attachment
;
3353 if (attachments_count
<= 0)
3355 vlc_mutex_unlock(&priv
->p_item
->lock
);
3356 *attachments
= NULL
;
3360 *attachments
= vlc_alloc(attachments_count
, sizeof(input_attachment_t
*));
3364 for (int i
= 0; i
< attachments_count
; i
++)
3365 (*attachments
)[i
] = vlc_input_attachment_Duplicate(priv
->attachment
[i
]);
3367 vlc_mutex_unlock(&priv
->p_item
->lock
);
3368 return attachments_count
;
3371 input_attachment_t
*input_GetAttachment(input_thread_t
*input
, const char *name
)
3373 input_thread_private_t
*priv
= input_priv(input
);
3375 vlc_mutex_lock(&priv
->p_item
->lock
);
3376 for (int i
= 0; i
< priv
->i_attachment
; i
++)
3378 if (!strcmp( priv
->attachment
[i
]->psz_name
, name
))
3380 input_attachment_t
*attachment
=
3381 vlc_input_attachment_Duplicate(priv
->attachment
[i
] );
3382 vlc_mutex_unlock( &priv
->p_item
->lock
);
3386 vlc_mutex_unlock( &priv
->p_item
->lock
);