1 /*****************************************************************************
2 * input.c: input thread
3 *****************************************************************************
4 * Copyright (C) 1998-2007 VLC authors and VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Laurent Aimar <fenrir@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
32 #include <vlc_common.h>
38 #include "input_internal.h"
41 #include "es_out_timeshift.h"
49 #include <vlc_dialog.h>
51 #include <vlc_charset.h>
53 #include <vlc_strings.h>
54 #include <vlc_modules.h>
55 #include <vlc_stream.h>
56 #include <vlc_stream_extractor.h>
57 #include <vlc_renderer_discovery.h>
59 /*****************************************************************************
61 *****************************************************************************/
62 static void *Run( void * );
63 static void *Preparse( void * );
65 static input_thread_t
* Create ( vlc_object_t
*, input_item_t
*,
66 const char *, bool, input_resource_t
*,
67 vlc_renderer_item_t
* );
68 static int Init ( input_thread_t
*p_input
);
69 static void End ( input_thread_t
*p_input
);
70 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
);
72 static inline int ControlPop( input_thread_t
*, int *, vlc_value_t
*, mtime_t i_deadline
, bool b_postpone_seek
);
73 static void ControlRelease( int i_type
, vlc_value_t val
);
74 static bool ControlIsSeekRequest( int i_type
);
75 static bool Control( input_thread_t
*, int, vlc_value_t
);
76 static void ControlPause( input_thread_t
*, mtime_t
);
78 static int UpdateTitleSeekpointFromDemux( input_thread_t
* );
79 static void UpdateGenericFromDemux( input_thread_t
* );
80 static void UpdateTitleListfromDemux( input_thread_t
* );
82 static void MRLSections( const char *, int *, int *, int *, int *);
84 static input_source_t
*InputSourceNew( input_thread_t
*, const char *,
85 const char *psz_forced_demux
,
87 static void InputSourceDestroy( input_source_t
* );
88 static void InputSourceMeta( input_thread_t
*, input_source_t
*, vlc_meta_t
* );
91 //static void InputGetAttachments( input_thread_t *, input_source_t * );
92 static void SlaveDemux( input_thread_t
*p_input
);
93 static void SlaveSeek( input_thread_t
*p_input
);
95 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
);
96 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
);
97 static void InputGetExtraFiles( input_thread_t
*p_input
,
98 int *pi_list
, char ***pppsz_list
,
99 const char **psz_access
, const char *psz_path
);
101 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
,
102 const demux_t
***ppp_attachment_demux
,
103 int i_new
, input_attachment_t
**pp_new
, const demux_t
*p_demux
);
105 #define SLAVE_ADD_NOFLAG 0
106 #define SLAVE_ADD_FORCED (1<<0)
107 #define SLAVE_ADD_CANFAIL (1<<1)
108 #define SLAVE_ADD_SET_TIME (1<<2)
110 static int input_SlaveSourceAdd( input_thread_t
*, enum slave_type
,
111 const char *, unsigned );
112 static char *input_SubtitleFile2Uri( input_thread_t
*, const char * );
113 static void input_ChangeState( input_thread_t
*p_input
, int i_state
); /* TODO fix name */
117 * Create a new input_thread_t.
119 * You need to call input_Start on it when you are done
120 * adding callback on the variables/events you want to monitor.
122 * \param p_parent a vlc_object
123 * \param p_item an input item
124 * \param psz_log an optional prefix for this input logs
125 * \param p_resource an optional input ressource
126 * \return a pointer to the spawned input thread
128 input_thread_t
*input_Create( vlc_object_t
*p_parent
,
129 input_item_t
*p_item
,
130 const char *psz_log
, input_resource_t
*p_resource
,
131 vlc_renderer_item_t
*p_renderer
)
133 return Create( p_parent
, p_item
, psz_log
, 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
)
146 input_thread_t
*p_input
= Create( p_parent
, p_item
, NULL
, false, NULL
, NULL
);
150 if( !Init( p_input
) )
152 MainLoop( p_input
, false );
156 vlc_object_release( p_input
);
160 input_thread_t
*input_CreatePreparser( vlc_object_t
*parent
,
163 return Create( parent
, item
, NULL
, true, NULL
, NULL
);
167 * Start a input_thread_t created by input_Create.
169 * You must not start an already running input_thread_t.
171 * \param the input thread to start
173 int input_Start( input_thread_t
*p_input
)
175 input_thread_private_t
*priv
= input_priv(p_input
);
176 void *(*func
)(void *) = Run
;
178 if( priv
->b_preparsing
)
181 assert( !priv
->is_running
);
182 /* Create thread and wait for its readiness. */
183 priv
->is_running
= !vlc_clone( &priv
->thread
, func
, priv
,
184 VLC_THREAD_PRIORITY_INPUT
);
185 if( !priv
->is_running
)
187 input_ChangeState( p_input
, ERROR_S
);
188 msg_Err( p_input
, "cannot create input thread" );
195 * Request a running input thread to stop and die
197 * \param p_input the input thread to stop
199 void input_Stop( input_thread_t
*p_input
)
201 input_thread_private_t
*sys
= input_priv(p_input
);
203 vlc_mutex_lock( &sys
->lock_control
);
204 /* Discard all pending controls */
205 for( int i
= 0; i
< sys
->i_control
; i
++ )
207 input_control_t
*ctrl
= &sys
->control
[i
];
208 ControlRelease( ctrl
->i_type
, ctrl
->val
);
211 sys
->is_stopped
= true;
212 vlc_cond_signal( &sys
->wait_control
);
213 vlc_mutex_unlock( &sys
->lock_control
);
214 vlc_interrupt_kill( &sys
->interrupt
);
220 * It does not call input_Stop itself.
222 void input_Close( input_thread_t
*p_input
)
224 if( input_priv(p_input
)->is_running
)
225 vlc_join( input_priv(p_input
)->thread
, NULL
);
226 vlc_interrupt_deinit( &input_priv(p_input
)->interrupt
);
227 vlc_object_release( p_input
);
231 * Input destructor (called when the object's refcount reaches 0).
233 static void input_Destructor( vlc_object_t
*obj
)
235 input_thread_t
*p_input
= (input_thread_t
*)obj
;
236 input_thread_private_t
*priv
= input_priv(p_input
);
238 char * psz_name
= input_item_GetName( priv
->p_item
);
239 msg_Dbg( p_input
, "Destroying the input for '%s'", psz_name
);
243 if( priv
->p_renderer
)
244 vlc_renderer_item_release( priv
->p_renderer
);
245 if( priv
->p_es_out_display
)
246 es_out_Delete( priv
->p_es_out_display
);
248 if( priv
->p_resource
)
249 input_resource_Release( priv
->p_resource
);
250 if( priv
->p_resource_private
)
251 input_resource_Release( priv
->p_resource_private
);
253 input_item_Release( priv
->p_item
);
255 if( priv
->stats
!= NULL
)
256 input_stats_Destroy( priv
->stats
);
258 for( int i
= 0; i
< priv
->i_control
; i
++ )
260 input_control_t
*p_ctrl
= &priv
->control
[i
];
261 ControlRelease( p_ctrl
->i_type
, p_ctrl
->val
);
264 vlc_cond_destroy( &priv
->wait_control
);
265 vlc_mutex_destroy( &priv
->lock_control
);
269 * Get the item from an input thread
270 * FIXME it does not increase ref count of the item.
271 * if it is used after p_input is destroyed nothing prevent it from
274 input_item_t
*input_GetItem( input_thread_t
*p_input
)
276 assert( p_input
!= NULL
);
277 return input_priv(p_input
)->p_item
;
280 /*****************************************************************************
281 * This function creates a new input, and returns a pointer
282 * to its description. On error, it returns NULL.
284 * XXX Do not forget to update vlc_input.h if you add new variables.
285 *****************************************************************************/
286 static input_thread_t
*Create( vlc_object_t
*p_parent
, input_item_t
*p_item
,
287 const char *psz_header
, bool b_preparsing
,
288 input_resource_t
*p_resource
,
289 vlc_renderer_item_t
*p_renderer
)
291 /* Allocate descriptor */
292 input_thread_private_t
*priv
;
294 priv
= vlc_custom_create( p_parent
, sizeof( *priv
), "input" );
295 if( unlikely(priv
== NULL
) )
298 input_thread_t
*p_input
= &priv
->input
;
300 char * psz_name
= input_item_GetName( p_item
);
301 msg_Dbg( p_input
, "Creating an input for %s'%s'",
302 b_preparsing
? "preparsing " : "", psz_name
);
305 /* Parse input options */
306 input_item_ApplyOptions( VLC_OBJECT(p_input
), p_item
);
308 p_input
->obj
.header
= psz_header
? strdup( psz_header
) : NULL
;
310 /* Init Common fields */
311 priv
->b_preparsing
= b_preparsing
;
312 priv
->b_can_pace_control
= true;
318 priv
->i_title_offset
= input_priv(p_input
)->i_seekpoint_offset
= 0;
319 priv
->i_state
= INIT_S
;
320 priv
->is_running
= false;
321 priv
->is_stopped
= false;
322 priv
->b_recording
= false;
323 priv
->i_rate
= INPUT_RATE_DEFAULT
;
324 memset( &priv
->bookmark
, 0, sizeof(priv
->bookmark
) );
325 TAB_INIT( priv
->i_bookmark
, priv
->pp_bookmark
);
326 TAB_INIT( priv
->i_attachment
, priv
->attachment
);
327 priv
->attachment_demux
= NULL
;
329 priv
->b_out_pace_control
= false;
330 priv
->p_renderer
= p_renderer
&& b_preparsing
== false ?
331 vlc_renderer_item_hold( p_renderer
) : NULL
;
333 priv
->viewpoint_changed
= false;
334 /* Fetch the viewpoint from the mediaplayer or the playlist if any */
335 vlc_viewpoint_t
*p_viewpoint
= var_InheritAddress( p_input
, "viewpoint" );
336 if (p_viewpoint
!= NULL
)
337 priv
->viewpoint
= *p_viewpoint
;
339 vlc_viewpoint_init( &priv
->viewpoint
);
341 input_item_Hold( p_item
); /* Released in Destructor() */
342 priv
->p_item
= p_item
;
344 /* Init Input fields */
346 vlc_mutex_lock( &p_item
->lock
);
348 if( !p_item
->p_stats
)
349 p_item
->p_stats
= calloc( 1, sizeof(*p_item
->p_stats
) );
351 /* setup the preparse depth of the item
352 * if we are preparsing, use the i_preparse_depth of the parent item */
353 if( !priv
->b_preparsing
)
355 char *psz_rec
= var_InheritString( p_parent
, "recursive" );
357 if( psz_rec
!= NULL
)
359 if ( !strcasecmp( psz_rec
, "none" ) )
360 p_item
->i_preparse_depth
= 0;
361 else if ( !strcasecmp( psz_rec
, "collapse" ) )
362 p_item
->i_preparse_depth
= 1;
364 p_item
->i_preparse_depth
= -1; /* default is expand */
367 p_item
->i_preparse_depth
= -1;
370 p_input
->obj
.flags
|= OBJECT_FLAGS_QUIET
| OBJECT_FLAGS_NOINTERACT
;
372 /* Make sure the interaction option is honored */
373 if( !var_InheritBool( p_input
, "interact" ) )
374 p_input
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
375 else if( p_item
->b_preparse_interact
)
377 /* If true, this item was asked explicitly to interact with the user
378 * (via libvlc_MetadataRequest). Sub items created from this input won't
379 * have this flag and won't interact with the user */
380 p_input
->obj
.flags
&= ~OBJECT_FLAGS_NOINTERACT
;
383 vlc_mutex_unlock( &p_item
->lock
);
392 priv
->p_resource_private
= NULL
;
393 priv
->p_resource
= input_resource_Hold( p_resource
);
397 priv
->p_resource_private
= input_resource_New( VLC_OBJECT( p_input
) );
398 priv
->p_resource
= input_resource_Hold( priv
->p_resource_private
);
400 input_resource_SetInput( priv
->p_resource
, p_input
);
402 /* Init control buffer */
403 vlc_mutex_init( &priv
->lock_control
);
404 vlc_cond_init( &priv
->wait_control
);
406 vlc_interrupt_init(&priv
->interrupt
);
408 /* Create Object Variables for private use only */
409 input_ConfigVarInit( p_input
);
411 /* Create Objects variables for public Get and Set */
412 input_ControlVarInit( p_input
);
415 if( !priv
->b_preparsing
)
417 char *psz_bookmarks
= var_GetNonEmptyString( p_input
, "bookmarks" );
420 /* FIXME: have a common cfg parsing routine used by sout and others */
421 char *psz_parser
, *psz_start
, *psz_end
;
422 psz_parser
= psz_bookmarks
;
423 while( (psz_start
= strchr( psz_parser
, '{' ) ) )
425 seekpoint_t
*p_seekpoint
;
428 psz_end
= strchr( psz_start
, '}' );
429 if( !psz_end
) break;
430 psz_parser
= psz_end
+ 1;
431 backup
= *psz_parser
;
435 p_seekpoint
= vlc_seekpoint_New();
437 if( unlikely( p_seekpoint
== NULL
) )
440 while( (psz_end
= strchr( psz_start
, ',' ) ) )
443 if( !strncmp( psz_start
, "name=", 5 ) )
445 free( p_seekpoint
->psz_name
);
447 p_seekpoint
->psz_name
= strdup(psz_start
+ 5);
449 else if( !strncmp( psz_start
, "time=", 5 ) )
451 p_seekpoint
->i_time_offset
= atof(psz_start
+ 5) *
454 psz_start
= psz_end
+ 1;
456 msg_Dbg( p_input
, "adding bookmark: %s, time=%"PRId64
,
457 p_seekpoint
->psz_name
,
458 p_seekpoint
->i_time_offset
);
459 input_Control( p_input
, INPUT_ADD_BOOKMARK
, p_seekpoint
);
460 vlc_seekpoint_Delete( p_seekpoint
);
461 *psz_parser
= backup
;
463 free( psz_bookmarks
);
467 /* Remove 'Now playing' info as it is probably outdated */
468 input_item_SetNowPlaying( p_item
, NULL
);
469 input_item_SetESNowPlaying( p_item
, NULL
);
470 input_SendEventMeta( p_input
);
473 if( !priv
->b_preparsing
&& var_InheritBool( p_input
, "stats" ) )
474 priv
->stats
= input_stats_Create();
478 priv
->p_es_out_display
= input_EsOutNew( p_input
, priv
->i_rate
);
479 priv
->p_es_out
= NULL
;
481 /* Set the destructor when we are sure we are initialized */
482 vlc_object_set_destructor( p_input
, input_Destructor
);
487 /*****************************************************************************
488 * Run: main thread loop
489 * This is the "normal" thread that spawns the input processing chain,
490 * reads the stream, cleans up and waits
491 *****************************************************************************/
492 static void *Run( void *data
)
494 input_thread_private_t
*priv
= data
;
495 input_thread_t
*p_input
= &priv
->input
;
497 vlc_interrupt_set(&priv
->interrupt
);
499 if( !Init( p_input
) )
501 if( priv
->b_can_pace_control
&& priv
->b_out_pace_control
)
503 /* We don't want a high input priority here or we'll
504 * end-up sucking up all the CPU time */
505 vlc_set_priority( priv
->thread
, VLC_THREAD_PRIORITY_LOW
);
508 MainLoop( p_input
, true ); /* FIXME it can be wrong (like with VLM) */
514 input_SendEventDead( p_input
);
518 static void *Preparse( void *data
)
520 input_thread_private_t
*priv
= data
;
521 input_thread_t
*p_input
= &priv
->input
;
523 vlc_interrupt_set(&priv
->interrupt
);
525 if( !Init( p_input
) )
526 { /* if the demux is a playlist, call Mainloop that will call
527 * demux_Demux in order to fetch sub items */
528 bool b_is_playlist
= false;
530 if ( input_item_ShouldPreparseSubItems( priv
->p_item
)
531 && demux_Control( priv
->master
->p_demux
, DEMUX_IS_PLAYLIST
,
533 b_is_playlist
= false;
535 MainLoop( p_input
, false );
539 input_SendEventDead( p_input
);
543 bool input_Stopped( input_thread_t
*input
)
545 input_thread_private_t
*sys
= input_priv(input
);
548 vlc_mutex_lock( &sys
->lock_control
);
549 ret
= sys
->is_stopped
;
550 vlc_mutex_unlock( &sys
->lock_control
);
554 /*****************************************************************************
555 * Main loop: Fill buffers from access, and demux
556 *****************************************************************************/
560 * It asks the demuxer to demux some data
562 static void MainLoopDemux( input_thread_t
*p_input
, bool *pb_changed
)
565 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
569 if( input_priv(p_input
)->i_stop
> 0 && input_priv(p_input
)->i_time
>= input_priv(p_input
)->i_stop
)
570 i_ret
= VLC_DEMUXER_EOF
;
572 i_ret
= demux_Demux( p_demux
);
574 i_ret
= i_ret
> 0 ? VLC_DEMUXER_SUCCESS
: ( i_ret
< 0 ? VLC_DEMUXER_EGENERIC
: VLC_DEMUXER_EOF
);
576 if( i_ret
== VLC_DEMUXER_SUCCESS
)
578 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE_LIST
) )
579 UpdateTitleListfromDemux( p_input
);
581 if( input_priv(p_input
)->master
->b_title_demux
)
583 i_ret
= UpdateTitleSeekpointFromDemux( p_input
);
587 UpdateGenericFromDemux( p_input
);
590 if( i_ret
== VLC_DEMUXER_EOF
)
592 msg_Dbg( p_input
, "EOF reached" );
593 input_priv(p_input
)->master
->b_eof
= true;
594 es_out_Eos(input_priv(p_input
)->p_es_out
);
596 else if( i_ret
== VLC_DEMUXER_EGENERIC
)
598 input_ChangeState( p_input
, ERROR_S
);
600 else if( input_priv(p_input
)->i_slave
> 0 )
601 SlaveDemux( p_input
);
604 static int MainLoopTryRepeat( input_thread_t
*p_input
)
606 int i_repeat
= var_GetInteger( p_input
, "input-repeat" );
612 msg_Dbg( p_input
, "repeating the same input (%d)", i_repeat
);
616 var_SetInteger( p_input
, "input-repeat", i_repeat
);
619 /* Seek to start title/seekpoint */
620 val
.i_int
= input_priv(p_input
)->master
->i_title_start
-
621 input_priv(p_input
)->master
->i_title_offset
;
622 if( val
.i_int
< 0 || val
.i_int
>= input_priv(p_input
)->master
->i_title
)
624 input_ControlPush( p_input
,
625 INPUT_CONTROL_SET_TITLE
, &val
);
627 val
.i_int
= input_priv(p_input
)->master
->i_seekpoint_start
-
628 input_priv(p_input
)->master
->i_seekpoint_offset
;
629 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
630 input_ControlPush( p_input
,
631 INPUT_CONTROL_SET_SEEKPOINT
, &val
);
633 /* Seek to start position */
634 if( input_priv(p_input
)->i_start
> 0 )
636 val
.i_int
= input_priv(p_input
)->i_start
;
637 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &val
);
642 input_ControlPush( p_input
, INPUT_CONTROL_SET_POSITION
, &val
);
649 * Update timing infos and statistics.
651 static void MainLoopStatistics( input_thread_t
*p_input
)
653 input_thread_private_t
*priv
= input_priv(p_input
);
654 double f_position
= 0.0;
656 mtime_t i_length
= 0;
658 /* update input status variables */
659 if( demux_Control( priv
->master
->p_demux
,
660 DEMUX_GET_POSITION
, &f_position
) )
663 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
665 input_priv(p_input
)->i_time
= i_time
;
667 if( demux_Control( priv
->master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
670 es_out_SetTimes( priv
->p_es_out
, f_position
, i_time
, i_length
);
672 /* update current bookmark */
673 vlc_mutex_lock( &priv
->p_item
->lock
);
674 priv
->bookmark
.i_time_offset
= i_time
;
676 if( priv
->stats
!= NULL
)
677 input_stats_Compute( priv
->stats
, priv
->p_item
->p_stats
);
678 vlc_mutex_unlock( &priv
->p_item
->lock
);
680 input_SendEventStatistics( p_input
);
685 * The main input loop.
687 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
)
689 mtime_t i_intf_update
= 0;
690 mtime_t i_last_seek_mdate
= 0;
692 if( b_interactive
&& var_InheritBool( p_input
, "start-paused" ) )
693 ControlPause( p_input
, mdate() );
695 bool b_pause_after_eof
= b_interactive
&&
696 var_InheritBool( p_input
, "play-and-pause" );
697 bool b_paused_at_eof
= false;
699 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
700 const bool b_can_demux
= p_demux
->pf_demux
!= NULL
;
702 while( !input_Stopped( p_input
) && input_priv(p_input
)->i_state
!= ERROR_S
)
704 mtime_t i_wakeup
= -1;
705 bool b_paused
= input_priv(p_input
)->i_state
== PAUSE_S
;
706 /* FIXME if input_priv(p_input)->i_state == PAUSE_S the access/access_demux
707 * is paused -> this may cause problem with some of them
708 * The same problem can be seen when seeking while paused */
710 b_paused
= !es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
711 || input_priv(p_input
)->master
->b_eof
;
715 if( !input_priv(p_input
)->master
->b_eof
)
717 bool b_force_update
= false;
719 MainLoopDemux( p_input
, &b_force_update
);
722 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
726 b_paused_at_eof
= false;
728 else if( !es_out_GetEmpty( input_priv(p_input
)->p_es_out
) )
730 msg_Dbg( p_input
, "waiting decoder fifos to empty" );
731 i_wakeup
= mdate() + INPUT_IDLE_SLEEP
;
733 /* Pause after eof only if the input is pausable.
734 * This way we won't trigger timeshifting for nothing */
735 else if( b_pause_after_eof
&& input_priv(p_input
)->b_can_pause
)
737 if( b_paused_at_eof
)
740 vlc_value_t val
= { .i_int
= PAUSE_S
};
742 msg_Dbg( p_input
, "pausing at EOF (pause after each)");
743 Control( p_input
, INPUT_CONTROL_SET_STATE
, val
);
746 b_paused_at_eof
= true;
750 if( MainLoopTryRepeat( p_input
) )
754 /* Update interface and statistics */
755 mtime_t now
= mdate();
756 if( now
>= i_intf_update
)
758 MainLoopStatistics( p_input
);
759 i_intf_update
= now
+ INT64_C(250000);
766 mtime_t i_deadline
= i_wakeup
;
768 /* Postpone seeking until ES buffering is complete or at most
770 bool b_postpone
= es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
771 && !input_priv(p_input
)->master
->b_eof
;
774 mtime_t now
= mdate();
776 /* Recheck ES buffer level every 20 ms when seeking */
777 if( now
< i_last_seek_mdate
+ INT64_C(125000)
778 && (i_deadline
< 0 || i_deadline
> now
+ INT64_C(20000)) )
779 i_deadline
= now
+ INT64_C(20000);
787 if( ControlPop( p_input
, &i_type
, &val
, i_deadline
, b_postpone
) )
791 break; /* Wake-up time reached */
795 msg_Dbg( p_input
, "control type=%d", i_type
);
797 if( Control( p_input
, i_type
, val
) )
799 if( ControlIsSeekRequest( i_type
) )
800 i_last_seek_mdate
= mdate();
804 /* Update the wakeup time */
806 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
812 static int InitSout( input_thread_t
* p_input
)
814 input_thread_private_t
*priv
= input_priv(p_input
);
816 if( priv
->b_preparsing
)
819 /* Find a usable sout and attach it to p_input */
821 if( priv
->p_renderer
)
823 const char *psz_renderer_sout
= vlc_renderer_item_sout( priv
->p_renderer
);
824 if( asprintf( &psz
, "#%s", psz_renderer_sout
) < 0 )
828 psz
= var_GetNonEmptyString( p_input
, "sout" );
829 if( psz
&& strncasecmp( priv
->p_item
->psz_uri
, "vlc:", 4 ) )
831 priv
->p_sout
= input_resource_RequestSout( priv
->p_resource
, NULL
, psz
);
832 if( priv
->p_sout
== NULL
)
834 input_ChangeState( p_input
, ERROR_S
);
835 msg_Err( p_input
, "cannot start stream output instance, " \
843 input_resource_RequestSout( priv
->p_resource
, NULL
, NULL
);
851 static void InitTitle( input_thread_t
* p_input
)
853 input_thread_private_t
*priv
= input_priv(p_input
);
854 input_source_t
*p_master
= priv
->master
;
856 if( priv
->b_preparsing
)
859 vlc_mutex_lock( &priv
->p_item
->lock
);
860 /* Create global title (from master) */
861 priv
->i_title
= p_master
->i_title
;
862 priv
->title
= p_master
->title
;
863 priv
->i_title_offset
= p_master
->i_title_offset
;
864 priv
->i_seekpoint_offset
= p_master
->i_seekpoint_offset
;
865 if( priv
->i_title
> 0 )
867 /* Setup variables */
868 input_ControlVarNavigation( p_input
);
869 input_SendEventTitle( p_input
, 0 );
873 priv
->b_can_pace_control
= p_master
->b_can_pace_control
;
874 priv
->b_can_pause
= p_master
->b_can_pause
;
875 priv
->b_can_rate_control
= p_master
->b_can_rate_control
;
876 vlc_mutex_unlock( &priv
->p_item
->lock
);
879 static void StartTitle( input_thread_t
* p_input
)
881 input_thread_private_t
*priv
= input_priv(p_input
);
884 /* Start title/chapter */
885 val
.i_int
= priv
->master
->i_title_start
- priv
->master
->i_title_offset
;
886 if( val
.i_int
> 0 && val
.i_int
< priv
->master
->i_title
)
887 input_ControlPush( p_input
, INPUT_CONTROL_SET_TITLE
, &val
);
889 val
.i_int
= priv
->master
->i_seekpoint_start
-
890 priv
->master
->i_seekpoint_offset
;
891 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
892 input_ControlPush( p_input
, INPUT_CONTROL_SET_SEEKPOINT
, &val
);
894 /* Start/stop/run time */
895 priv
->i_start
= llroundf(1000000.f
896 * var_GetFloat( p_input
, "start-time" ));
897 priv
->i_stop
= llroundf(1000000.f
898 * var_GetFloat( p_input
, "stop-time" ));
899 if( priv
->i_stop
<= 0 )
901 priv
->i_stop
= llroundf(1000000.f
902 * var_GetFloat( p_input
, "run-time" ));
903 if( priv
->i_stop
< 0 )
905 msg_Warn( p_input
, "invalid run-time ignored" );
909 priv
->i_stop
+= priv
->i_start
;
912 if( priv
->i_start
> 0 )
916 msg_Dbg( p_input
, "starting at time: %"PRId64
"s",
917 priv
->i_start
/ CLOCK_FREQ
);
919 s
.i_int
= priv
->i_start
;
920 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &s
);
922 if( priv
->i_stop
> 0 && priv
->i_stop
<= priv
->i_start
)
924 msg_Warn( p_input
, "invalid stop-time ignored" );
927 priv
->b_fast_seek
= var_GetBool( p_input
, "input-fast-seek" );
930 static int SlaveCompare(const void *a
, const void *b
)
932 const input_item_slave_t
*p_slave0
= *((const input_item_slave_t
**) a
);
933 const input_item_slave_t
*p_slave1
= *((const input_item_slave_t
**) b
);
935 if( p_slave0
== NULL
|| p_slave1
== NULL
)
937 /* Put NULL (or rejected) subs at the end */
938 return p_slave0
== NULL
? 1 : p_slave1
== NULL
? -1 : 0;
941 if( p_slave0
->i_priority
> p_slave1
->i_priority
)
944 if( p_slave0
->i_priority
< p_slave1
->i_priority
)
950 static bool SlaveExists( input_item_slave_t
**pp_slaves
, int i_slaves
,
953 for( int i
= 0; i
< i_slaves
; i
++ )
955 if( pp_slaves
[i
] != NULL
956 && !strcmp( pp_slaves
[i
]->psz_uri
, psz_uri
) )
962 static void SetSubtitlesOptions( input_thread_t
*p_input
)
964 /* Get fps and set it if not already set */
965 const float f_fps
= input_priv(p_input
)->master
->f_fps
;
968 var_Create( p_input
, "sub-original-fps", VLC_VAR_FLOAT
);
969 var_SetFloat( p_input
, "sub-original-fps", f_fps
);
971 float f_requested_fps
= var_CreateGetFloat( p_input
, "sub-fps" );
972 if( f_requested_fps
!= f_fps
)
974 var_Create( p_input
, "sub-fps", VLC_VAR_FLOAT
|
976 var_SetFloat( p_input
, "sub-fps", f_requested_fps
);
980 const int i_delay
= var_CreateGetInteger( p_input
, "sub-delay" );
982 var_SetInteger( p_input
, "spu-delay", (mtime_t
)i_delay
* 100000 );
985 static void GetVarSlaves( input_thread_t
*p_input
,
986 input_item_slave_t
***ppp_slaves
, int *p_slaves
)
988 char *psz
= var_GetNonEmptyString( p_input
, "input-slave" );
992 input_item_slave_t
**pp_slaves
= *ppp_slaves
;
993 int i_slaves
= *p_slaves
;
998 while( *psz
== ' ' || *psz
== '#' )
1001 char *psz_delim
= strchr( psz
, '#' );
1003 *psz_delim
++ = '\0';
1008 char *uri
= strstr(psz
, "://")
1009 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
1014 input_item_slave_t
*p_slave
=
1015 input_item_slave_New( uri
, SLAVE_TYPE_AUDIO
, SLAVE_PRIORITY_USER
);
1018 if( unlikely( p_slave
== NULL
) )
1020 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1024 *ppp_slaves
= pp_slaves
; /* in case of realloc */
1025 *p_slaves
= i_slaves
;
1028 static void LoadSlaves( input_thread_t
*p_input
)
1030 input_item_slave_t
**pp_slaves
;
1032 TAB_INIT( i_slaves
, pp_slaves
);
1034 /* Look for and add slaves */
1036 char *psz_subtitle
= var_GetNonEmptyString( p_input
, "sub-file" );
1037 if( psz_subtitle
!= NULL
)
1039 msg_Dbg( p_input
, "forced subtitle: %s", psz_subtitle
);
1040 char *psz_uri
= input_SubtitleFile2Uri( p_input
, psz_subtitle
);
1041 free( psz_subtitle
);
1042 psz_subtitle
= NULL
;
1043 if( psz_uri
!= NULL
)
1045 input_item_slave_t
*p_slave
=
1046 input_item_slave_New( psz_uri
, SLAVE_TYPE_SPU
,
1047 SLAVE_PRIORITY_USER
);
1051 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1052 psz_subtitle
= p_slave
->psz_uri
;
1057 if( var_GetBool( p_input
, "sub-autodetect-file" ) )
1059 /* Add local subtitles */
1060 char *psz_autopath
= var_GetNonEmptyString( p_input
, "sub-autodetect-path" );
1062 if( subtitles_Detect( p_input
, psz_autopath
, input_priv(p_input
)->p_item
->psz_uri
,
1063 &pp_slaves
, &i_slaves
) == VLC_SUCCESS
)
1065 /* check that we did not add the subtitle through sub-file */
1066 if( psz_subtitle
!= NULL
)
1068 for( int i
= 1; i
< i_slaves
; i
++ )
1070 input_item_slave_t
*p_curr
= pp_slaves
[i
];
1072 && !strcmp( psz_subtitle
, p_curr
->psz_uri
) )
1074 /* reject current sub */
1075 input_item_slave_Delete( p_curr
);
1076 pp_slaves
[i
] = NULL
;
1081 free( psz_autopath
);
1084 /* Add slaves found by the directory demuxer or via libvlc */
1085 input_item_t
*p_item
= input_priv(p_input
)->p_item
;
1086 vlc_mutex_lock( &p_item
->lock
);
1088 /* Move item slaves to local pp_slaves */
1089 for( int i
= 0; i
< p_item
->i_slaves
; i
++ )
1091 input_item_slave_t
*p_slave
= p_item
->pp_slaves
[i
];
1092 if( !SlaveExists( pp_slaves
, i_slaves
, p_slave
->psz_uri
) )
1093 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1095 input_item_slave_Delete( p_slave
);
1097 /* Slaves that are successfully loaded will be added back to the item */
1098 TAB_CLEAN( p_item
->i_slaves
, p_item
->pp_slaves
);
1099 vlc_mutex_unlock( &p_item
->lock
);
1101 /* Add slaves from the "input-slave" option */
1102 GetVarSlaves( p_input
, &pp_slaves
, &i_slaves
);
1105 qsort( pp_slaves
, i_slaves
, sizeof (input_item_slave_t
*),
1108 /* add all detected slaves */
1109 bool p_forced
[2] = { false, false };
1110 static_assert( SLAVE_TYPE_AUDIO
<= 1 && SLAVE_TYPE_SPU
<= 1,
1111 "slave type size mismatch");
1112 for( int i
= 0; i
< i_slaves
&& pp_slaves
[i
] != NULL
; i
++ )
1114 input_item_slave_t
*p_slave
= pp_slaves
[i
];
1115 /* Slaves added via options should not fail */
1116 unsigned i_flags
= p_slave
->i_priority
!= SLAVE_PRIORITY_USER
1117 ? SLAVE_ADD_CANFAIL
: SLAVE_ADD_NOFLAG
;
1118 bool b_forced
= false;
1120 /* Force the first subtitle with the highest priority or with the
1122 if( !p_forced
[p_slave
->i_type
]
1123 && ( p_slave
->b_forced
|| p_slave
->i_priority
== SLAVE_PRIORITY_USER
) )
1125 i_flags
|= SLAVE_ADD_FORCED
;
1129 if( input_SlaveSourceAdd( p_input
, p_slave
->i_type
, p_slave
->psz_uri
,
1130 i_flags
) == VLC_SUCCESS
)
1132 input_item_AddSlave( input_priv(p_input
)->p_item
, p_slave
);
1134 p_forced
[p_slave
->i_type
] = true;
1137 input_item_slave_Delete( p_slave
);
1139 TAB_CLEAN( i_slaves
, pp_slaves
);
1141 /* Load subtitles from attachments */
1142 int i_attachment
= 0;
1143 input_attachment_t
**pp_attachment
= NULL
;
1145 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
1146 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
1148 const input_attachment_t
*a
= input_priv(p_input
)->attachment
[i
];
1149 if( !strcmp( a
->psz_mime
, "application/x-srt" ) )
1150 TAB_APPEND( i_attachment
, pp_attachment
,
1151 vlc_input_attachment_New( a
->psz_name
, NULL
,
1152 a
->psz_description
, NULL
, 0 ) );
1154 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1156 if( i_attachment
> 0 )
1157 var_Create( p_input
, "sub-description", VLC_VAR_STRING
);
1158 for( int i
= 0; i
< i_attachment
; i
++ )
1160 input_attachment_t
*a
= pp_attachment
[i
];
1164 if( a
->psz_name
[0] &&
1165 asprintf( &psz_mrl
, "attachment://%s", a
->psz_name
) >= 0 )
1167 var_SetString( p_input
, "sub-description", a
->psz_description
? a
->psz_description
: "");
1169 /* Force the first subtitle from attachment if there is no
1170 * subtitles already forced */
1171 if( input_SlaveSourceAdd( p_input
, SLAVE_TYPE_SPU
, psz_mrl
,
1172 p_forced
[ SLAVE_TYPE_SPU
] ?
1173 SLAVE_ADD_NOFLAG
: SLAVE_ADD_FORCED
) == VLC_SUCCESS
)
1174 p_forced
[ SLAVE_TYPE_SPU
] = true;
1177 /* Don't update item slaves for attachements */
1179 vlc_input_attachment_Delete( a
);
1181 free( pp_attachment
);
1182 if( i_attachment
> 0 )
1183 var_Destroy( p_input
, "sub-description" );
1186 static void UpdatePtsDelay( input_thread_t
*p_input
)
1188 input_thread_private_t
*p_sys
= input_priv(p_input
);
1190 /* Get max pts delay from input source */
1191 mtime_t i_pts_delay
= p_sys
->master
->i_pts_delay
;
1192 for( int i
= 0; i
< p_sys
->i_slave
; i
++ )
1193 i_pts_delay
= __MAX( i_pts_delay
, p_sys
->slave
[i
]->i_pts_delay
);
1195 if( i_pts_delay
< 0 )
1198 /* Take care of audio/spu delay */
1199 const mtime_t i_audio_delay
= var_GetInteger( p_input
, "audio-delay" );
1200 const mtime_t i_spu_delay
= var_GetInteger( p_input
, "spu-delay" );
1201 const mtime_t i_extra_delay
= __MIN( i_audio_delay
, i_spu_delay
);
1202 if( i_extra_delay
< 0 )
1203 i_pts_delay
-= i_extra_delay
;
1205 /* Update cr_average depending on the caching */
1206 const int i_cr_average
= var_GetInteger( p_input
, "cr-average" ) * i_pts_delay
/ DEFAULT_PTS_DELAY
;
1209 es_out_SetDelay( input_priv(p_input
)->p_es_out_display
, AUDIO_ES
, i_audio_delay
);
1210 es_out_SetDelay( input_priv(p_input
)->p_es_out_display
, SPU_ES
, i_spu_delay
);
1211 es_out_SetJitter( input_priv(p_input
)->p_es_out
, i_pts_delay
, 0, i_cr_average
);
1214 static void InitPrograms( input_thread_t
* p_input
)
1219 /* Compute correct pts_delay */
1220 UpdatePtsDelay( p_input
);
1223 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1224 if( input_priv(p_input
)->p_sout
&& !input_priv(p_input
)->p_renderer
)
1228 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1232 TAB_INIT( list
.i_count
, list
.p_values
);
1233 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1235 prgm
= strtok_r( NULL
, ",", &buf
) )
1237 vlc_value_t val
= { .i_int
= atoi( prgm
) };
1238 TAB_APPEND(list
.i_count
, list
.p_values
, val
);
1241 if( list
.i_count
> 0 )
1242 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1243 /* Note : we should remove the "program" callback. */
1247 else if( var_GetBool( p_input
, "sout-all" ) )
1249 i_es_out_mode
= ES_OUT_MODE_ALL
;
1252 es_out_SetMode( input_priv(p_input
)->p_es_out
, i_es_out_mode
);
1254 /* Inform the demuxer about waited group (needed only for DVB) */
1255 if( i_es_out_mode
== ES_OUT_MODE_ALL
)
1257 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, -1, NULL
);
1259 else if( i_es_out_mode
== ES_OUT_MODE_PARTIAL
)
1261 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, -1,
1263 TAB_CLEAN( list
.i_count
, list
.p_values
);
1267 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
,
1268 es_out_GetGroupForced( input_priv(p_input
)->p_es_out
), NULL
);
1272 static int Init( input_thread_t
* p_input
)
1274 input_thread_private_t
*priv
= input_priv(p_input
);
1275 input_source_t
*master
;
1277 if( var_Type( p_input
->obj
.parent
, "meta-file" ) )
1279 msg_Dbg( p_input
, "Input is a meta file: disabling unneeded options" );
1280 var_SetString( p_input
, "sout", "" );
1281 var_SetBool( p_input
, "sout-all", false );
1282 var_SetString( p_input
, "input-slave", "" );
1283 var_SetInteger( p_input
, "input-repeat", 0 );
1284 var_SetString( p_input
, "sub-file", "" );
1285 var_SetBool( p_input
, "sub-autodetect-file", false );
1289 if( InitSout( p_input
) )
1294 priv
->p_es_out
= input_EsOutTimeshiftNew( p_input
, priv
->p_es_out_display
,
1296 if( priv
->p_es_out
== NULL
)
1300 input_ChangeState( p_input
, OPENING_S
);
1301 input_SendEventCache( p_input
, 0.0 );
1304 master
= InputSourceNew( p_input
, priv
->p_item
->psz_uri
, NULL
, false );
1305 if( master
== NULL
)
1307 priv
->master
= master
;
1309 InitTitle( p_input
);
1311 /* Load master infos */
1314 if( demux_Control( master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
1317 i_length
= input_item_GetDuration( priv
->p_item
);
1318 input_SendEventLength( p_input
, i_length
);
1320 input_SendEventPosition( p_input
, 0.0, 0 );
1322 if( !priv
->b_preparsing
)
1324 StartTitle( p_input
);
1325 SetSubtitlesOptions( p_input
);
1326 LoadSlaves( p_input
);
1327 InitPrograms( p_input
);
1329 double f_rate
= var_InheritFloat( p_input
, "rate" );
1330 if( f_rate
!= 0.0 && f_rate
!= 1.0 )
1332 vlc_value_t val
= { .i_int
= INPUT_RATE_DEFAULT
/ f_rate
};
1333 input_ControlPush( p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1337 if( !priv
->b_preparsing
&& priv
->p_sout
)
1339 priv
->b_out_pace_control
= priv
->p_sout
->i_out_pace_nocontrol
> 0;
1341 msg_Dbg( p_input
, "starting in %ssync mode",
1342 priv
->b_out_pace_control
? "a" : "" );
1345 vlc_meta_t
*p_meta
= vlc_meta_New();
1346 if( p_meta
!= NULL
)
1348 /* Get meta data from users */
1349 InputMetaUser( p_input
, p_meta
);
1351 /* Get meta data from master input */
1352 InputSourceMeta( p_input
, master
, p_meta
);
1354 /* And from slave */
1355 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1356 InputSourceMeta( p_input
, priv
->slave
[i
], p_meta
);
1358 es_out_ControlSetMeta( priv
->p_es_out
, p_meta
);
1359 vlc_meta_Delete( p_meta
);
1362 msg_Dbg( p_input
, "`%s' successfully opened",
1363 input_priv(p_input
)->p_item
->psz_uri
);
1365 /* initialization is complete */
1366 input_ChangeState( p_input
, PLAYING_S
);
1371 input_ChangeState( p_input
, ERROR_S
);
1373 if( input_priv(p_input
)->p_es_out
)
1374 es_out_Delete( input_priv(p_input
)->p_es_out
);
1375 es_out_SetMode( input_priv(p_input
)->p_es_out_display
, ES_OUT_MODE_END
);
1376 if( input_priv(p_input
)->p_resource
)
1378 if( input_priv(p_input
)->p_sout
)
1379 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1380 input_priv(p_input
)->p_sout
, NULL
);
1381 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1382 if( input_priv(p_input
)->p_resource_private
)
1383 input_resource_Terminate( input_priv(p_input
)->p_resource_private
);
1386 /* Mark them deleted */
1387 input_priv(p_input
)->p_es_out
= NULL
;
1388 input_priv(p_input
)->p_sout
= NULL
;
1390 return VLC_EGENERIC
;
1393 /*****************************************************************************
1394 * End: end the input thread
1395 *****************************************************************************/
1396 static void End( input_thread_t
* p_input
)
1398 input_thread_private_t
*priv
= input_priv(p_input
);
1400 /* We are at the end */
1401 input_ChangeState( p_input
, END_S
);
1403 /* Clean control variables */
1404 input_ControlVarStop( p_input
);
1406 /* Stop es out activity */
1407 es_out_SetMode( priv
->p_es_out
, ES_OUT_MODE_NONE
);
1410 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1411 InputSourceDestroy( priv
->slave
[i
] );
1412 free( priv
->slave
);
1414 /* Clean up master */
1415 InputSourceDestroy( priv
->master
);
1418 priv
->i_title_offset
= 0;
1419 priv
->i_seekpoint_offset
= 0;
1421 /* Unload all modules */
1422 if( priv
->p_es_out
)
1423 es_out_Delete( priv
->p_es_out
);
1424 es_out_SetMode( priv
->p_es_out_display
, ES_OUT_MODE_END
);
1426 if( priv
->stats
!= NULL
)
1428 input_item_t
*item
= priv
->p_item
;
1429 /* make sure we are up to date */
1430 vlc_mutex_lock( &item
->lock
);
1431 input_stats_Compute( priv
->stats
, item
->p_stats
);
1432 vlc_mutex_unlock( &item
->lock
);
1435 vlc_mutex_lock( &priv
->p_item
->lock
);
1436 if( priv
->i_attachment
> 0 )
1438 for( int i
= 0; i
< priv
->i_attachment
; i
++ )
1439 vlc_input_attachment_Delete( priv
->attachment
[i
] );
1440 TAB_CLEAN( priv
->i_attachment
, priv
->attachment
);
1441 free( priv
->attachment_demux
);
1442 priv
->attachment_demux
= NULL
;
1445 /* clean bookmarks */
1446 for( int i
= 0; i
< priv
->i_bookmark
; ++i
)
1447 vlc_seekpoint_Delete( priv
->pp_bookmark
[i
] );
1448 TAB_CLEAN( priv
->i_bookmark
, priv
->pp_bookmark
);
1450 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1453 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1454 input_priv(p_input
)->p_sout
, NULL
);
1455 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1456 if( input_priv(p_input
)->p_resource_private
)
1457 input_resource_Terminate( input_priv(p_input
)->p_resource_private
);
1460 /*****************************************************************************
1462 *****************************************************************************/
1463 void input_ControlPush( input_thread_t
*p_input
,
1464 int i_type
, vlc_value_t
*p_val
)
1466 input_thread_private_t
*sys
= input_priv(p_input
);
1468 vlc_mutex_lock( &sys
->lock_control
);
1469 if( sys
->is_stopped
|| sys
->i_control
>= INPUT_CONTROL_FIFO_SIZE
)
1471 if( sys
->is_stopped
)
1472 msg_Dbg( p_input
, "input control stopped, trashing type=%d",
1475 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1478 ControlRelease( i_type
, *p_val
);
1487 memset( &c
.val
, 0, sizeof(c
.val
) );
1489 sys
->control
[sys
->i_control
++] = c
;
1491 vlc_cond_signal( &sys
->wait_control
);
1493 vlc_mutex_unlock( &sys
->lock_control
);
1496 static int ControlGetReducedIndexLocked( input_thread_t
*p_input
)
1498 const int i_lt
= input_priv(p_input
)->control
[0].i_type
;
1500 for( i
= 1; i
< input_priv(p_input
)->i_control
; i
++ )
1502 const int i_ct
= input_priv(p_input
)->control
[i
].i_type
;
1505 ( i_ct
== INPUT_CONTROL_SET_STATE
||
1506 i_ct
== INPUT_CONTROL_SET_RATE
||
1507 i_ct
== INPUT_CONTROL_SET_POSITION
||
1508 i_ct
== INPUT_CONTROL_SET_TIME
||
1509 i_ct
== INPUT_CONTROL_SET_PROGRAM
||
1510 i_ct
== INPUT_CONTROL_SET_TITLE
||
1511 i_ct
== INPUT_CONTROL_SET_SEEKPOINT
||
1512 i_ct
== INPUT_CONTROL_SET_BOOKMARK
) )
1518 /* TODO but that's not that important
1519 - merge SET_X with SET_X_CMD
1520 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1521 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1531 static inline int ControlPop( input_thread_t
*p_input
,
1532 int *pi_type
, vlc_value_t
*p_val
,
1533 mtime_t i_deadline
, bool b_postpone_seek
)
1535 input_thread_private_t
*p_sys
= input_priv(p_input
);
1537 vlc_mutex_lock( &p_sys
->lock_control
);
1538 while( p_sys
->i_control
<= 0 ||
1539 ( b_postpone_seek
&& ControlIsSeekRequest( p_sys
->control
[0].i_type
) ) )
1541 if( p_sys
->is_stopped
)
1543 vlc_mutex_unlock( &p_sys
->lock_control
);
1544 return VLC_EGENERIC
;
1547 if( i_deadline
>= 0 )
1549 if( vlc_cond_timedwait( &p_sys
->wait_control
, &p_sys
->lock_control
,
1552 vlc_mutex_unlock( &p_sys
->lock_control
);
1553 return VLC_EGENERIC
;
1557 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
1561 const int i_index
= ControlGetReducedIndexLocked( p_input
);
1563 for( int i
= 0; i
< i_index
; ++i
)
1565 /* Release Reduced controls */
1566 ControlRelease( p_sys
->control
[i
].i_type
, p_sys
->control
[i
].val
);
1570 *pi_type
= p_sys
->control
[i_index
].i_type
;
1571 *p_val
= p_sys
->control
[i_index
].val
;
1573 p_sys
->i_control
-= i_index
+ 1;
1574 if( p_sys
->i_control
> 0 )
1575 memmove( &p_sys
->control
[0], &p_sys
->control
[i_index
+1],
1576 sizeof(*p_sys
->control
) * p_sys
->i_control
);
1577 vlc_mutex_unlock( &p_sys
->lock_control
);
1581 static bool ControlIsSeekRequest( int i_type
)
1585 case INPUT_CONTROL_SET_POSITION
:
1586 case INPUT_CONTROL_SET_TIME
:
1587 case INPUT_CONTROL_SET_TITLE
:
1588 case INPUT_CONTROL_SET_TITLE_NEXT
:
1589 case INPUT_CONTROL_SET_TITLE_PREV
:
1590 case INPUT_CONTROL_SET_SEEKPOINT
:
1591 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1592 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1593 case INPUT_CONTROL_SET_BOOKMARK
:
1594 case INPUT_CONTROL_NAV_ACTIVATE
:
1595 case INPUT_CONTROL_NAV_UP
:
1596 case INPUT_CONTROL_NAV_DOWN
:
1597 case INPUT_CONTROL_NAV_LEFT
:
1598 case INPUT_CONTROL_NAV_RIGHT
:
1599 case INPUT_CONTROL_NAV_POPUP
:
1600 case INPUT_CONTROL_NAV_MENU
:
1607 static void ControlRelease( int i_type
, vlc_value_t val
)
1611 case INPUT_CONTROL_ADD_SLAVE
:
1613 input_item_slave_Delete( val
.p_address
);
1615 case INPUT_CONTROL_SET_VIEWPOINT
:
1616 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT
:
1617 case INPUT_CONTROL_UPDATE_VIEWPOINT
:
1618 free( val
.p_address
);
1620 case INPUT_CONTROL_SET_RENDERER
:
1622 vlc_renderer_item_release( val
.p_address
);
1631 static void ControlPause( input_thread_t
*p_input
, mtime_t i_control_date
)
1633 int i_state
= PAUSE_S
;
1635 if( input_priv(p_input
)->b_can_pause
)
1637 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1639 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, true ) )
1641 msg_Warn( p_input
, "cannot set pause state" );
1647 if( es_out_SetPauseState( input_priv(p_input
)->p_es_out
, input_priv(p_input
)->b_can_pause
,
1648 true, i_control_date
) )
1650 msg_Warn( p_input
, "cannot set pause state at es_out level" );
1654 /* Switch to new state */
1655 input_ChangeState( p_input
, i_state
);
1658 static void ControlUnpause( input_thread_t
*p_input
, mtime_t i_control_date
)
1660 if( input_priv(p_input
)->b_can_pause
)
1662 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1664 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, false ) )
1666 msg_Err( p_input
, "cannot resume" );
1667 input_ChangeState( p_input
, ERROR_S
);
1672 /* Switch to play */
1673 input_ChangeState( p_input
, PLAYING_S
);
1674 es_out_SetPauseState( input_priv(p_input
)->p_es_out
, false, false, i_control_date
);
1677 static void ViewpointApply( input_thread_t
*p_input
)
1679 input_thread_private_t
*priv
= input_priv(p_input
);
1681 vlc_viewpoint_clip( &priv
->viewpoint
);
1683 vout_thread_t
**pp_vout
;
1685 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1687 for( size_t i
= 0; i
< i_vout
; ++i
)
1689 var_SetAddress( pp_vout
[i
], "viewpoint", &priv
->viewpoint
);
1690 /* This variable can only be read from callbacks */
1691 var_Change( pp_vout
[i
], "viewpoint", VLC_VAR_SETVALUE
,
1692 &(vlc_value_t
) { .p_address
= NULL
}, NULL
);
1693 vlc_object_release( pp_vout
[i
] );
1697 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1701 var_SetAddress( p_aout
, "viewpoint", &priv
->viewpoint
);
1702 /* This variable can only be read from callbacks */
1703 var_Change( p_aout
, "viewpoint", VLC_VAR_SETVALUE
,
1704 &(vlc_value_t
) { .p_address
= NULL
}, NULL
);
1705 vlc_object_release( p_aout
);
1709 static void ControlNav( input_thread_t
*p_input
, int i_type
)
1711 input_thread_private_t
*priv
= input_priv(p_input
);
1713 if( !demux_Control( priv
->master
->p_demux
, i_type
1714 - INPUT_CONTROL_NAV_ACTIVATE
+ DEMUX_NAV_ACTIVATE
) )
1715 return; /* The demux handled the navigation control */
1717 /* Handle Up/Down/Left/Right if the demux can't navigate */
1718 vlc_viewpoint_t vp
= {};
1719 int vol_direction
= 0;
1720 int seek_direction
= 0;
1723 case INPUT_CONTROL_NAV_UP
:
1727 case INPUT_CONTROL_NAV_DOWN
:
1731 case INPUT_CONTROL_NAV_LEFT
:
1732 seek_direction
= -1;
1735 case INPUT_CONTROL_NAV_RIGHT
:
1739 case INPUT_CONTROL_NAV_ACTIVATE
:
1740 case INPUT_CONTROL_NAV_POPUP
:
1741 case INPUT_CONTROL_NAV_MENU
:
1744 vlc_assert_unreachable();
1747 /* Try to change the viewpoint if possible */
1748 vout_thread_t
**pp_vout
;
1750 bool b_viewpoint_ch
= false;
1751 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1752 for( size_t i
= 0; i
< i_vout
; ++i
)
1755 && var_GetBool( pp_vout
[i
], "viewpoint-changeable" ) )
1756 b_viewpoint_ch
= true;
1757 vlc_object_release( pp_vout
[i
] );
1761 if( b_viewpoint_ch
)
1763 priv
->viewpoint_changed
= true;
1764 priv
->viewpoint
.yaw
+= vp
.yaw
;
1765 priv
->viewpoint
.pitch
+= vp
.pitch
;
1766 priv
->viewpoint
.roll
+= vp
.roll
;
1767 priv
->viewpoint
.fov
+= vp
.fov
;
1768 ViewpointApply( p_input
);
1772 /* Seek or change volume if the input doesn't have navigation or viewpoint */
1773 if( seek_direction
!= 0 )
1775 mtime_t it
= var_InheritInteger( p_input
, "short-jump-size" );
1776 var_SetInteger( p_input
, "time-offset", it
* seek_direction
* CLOCK_FREQ
);
1780 assert( vol_direction
!= 0 );
1781 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1784 aout_VolumeUpdate( p_aout
, vol_direction
, NULL
);
1785 vlc_object_release( p_aout
);
1791 static void ControlUpdateSout( input_thread_t
*p_input
, const char* psz_chain
)
1793 var_SetString( p_input
, "sout", psz_chain
);
1794 if( psz_chain
&& *psz_chain
)
1796 if( InitSout( p_input
) != VLC_SUCCESS
)
1798 msg_Err( p_input
, "Failed to start sout" );
1804 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1805 input_priv(p_input
)->p_sout
, NULL
);
1806 input_priv(p_input
)->p_sout
= NULL
;
1808 es_out_Control( input_priv(p_input
)->p_es_out
, ES_OUT_RESTART_ALL_ES
);
1812 static void ControlInsertDemuxFilter( input_thread_t
* p_input
, const char* psz_demux_chain
)
1814 input_source_t
*p_inputSource
= input_priv(p_input
)->master
;
1815 demux_t
*p_filtered_demux
= demux_FilterChainNew( p_inputSource
->p_demux
, psz_demux_chain
);
1816 if ( p_filtered_demux
!= NULL
)
1817 p_inputSource
->p_demux
= p_filtered_demux
;
1818 else if ( psz_demux_chain
!= NULL
)
1819 msg_Dbg(p_input
, "Failed to create demux filter %s", psz_demux_chain
);
1822 static bool Control( input_thread_t
*p_input
,
1823 int i_type
, vlc_value_t val
)
1825 const mtime_t i_control_date
= mdate();
1826 /* FIXME b_force_update is abused, it should be carefully checked */
1827 bool b_force_update
= false;
1830 return b_force_update
;
1834 case INPUT_CONTROL_SET_POSITION
:
1836 if( input_priv(p_input
)->b_recording
)
1838 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION ignored while recording" );
1842 float f_pos
= val
.f_float
;
1845 else if( f_pos
> 1.f
)
1847 /* Reset the decoders states and clock sync (before calling the demuxer */
1848 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
1849 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_POSITION
,
1850 (double) f_pos
, !input_priv(p_input
)->b_fast_seek
) )
1852 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION "
1853 "%2.1f%% failed", (double)(f_pos
* 100.f
) );
1857 if( input_priv(p_input
)->i_slave
> 0 )
1858 SlaveSeek( p_input
);
1859 input_priv(p_input
)->master
->b_eof
= false;
1861 b_force_update
= true;
1866 case INPUT_CONTROL_SET_TIME
:
1871 if( input_priv(p_input
)->b_recording
)
1873 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME ignored while recording" );
1881 /* Reset the decoders states and clock sync (before calling the demuxer */
1882 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
1884 i_ret
= demux_Control( input_priv(p_input
)->master
->p_demux
,
1885 DEMUX_SET_TIME
, i_time
,
1886 !input_priv(p_input
)->b_fast_seek
);
1891 /* Emulate it with a SET_POS */
1892 if( !demux_Control( input_priv(p_input
)->master
->p_demux
,
1893 DEMUX_GET_LENGTH
, &i_length
) && i_length
> 0 )
1895 double f_pos
= (double)i_time
/ (double)i_length
;
1896 i_ret
= demux_Control( input_priv(p_input
)->master
->p_demux
,
1897 DEMUX_SET_POSITION
, f_pos
,
1898 !input_priv(p_input
)->b_fast_seek
);
1903 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME %"PRId64
1904 " failed or not possible", i_time
);
1908 if( input_priv(p_input
)->i_slave
> 0 )
1909 SlaveSeek( p_input
);
1910 input_priv(p_input
)->master
->b_eof
= false;
1912 b_force_update
= true;
1917 case INPUT_CONTROL_SET_STATE
:
1921 if( input_priv(p_input
)->i_state
== PAUSE_S
)
1923 ControlUnpause( p_input
, i_control_date
);
1924 b_force_update
= true;
1928 if( input_priv(p_input
)->i_state
== PLAYING_S
)
1930 ControlPause( p_input
, i_control_date
);
1931 b_force_update
= true;
1935 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
1939 case INPUT_CONTROL_SET_RATE
:
1941 /* Get rate and direction */
1942 long long i_rate
= llabs( val
.i_int
);
1943 int i_rate_sign
= val
.i_int
< 0 ? -1 : 1;
1945 /* Check rate bound */
1946 if( i_rate
< INPUT_RATE_MIN
)
1948 msg_Dbg( p_input
, "cannot set rate faster" );
1949 i_rate
= INPUT_RATE_MIN
;
1951 else if( i_rate
> INPUT_RATE_MAX
)
1953 msg_Dbg( p_input
, "cannot set rate slower" );
1954 i_rate
= INPUT_RATE_MAX
;
1957 /* Apply direction */
1958 if( i_rate_sign
< 0 )
1960 if( input_priv(p_input
)->master
->b_rescale_ts
)
1962 msg_Dbg( p_input
, "cannot set negative rate" );
1963 i_rate
= input_priv(p_input
)->i_rate
;
1964 assert( i_rate
> 0 );
1968 i_rate
*= i_rate_sign
;
1972 if( i_rate
!= INPUT_RATE_DEFAULT
&&
1973 ( ( !input_priv(p_input
)->b_can_rate_control
&& !input_priv(p_input
)->master
->b_rescale_ts
) ||
1974 ( input_priv(p_input
)->p_sout
&& !input_priv(p_input
)->b_out_pace_control
) ) )
1976 msg_Dbg( p_input
, "cannot change rate" );
1977 i_rate
= INPUT_RATE_DEFAULT
;
1979 if( i_rate
!= input_priv(p_input
)->i_rate
&&
1980 !input_priv(p_input
)->b_can_pace_control
&& input_priv(p_input
)->b_can_rate_control
)
1982 if( !input_priv(p_input
)->master
->b_rescale_ts
)
1983 es_out_Control( input_priv(p_input
)->p_es_out
, ES_OUT_RESET_PCR
);
1985 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_RATE
,
1988 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
1989 i_rate
= input_priv(p_input
)->i_rate
;
1994 if( i_rate
!= input_priv(p_input
)->i_rate
)
1996 input_priv(p_input
)->i_rate
= i_rate
;
1997 input_SendEventRate( p_input
, i_rate
);
1999 if( input_priv(p_input
)->master
->b_rescale_ts
)
2001 const int i_rate_source
= (input_priv(p_input
)->b_can_pace_control
|| input_priv(p_input
)->b_can_rate_control
) ? i_rate
: INPUT_RATE_DEFAULT
;
2002 es_out_SetRate( input_priv(p_input
)->p_es_out
, i_rate_source
, i_rate
);
2005 b_force_update
= true;
2010 case INPUT_CONTROL_SET_PROGRAM
:
2011 /* No need to force update, es_out does it if needed */
2012 es_out_Control( input_priv(p_input
)->p_es_out
,
2013 ES_OUT_SET_GROUP
, val
.i_int
);
2015 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, val
.i_int
,
2019 case INPUT_CONTROL_SET_ES
:
2020 /* No need to force update, es_out does it if needed */
2021 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2022 ES_OUT_SET_ES_BY_ID
, (int)val
.i_int
);
2024 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_ES
, (int)val
.i_int
);
2027 case INPUT_CONTROL_RESTART_ES
:
2028 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2029 ES_OUT_RESTART_ES_BY_ID
, (int)val
.i_int
);
2032 case INPUT_CONTROL_SET_VIEWPOINT
:
2033 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT
:
2034 case INPUT_CONTROL_UPDATE_VIEWPOINT
:
2036 input_thread_private_t
*priv
= input_priv(p_input
);
2037 const vlc_viewpoint_t
*p_vp
= val
.p_address
;
2039 if ( i_type
== INPUT_CONTROL_SET_INITIAL_VIEWPOINT
)
2042 /* Set the initial viewpoint if it had not been changed by the
2044 if( !priv
->viewpoint_changed
)
2045 priv
->viewpoint
= *p_vp
;
2046 /* Update viewpoints of aout and every vouts in all cases. */
2048 else if ( i_type
== INPUT_CONTROL_SET_VIEWPOINT
)
2050 priv
->viewpoint_changed
= true;
2051 priv
->viewpoint
= *p_vp
;
2055 priv
->viewpoint_changed
= true;
2056 priv
->viewpoint
.yaw
+= p_vp
->yaw
;
2057 priv
->viewpoint
.pitch
+= p_vp
->pitch
;
2058 priv
->viewpoint
.roll
+= p_vp
->roll
;
2059 priv
->viewpoint
.fov
+= p_vp
->fov
;
2062 ViewpointApply( p_input
);
2066 case INPUT_CONTROL_SET_AUDIO_DELAY
:
2067 input_SendEventAudioDelay( p_input
, val
.i_int
);
2068 UpdatePtsDelay( p_input
);
2071 case INPUT_CONTROL_SET_SPU_DELAY
:
2072 input_SendEventSubtitleDelay( p_input
, val
.i_int
);
2073 UpdatePtsDelay( p_input
);
2076 case INPUT_CONTROL_SET_TITLE
:
2077 case INPUT_CONTROL_SET_TITLE_NEXT
:
2078 case INPUT_CONTROL_SET_TITLE_PREV
:
2080 if( input_priv(p_input
)->b_recording
)
2082 msg_Err( p_input
, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
2085 if( input_priv(p_input
)->master
->i_title
<= 0 )
2088 int i_title
= demux_GetTitle( input_priv(p_input
)->master
->p_demux
);
2089 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
2091 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
2094 i_title
= val
.i_int
;
2095 if( i_title
< 0 || i_title
>= input_priv(p_input
)->master
->i_title
)
2098 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
2099 demux_Control( input_priv(p_input
)->master
->p_demux
,
2100 DEMUX_SET_TITLE
, i_title
);
2101 input_SendEventTitle( p_input
, i_title
);
2104 case INPUT_CONTROL_SET_SEEKPOINT
:
2105 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
2106 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
2108 if( input_priv(p_input
)->b_recording
)
2110 msg_Err( p_input
, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
2113 if( input_priv(p_input
)->master
->i_title
<= 0 )
2116 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2118 int i_title
= demux_GetTitle( p_demux
);
2119 int i_seekpoint
= demux_GetSeekpoint( p_demux
);
2121 if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_PREV
)
2123 int64_t i_seekpoint_time
= input_priv(p_input
)->master
->title
[i_title
]->seekpoint
[i_seekpoint
]->i_time_offset
;
2124 int64_t i_input_time
= var_GetInteger( p_input
, "time" );
2125 if( i_seekpoint_time
>= 0 && i_input_time
>= 0 )
2127 if( i_input_time
< i_seekpoint_time
+ 3000000 )
2133 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
2136 i_seekpoint
= val
.i_int
;
2138 || i_seekpoint
>= input_priv(p_input
)->master
->title
[i_title
]->i_seekpoint
)
2141 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
2142 demux_Control( input_priv(p_input
)->master
->p_demux
,
2143 DEMUX_SET_SEEKPOINT
, i_seekpoint
);
2144 input_SendEventSeekpoint( p_input
, i_title
, i_seekpoint
);
2148 case INPUT_CONTROL_ADD_SLAVE
:
2151 input_item_slave_t
*p_item_slave
= val
.p_address
;
2152 unsigned i_flags
= SLAVE_ADD_CANFAIL
| SLAVE_ADD_SET_TIME
;
2153 if( p_item_slave
->b_forced
)
2154 i_flags
|= SLAVE_ADD_FORCED
;
2156 if( input_SlaveSourceAdd( p_input
, p_item_slave
->i_type
,
2157 p_item_slave
->psz_uri
, i_flags
)
2160 /* Update item slaves */
2161 input_item_AddSlave( input_priv(p_input
)->p_item
, p_item_slave
);
2162 /* The slave is now owned by the item */
2163 val
.p_address
= NULL
;
2168 case INPUT_CONTROL_SET_RECORD_STATE
:
2169 if( !!input_priv(p_input
)->b_recording
!= !!val
.b_bool
)
2171 if( input_priv(p_input
)->master
->b_can_stream_record
)
2173 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
2174 DEMUX_SET_RECORD_STATE
, val
.b_bool
) )
2179 if( es_out_SetRecordState( input_priv(p_input
)->p_es_out_display
, val
.b_bool
) )
2182 input_priv(p_input
)->b_recording
= val
.b_bool
;
2184 input_SendEventRecord( p_input
, val
.b_bool
);
2186 b_force_update
= true;
2190 case INPUT_CONTROL_SET_FRAME_NEXT
:
2191 if( input_priv(p_input
)->i_state
== PAUSE_S
)
2193 es_out_SetFrameNext( input_priv(p_input
)->p_es_out
);
2195 else if( input_priv(p_input
)->i_state
== PLAYING_S
)
2197 ControlPause( p_input
, i_control_date
);
2201 msg_Err( p_input
, "invalid state for frame next" );
2203 b_force_update
= true;
2206 case INPUT_CONTROL_SET_BOOKMARK
:
2208 mtime_t time_offset
= -1;
2210 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2211 if( val
.i_int
>= 0 && val
.i_int
< input_priv(p_input
)->i_bookmark
)
2213 const seekpoint_t
*p_bookmark
= input_priv(p_input
)->pp_bookmark
[val
.i_int
];
2214 time_offset
= p_bookmark
->i_time_offset
;
2216 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2218 if( time_offset
< 0 )
2220 msg_Err( p_input
, "invalid bookmark %"PRId64
, val
.i_int
);
2224 val
.i_int
= time_offset
;
2225 b_force_update
= Control( p_input
, INPUT_CONTROL_SET_TIME
, val
);
2228 case INPUT_CONTROL_SET_RENDERER
:
2231 vlc_renderer_item_t
*p_item
= val
.p_address
;
2232 input_thread_private_t
*p_priv
= input_priv( p_input
);
2233 // We do not support switching from a renderer to another for now
2234 if ( p_item
== NULL
&& p_priv
->p_renderer
== NULL
)
2237 if ( p_priv
->p_renderer
)
2239 ControlUpdateSout( p_input
, NULL
);
2240 demux_FilterDisable( p_priv
->master
->p_demux
,
2241 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) );
2242 vlc_renderer_item_release( p_priv
->p_renderer
);
2243 p_priv
->p_renderer
= NULL
;
2245 if( p_item
!= NULL
)
2247 p_priv
->p_renderer
= vlc_renderer_item_hold( p_item
);
2248 ControlUpdateSout( p_input
, vlc_renderer_item_sout( p_item
) );
2249 if( !demux_FilterEnable( p_priv
->master
->p_demux
,
2250 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) ) )
2252 ControlInsertDemuxFilter( p_input
,
2253 vlc_renderer_item_demux_filter( p_item
) );
2255 input_resource_TerminateVout( p_priv
->p_resource
);
2261 case INPUT_CONTROL_NAV_ACTIVATE
:
2262 case INPUT_CONTROL_NAV_UP
:
2263 case INPUT_CONTROL_NAV_DOWN
:
2264 case INPUT_CONTROL_NAV_LEFT
:
2265 case INPUT_CONTROL_NAV_RIGHT
:
2266 case INPUT_CONTROL_NAV_POPUP
:
2267 case INPUT_CONTROL_NAV_MENU
:
2268 ControlNav( p_input
, i_type
);
2272 msg_Err( p_input
, "not yet implemented" );
2276 ControlRelease( i_type
, val
);
2277 return b_force_update
;
2280 /*****************************************************************************
2281 * UpdateTitleSeekpoint
2282 *****************************************************************************/
2283 static int UpdateTitleSeekpoint( input_thread_t
*p_input
,
2284 int i_title
, int i_seekpoint
)
2286 int i_title_end
= input_priv(p_input
)->master
->i_title_end
-
2287 input_priv(p_input
)->master
->i_title_offset
;
2288 int i_seekpoint_end
= input_priv(p_input
)->master
->i_seekpoint_end
-
2289 input_priv(p_input
)->master
->i_seekpoint_offset
;
2291 if( i_title_end
>= 0 && i_seekpoint_end
>= 0 )
2293 if( i_title
> i_title_end
||
2294 ( i_title
== i_title_end
&& i_seekpoint
> i_seekpoint_end
) )
2295 return VLC_DEMUXER_EOF
;
2297 else if( i_seekpoint_end
>= 0 )
2299 if( i_seekpoint
> i_seekpoint_end
)
2300 return VLC_DEMUXER_EOF
;
2302 else if( i_title_end
>= 0 )
2304 if( i_title
> i_title_end
)
2305 return VLC_DEMUXER_EOF
;
2307 return VLC_DEMUXER_SUCCESS
;
2309 /*****************************************************************************
2311 *****************************************************************************/
2312 static int UpdateTitleSeekpointFromDemux( input_thread_t
*p_input
)
2314 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2316 /* TODO event-like */
2317 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE
) )
2318 input_SendEventTitle( p_input
, demux_GetTitle( p_demux
) );
2320 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_SEEKPOINT
) )
2321 input_SendEventSeekpoint( p_input
, demux_GetTitle( p_demux
),
2322 demux_GetSeekpoint( p_demux
) );
2324 return UpdateTitleSeekpoint( p_input
,
2325 demux_GetTitle( p_demux
),
2326 demux_GetSeekpoint( p_demux
) );
2329 static void UpdateGenericFromDemux( input_thread_t
*p_input
)
2331 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2333 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_META
) )
2334 InputUpdateMeta( p_input
, p_demux
);
2340 if( !demux_Control( p_demux
, DEMUX_GET_SIGNAL
, &quality
, &strength
) )
2341 input_SendEventSignal( p_input
, quality
, strength
);
2345 static void UpdateTitleListfromDemux( input_thread_t
*p_input
)
2347 input_thread_private_t
*priv
= input_priv(p_input
);
2348 input_source_t
*in
= priv
->master
;
2350 /* Delete the preexisting titles */
2351 if( in
->i_title
> 0 )
2353 for( int i
= 0; i
< in
->i_title
; i
++ )
2354 vlc_input_title_Delete( in
->title
[i
] );
2355 TAB_CLEAN( in
->i_title
, in
->title
);
2358 in
->b_title_demux
= false;
2361 /* Get the new title list */
2362 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2363 &in
->title
, &in
->i_title
,
2364 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2365 TAB_INIT( in
->i_title
, in
->title
);
2367 in
->b_title_demux
= true;
2369 InitTitle( p_input
);
2373 InputStreamHandleAnchor( input_source_t
*source
, stream_t
**stream
,
2374 char const *anchor
)
2377 if( stream_extractor_AttachParsed( stream
, anchor
, &extra
) )
2379 msg_Err( source
, "unable to attach stream-extractors for %s",
2380 (*stream
)->psz_url
);
2382 return VLC_EGENERIC
;
2385 if( vlc_stream_directory_Attach( stream
, NULL
) )
2386 msg_Dbg( source
, "attachment of directory-extractor failed for %s",
2387 (*stream
)->psz_url
);
2389 MRLSections( extra
? extra
: "",
2390 &source
->i_title_start
, &source
->i_title_end
,
2391 &source
->i_seekpoint_start
, &source
->i_seekpoint_end
);
2396 static demux_t
*InputDemuxNew( input_thread_t
*p_input
, input_source_t
*p_source
,
2397 const char *psz_access
, const char *psz_demux
,
2398 const char *psz_path
, const char *psz_anchor
)
2400 input_thread_private_t
*priv
= input_priv(p_input
);
2401 demux_t
*p_demux
= NULL
;
2403 /* first, try to create an access demux */
2404 p_demux
= demux_NewAdvanced( VLC_OBJECT( p_source
), p_input
,
2405 psz_access
, psz_access
, psz_path
,
2406 NULL
, priv
->p_es_out
, priv
->b_preparsing
);
2409 MRLSections( psz_anchor
,
2410 &p_source
->i_title_start
, &p_source
->i_title_end
,
2411 &p_source
->i_seekpoint_start
, &p_source
->i_seekpoint_end
);
2416 /* not an access-demux: create the underlying access stream */
2419 if( asprintf( &psz_base_mrl
, "%s://%s", psz_access
, psz_path
) < 0 )
2422 char *psz_filters
= var_InheritString( p_source
, "stream-filter" );
2423 stream_t
* p_stream
= stream_AccessNew( VLC_OBJECT( p_source
), p_input
,
2426 FREENULL( psz_base_mrl
);
2428 if( p_stream
== NULL
)
2431 /* attach explicit stream filters to stream */
2433 p_stream
= stream_FilterChainNew( p_stream
, psz_filters
);
2435 FREENULL( psz_filters
);
2437 /* handle anchors */
2438 if( InputStreamHandleAnchor( p_source
, &p_stream
, psz_anchor
) )
2441 /* attach conditional record stream-filter */
2442 if( var_InheritBool( p_source
, "input-record-native" ) )
2443 p_stream
= stream_FilterChainNew( p_stream
, "record" );
2445 /* create a regular demux with the access stream created */
2446 p_demux
= demux_NewAdvanced( VLC_OBJECT( p_source
), p_input
,
2447 psz_access
, psz_demux
, psz_path
,
2448 p_stream
, priv
->p_es_out
,
2449 priv
->b_preparsing
);
2454 free( psz_base_mrl
);
2455 free( psz_filters
);
2458 vlc_stream_Delete( p_stream
);
2463 /*****************************************************************************
2465 *****************************************************************************/
2466 static input_source_t
*InputSourceNew( input_thread_t
*p_input
,
2467 const char *psz_mrl
,
2468 const char *psz_forced_demux
,
2469 bool b_in_can_fail
)
2471 input_thread_private_t
*priv
= input_priv(p_input
);
2472 input_source_t
*in
= vlc_custom_create( p_input
, sizeof( *in
),
2474 if( unlikely(in
== NULL
) )
2477 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2480 char *psz_dup
= strdup( psz_mrl
);
2481 char *psz_demux_var
= NULL
;
2483 if( psz_dup
== NULL
)
2485 vlc_object_release( in
);
2490 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, &psz_anchor
, psz_dup
);
2492 if( psz_demux
== NULL
|| psz_demux
[0] == '\0' )
2493 psz_demux
= psz_demux_var
= var_InheritString( in
, "demux" );
2495 if( psz_forced_demux
!= NULL
)
2496 psz_demux
= psz_forced_demux
;
2498 if( psz_demux
== NULL
)
2501 msg_Dbg( p_input
, "`%s' gives access `%s' demux `%s' path `%s'",
2502 psz_mrl
, psz_access
, psz_demux
, psz_path
);
2504 if( input_priv(p_input
)->master
== NULL
/* XXX ugly */)
2505 { /* On master stream only, use input-list */
2506 char *str
= var_InheritString( p_input
, "input-list" );
2511 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2512 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2515 var_SetString( p_input
, "concat-list", list
);
2519 psz_access
= "concat";
2523 if( strcasecmp( psz_access
, "concat" ) )
2524 { /* Autodetect extra files if none specified */
2528 TAB_INIT( count
, tab
);
2529 InputGetExtraFiles( p_input
, &count
, &tab
, &psz_access
, psz_path
);
2534 for( int i
= 0; i
< count
; i
++ )
2537 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2546 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2547 if( likely(list
!= NULL
) )
2549 var_SetString( p_input
, "concat-list", list
);
2553 TAB_CLEAN( count
, tab
);
2556 in
->p_demux
= InputDemuxNew( p_input
, in
, psz_access
, psz_demux
,
2557 psz_path
, psz_anchor
);
2559 free( psz_demux_var
);
2562 if( in
->p_demux
== NULL
)
2564 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2565 vlc_dialog_display_error( p_input
, _("Your input can't be opened"),
2566 _("VLC is unable to open the MRL '%s'."
2567 " Check the log for details."), psz_mrl
);
2568 vlc_object_release( in
);
2572 char *psz_demux_chain
= NULL
;
2573 if( priv
->p_renderer
)
2575 const char* psz_renderer_demux
= vlc_renderer_item_demux_filter(
2577 if( psz_renderer_demux
)
2578 psz_demux_chain
= strdup( psz_renderer_demux
);
2580 if( !psz_demux_chain
)
2581 psz_demux_chain
= var_GetNonEmptyString(p_input
, "demux-filter");
2582 if( psz_demux_chain
!= NULL
) /* add the chain of demux filters */
2584 in
->p_demux
= demux_FilterChainNew( in
->p_demux
, psz_demux_chain
);
2585 free( psz_demux_chain
);
2587 if( in
->p_demux
== NULL
)
2589 msg_Err(p_input
, "Failed to create demux filter");
2590 vlc_object_release( in
);
2595 /* Get infos from (access_)demux */
2597 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &b_can_seek
) )
2599 var_SetBool( p_input
, "can-seek", b_can_seek
);
2601 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_PACE
,
2602 &in
->b_can_pace_control
) )
2603 in
->b_can_pace_control
= false;
2605 assert( in
->p_demux
->pf_demux
!= NULL
|| !in
->b_can_pace_control
);
2607 if( !in
->b_can_pace_control
)
2609 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_RATE
,
2610 &in
->b_can_rate_control
) )
2612 in
->b_can_rate_control
= false;
2613 in
->b_rescale_ts
= true;
2616 in
->b_rescale_ts
= !in
->b_can_rate_control
;
2620 in
->b_can_rate_control
= true;
2621 in
->b_rescale_ts
= true;
2624 demux_Control( in
->p_demux
, DEMUX_CAN_PAUSE
, &in
->b_can_pause
);
2626 var_SetBool( p_input
, "can-pause", in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2627 var_SetBool( p_input
, "can-rate", !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2628 var_SetBool( p_input
, "can-rewind", !in
->b_rescale_ts
&& !in
->b_can_pace_control
&& in
->b_can_rate_control
);
2630 /* Set record capabilities */
2631 if( demux_Control( in
->p_demux
, DEMUX_CAN_RECORD
, &in
->b_can_stream_record
) )
2632 in
->b_can_stream_record
= false;
2634 if( !var_GetBool( p_input
, "input-record-native" ) )
2635 in
->b_can_stream_record
= false;
2636 var_SetBool( p_input
, "can-record", true );
2638 var_SetBool( p_input
, "can-record", in
->b_can_stream_record
);
2642 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2643 if( !input_priv(p_input
)->b_preparsing
)
2645 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2646 &in
->title
, &in
->i_title
,
2647 &in
->i_title_offset
, &in
->i_seekpoint_offset
))
2649 TAB_INIT( in
->i_title
, in
->title
);
2653 in
->b_title_demux
= true;
2657 input_attachment_t
**attachment
;
2658 if( !demux_Control( in
->p_demux
, DEMUX_GET_ATTACHMENTS
,
2659 &attachment
, &i_attachment
) )
2661 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2662 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2663 i_attachment
, attachment
, in
->p_demux
);
2664 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2667 demux_Control( in
->p_demux
, DEMUX_GET_PTS_DELAY
, &in
->i_pts_delay
);
2668 if( in
->i_pts_delay
> INPUT_PTS_DELAY_MAX
)
2669 in
->i_pts_delay
= INPUT_PTS_DELAY_MAX
;
2670 else if( in
->i_pts_delay
< 0 )
2671 in
->i_pts_delay
= 0;
2674 if( demux_Control( in
->p_demux
, DEMUX_GET_FPS
, &in
->f_fps
) )
2677 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2678 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2683 /*****************************************************************************
2684 * InputSourceDestroy:
2685 *****************************************************************************/
2686 static void InputSourceDestroy( input_source_t
*in
)
2691 demux_Delete( in
->p_demux
);
2693 if( in
->i_title
> 0 )
2695 for( i
= 0; i
< in
->i_title
; i
++ )
2696 vlc_input_title_Delete( in
->title
[i
] );
2697 TAB_CLEAN( in
->i_title
, in
->title
);
2700 vlc_object_release( in
);
2703 /*****************************************************************************
2705 *****************************************************************************/
2706 static void InputSourceMeta( input_thread_t
*p_input
,
2707 input_source_t
*p_source
, vlc_meta_t
*p_meta
)
2709 demux_t
*p_demux
= p_source
->p_demux
;
2711 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2714 bool has_meta
= false;
2716 /* Read demux meta */
2717 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
2720 bool has_unsupported
;
2721 if( demux_Control( p_demux
, DEMUX_HAS_UNSUPPORTED_META
, &has_unsupported
) )
2722 has_unsupported
= true;
2724 /* If the demux report unsupported meta data, or if we don't have meta data
2725 * try an external "meta reader" */
2726 if( has_meta
&& !has_unsupported
)
2729 demux_meta_t
*p_demux_meta
=
2730 vlc_custom_create( p_source
, sizeof( *p_demux_meta
), "demux meta" );
2731 if( unlikely(p_demux_meta
== NULL
) )
2733 p_demux_meta
->p_item
= input_priv(p_input
)->p_item
;
2735 module_t
*p_id3
= module_need( p_demux_meta
, "meta reader", NULL
, false );
2738 if( p_demux_meta
->p_meta
)
2740 vlc_meta_Merge( p_meta
, p_demux_meta
->p_meta
);
2741 vlc_meta_Delete( p_demux_meta
->p_meta
);
2744 if( p_demux_meta
->i_attachments
> 0 )
2746 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2747 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2748 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
, p_demux
);
2749 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2751 module_unneed( p_demux
, p_id3
);
2753 vlc_object_release( p_demux_meta
);
2757 static void SlaveDemux( input_thread_t
*p_input
)
2762 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2764 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2768 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2770 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2776 /* Call demux_Demux until we have read enough data */
2777 if( demux_Control( in
->p_demux
, DEMUX_SET_NEXT_DEMUX_TIME
, i_time
) )
2782 if( demux_Control( in
->p_demux
, DEMUX_GET_TIME
, &i_stime
) )
2784 msg_Err( p_input
, "slave[%d] doesn't like "
2785 "DEMUX_GET_TIME -> EOF", i
);
2790 if( i_stime
>= i_time
)
2796 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2802 i_ret
= demux_Demux( in
->p_demux
);
2807 msg_Dbg( p_input
, "slave %d EOF", i
);
2813 static void SlaveSeek( input_thread_t
*p_input
)
2818 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2820 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2824 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2826 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2828 if( demux_Control( in
->p_demux
, DEMUX_SET_TIME
, i_time
, true ) )
2831 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2841 /*****************************************************************************
2843 *****************************************************************************/
2844 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
)
2846 static const struct { int i_meta
; const char *psz_name
; } p_list
[] = {
2847 { vlc_meta_Title
, "meta-title" },
2848 { vlc_meta_Artist
, "meta-artist" },
2849 { vlc_meta_Genre
, "meta-genre" },
2850 { vlc_meta_Copyright
, "meta-copyright" },
2851 { vlc_meta_Description
, "meta-description" },
2852 { vlc_meta_Date
, "meta-date" },
2853 { vlc_meta_URL
, "meta-url" },
2857 /* Get meta information from user */
2858 for( int i
= 0; p_list
[i
].psz_name
; i
++ )
2860 char *psz_string
= var_GetNonEmptyString( p_input
, p_list
[i
].psz_name
);
2864 EnsureUTF8( psz_string
);
2865 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, psz_string
);
2870 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
,
2871 const demux_t
***ppp_attachment_demux
,
2872 int i_new
, input_attachment_t
**pp_new
, const demux_t
*p_demux
)
2874 int i_attachment
= *pi_attachment
;
2877 input_attachment_t
**pp_att
= realloc( *ppp_attachment
,
2878 sizeof(*pp_att
) * ( i_attachment
+ i_new
) );
2879 if( likely(pp_att
) )
2881 *ppp_attachment
= pp_att
;
2882 const demux_t
**pp_attdmx
= realloc( *ppp_attachment_demux
,
2883 sizeof(*pp_attdmx
) * ( i_attachment
+ i_new
) );
2884 if( likely(pp_attdmx
) )
2886 *ppp_attachment_demux
= pp_attdmx
;
2888 for( i
= 0; i
< i_new
; i
++ )
2890 pp_att
[i_attachment
] = pp_new
[i
];
2891 pp_attdmx
[i_attachment
++] = p_demux
;
2894 *pi_attachment
= i_attachment
;
2900 /* on alloc errors */
2901 for( i
= 0; i
< i_new
; i
++ )
2902 vlc_input_attachment_Delete( pp_new
[i
] );
2906 /*****************************************************************************
2907 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
2908 * arturl and locking issue.
2909 *****************************************************************************/
2910 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
)
2912 vlc_meta_t
*p_meta
= vlc_meta_New();
2913 if( unlikely(p_meta
== NULL
) )
2916 demux_Control( p_demux
, DEMUX_GET_META
, p_meta
);
2918 /* If metadata changed, then the attachments might have changed.
2919 We need to update them in case they contain album art. */
2920 input_attachment_t
**attachment
;
2923 if( !demux_Control( p_demux
, DEMUX_GET_ATTACHMENTS
,
2924 &attachment
, &i_attachment
) )
2926 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2927 if( input_priv(p_input
)->i_attachment
> 0 )
2930 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
2932 if( input_priv(p_input
)->attachment_demux
[i
] == p_demux
)
2933 vlc_input_attachment_Delete( input_priv(p_input
)->attachment
[i
] );
2936 input_priv(p_input
)->attachment
[j
] = input_priv(p_input
)->attachment
[i
];
2937 input_priv(p_input
)->attachment_demux
[j
] = input_priv(p_input
)->attachment_demux
[i
];
2941 input_priv(p_input
)->i_attachment
= j
;
2943 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2944 i_attachment
, attachment
, p_demux
);
2945 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2948 es_out_ControlSetMeta( input_priv(p_input
)->p_es_out
, p_meta
);
2949 vlc_meta_Delete( p_meta
);
2952 /*****************************************************************************
2953 * InputGetExtraFiles
2954 * Autodetect extra input list
2955 *****************************************************************************/
2956 static void InputGetExtraFilesPattern( input_thread_t
*p_input
,
2957 int *pi_list
, char ***pppsz_list
,
2958 const char *psz_path
,
2959 const char *psz_match
,
2960 const char *psz_format
,
2961 int i_start
, int i_stop
)
2965 TAB_INIT( i_list
, ppsz_list
);
2967 char *psz_base
= strdup( psz_path
);
2971 /* Remove the extension */
2972 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
2973 assert( psz_end
>= psz_base
);
2976 /* Try to list files */
2977 for( int i
= i_start
; i
<= i_stop
; i
++ )
2980 if( asprintf( &psz_probe
, psz_format
, psz_base
, i
) < 0 )
2983 char *filepath
= get_path( psz_probe
);
2986 if( filepath
== NULL
||
2987 vlc_stat( filepath
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
2994 msg_Dbg( p_input
, "Detected extra file `%s'", filepath
);
2996 char* psz_uri
= vlc_path2uri( filepath
, NULL
);
2998 TAB_APPEND( i_list
, ppsz_list
, psz_uri
);
3006 *pppsz_list
= ppsz_list
;
3009 static void InputGetExtraFiles( input_thread_t
*p_input
,
3010 int *pi_list
, char ***pppsz_list
,
3011 const char **ppsz_access
, const char *psz_path
)
3013 static const struct pattern
3015 const char *psz_access_force
;
3016 const char *psz_match
;
3017 const char *psz_format
;
3021 /* XXX the order is important */
3022 { "concat", ".001", "%s.%.3d", 2, 999 },
3023 { NULL
, ".part1.rar","%s.part%.1d.rar", 2, 9 },
3024 { NULL
, ".part01.rar","%s.part%.2d.rar", 2, 99, },
3025 { NULL
, ".part001.rar", "%s.part%.3d.rar", 2, 999 },
3026 { NULL
, ".rar", "%s.r%.2d", 0, 99 },
3029 TAB_INIT( *pi_list
, *pppsz_list
);
3031 if( ( **ppsz_access
&& strcmp( *ppsz_access
, "file" ) ) || !psz_path
)
3034 const size_t i_path
= strlen(psz_path
);
3036 for( size_t i
= 0; i
< ARRAY_SIZE( patterns
); ++i
)
3038 const struct pattern
* pat
= &patterns
[i
];
3039 const size_t i_ext
= strlen( pat
->psz_match
);
3041 if( i_path
< i_ext
)
3044 if( !strcmp( &psz_path
[i_path
-i_ext
], pat
->psz_match
) )
3046 InputGetExtraFilesPattern( p_input
, pi_list
, pppsz_list
, psz_path
,
3047 pat
->psz_match
, pat
->psz_format
, pat
->i_start
, pat
->i_stop
);
3049 if( *pi_list
> 0 && pat
->psz_access_force
)
3050 *ppsz_access
= pat
->psz_access_force
;
3057 static void input_ChangeState( input_thread_t
*p_input
, int i_state
)
3059 if( input_priv(p_input
)->i_state
== i_state
)
3062 input_priv(p_input
)->i_state
= i_state
;
3063 if( i_state
== ERROR_S
)
3064 input_item_SetErrorWhenReading( input_priv(p_input
)->p_item
, true );
3065 input_SendEventState( p_input
, i_state
);
3069 /*****************************************************************************
3070 * MRLSplit: parse the access, demux and url part of the
3071 * Media Resource Locator.
3072 *****************************************************************************/
3073 void input_SplitMRL( const char **access
, const char **demux
,
3074 const char **path
, const char **anchor
, char *buf
)
3078 /* Separate <path> from <access>[/<demux>]:// */
3079 p
= strstr( buf
, "://" );
3083 p
+= 3; /* skips "://" */
3086 /* Remove HTML anchor if present (not supported).
3087 * The hash symbol itself should be URI-encoded. */
3088 p
= strchr( p
, '#' );
3100 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
3103 /* Note: this is a valid non const pointer to "": */
3104 *path
= buf
+ strlen( buf
);
3107 /* Separate access from demux */
3108 p
= strchr( buf
, '/' );
3119 /* We really don't want module name substitution here! */
3126 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
3131 /* Look for the title */
3132 u
= strtoul( str
, &end
, 0 );
3133 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3136 /* Look for the chapter */
3140 u
= strtoul( str
, &end
, 0 );
3141 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3151 /*****************************************************************************
3152 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
3155 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
3156 *****************************************************************************/
3157 static void MRLSections( const char *p
,
3158 int *pi_title_start
, int *pi_title_end
,
3159 int *pi_chapter_start
, int *pi_chapter_end
)
3161 *pi_title_start
= *pi_title_end
= *pi_chapter_start
= *pi_chapter_end
= -1;
3163 int title_start
, chapter_start
, title_end
, chapter_end
;
3169 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
3171 title_start
= chapter_start
= -1;
3174 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
3176 title_end
= chapter_end
= -1;
3178 if( *p
) /* syntax error */
3181 *pi_title_start
= title_start
;
3182 *pi_title_end
= title_end
;
3183 *pi_chapter_start
= chapter_start
;
3184 *pi_chapter_end
= chapter_end
;
3187 static int input_SlaveSourceAdd( input_thread_t
*p_input
,
3188 enum slave_type i_type
, const char *psz_uri
,
3193 const char *psz_forced_demux
;
3194 const bool b_can_fail
= i_flags
& SLAVE_ADD_CANFAIL
;
3195 const bool b_forced
= i_flags
& SLAVE_ADD_FORCED
;
3196 const bool b_set_time
= i_flags
& SLAVE_ADD_SET_TIME
;
3200 case SLAVE_TYPE_SPU
:
3202 psz_forced_demux
= "subtitle";
3204 case SLAVE_TYPE_AUDIO
:
3205 psz_es
= "audio-es";
3206 psz_forced_demux
= NULL
;
3209 vlc_assert_unreachable();
3213 var_Change( p_input
, psz_es
, VLC_VAR_CHOICESCOUNT
, &count
, NULL
);
3215 msg_Dbg( p_input
, "loading %s slave: %s (forced: %d)", psz_es
, psz_uri
,
3218 input_source_t
*p_source
= InputSourceNew( p_input
, psz_uri
,
3220 b_can_fail
|| psz_forced_demux
);
3222 if( psz_forced_demux
&& p_source
== NULL
)
3223 p_source
= InputSourceNew( p_input
, psz_uri
, NULL
, b_can_fail
);
3225 if( p_source
== NULL
)
3227 msg_Warn( p_input
, "failed to add %s as slave", psz_uri
);
3228 return VLC_EGENERIC
;
3231 if( i_type
== SLAVE_TYPE_AUDIO
)
3238 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
3239 DEMUX_GET_TIME
, &i_time
) )
3241 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
3242 InputSourceDestroy( p_source
);
3243 return VLC_EGENERIC
;
3246 if( demux_Control( p_source
->p_demux
,
3247 DEMUX_SET_TIME
, i_time
, true ) )
3249 msg_Err( p_input
, "seek failed for new slave" );
3250 InputSourceDestroy( p_source
);
3251 return VLC_EGENERIC
;
3255 /* Get meta (access and demux) */
3256 InputUpdateMeta( p_input
, p_source
->p_demux
);
3259 TAB_APPEND( input_priv(p_input
)->i_slave
, input_priv(p_input
)->slave
, p_source
);
3267 if( var_Change( p_input
, psz_es
, VLC_VAR_GETCHOICES
, &list
, NULL
) )
3270 if( count
.i_int
== 0 )
3272 /* if it was first one, there is disable too */
3274 if( count
.i_int
< list
.p_list
->i_count
)
3276 const int i_id
= list
.p_list
->p_values
[count
.i_int
].i_int
;
3278 es_out_Control( input_priv(p_input
)->p_es_out_display
, ES_OUT_SET_ES_DEFAULT_BY_ID
, i_id
);
3279 es_out_Control( input_priv(p_input
)->p_es_out_display
, ES_OUT_SET_ES_BY_ID
, i_id
);
3281 var_FreeList( &list
, NULL
);
3286 static char *input_SubtitleFile2Uri( input_thread_t
*p_input
,
3287 const char *psz_subtitle
)
3289 /* if we are provided a subtitle.sub file,
3290 * see if we don't have a subtitle.idx and use it instead */
3291 char *psz_idxpath
= NULL
;
3292 char *psz_extension
= strrchr( psz_subtitle
, '.');
3293 if( psz_extension
&& strcmp( psz_extension
, ".sub" ) == 0 )
3295 psz_idxpath
= strdup( psz_subtitle
);
3300 psz_extension
= psz_extension
- psz_subtitle
+ psz_idxpath
;
3301 strcpy( psz_extension
, ".idx" );
3303 if( !vlc_stat( psz_idxpath
, &st
) && S_ISREG( st
.st_mode
) )
3305 msg_Dbg( p_input
, "using %s as subtitle file instead of %s",
3306 psz_idxpath
, psz_subtitle
);
3307 psz_subtitle
= psz_idxpath
;
3312 char *psz_uri
= vlc_path2uri( psz_subtitle
, NULL
);
3313 free( psz_idxpath
);
3318 /* TODO FIXME nearly the same logic that snapshot code */
3319 char *input_CreateFilename(input_thread_t
*input
, const char *dir
,
3320 const char *filenamefmt
, const char *ext
)
3323 char *filename
= str_format(input
, filenamefmt
);
3324 if (unlikely(filename
== NULL
))
3327 filename_sanitize(filename
);
3330 ? asprintf(&path
, "%s"DIR_SEP
"%s.%s", dir
, filename
, ext
)
3331 : asprintf(&path
, "%s"DIR_SEP
"%s", dir
, filename
)) < 0)