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>
37 #include "input_internal.h"
47 #include <vlc_dialog.h>
49 #include <vlc_charset.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 /*****************************************************************************
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
,
85 static void InputSourceDestroy( input_source_t
* );
86 static void InputSourceMeta( input_thread_t
*, input_source_t
*, vlc_meta_t
* );
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 */
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
);
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
);
152 if( !Init( p_input
) )
154 MainLoop( p_input
, false );
158 vlc_object_release( p_input
);
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
)
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" );
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
);
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
);
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
, ¶m
);
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
, ¶m
);
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
);
265 char * psz_name
= input_item_GetName( priv
->p_item
);
266 msg_Dbg( p_input
, "Destroying the input for '%s'", psz_name
);
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
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
) )
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
);
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;
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
;
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
;
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
;
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 */
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;
398 p_item
->i_preparse_depth
= -1; /* default is expand */
401 p_item
->i_preparse_depth
= -1;
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
);
426 priv
->p_resource_private
= NULL
;
427 priv
->p_resource
= input_resource_Hold( p_resource
);
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
);
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;
450 if( !priv
->b_preparsing
)
452 char *psz_bookmarks
= var_GetNonEmptyString( p_input
, "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
;
463 psz_end
= strchr( psz_start
, '}' );
464 if( !psz_end
) break;
465 psz_parser
= psz_end
+ 1;
466 backup
= *psz_parser
;
470 p_seekpoint
= vlc_seekpoint_New();
472 if( unlikely( p_seekpoint
== NULL
) )
475 while( (psz_end
= strchr( psz_start
, ',' ) ) )
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
);
506 if( !priv
->b_preparsing
&& var_InheritBool( p_input
, "stats" ) )
507 priv
->stats
= input_stats_Create();
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
);
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) */
547 input_SendEventDead( p_input
);
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
,
566 b_is_playlist
= false;
568 MainLoop( p_input
, false );
572 input_SendEventDead( p_input
);
576 bool input_Stopped( input_thread_t
*input
)
578 input_thread_private_t
*sys
= input_priv(input
);
581 vlc_mutex_lock( &sys
->lock_control
);
582 ret
= sys
->is_stopped
;
583 vlc_mutex_unlock( &sys
->lock_control
);
587 /*****************************************************************************
588 * Main loop: Fill buffers from access, and demux
589 *****************************************************************************/
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
;
603 if( p_priv
->i_stop
> 0 )
605 if( demux_Control( p_demux
, DEMUX_GET_TIME
, &p_priv
->i_time
) )
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
);
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" );
653 msg_Dbg( p_input
, "repeating the same input (%d)", 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
)
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 );
678 input_SetPosition( p_input
, 0.0f
, false );
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
) )
698 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
700 input_priv(p_input
)->i_time
= i_time
;
702 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
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
);
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 */
748 b_paused
= !es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
749 || input_priv(p_input
)->master
->b_eof
;
753 if( !input_priv(p_input
)->master
->b_eof
)
755 bool b_force_update
= false;
757 MainLoopDemux( p_input
, &b_force_update
);
760 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
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
)
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
);
785 b_paused_at_eof
= true;
789 if( MainLoopTryRepeat( p_input
) )
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);
805 vlc_tick_t i_deadline
= i_wakeup
;
807 /* Postpone seeking until ES buffering is complete or at most
809 bool b_postpone
= es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
810 && !input_priv(p_input
)->master
->b_eof
;
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);
824 input_control_param_t param
;
826 if( ControlPop( p_input
, &i_type
, ¶m
, i_deadline
, b_postpone
) )
830 break; /* Wake-up time reached */
834 msg_Dbg( p_input
, "control type=%d", i_type
);
836 if( Control( p_input
, i_type
, param
) )
838 if( ControlIsSeekRequest( i_type
) )
839 i_last_seek_mdate
= vlc_tick_now();
843 /* Update the wakeup time */
845 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
851 static int InitSout( input_thread_t
* p_input
)
853 input_thread_private_t
*priv
= input_priv(p_input
);
855 if( priv
->b_preparsing
)
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
864 bool keep_sout
= psz
== NULL
;
867 const char *psz_renderer_sout
= vlc_renderer_item_sout( priv
->p_renderer
);
868 if( asprintf( &psz
, "#%s", psz_renderer_sout
) < 0 )
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, " \
887 input_resource_RequestSout( priv
->p_resource
, NULL
, NULL
);
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
)
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
;
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
,
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
);
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" );
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" );
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
)
988 if( p_slave0
->i_priority
< p_slave1
->i_priority
)
994 static bool SlaveExists( input_item_slave_t
**pp_slaves
, int i_slaves
,
997 for( int i
= 0; i
< i_slaves
; i
++ )
999 if( pp_slaves
[i
] != NULL
1000 && !strcmp( pp_slaves
[i
]->psz_uri
, psz_uri
) )
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
;
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" );
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
== '#' )
1053 char *psz_delim
= strchr( psz
, '#' );
1055 *psz_delim
++ = '\0';
1060 char *uri
= strstr(psz
, "://")
1061 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
1066 input_item_slave_t
*p_slave
=
1067 input_item_slave_New( uri
, SLAVE_TYPE_AUDIO
, SLAVE_PRIORITY_USER
);
1070 if( unlikely( p_slave
== NULL
) )
1072 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
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
;
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
);
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
];
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
);
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
);
1157 qsort( pp_slaves
, i_slaves
, sizeof (input_item_slave_t
*),
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
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
;
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
);
1186 p_forced
[p_slave
->i_type
] = true;
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
];
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;
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 )
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
;
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
)
1272 /* Compute correct pts_delay */
1273 UpdatePtsDelay( p_input
);
1276 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1277 if( input_priv(p_input
)->p_sout
&& !input_priv(p_input
)->p_renderer
)
1281 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1285 TAB_INIT(count
, tab
);
1286 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1288 prgm
= strtok_r( NULL
, ",", &buf
) )
1290 TAB_APPEND(count
, tab
, atoi(prgm
));
1294 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1295 /* Note : we should remove the "program" callback. */
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
);
1320 int program
= es_out_GetGroupForced( input_priv(p_input
)->p_es_out
);
1322 demux_Control( input_priv(p_input
)->master
->p_demux
,
1323 DEMUX_SET_GROUP_DEFAULT
);
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
;
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 );
1352 if( InitSout( p_input
) )
1357 priv
->p_es_out
= input_EsOutTimeshiftNew( p_input
, priv
->p_es_out_display
, priv
->rate
);
1358 if( priv
->p_es_out
== NULL
)
1362 master
= InputSourceNew( p_input
, priv
->p_item
->psz_uri
, NULL
, false );
1363 if( master
== NULL
)
1365 priv
->master
= master
;
1367 InitTitle( p_input
, false );
1369 /* Load master infos */
1371 vlc_tick_t i_length
;
1372 if( demux_Control( master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
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
);
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
);
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
);
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
);
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 /*****************************************************************************
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",
1530 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1533 ControlRelease( i_type
, p_param
);
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
;
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
;
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
) )
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
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
,
1607 vlc_mutex_unlock( &p_sys
->lock_control
);
1608 return VLC_EGENERIC
;
1612 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
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
);
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
);
1636 static bool ControlIsSeekRequest( int 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
:
1664 static void ControlRelease( int i_type
, const input_control_param_t
*p_param
)
1666 if( p_param
== NULL
)
1671 case INPUT_CONTROL_ADD_SLAVE
:
1672 if( p_param
->val
.p_address
)
1673 input_item_slave_Delete( p_param
->val
.p_address
);
1675 case INPUT_CONTROL_SET_RENDERER
:
1676 if( p_param
->val
.p_address
)
1677 vlc_renderer_item_release( p_param
->val
.p_address
);
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
);
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" );
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" );
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
);
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
;
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
] );
1757 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
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;
1783 case INPUT_CONTROL_NAV_UP
:
1787 case INPUT_CONTROL_NAV_DOWN
:
1791 case INPUT_CONTROL_NAV_LEFT
:
1792 seek_direction
= -1;
1795 case INPUT_CONTROL_NAV_RIGHT
:
1799 case INPUT_CONTROL_NAV_ACTIVATE
:
1800 case INPUT_CONTROL_NAV_POPUP
:
1801 case INPUT_CONTROL_NAV_MENU
:
1804 vlc_assert_unreachable();
1807 /* Try to change the viewpoint if possible */
1808 vout_thread_t
**pp_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
)
1815 && var_GetBool( pp_vout
[i
], "viewpoint-changeable" ) )
1816 b_viewpoint_ch
= true;
1817 vlc_object_release( pp_vout
[i
] );
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
);
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,
1843 assert( vol_direction
!= 0 );
1844 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1847 aout_VolumeUpdate( p_aout
, vol_direction
, NULL
);
1848 vlc_object_release( p_aout
);
1854 static void ControlUpdateRenderer( input_thread_t
*p_input
, bool b_enable
)
1858 if( InitSout( p_input
) != VLC_SUCCESS
)
1860 msg_Err( p_input
, "Failed to start sout" );
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
;
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;
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" );
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 "
1912 absolute
? "@" : param
.pos
.f_val
>= 0 ? "+" : "",
1913 param
.pos
.f_val
* 100.f
);
1917 if( priv
->i_slave
> 0 )
1918 SlaveSeek( p_input
);
1919 priv
->master
->b_eof
= false;
1921 b_force_update
= true;
1926 case INPUT_CONTROL_SET_TIME
:
1927 case INPUT_CONTROL_JUMP_TIME
:
1929 const bool absolute
= i_type
== INPUT_CONTROL_SET_TIME
;
1932 if( priv
->b_recording
)
1934 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME ignored while recording" );
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
);
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
,
1959 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME %s%"PRId64
1960 " failed or not possible",
1961 absolute
? "@" : param
.time
.i_val
>= 0 ? "+" : "",
1966 if( priv
->i_slave
> 0 )
1967 SlaveSeek( p_input
);
1968 priv
->master
->b_eof
= false;
1970 b_force_update
= true;
1975 case INPUT_CONTROL_SET_STATE
:
1976 switch( param
.val
.i_int
)
1979 if( priv
->i_state
== PAUSE_S
)
1981 ControlUnpause( p_input
, i_control_date
);
1982 b_force_update
= true;
1986 if( priv
->i_state
== PLAYING_S
)
1988 ControlPause( p_input
, i_control_date
);
1989 b_force_update
= true;
1993 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
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" );
2026 rate
*= i_rate_sign
;
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" );
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
,
2046 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
2052 if( rate
!= priv
->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;
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
);
2077 demux_Control( priv
->master
->p_demux
,
2078 DEMUX_SET_GROUP_LIST
,
2079 (size_t)1, &(const int){ param
.val
.i_int
});
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
);
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
);
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
) );
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
);
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
);
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
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
;
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
);
2140 case INPUT_CONTROL_SET_AUDIO_DELAY
:
2141 if( param
.delay
.b_absolute
)
2142 priv
->i_audio_delay
= param
.delay
.i_val
;
2144 priv
->i_audio_delay
+= param
.delay
.i_val
;
2145 input_SendEventAudioDelay( p_input
, priv
->i_audio_delay
);
2146 UpdatePtsDelay( p_input
);
2149 case INPUT_CONTROL_SET_SPU_DELAY
:
2150 if( param
.delay
.b_absolute
)
2151 priv
->i_spu_delay
= param
.delay
.i_val
;
2153 priv
->i_spu_delay
+= param
.delay
.i_val
;
2154 input_SendEventSubtitleDelay( p_input
, priv
->i_spu_delay
);
2155 UpdatePtsDelay( p_input
);
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" );
2167 if( priv
->master
->i_title
<= 0 )
2170 int i_title
= demux_GetTitle( priv
->master
->p_demux
);
2171 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
2173 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
2176 i_title
= param
.val
.i_int
;
2177 if( i_title
< 0 || i_title
>= priv
->master
->i_title
)
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
,
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" );
2198 if( priv
->master
->i_title
<= 0 )
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) )
2218 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
2221 i_seekpoint
= param
.val
.i_int
;
2223 || i_seekpoint
>= priv
->master
->title
[i_title
]->i_seekpoint
)
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
);
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
)
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
;
2252 case INPUT_CONTROL_SET_SUBS_FPS
:
2253 RequestSubRate( p_input
, param
.val
.f_float
);
2254 input_SendEventSubsFPS( p_input
, param
.val
.f_float
);
2257 case INPUT_CONTROL_SET_RECORD_STATE
:
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
) )
2269 if( es_out_SetRecordState( priv
->p_es_out_display
, val
.b_bool
) )
2272 priv
->b_recording
= val
.b_bool
;
2274 input_SendEventRecord( p_input
, val
.b_bool
);
2276 b_force_update
= true;
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
);
2291 msg_Err( p_input
, "invalid state for frame next" );
2293 b_force_update
= true;
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
);
2316 Control( p_input
, INPUT_CONTROL_SET_TIME
,
2317 (input_control_param_t
) { .time
.i_val
= time_offset
,
2318 .time
.b_fast_seek
= false } );
2321 case INPUT_CONTROL_SET_RENDERER
:
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
)
2331 if( es_out_Control( priv
->p_es_out_display
,
2332 ES_OUT_STOP_ALL_ES
, &context
) != VLC_SUCCESS
)
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
,
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
);
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
);
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
);
2381 msg_Err( p_input
, "not yet implemented" );
2385 ControlRelease( i_type
, ¶m
);
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 /*****************************************************************************
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
);
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 )
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
);
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
);
2481 in
->b_title_demux
= true;
2483 InitTitle( p_input
, had_titles
);
2487 InputStreamHandleAnchor( input_thread_t
*p_input
, input_source_t
*source
,
2488 stream_t
**stream
, char const *anchor
)
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
);
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
)
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
);
2534 /* attach explicit stream filters to stream */
2535 char *psz_filters
= var_InheritString( p_input
, "stream-filter" );
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
) )
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
);
2557 vlc_stream_Delete( p_stream
);
2561 static void input_SplitMRL( const char **, const char **, const char **,
2562 const char **, char * );
2564 /*****************************************************************************
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
) )
2577 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2580 char *psz_dup
= strdup( psz_mrl
);
2581 char *psz_demux_var
= NULL
;
2583 if( psz_dup
== NULL
)
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
)
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" );
2611 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2612 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2615 var_SetString( p_input
, "concat-list", list
);
2619 psz_access
= "concat";
2623 if( strcasecmp( psz_access
, "concat" ) )
2624 { /* Autodetect extra files if none specified */
2628 TAB_INIT( count
, tab
);
2629 InputGetExtraFiles( p_input
, &count
, &tab
, &psz_access
, psz_path
);
2634 for( int i
= 0; i
< count
; i
++ )
2637 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2646 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2647 if( likely(list
!= NULL
) )
2649 var_SetString( p_input
, "concat-list", list
);
2653 TAB_CLEAN( count
, tab
);
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
);
2665 free( psz_demux_var
);
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
);
2678 char *psz_demux_chain
= NULL
;
2679 if( priv
->p_renderer
)
2681 const char* psz_renderer_demux
= vlc_renderer_item_demux_filter(
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");
2701 /* Get infos from (access_)demux */
2702 int capabilites
= 0;
2704 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &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;
2724 in
->b_rescale_ts
= !in
->b_can_rate_control
;
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;
2745 if( !var_GetBool( p_input
, "input-record-native" ) )
2746 in
->b_can_stream_record
= false;
2747 capabilites
|= VLC_INPUT_CAPABILITIES_RECORDABLE
;
2749 if( in
->b_can_stream_record
)
2750 capabilites
|= VLC_INPUT_CAPABILITIES_RECORDABLE
;
2753 input_SendEventCapabilities( p_input
, capabilites
);
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
);
2767 in
->b_title_demux
= true;
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
) )
2791 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2792 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2797 /*****************************************************************************
2798 * InputSourceDestroy:
2799 *****************************************************************************/
2800 static void InputSourceDestroy( input_source_t
*in
)
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
);
2817 /*****************************************************************************
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
2828 bool has_meta
= false;
2830 /* Read demux meta */
2831 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
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
)
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
) )
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 );
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
);
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" );
2883 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2885 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
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
) )
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
);
2918 if( i_stime
>= i_time
)
2924 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2930 i_ret
= demux_Demux( in
->p_demux
);
2935 msg_Dbg( p_input
, "slave %d EOF", i
);
2941 static void SlaveSeek( input_thread_t
*p_input
)
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" );
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 ) )
2959 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2969 /*****************************************************************************
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" },
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
);
2992 EnsureUTF8( psz_string
);
2993 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, 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
;
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
;
3022 *pi_attachment
= i_attachment
;
3028 /* on alloc errors */
3029 for( i
= 0; i
< i_new
; i
++ )
3030 vlc_input_attachment_Delete( pp_new
[i
] );
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
) )
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
;
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 )
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
] );
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
];
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
)
3093 TAB_INIT( i_list
, ppsz_list
);
3095 char *psz_base
= strdup( psz_path
);
3099 /* Remove the extension */
3100 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
3101 assert( psz_end
>= psz_base
);
3104 /* Try to list files */
3105 for( int i
= i_start
; i
<= i_stop
; i
++ )
3108 if( asprintf( &psz_probe
, psz_format
, psz_base
, i
) < 0 )
3111 char *filepath
= get_path( psz_probe
);
3114 if( filepath
== NULL
||
3115 vlc_stat( filepath
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
3122 msg_Dbg( p_input
, "Detected extra file `%s'", filepath
);
3124 char* psz_uri
= vlc_path2uri( filepath
, NULL
);
3126 TAB_APPEND( i_list
, ppsz_list
, psz_uri
);
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
;
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
)
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
)
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
;
3186 static void input_ChangeState( input_thread_t
*p_input
, int i_state
)
3188 if( input_priv(p_input
)->i_state
== i_state
)
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
)
3207 /* Separate <path> from <access>[/<demux>]:// */
3208 p
= strstr( buf
, "://" );
3212 p
+= 3; /* skips "://" */
3215 /* Remove HTML anchor if present (not supported).
3216 * The hash symbol itself should be URI-encoded. */
3217 p
= strchr( p
, '#' );
3229 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
3232 /* Note: this is a valid non const pointer to "": */
3233 *path
= buf
+ strlen( buf
);
3236 /* Separate access from demux */
3237 p
= strchr( buf
, '/' );
3248 /* We really don't want module name substitution here! */
3255 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
3260 /* Look for the title */
3261 u
= strtoul( str
, &end
, 0 );
3262 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3265 /* Look for the chapter */
3269 u
= strtoul( str
, &end
, 0 );
3270 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3280 /*****************************************************************************
3281 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
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
;
3298 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
3300 title_start
= chapter_start
= -1;
3303 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
3305 title_end
= chapter_end
= -1;
3307 if( *p
) /* syntax error */
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
,
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
;
3329 case SLAVE_TYPE_SPU
:
3330 psz_forced_demux
= "subtitle";
3333 case SLAVE_TYPE_AUDIO
:
3334 psz_forced_demux
= NULL
;
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
,
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
)
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
);
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
)
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 );
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
);
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
);
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
,
3441 char *filename
= str_format(input
, item
, filenamefmt
);
3442 if (unlikely(filename
== NULL
))
3445 filename_sanitize(filename
);
3448 ? asprintf(&path
, "%s"DIR_SEP
"%s.%s", dir
, filename
, ext
)
3449 : asprintf(&path
, "%s"DIR_SEP
"%s", dir
, filename
)) < 0)
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
;
3470 *attachments
= vlc_alloc(attachments_count
, sizeof(input_attachment_t
*));
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
);
3496 vlc_mutex_unlock( &priv
->p_item
->lock
);