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 vlc_mutex_destroy( &priv
->counters
.counters_lock
);
257 for( int i
= 0; i
< priv
->i_control
; i
++ )
259 input_control_t
*p_ctrl
= &priv
->control
[i
];
260 ControlRelease( p_ctrl
->i_type
, p_ctrl
->val
);
263 vlc_cond_destroy( &priv
->wait_control
);
264 vlc_mutex_destroy( &priv
->lock_control
);
268 * Get the item from an input thread
269 * FIXME it does not increase ref count of the item.
270 * if it is used after p_input is destroyed nothing prevent it from
273 input_item_t
*input_GetItem( input_thread_t
*p_input
)
275 assert( p_input
!= NULL
);
276 return input_priv(p_input
)->p_item
;
279 /*****************************************************************************
280 * This function creates a new input, and returns a pointer
281 * to its description. On error, it returns NULL.
283 * XXX Do not forget to update vlc_input.h if you add new variables.
284 *****************************************************************************/
285 static input_thread_t
*Create( vlc_object_t
*p_parent
, input_item_t
*p_item
,
286 const char *psz_header
, bool b_preparsing
,
287 input_resource_t
*p_resource
,
288 vlc_renderer_item_t
*p_renderer
)
290 /* Allocate descriptor */
291 input_thread_private_t
*priv
;
293 priv
= vlc_custom_create( p_parent
, sizeof( *priv
), "input" );
294 if( unlikely(priv
== NULL
) )
297 input_thread_t
*p_input
= &priv
->input
;
299 char * psz_name
= input_item_GetName( p_item
);
300 msg_Dbg( p_input
, "Creating an input for %s'%s'",
301 b_preparsing
? "preparsing " : "", psz_name
);
304 /* Parse input options */
305 input_item_ApplyOptions( VLC_OBJECT(p_input
), p_item
);
307 p_input
->obj
.header
= psz_header
? strdup( psz_header
) : NULL
;
309 /* Init Common fields */
310 priv
->b_preparsing
= b_preparsing
;
311 priv
->b_can_pace_control
= true;
317 priv
->i_title_offset
= input_priv(p_input
)->i_seekpoint_offset
= 0;
318 priv
->i_state
= INIT_S
;
319 priv
->is_running
= false;
320 priv
->is_stopped
= false;
321 priv
->b_recording
= false;
322 priv
->i_rate
= INPUT_RATE_DEFAULT
;
323 memset( &priv
->bookmark
, 0, sizeof(priv
->bookmark
) );
324 TAB_INIT( priv
->i_bookmark
, priv
->pp_bookmark
);
325 TAB_INIT( priv
->i_attachment
, priv
->attachment
);
326 priv
->attachment_demux
= NULL
;
328 priv
->b_out_pace_control
= false;
329 priv
->p_renderer
= p_renderer
? vlc_renderer_item_hold( p_renderer
) : NULL
;
331 priv
->viewpoint_changed
= false;
332 /* Fetch the viewpoint from the mediaplayer or the playlist if any */
333 vlc_viewpoint_t
*p_viewpoint
= var_InheritAddress( p_input
, "viewpoint" );
334 if (p_viewpoint
!= NULL
)
335 priv
->viewpoint
= *p_viewpoint
;
337 vlc_viewpoint_init( &priv
->viewpoint
);
339 input_item_Hold( p_item
); /* Released in Destructor() */
340 priv
->p_item
= p_item
;
342 /* Init Input fields */
344 vlc_mutex_lock( &p_item
->lock
);
346 if( !p_item
->p_stats
)
347 p_item
->p_stats
= stats_NewInputStats( p_input
);
349 /* setup the preparse depth of the item
350 * if we are preparsing, use the i_preparse_depth of the parent item */
351 if( !priv
->b_preparsing
)
353 char *psz_rec
= var_InheritString( p_parent
, "recursive" );
355 if( psz_rec
!= NULL
)
357 if ( !strcasecmp( psz_rec
, "none" ) )
358 p_item
->i_preparse_depth
= 0;
359 else if ( !strcasecmp( psz_rec
, "collapse" ) )
360 p_item
->i_preparse_depth
= 1;
362 p_item
->i_preparse_depth
= -1; /* default is expand */
365 p_item
->i_preparse_depth
= -1;
368 p_input
->obj
.flags
|= OBJECT_FLAGS_QUIET
| OBJECT_FLAGS_NOINTERACT
;
370 /* Make sure the interaction option is honored */
371 if( !var_InheritBool( p_input
, "interact" ) )
372 p_input
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
373 else if( p_item
->b_preparse_interact
)
375 /* If true, this item was asked explicitly to interact with the user
376 * (via libvlc_MetadataRequest). Sub items created from this input won't
377 * have this flag and won't interact with the user */
378 p_input
->obj
.flags
&= ~OBJECT_FLAGS_NOINTERACT
;
381 vlc_mutex_unlock( &p_item
->lock
);
390 priv
->p_resource_private
= NULL
;
391 priv
->p_resource
= input_resource_Hold( p_resource
);
395 priv
->p_resource_private
= input_resource_New( VLC_OBJECT( p_input
) );
396 priv
->p_resource
= input_resource_Hold( priv
->p_resource_private
);
398 input_resource_SetInput( priv
->p_resource
, p_input
);
400 /* Init control buffer */
401 vlc_mutex_init( &priv
->lock_control
);
402 vlc_cond_init( &priv
->wait_control
);
404 vlc_interrupt_init(&priv
->interrupt
);
406 /* Create Object Variables for private use only */
407 input_ConfigVarInit( p_input
);
409 /* Create Objects variables for public Get and Set */
410 input_ControlVarInit( p_input
);
413 if( !priv
->b_preparsing
)
415 char *psz_bookmarks
= var_GetNonEmptyString( p_input
, "bookmarks" );
418 /* FIXME: have a common cfg parsing routine used by sout and others */
419 char *psz_parser
, *psz_start
, *psz_end
;
420 psz_parser
= psz_bookmarks
;
421 while( (psz_start
= strchr( psz_parser
, '{' ) ) )
423 seekpoint_t
*p_seekpoint
;
426 psz_end
= strchr( psz_start
, '}' );
427 if( !psz_end
) break;
428 psz_parser
= psz_end
+ 1;
429 backup
= *psz_parser
;
433 p_seekpoint
= vlc_seekpoint_New();
435 if( unlikely( p_seekpoint
== NULL
) )
438 while( (psz_end
= strchr( psz_start
, ',' ) ) )
441 if( !strncmp( psz_start
, "name=", 5 ) )
443 free( p_seekpoint
->psz_name
);
445 p_seekpoint
->psz_name
= strdup(psz_start
+ 5);
447 else if( !strncmp( psz_start
, "time=", 5 ) )
449 p_seekpoint
->i_time_offset
= atof(psz_start
+ 5) *
452 psz_start
= psz_end
+ 1;
454 msg_Dbg( p_input
, "adding bookmark: %s, time=%"PRId64
,
455 p_seekpoint
->psz_name
,
456 p_seekpoint
->i_time_offset
);
457 input_Control( p_input
, INPUT_ADD_BOOKMARK
, p_seekpoint
);
458 vlc_seekpoint_Delete( p_seekpoint
);
459 *psz_parser
= backup
;
461 free( psz_bookmarks
);
465 /* Remove 'Now playing' info as it is probably outdated */
466 input_item_SetNowPlaying( p_item
, NULL
);
467 input_item_SetESNowPlaying( p_item
, NULL
);
468 input_SendEventMeta( p_input
);
471 memset( &priv
->counters
, 0, sizeof( priv
->counters
) );
472 vlc_mutex_init( &priv
->counters
.counters_lock
);
474 priv
->p_es_out_display
= input_EsOutNew( p_input
, priv
->i_rate
);
475 priv
->p_es_out
= NULL
;
477 /* Set the destructor when we are sure we are initialized */
478 vlc_object_set_destructor( p_input
, input_Destructor
);
483 /*****************************************************************************
484 * Run: main thread loop
485 * This is the "normal" thread that spawns the input processing chain,
486 * reads the stream, cleans up and waits
487 *****************************************************************************/
488 static void *Run( void *data
)
490 input_thread_private_t
*priv
= data
;
491 input_thread_t
*p_input
= &priv
->input
;
493 vlc_interrupt_set(&priv
->interrupt
);
495 if( !Init( p_input
) )
497 if( priv
->b_can_pace_control
&& priv
->b_out_pace_control
)
499 /* We don't want a high input priority here or we'll
500 * end-up sucking up all the CPU time */
501 vlc_set_priority( priv
->thread
, VLC_THREAD_PRIORITY_LOW
);
504 MainLoop( p_input
, true ); /* FIXME it can be wrong (like with VLM) */
510 input_SendEventDead( p_input
);
514 static void *Preparse( void *data
)
516 input_thread_private_t
*priv
= data
;
517 input_thread_t
*p_input
= &priv
->input
;
519 vlc_interrupt_set(&priv
->interrupt
);
521 if( !Init( p_input
) )
522 { /* if the demux is a playlist, call Mainloop that will call
523 * demux_Demux in order to fetch sub items */
524 bool b_is_playlist
= false;
526 if ( input_item_ShouldPreparseSubItems( priv
->p_item
)
527 && demux_Control( priv
->master
->p_demux
, DEMUX_IS_PLAYLIST
,
529 b_is_playlist
= false;
531 MainLoop( p_input
, false );
535 input_SendEventDead( p_input
);
539 bool input_Stopped( input_thread_t
*input
)
541 input_thread_private_t
*sys
= input_priv(input
);
544 vlc_mutex_lock( &sys
->lock_control
);
545 ret
= sys
->is_stopped
;
546 vlc_mutex_unlock( &sys
->lock_control
);
550 /*****************************************************************************
551 * Main loop: Fill buffers from access, and demux
552 *****************************************************************************/
556 * It asks the demuxer to demux some data
558 static void MainLoopDemux( input_thread_t
*p_input
, bool *pb_changed
)
561 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
565 if( input_priv(p_input
)->i_stop
> 0 && input_priv(p_input
)->i_time
>= input_priv(p_input
)->i_stop
)
566 i_ret
= VLC_DEMUXER_EOF
;
568 i_ret
= demux_Demux( p_demux
);
570 i_ret
= i_ret
> 0 ? VLC_DEMUXER_SUCCESS
: ( i_ret
< 0 ? VLC_DEMUXER_EGENERIC
: VLC_DEMUXER_EOF
);
572 if( i_ret
== VLC_DEMUXER_SUCCESS
)
574 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE_LIST
) )
575 UpdateTitleListfromDemux( p_input
);
577 if( input_priv(p_input
)->master
->b_title_demux
)
579 i_ret
= UpdateTitleSeekpointFromDemux( p_input
);
583 UpdateGenericFromDemux( p_input
);
586 if( i_ret
== VLC_DEMUXER_EOF
)
588 msg_Dbg( p_input
, "EOF reached" );
589 input_priv(p_input
)->master
->b_eof
= true;
590 es_out_Eos(input_priv(p_input
)->p_es_out
);
592 else if( i_ret
== VLC_DEMUXER_EGENERIC
)
594 input_ChangeState( p_input
, ERROR_S
);
596 else if( input_priv(p_input
)->i_slave
> 0 )
597 SlaveDemux( p_input
);
600 static int MainLoopTryRepeat( input_thread_t
*p_input
)
602 int i_repeat
= var_GetInteger( p_input
, "input-repeat" );
608 msg_Dbg( p_input
, "repeating the same input (%d)", i_repeat
);
612 var_SetInteger( p_input
, "input-repeat", i_repeat
);
615 /* Seek to start title/seekpoint */
616 val
.i_int
= input_priv(p_input
)->master
->i_title_start
-
617 input_priv(p_input
)->master
->i_title_offset
;
618 if( val
.i_int
< 0 || val
.i_int
>= input_priv(p_input
)->master
->i_title
)
620 input_ControlPush( p_input
,
621 INPUT_CONTROL_SET_TITLE
, &val
);
623 val
.i_int
= input_priv(p_input
)->master
->i_seekpoint_start
-
624 input_priv(p_input
)->master
->i_seekpoint_offset
;
625 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
626 input_ControlPush( p_input
,
627 INPUT_CONTROL_SET_SEEKPOINT
, &val
);
629 /* Seek to start position */
630 if( input_priv(p_input
)->i_start
> 0 )
632 val
.i_int
= input_priv(p_input
)->i_start
;
633 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &val
);
638 input_ControlPush( p_input
, INPUT_CONTROL_SET_POSITION
, &val
);
645 * Update timing infos and statistics.
647 static void MainLoopStatistics( input_thread_t
*p_input
)
649 double f_position
= 0.0;
651 mtime_t i_length
= 0;
653 /* update input status variables */
654 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
655 DEMUX_GET_POSITION
, &f_position
) )
658 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
659 DEMUX_GET_TIME
, &i_time
) )
661 input_priv(p_input
)->i_time
= i_time
;
663 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
664 DEMUX_GET_LENGTH
, &i_length
) )
667 es_out_SetTimes( input_priv(p_input
)->p_es_out
, f_position
, i_time
, i_length
);
669 /* update current bookmark */
670 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
671 input_priv(p_input
)->bookmark
.i_time_offset
= i_time
;
672 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
674 stats_ComputeInputStats( p_input
, input_priv(p_input
)->p_item
->p_stats
);
675 input_SendEventStatistics( p_input
);
680 * The main input loop.
682 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
)
684 mtime_t i_intf_update
= 0;
685 mtime_t i_last_seek_mdate
= 0;
687 if( b_interactive
&& var_InheritBool( p_input
, "start-paused" ) )
688 ControlPause( p_input
, mdate() );
690 bool b_pause_after_eof
= b_interactive
&&
691 var_InheritBool( p_input
, "play-and-pause" );
692 bool b_paused_at_eof
= false;
694 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
695 const bool b_can_demux
= p_demux
->pf_demux
!= NULL
;
697 while( !input_Stopped( p_input
) && input_priv(p_input
)->i_state
!= ERROR_S
)
699 mtime_t i_wakeup
= -1;
700 bool b_paused
= input_priv(p_input
)->i_state
== PAUSE_S
;
701 /* FIXME if input_priv(p_input)->i_state == PAUSE_S the access/access_demux
702 * is paused -> this may cause problem with some of them
703 * The same problem can be seen when seeking while paused */
705 b_paused
= !es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
706 || input_priv(p_input
)->master
->b_eof
;
710 if( !input_priv(p_input
)->master
->b_eof
)
712 bool b_force_update
= false;
714 MainLoopDemux( p_input
, &b_force_update
);
717 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
721 b_paused_at_eof
= false;
723 else if( !es_out_GetEmpty( input_priv(p_input
)->p_es_out
) )
725 msg_Dbg( p_input
, "waiting decoder fifos to empty" );
726 i_wakeup
= mdate() + INPUT_IDLE_SLEEP
;
728 /* Pause after eof only if the input is pausable.
729 * This way we won't trigger timeshifting for nothing */
730 else if( b_pause_after_eof
&& input_priv(p_input
)->b_can_pause
)
732 if( b_paused_at_eof
)
735 vlc_value_t val
= { .i_int
= PAUSE_S
};
737 msg_Dbg( p_input
, "pausing at EOF (pause after each)");
738 Control( p_input
, INPUT_CONTROL_SET_STATE
, val
);
741 b_paused_at_eof
= true;
745 if( MainLoopTryRepeat( p_input
) )
749 /* Update interface and statistics */
750 mtime_t now
= mdate();
751 if( now
>= i_intf_update
)
753 MainLoopStatistics( p_input
);
754 i_intf_update
= now
+ INT64_C(250000);
761 mtime_t i_deadline
= i_wakeup
;
763 /* Postpone seeking until ES buffering is complete or at most
765 bool b_postpone
= es_out_GetBuffering( input_priv(p_input
)->p_es_out
)
766 && !input_priv(p_input
)->master
->b_eof
;
769 mtime_t now
= mdate();
771 /* Recheck ES buffer level every 20 ms when seeking */
772 if( now
< i_last_seek_mdate
+ INT64_C(125000)
773 && (i_deadline
< 0 || i_deadline
> now
+ INT64_C(20000)) )
774 i_deadline
= now
+ INT64_C(20000);
782 if( ControlPop( p_input
, &i_type
, &val
, i_deadline
, b_postpone
) )
786 break; /* Wake-up time reached */
790 msg_Dbg( p_input
, "control type=%d", i_type
);
792 if( Control( p_input
, i_type
, val
) )
794 if( ControlIsSeekRequest( i_type
) )
795 i_last_seek_mdate
= mdate();
799 /* Update the wakeup time */
801 i_wakeup
= es_out_GetWakeup( input_priv(p_input
)->p_es_out
);
806 static void InitStatistics( input_thread_t
*p_input
)
808 input_thread_private_t
*priv
= input_priv(p_input
);
810 if( priv
->b_preparsing
) return;
812 /* Prepare statistics */
813 #define INIT_COUNTER( c, compute ) free( priv->counters.p_##c ); \
814 priv->counters.p_##c = \
815 stats_CounterCreate( STATS_##compute);
816 if( libvlc_stats( p_input
) )
818 priv
->counters
.read_bytes
= 0;
819 priv
->counters
.read_packets
= 0;
820 priv
->counters
.demux_read
= 0;
821 INIT_COUNTER( input_bitrate
, DERIVATIVE
);
822 INIT_COUNTER( demux_bitrate
, DERIVATIVE
);
823 priv
->counters
.demux_corrupted
= 0;
824 priv
->counters
.demux_discontinuity
= 0;
825 priv
->counters
.played_abuffers
= 0;
826 priv
->counters
.lost_abuffers
= 0;
827 priv
->counters
.displayed_pictures
= 0;
828 priv
->counters
.lost_pictures
= 0;
829 priv
->counters
.decoded_audio
= 0;
830 priv
->counters
.decoded_video
= 0;
831 priv
->counters
.decoded_sub
= 0;
832 priv
->counters
.p_sout_send_bitrate
= NULL
;
833 priv
->counters
.sout_sent_packets
= 0;
834 priv
->counters
.sout_sent_bytes
= 0;
839 static int InitSout( input_thread_t
* p_input
)
841 input_thread_private_t
*priv
= input_priv(p_input
);
843 if( priv
->b_preparsing
)
846 /* Find a usable sout and attach it to p_input */
848 if( priv
->p_renderer
)
850 const char *psz_renderer_sout
= vlc_renderer_item_sout( priv
->p_renderer
);
851 if( asprintf( &psz
, "#%s", psz_renderer_sout
) < 0 )
855 psz
= var_GetNonEmptyString( p_input
, "sout" );
856 if( psz
&& strncasecmp( priv
->p_item
->psz_uri
, "vlc:", 4 ) )
858 priv
->p_sout
= input_resource_RequestSout( priv
->p_resource
, NULL
, psz
);
859 if( priv
->p_sout
== NULL
)
861 input_ChangeState( p_input
, ERROR_S
);
862 msg_Err( p_input
, "cannot start stream output instance, " \
867 if( libvlc_stats( p_input
) )
869 INIT_COUNTER( sout_send_bitrate
, DERIVATIVE
);
874 input_resource_RequestSout( priv
->p_resource
, NULL
, NULL
);
882 static void InitTitle( input_thread_t
* p_input
)
884 input_thread_private_t
*priv
= input_priv(p_input
);
885 input_source_t
*p_master
= priv
->master
;
887 if( priv
->b_preparsing
)
890 vlc_mutex_lock( &priv
->p_item
->lock
);
891 /* Create global title (from master) */
892 priv
->i_title
= p_master
->i_title
;
893 priv
->title
= p_master
->title
;
894 priv
->i_title_offset
= p_master
->i_title_offset
;
895 priv
->i_seekpoint_offset
= p_master
->i_seekpoint_offset
;
896 if( priv
->i_title
> 0 )
898 /* Setup variables */
899 input_ControlVarNavigation( p_input
);
900 input_SendEventTitle( p_input
, 0 );
904 priv
->b_can_pace_control
= p_master
->b_can_pace_control
;
905 priv
->b_can_pause
= p_master
->b_can_pause
;
906 priv
->b_can_rate_control
= p_master
->b_can_rate_control
;
907 vlc_mutex_unlock( &priv
->p_item
->lock
);
910 static void StartTitle( input_thread_t
* p_input
)
912 input_thread_private_t
*priv
= input_priv(p_input
);
915 /* Start title/chapter */
916 val
.i_int
= priv
->master
->i_title_start
- priv
->master
->i_title_offset
;
917 if( val
.i_int
> 0 && val
.i_int
< priv
->master
->i_title
)
918 input_ControlPush( p_input
, INPUT_CONTROL_SET_TITLE
, &val
);
920 val
.i_int
= priv
->master
->i_seekpoint_start
-
921 priv
->master
->i_seekpoint_offset
;
922 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
923 input_ControlPush( p_input
, INPUT_CONTROL_SET_SEEKPOINT
, &val
);
925 /* Start/stop/run time */
926 priv
->i_start
= llroundf(1000000.f
927 * var_GetFloat( p_input
, "start-time" ));
928 priv
->i_stop
= llroundf(1000000.f
929 * var_GetFloat( p_input
, "stop-time" ));
930 if( priv
->i_stop
<= 0 )
932 priv
->i_stop
= llroundf(1000000.f
933 * var_GetFloat( p_input
, "run-time" ));
934 if( priv
->i_stop
< 0 )
936 msg_Warn( p_input
, "invalid run-time ignored" );
940 priv
->i_stop
+= priv
->i_start
;
943 if( priv
->i_start
> 0 )
947 msg_Dbg( p_input
, "starting at time: %"PRId64
"s",
948 priv
->i_start
/ CLOCK_FREQ
);
950 s
.i_int
= priv
->i_start
;
951 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &s
);
953 if( priv
->i_stop
> 0 && priv
->i_stop
<= priv
->i_start
)
955 msg_Warn( p_input
, "invalid stop-time ignored" );
958 priv
->b_fast_seek
= var_GetBool( p_input
, "input-fast-seek" );
961 static int SlaveCompare(const void *a
, const void *b
)
963 const input_item_slave_t
*p_slave0
= *((const input_item_slave_t
**) a
);
964 const input_item_slave_t
*p_slave1
= *((const input_item_slave_t
**) b
);
966 if( p_slave0
== NULL
|| p_slave1
== NULL
)
968 /* Put NULL (or rejected) subs at the end */
969 return p_slave0
== NULL
? 1 : p_slave1
== NULL
? -1 : 0;
972 if( p_slave0
->i_priority
> p_slave1
->i_priority
)
975 if( p_slave0
->i_priority
< p_slave1
->i_priority
)
981 static bool SlaveExists( input_item_slave_t
**pp_slaves
, int i_slaves
,
984 for( int i
= 0; i
< i_slaves
; i
++ )
986 if( pp_slaves
[i
] != NULL
987 && !strcmp( pp_slaves
[i
]->psz_uri
, psz_uri
) )
993 static void SetSubtitlesOptions( input_thread_t
*p_input
)
995 /* Get fps and set it if not already set */
996 const float f_fps
= input_priv(p_input
)->master
->f_fps
;
999 var_Create( p_input
, "sub-original-fps", VLC_VAR_FLOAT
);
1000 var_SetFloat( p_input
, "sub-original-fps", f_fps
);
1002 float f_requested_fps
= var_CreateGetFloat( p_input
, "sub-fps" );
1003 if( f_requested_fps
!= f_fps
)
1005 var_Create( p_input
, "sub-fps", VLC_VAR_FLOAT
|
1006 VLC_VAR_DOINHERIT
);
1007 var_SetFloat( p_input
, "sub-fps", f_requested_fps
);
1011 const int i_delay
= var_CreateGetInteger( p_input
, "sub-delay" );
1013 var_SetInteger( p_input
, "spu-delay", (mtime_t
)i_delay
* 100000 );
1016 static void GetVarSlaves( input_thread_t
*p_input
,
1017 input_item_slave_t
***ppp_slaves
, int *p_slaves
)
1019 char *psz
= var_GetNonEmptyString( p_input
, "input-slave" );
1023 input_item_slave_t
**pp_slaves
= *ppp_slaves
;
1024 int i_slaves
= *p_slaves
;
1026 char *psz_org
= psz
;
1027 while( psz
&& *psz
)
1029 while( *psz
== ' ' || *psz
== '#' )
1032 char *psz_delim
= strchr( psz
, '#' );
1034 *psz_delim
++ = '\0';
1039 char *uri
= strstr(psz
, "://")
1040 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
1045 input_item_slave_t
*p_slave
=
1046 input_item_slave_New( uri
, SLAVE_TYPE_AUDIO
, SLAVE_PRIORITY_USER
);
1049 if( unlikely( p_slave
== NULL
) )
1051 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1055 *ppp_slaves
= pp_slaves
; /* in case of realloc */
1056 *p_slaves
= i_slaves
;
1059 static void LoadSlaves( input_thread_t
*p_input
)
1061 input_item_slave_t
**pp_slaves
;
1063 TAB_INIT( i_slaves
, pp_slaves
);
1065 /* Look for and add slaves */
1067 char *psz_subtitle
= var_GetNonEmptyString( p_input
, "sub-file" );
1068 if( psz_subtitle
!= NULL
)
1070 msg_Dbg( p_input
, "forced subtitle: %s", psz_subtitle
);
1071 char *psz_uri
= input_SubtitleFile2Uri( p_input
, psz_subtitle
);
1072 free( psz_subtitle
);
1073 psz_subtitle
= NULL
;
1074 if( psz_uri
!= NULL
)
1076 input_item_slave_t
*p_slave
=
1077 input_item_slave_New( psz_uri
, SLAVE_TYPE_SPU
,
1078 SLAVE_PRIORITY_USER
);
1082 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1083 psz_subtitle
= p_slave
->psz_uri
;
1088 if( var_GetBool( p_input
, "sub-autodetect-file" ) )
1090 /* Add local subtitles */
1091 char *psz_autopath
= var_GetNonEmptyString( p_input
, "sub-autodetect-path" );
1093 if( subtitles_Detect( p_input
, psz_autopath
, input_priv(p_input
)->p_item
->psz_uri
,
1094 &pp_slaves
, &i_slaves
) == VLC_SUCCESS
)
1096 /* check that we did not add the subtitle through sub-file */
1097 if( psz_subtitle
!= NULL
)
1099 for( int i
= 1; i
< i_slaves
; i
++ )
1101 input_item_slave_t
*p_curr
= pp_slaves
[i
];
1103 && !strcmp( psz_subtitle
, p_curr
->psz_uri
) )
1105 /* reject current sub */
1106 input_item_slave_Delete( p_curr
);
1107 pp_slaves
[i
] = NULL
;
1112 free( psz_autopath
);
1115 /* Add slaves found by the directory demuxer or via libvlc */
1116 input_item_t
*p_item
= input_priv(p_input
)->p_item
;
1117 vlc_mutex_lock( &p_item
->lock
);
1119 /* Move item slaves to local pp_slaves */
1120 for( int i
= 0; i
< p_item
->i_slaves
; i
++ )
1122 input_item_slave_t
*p_slave
= p_item
->pp_slaves
[i
];
1123 if( !SlaveExists( pp_slaves
, i_slaves
, p_slave
->psz_uri
) )
1124 TAB_APPEND(i_slaves
, pp_slaves
, p_slave
);
1126 input_item_slave_Delete( p_slave
);
1128 /* Slaves that are successfully loaded will be added back to the item */
1129 TAB_CLEAN( p_item
->i_slaves
, p_item
->pp_slaves
);
1130 vlc_mutex_unlock( &p_item
->lock
);
1132 /* Add slaves from the "input-slave" option */
1133 GetVarSlaves( p_input
, &pp_slaves
, &i_slaves
);
1136 qsort( pp_slaves
, i_slaves
, sizeof (input_item_slave_t
*),
1139 /* add all detected slaves */
1140 bool p_forced
[2] = { false, false };
1141 static_assert( SLAVE_TYPE_AUDIO
<= 1 && SLAVE_TYPE_SPU
<= 1,
1142 "slave type size mismatch");
1143 for( int i
= 0; i
< i_slaves
&& pp_slaves
[i
] != NULL
; i
++ )
1145 input_item_slave_t
*p_slave
= pp_slaves
[i
];
1146 /* Slaves added via options should not fail */
1147 unsigned i_flags
= p_slave
->i_priority
!= SLAVE_PRIORITY_USER
1148 ? SLAVE_ADD_CANFAIL
: SLAVE_ADD_NOFLAG
;
1149 bool b_forced
= false;
1151 /* Force the first subtitle with the highest priority or with the
1153 if( !p_forced
[p_slave
->i_type
]
1154 && ( p_slave
->b_forced
|| p_slave
->i_priority
== SLAVE_PRIORITY_USER
) )
1156 i_flags
|= SLAVE_ADD_FORCED
;
1160 if( input_SlaveSourceAdd( p_input
, p_slave
->i_type
, p_slave
->psz_uri
,
1161 i_flags
) == VLC_SUCCESS
)
1163 input_item_AddSlave( input_priv(p_input
)->p_item
, p_slave
);
1165 p_forced
[p_slave
->i_type
] = true;
1168 input_item_slave_Delete( p_slave
);
1170 TAB_CLEAN( i_slaves
, pp_slaves
);
1172 /* Load subtitles from attachments */
1173 int i_attachment
= 0;
1174 input_attachment_t
**pp_attachment
= NULL
;
1176 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
1177 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
1179 const input_attachment_t
*a
= input_priv(p_input
)->attachment
[i
];
1180 if( !strcmp( a
->psz_mime
, "application/x-srt" ) )
1181 TAB_APPEND( i_attachment
, pp_attachment
,
1182 vlc_input_attachment_New( a
->psz_name
, NULL
,
1183 a
->psz_description
, NULL
, 0 ) );
1185 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1187 if( i_attachment
> 0 )
1188 var_Create( p_input
, "sub-description", VLC_VAR_STRING
);
1189 for( int i
= 0; i
< i_attachment
; i
++ )
1191 input_attachment_t
*a
= pp_attachment
[i
];
1195 if( a
->psz_name
[0] &&
1196 asprintf( &psz_mrl
, "attachment://%s", a
->psz_name
) >= 0 )
1198 var_SetString( p_input
, "sub-description", a
->psz_description
? a
->psz_description
: "");
1200 /* Force the first subtitle from attachment if there is no
1201 * subtitles already forced */
1202 if( input_SlaveSourceAdd( p_input
, SLAVE_TYPE_SPU
, psz_mrl
,
1203 p_forced
[ SLAVE_TYPE_SPU
] ?
1204 SLAVE_ADD_NOFLAG
: SLAVE_ADD_FORCED
) == VLC_SUCCESS
)
1205 p_forced
[ SLAVE_TYPE_SPU
] = true;
1208 /* Don't update item slaves for attachements */
1210 vlc_input_attachment_Delete( a
);
1212 free( pp_attachment
);
1213 if( i_attachment
> 0 )
1214 var_Destroy( p_input
, "sub-description" );
1217 static void UpdatePtsDelay( input_thread_t
*p_input
)
1219 input_thread_private_t
*p_sys
= input_priv(p_input
);
1221 /* Get max pts delay from input source */
1222 mtime_t i_pts_delay
= p_sys
->master
->i_pts_delay
;
1223 for( int i
= 0; i
< p_sys
->i_slave
; i
++ )
1224 i_pts_delay
= __MAX( i_pts_delay
, p_sys
->slave
[i
]->i_pts_delay
);
1226 if( i_pts_delay
< 0 )
1229 /* Take care of audio/spu delay */
1230 const mtime_t i_audio_delay
= var_GetInteger( p_input
, "audio-delay" );
1231 const mtime_t i_spu_delay
= var_GetInteger( p_input
, "spu-delay" );
1232 const mtime_t i_extra_delay
= __MIN( i_audio_delay
, i_spu_delay
);
1233 if( i_extra_delay
< 0 )
1234 i_pts_delay
-= i_extra_delay
;
1236 /* Update cr_average depending on the caching */
1237 const int i_cr_average
= var_GetInteger( p_input
, "cr-average" ) * i_pts_delay
/ DEFAULT_PTS_DELAY
;
1240 es_out_SetDelay( input_priv(p_input
)->p_es_out_display
, AUDIO_ES
, i_audio_delay
);
1241 es_out_SetDelay( input_priv(p_input
)->p_es_out_display
, SPU_ES
, i_spu_delay
);
1242 es_out_SetJitter( input_priv(p_input
)->p_es_out
, i_pts_delay
, 0, i_cr_average
);
1245 static void InitPrograms( input_thread_t
* p_input
)
1250 /* Compute correct pts_delay */
1251 UpdatePtsDelay( p_input
);
1254 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1255 if( input_priv(p_input
)->p_sout
)
1259 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1263 TAB_INIT( list
.i_count
, list
.p_values
);
1264 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1266 prgm
= strtok_r( NULL
, ",", &buf
) )
1268 vlc_value_t val
= { .i_int
= atoi( prgm
) };
1269 TAB_APPEND(list
.i_count
, list
.p_values
, val
);
1272 if( list
.i_count
> 0 )
1273 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1274 /* Note : we should remove the "program" callback. */
1278 else if( var_GetBool( p_input
, "sout-all" ) )
1280 i_es_out_mode
= ES_OUT_MODE_ALL
;
1283 es_out_SetMode( input_priv(p_input
)->p_es_out
, i_es_out_mode
);
1285 /* Inform the demuxer about waited group (needed only for DVB) */
1286 if( i_es_out_mode
== ES_OUT_MODE_ALL
)
1288 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, -1, NULL
);
1290 else if( i_es_out_mode
== ES_OUT_MODE_PARTIAL
)
1292 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, -1,
1294 TAB_CLEAN( list
.i_count
, list
.p_values
);
1298 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
,
1299 es_out_GetGroupForced( input_priv(p_input
)->p_es_out
), NULL
);
1303 static int Init( input_thread_t
* p_input
)
1305 input_thread_private_t
*priv
= input_priv(p_input
);
1306 input_source_t
*master
;
1308 if( var_Type( p_input
->obj
.parent
, "meta-file" ) )
1310 msg_Dbg( p_input
, "Input is a meta file: disabling unneeded options" );
1311 var_SetString( p_input
, "sout", "" );
1312 var_SetBool( p_input
, "sout-all", false );
1313 var_SetString( p_input
, "input-slave", "" );
1314 var_SetInteger( p_input
, "input-repeat", 0 );
1315 var_SetString( p_input
, "sub-file", "" );
1316 var_SetBool( p_input
, "sub-autodetect-file", false );
1319 InitStatistics( p_input
);
1321 if( InitSout( p_input
) )
1326 priv
->p_es_out
= input_EsOutTimeshiftNew( p_input
, priv
->p_es_out_display
,
1328 if( priv
->p_es_out
== NULL
)
1332 input_ChangeState( p_input
, OPENING_S
);
1333 input_SendEventCache( p_input
, 0.0 );
1336 master
= InputSourceNew( p_input
, priv
->p_item
->psz_uri
, NULL
, false );
1337 if( master
== NULL
)
1339 priv
->master
= master
;
1341 InitTitle( p_input
);
1343 /* Load master infos */
1346 if( demux_Control( master
->p_demux
, DEMUX_GET_LENGTH
, &i_length
) )
1349 i_length
= input_item_GetDuration( priv
->p_item
);
1350 input_SendEventLength( p_input
, i_length
);
1352 input_SendEventPosition( p_input
, 0.0, 0 );
1354 if( !priv
->b_preparsing
)
1356 StartTitle( p_input
);
1357 SetSubtitlesOptions( p_input
);
1358 LoadSlaves( p_input
);
1359 InitPrograms( p_input
);
1361 double f_rate
= var_InheritFloat( p_input
, "rate" );
1362 if( f_rate
!= 0.0 && f_rate
!= 1.0 )
1364 vlc_value_t val
= { .i_int
= INPUT_RATE_DEFAULT
/ f_rate
};
1365 input_ControlPush( p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1369 if( !priv
->b_preparsing
&& priv
->p_sout
)
1371 priv
->b_out_pace_control
= priv
->p_sout
->i_out_pace_nocontrol
> 0;
1373 msg_Dbg( p_input
, "starting in %ssync mode",
1374 priv
->b_out_pace_control
? "a" : "" );
1377 vlc_meta_t
*p_meta
= vlc_meta_New();
1378 if( p_meta
!= NULL
)
1380 /* Get meta data from users */
1381 InputMetaUser( p_input
, p_meta
);
1383 /* Get meta data from master input */
1384 InputSourceMeta( p_input
, master
, p_meta
);
1386 /* And from slave */
1387 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1388 InputSourceMeta( p_input
, priv
->slave
[i
], p_meta
);
1390 es_out_ControlSetMeta( priv
->p_es_out
, p_meta
);
1391 vlc_meta_Delete( p_meta
);
1394 msg_Dbg( p_input
, "`%s' successfully opened",
1395 input_priv(p_input
)->p_item
->psz_uri
);
1397 /* initialization is complete */
1398 input_ChangeState( p_input
, PLAYING_S
);
1403 input_ChangeState( p_input
, ERROR_S
);
1405 if( input_priv(p_input
)->p_es_out
)
1406 es_out_Delete( input_priv(p_input
)->p_es_out
);
1407 es_out_SetMode( input_priv(p_input
)->p_es_out_display
, ES_OUT_MODE_END
);
1408 if( input_priv(p_input
)->p_resource
)
1410 if( input_priv(p_input
)->p_sout
)
1411 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1412 input_priv(p_input
)->p_sout
, NULL
);
1413 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1414 if( input_priv(p_input
)->p_resource_private
)
1415 input_resource_Terminate( input_priv(p_input
)->p_resource_private
);
1418 if( !priv
->b_preparsing
&& libvlc_stats( p_input
) )
1420 #define EXIT_COUNTER( c ) do { if( input_priv(p_input)->counters.p_##c ) \
1421 stats_CounterClean( input_priv(p_input)->counters.p_##c );\
1422 input_priv(p_input)->counters.p_##c = NULL; } while(0)
1423 EXIT_COUNTER( input_bitrate
);
1424 EXIT_COUNTER( demux_bitrate
);
1426 if( input_priv(p_input
)->p_sout
)
1428 EXIT_COUNTER( sout_send_bitrate
);
1433 /* Mark them deleted */
1434 input_priv(p_input
)->p_es_out
= NULL
;
1435 input_priv(p_input
)->p_sout
= NULL
;
1437 return VLC_EGENERIC
;
1440 /*****************************************************************************
1441 * End: end the input thread
1442 *****************************************************************************/
1443 static void End( input_thread_t
* p_input
)
1445 input_thread_private_t
*priv
= input_priv(p_input
);
1447 /* We are at the end */
1448 input_ChangeState( p_input
, END_S
);
1450 /* Clean control variables */
1451 input_ControlVarStop( p_input
);
1453 /* Stop es out activity */
1454 es_out_SetMode( priv
->p_es_out
, ES_OUT_MODE_NONE
);
1457 for( int i
= 0; i
< priv
->i_slave
; i
++ )
1458 InputSourceDestroy( priv
->slave
[i
] );
1459 free( priv
->slave
);
1461 /* Clean up master */
1462 InputSourceDestroy( priv
->master
);
1465 priv
->i_title_offset
= 0;
1466 priv
->i_seekpoint_offset
= 0;
1468 /* Unload all modules */
1469 if( priv
->p_es_out
)
1470 es_out_Delete( priv
->p_es_out
);
1471 es_out_SetMode( priv
->p_es_out_display
, ES_OUT_MODE_END
);
1473 if( !priv
->b_preparsing
)
1475 #define CL_CO( c ) \
1477 stats_CounterClean( priv->counters.p_##c ); \
1478 priv->counters.p_##c = NULL; \
1481 if( libvlc_stats( p_input
) )
1483 /* make sure we are up to date */
1484 stats_ComputeInputStats( p_input
, priv
->p_item
->p_stats
);
1485 CL_CO( input_bitrate
);
1486 CL_CO( demux_bitrate
);
1489 /* Close optional stream output instance */
1492 CL_CO( sout_send_bitrate
);
1497 vlc_mutex_lock( &priv
->p_item
->lock
);
1498 if( priv
->i_attachment
> 0 )
1500 for( int i
= 0; i
< priv
->i_attachment
; i
++ )
1501 vlc_input_attachment_Delete( priv
->attachment
[i
] );
1502 TAB_CLEAN( priv
->i_attachment
, priv
->attachment
);
1503 free( priv
->attachment_demux
);
1504 priv
->attachment_demux
= NULL
;
1507 /* clean bookmarks */
1508 for( int i
= 0; i
< priv
->i_bookmark
; ++i
)
1509 vlc_seekpoint_Delete( priv
->pp_bookmark
[i
] );
1510 TAB_CLEAN( priv
->i_bookmark
, priv
->pp_bookmark
);
1512 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
1515 input_resource_RequestSout( input_priv(p_input
)->p_resource
,
1516 input_priv(p_input
)->p_sout
, NULL
);
1517 input_resource_SetInput( input_priv(p_input
)->p_resource
, NULL
);
1518 if( input_priv(p_input
)->p_resource_private
)
1519 input_resource_Terminate( input_priv(p_input
)->p_resource_private
);
1522 /*****************************************************************************
1524 *****************************************************************************/
1525 void input_ControlPush( input_thread_t
*p_input
,
1526 int i_type
, vlc_value_t
*p_val
)
1528 input_thread_private_t
*sys
= input_priv(p_input
);
1530 vlc_mutex_lock( &sys
->lock_control
);
1531 if( sys
->is_stopped
|| sys
->i_control
>= INPUT_CONTROL_FIFO_SIZE
)
1533 if( sys
->is_stopped
)
1534 msg_Dbg( p_input
, "input control stopped, trashing type=%d",
1537 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1540 ControlRelease( i_type
, *p_val
);
1549 memset( &c
.val
, 0, sizeof(c
.val
) );
1551 sys
->control
[sys
->i_control
++] = c
;
1553 vlc_cond_signal( &sys
->wait_control
);
1555 vlc_mutex_unlock( &sys
->lock_control
);
1558 static int ControlGetReducedIndexLocked( input_thread_t
*p_input
)
1560 const int i_lt
= input_priv(p_input
)->control
[0].i_type
;
1562 for( i
= 1; i
< input_priv(p_input
)->i_control
; i
++ )
1564 const int i_ct
= input_priv(p_input
)->control
[i
].i_type
;
1567 ( i_ct
== INPUT_CONTROL_SET_STATE
||
1568 i_ct
== INPUT_CONTROL_SET_RATE
||
1569 i_ct
== INPUT_CONTROL_SET_POSITION
||
1570 i_ct
== INPUT_CONTROL_SET_TIME
||
1571 i_ct
== INPUT_CONTROL_SET_PROGRAM
||
1572 i_ct
== INPUT_CONTROL_SET_TITLE
||
1573 i_ct
== INPUT_CONTROL_SET_SEEKPOINT
||
1574 i_ct
== INPUT_CONTROL_SET_BOOKMARK
) )
1580 /* TODO but that's not that important
1581 - merge SET_X with SET_X_CMD
1582 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1583 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1593 static inline int ControlPop( input_thread_t
*p_input
,
1594 int *pi_type
, vlc_value_t
*p_val
,
1595 mtime_t i_deadline
, bool b_postpone_seek
)
1597 input_thread_private_t
*p_sys
= input_priv(p_input
);
1599 vlc_mutex_lock( &p_sys
->lock_control
);
1600 while( p_sys
->i_control
<= 0 ||
1601 ( b_postpone_seek
&& ControlIsSeekRequest( p_sys
->control
[0].i_type
) ) )
1603 if( p_sys
->is_stopped
)
1605 vlc_mutex_unlock( &p_sys
->lock_control
);
1606 return VLC_EGENERIC
;
1609 if( i_deadline
>= 0 )
1611 if( vlc_cond_timedwait( &p_sys
->wait_control
, &p_sys
->lock_control
,
1614 vlc_mutex_unlock( &p_sys
->lock_control
);
1615 return VLC_EGENERIC
;
1619 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
1623 const int i_index
= ControlGetReducedIndexLocked( p_input
);
1625 for( int i
= 0; i
< i_index
; ++i
)
1627 /* Release Reduced controls */
1628 ControlRelease( p_sys
->control
[i
].i_type
, p_sys
->control
[i
].val
);
1632 *pi_type
= p_sys
->control
[i_index
].i_type
;
1633 *p_val
= p_sys
->control
[i_index
].val
;
1635 p_sys
->i_control
-= i_index
+ 1;
1636 if( p_sys
->i_control
> 0 )
1637 memmove( &p_sys
->control
[0], &p_sys
->control
[i_index
+1],
1638 sizeof(*p_sys
->control
) * p_sys
->i_control
);
1639 vlc_mutex_unlock( &p_sys
->lock_control
);
1643 static bool ControlIsSeekRequest( int i_type
)
1647 case INPUT_CONTROL_SET_POSITION
:
1648 case INPUT_CONTROL_SET_TIME
:
1649 case INPUT_CONTROL_SET_TITLE
:
1650 case INPUT_CONTROL_SET_TITLE_NEXT
:
1651 case INPUT_CONTROL_SET_TITLE_PREV
:
1652 case INPUT_CONTROL_SET_SEEKPOINT
:
1653 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1654 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1655 case INPUT_CONTROL_SET_BOOKMARK
:
1656 case INPUT_CONTROL_NAV_ACTIVATE
:
1657 case INPUT_CONTROL_NAV_UP
:
1658 case INPUT_CONTROL_NAV_DOWN
:
1659 case INPUT_CONTROL_NAV_LEFT
:
1660 case INPUT_CONTROL_NAV_RIGHT
:
1661 case INPUT_CONTROL_NAV_POPUP
:
1662 case INPUT_CONTROL_NAV_MENU
:
1669 static void ControlRelease( int i_type
, vlc_value_t val
)
1673 case INPUT_CONTROL_ADD_SLAVE
:
1675 input_item_slave_Delete( val
.p_address
);
1677 case INPUT_CONTROL_SET_VIEWPOINT
:
1678 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT
:
1679 case INPUT_CONTROL_UPDATE_VIEWPOINT
:
1680 free( val
.p_address
);
1682 case INPUT_CONTROL_SET_RENDERER
:
1684 vlc_renderer_item_release( val
.p_address
);
1693 static void ControlPause( input_thread_t
*p_input
, mtime_t i_control_date
)
1695 int i_state
= PAUSE_S
;
1697 if( input_priv(p_input
)->b_can_pause
)
1699 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1701 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, true ) )
1703 msg_Warn( p_input
, "cannot set pause state" );
1709 if( es_out_SetPauseState( input_priv(p_input
)->p_es_out
, input_priv(p_input
)->b_can_pause
,
1710 true, i_control_date
) )
1712 msg_Warn( p_input
, "cannot set pause state at es_out level" );
1716 /* Switch to new state */
1717 input_ChangeState( p_input
, i_state
);
1720 static void ControlUnpause( input_thread_t
*p_input
, mtime_t i_control_date
)
1722 if( input_priv(p_input
)->b_can_pause
)
1724 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
1726 if( demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, false ) )
1728 msg_Err( p_input
, "cannot resume" );
1729 input_ChangeState( p_input
, ERROR_S
);
1734 /* Switch to play */
1735 input_ChangeState( p_input
, PLAYING_S
);
1736 es_out_SetPauseState( input_priv(p_input
)->p_es_out
, false, false, i_control_date
);
1739 static void ViewpointApply( input_thread_t
*p_input
)
1741 input_thread_private_t
*priv
= input_priv(p_input
);
1743 vlc_viewpoint_clip( &priv
->viewpoint
);
1745 vout_thread_t
**pp_vout
;
1747 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1749 for( size_t i
= 0; i
< i_vout
; ++i
)
1751 var_SetAddress( pp_vout
[i
], "viewpoint", &priv
->viewpoint
);
1752 /* This variable can only be read from callbacks */
1753 var_Change( pp_vout
[i
], "viewpoint", VLC_VAR_SETVALUE
,
1754 &(vlc_value_t
) { .p_address
= NULL
}, NULL
);
1755 vlc_object_release( pp_vout
[i
] );
1759 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1763 var_SetAddress( p_aout
, "viewpoint", &priv
->viewpoint
);
1764 /* This variable can only be read from callbacks */
1765 var_Change( p_aout
, "viewpoint", VLC_VAR_SETVALUE
,
1766 &(vlc_value_t
) { .p_address
= NULL
}, NULL
);
1767 vlc_object_release( p_aout
);
1771 static void ControlNav( input_thread_t
*p_input
, int i_type
)
1773 input_thread_private_t
*priv
= input_priv(p_input
);
1775 if( !demux_Control( priv
->master
->p_demux
, i_type
1776 - INPUT_CONTROL_NAV_ACTIVATE
+ DEMUX_NAV_ACTIVATE
) )
1777 return; /* The demux handled the navigation control */
1779 /* Handle Up/Down/Left/Right if the demux can't navigate */
1780 vlc_viewpoint_t vp
= {};
1781 int vol_direction
= 0;
1782 int seek_direction
= 0;
1785 case INPUT_CONTROL_NAV_UP
:
1789 case INPUT_CONTROL_NAV_DOWN
:
1793 case INPUT_CONTROL_NAV_LEFT
:
1794 seek_direction
= -1;
1797 case INPUT_CONTROL_NAV_RIGHT
:
1801 case INPUT_CONTROL_NAV_ACTIVATE
:
1802 case INPUT_CONTROL_NAV_POPUP
:
1803 case INPUT_CONTROL_NAV_MENU
:
1806 vlc_assert_unreachable();
1809 /* Try to change the viewpoint if possible */
1810 vout_thread_t
**pp_vout
;
1812 bool b_viewpoint_ch
= false;
1813 input_resource_HoldVouts( priv
->p_resource
, &pp_vout
, &i_vout
);
1814 for( size_t i
= 0; i
< i_vout
; ++i
)
1817 && var_GetBool( pp_vout
[i
], "viewpoint-changeable" ) )
1818 b_viewpoint_ch
= true;
1819 vlc_object_release( pp_vout
[i
] );
1823 if( b_viewpoint_ch
)
1825 priv
->viewpoint_changed
= true;
1826 priv
->viewpoint
.yaw
+= vp
.yaw
;
1827 priv
->viewpoint
.pitch
+= vp
.pitch
;
1828 priv
->viewpoint
.roll
+= vp
.roll
;
1829 priv
->viewpoint
.fov
+= vp
.fov
;
1830 ViewpointApply( p_input
);
1834 /* Seek or change volume if the input doesn't have navigation or viewpoint */
1835 if( seek_direction
!= 0 )
1837 mtime_t it
= var_InheritInteger( p_input
, "short-jump-size" );
1838 var_SetInteger( p_input
, "time-offset", it
* seek_direction
* CLOCK_FREQ
);
1842 assert( vol_direction
!= 0 );
1843 audio_output_t
*p_aout
= input_resource_HoldAout( priv
->p_resource
);
1846 aout_VolumeUpdate( p_aout
, vol_direction
, NULL
);
1847 vlc_object_release( p_aout
);
1853 static void ControlUpdateSout( input_thread_t
*p_input
, const char* psz_chain
)
1855 var_SetString( p_input
, "sout", psz_chain
);
1856 if( psz_chain
&& *psz_chain
)
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
;
1870 es_out_Control( input_priv(p_input
)->p_es_out
, ES_OUT_RESTART_ALL_ES
);
1874 static void ControlInsertDemuxFilter( input_thread_t
* p_input
, const char* psz_demux_chain
)
1876 input_source_t
*p_inputSource
= input_priv(p_input
)->master
;
1877 demux_t
*p_filtered_demux
= demux_FilterChainNew( p_inputSource
->p_demux
, psz_demux_chain
);
1878 if ( p_filtered_demux
!= NULL
)
1879 p_inputSource
->p_demux
= p_filtered_demux
;
1880 else if ( psz_demux_chain
!= NULL
)
1881 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
, vlc_value_t val
)
1887 const mtime_t i_control_date
= mdate();
1888 /* FIXME b_force_update is abused, it should be carefully checked */
1889 bool b_force_update
= false;
1892 return b_force_update
;
1896 case INPUT_CONTROL_SET_POSITION
:
1898 if( input_priv(p_input
)->b_recording
)
1900 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION ignored while recording" );
1904 float f_pos
= val
.f_float
;
1907 else if( f_pos
> 1.f
)
1909 /* Reset the decoders states and clock sync (before calling the demuxer */
1910 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
1911 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_POSITION
,
1912 (double) f_pos
, !input_priv(p_input
)->b_fast_seek
) )
1914 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION "
1915 "%2.1f%% failed", (double)(f_pos
* 100.f
) );
1919 if( input_priv(p_input
)->i_slave
> 0 )
1920 SlaveSeek( p_input
);
1921 input_priv(p_input
)->master
->b_eof
= false;
1923 b_force_update
= true;
1928 case INPUT_CONTROL_SET_TIME
:
1933 if( input_priv(p_input
)->b_recording
)
1935 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME ignored while recording" );
1943 /* Reset the decoders states and clock sync (before calling the demuxer */
1944 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
1946 i_ret
= demux_Control( input_priv(p_input
)->master
->p_demux
,
1947 DEMUX_SET_TIME
, i_time
,
1948 !input_priv(p_input
)->b_fast_seek
);
1953 /* Emulate it with a SET_POS */
1954 if( !demux_Control( input_priv(p_input
)->master
->p_demux
,
1955 DEMUX_GET_LENGTH
, &i_length
) && i_length
> 0 )
1957 double f_pos
= (double)i_time
/ (double)i_length
;
1958 i_ret
= demux_Control( input_priv(p_input
)->master
->p_demux
,
1959 DEMUX_SET_POSITION
, f_pos
,
1960 !input_priv(p_input
)->b_fast_seek
);
1965 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME %"PRId64
1966 " failed or not possible", i_time
);
1970 if( input_priv(p_input
)->i_slave
> 0 )
1971 SlaveSeek( p_input
);
1972 input_priv(p_input
)->master
->b_eof
= false;
1974 b_force_update
= true;
1979 case INPUT_CONTROL_SET_STATE
:
1983 if( input_priv(p_input
)->i_state
== PAUSE_S
)
1985 ControlUnpause( p_input
, i_control_date
);
1986 b_force_update
= true;
1990 if( input_priv(p_input
)->i_state
== PLAYING_S
)
1992 ControlPause( p_input
, i_control_date
);
1993 b_force_update
= true;
1997 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
2001 case INPUT_CONTROL_SET_RATE
:
2003 /* Get rate and direction */
2004 long long i_rate
= llabs( val
.i_int
);
2005 int i_rate_sign
= val
.i_int
< 0 ? -1 : 1;
2007 /* Check rate bound */
2008 if( i_rate
< INPUT_RATE_MIN
)
2010 msg_Dbg( p_input
, "cannot set rate faster" );
2011 i_rate
= INPUT_RATE_MIN
;
2013 else if( i_rate
> INPUT_RATE_MAX
)
2015 msg_Dbg( p_input
, "cannot set rate slower" );
2016 i_rate
= INPUT_RATE_MAX
;
2019 /* Apply direction */
2020 if( i_rate_sign
< 0 )
2022 if( input_priv(p_input
)->master
->b_rescale_ts
)
2024 msg_Dbg( p_input
, "cannot set negative rate" );
2025 i_rate
= input_priv(p_input
)->i_rate
;
2026 assert( i_rate
> 0 );
2030 i_rate
*= i_rate_sign
;
2034 if( i_rate
!= INPUT_RATE_DEFAULT
&&
2035 ( ( !input_priv(p_input
)->b_can_rate_control
&& !input_priv(p_input
)->master
->b_rescale_ts
) ||
2036 ( input_priv(p_input
)->p_sout
&& !input_priv(p_input
)->b_out_pace_control
) ) )
2038 msg_Dbg( p_input
, "cannot change rate" );
2039 i_rate
= INPUT_RATE_DEFAULT
;
2041 if( i_rate
!= input_priv(p_input
)->i_rate
&&
2042 !input_priv(p_input
)->b_can_pace_control
&& input_priv(p_input
)->b_can_rate_control
)
2044 if( !input_priv(p_input
)->master
->b_rescale_ts
)
2045 es_out_Control( input_priv(p_input
)->p_es_out
, ES_OUT_RESET_PCR
);
2047 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_RATE
,
2050 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
2051 i_rate
= input_priv(p_input
)->i_rate
;
2056 if( i_rate
!= input_priv(p_input
)->i_rate
)
2058 input_priv(p_input
)->i_rate
= i_rate
;
2059 input_SendEventRate( p_input
, i_rate
);
2061 if( input_priv(p_input
)->master
->b_rescale_ts
)
2063 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
;
2064 es_out_SetRate( input_priv(p_input
)->p_es_out
, i_rate_source
, i_rate
);
2067 b_force_update
= true;
2072 case INPUT_CONTROL_SET_PROGRAM
:
2073 /* No need to force update, es_out does it if needed */
2074 es_out_Control( input_priv(p_input
)->p_es_out
,
2075 ES_OUT_SET_GROUP
, val
.i_int
);
2077 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_GROUP
, val
.i_int
,
2081 case INPUT_CONTROL_SET_ES
:
2082 /* No need to force update, es_out does it if needed */
2083 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2084 ES_OUT_SET_ES_BY_ID
, (int)val
.i_int
);
2086 demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_SET_ES
, (int)val
.i_int
);
2089 case INPUT_CONTROL_RESTART_ES
:
2090 es_out_Control( input_priv(p_input
)->p_es_out_display
,
2091 ES_OUT_RESTART_ES_BY_ID
, (int)val
.i_int
);
2094 case INPUT_CONTROL_SET_VIEWPOINT
:
2095 case INPUT_CONTROL_SET_INITIAL_VIEWPOINT
:
2096 case INPUT_CONTROL_UPDATE_VIEWPOINT
:
2098 input_thread_private_t
*priv
= input_priv(p_input
);
2099 const vlc_viewpoint_t
*p_vp
= val
.p_address
;
2101 if ( i_type
== INPUT_CONTROL_SET_INITIAL_VIEWPOINT
)
2104 /* Set the initial viewpoint if it had not been changed by the
2106 if( !priv
->viewpoint_changed
)
2107 priv
->viewpoint
= *p_vp
;
2108 /* Update viewpoints of aout and every vouts in all cases. */
2110 else if ( i_type
== INPUT_CONTROL_SET_VIEWPOINT
)
2112 priv
->viewpoint_changed
= true;
2113 priv
->viewpoint
= *p_vp
;
2117 priv
->viewpoint_changed
= true;
2118 priv
->viewpoint
.yaw
+= p_vp
->yaw
;
2119 priv
->viewpoint
.pitch
+= p_vp
->pitch
;
2120 priv
->viewpoint
.roll
+= p_vp
->roll
;
2121 priv
->viewpoint
.fov
+= p_vp
->fov
;
2124 ViewpointApply( p_input
);
2128 case INPUT_CONTROL_SET_AUDIO_DELAY
:
2129 input_SendEventAudioDelay( p_input
, val
.i_int
);
2130 UpdatePtsDelay( p_input
);
2133 case INPUT_CONTROL_SET_SPU_DELAY
:
2134 input_SendEventSubtitleDelay( p_input
, val
.i_int
);
2135 UpdatePtsDelay( p_input
);
2138 case INPUT_CONTROL_SET_TITLE
:
2139 case INPUT_CONTROL_SET_TITLE_NEXT
:
2140 case INPUT_CONTROL_SET_TITLE_PREV
:
2142 if( input_priv(p_input
)->b_recording
)
2144 msg_Err( p_input
, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
2147 if( input_priv(p_input
)->master
->i_title
<= 0 )
2150 int i_title
= demux_GetTitle( input_priv(p_input
)->master
->p_demux
);
2151 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
2153 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
2156 i_title
= val
.i_int
;
2157 if( i_title
< 0 || i_title
>= input_priv(p_input
)->master
->i_title
)
2160 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
2161 demux_Control( input_priv(p_input
)->master
->p_demux
,
2162 DEMUX_SET_TITLE
, i_title
);
2163 input_SendEventTitle( p_input
, i_title
);
2166 case INPUT_CONTROL_SET_SEEKPOINT
:
2167 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
2168 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
2170 if( input_priv(p_input
)->b_recording
)
2172 msg_Err( p_input
, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
2175 if( input_priv(p_input
)->master
->i_title
<= 0 )
2178 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2180 int i_title
= demux_GetTitle( p_demux
);
2181 int i_seekpoint
= demux_GetSeekpoint( p_demux
);
2183 if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_PREV
)
2185 int64_t i_seekpoint_time
= input_priv(p_input
)->master
->title
[i_title
]->seekpoint
[i_seekpoint
]->i_time_offset
;
2186 int64_t i_input_time
= var_GetInteger( p_input
, "time" );
2187 if( i_seekpoint_time
>= 0 && i_input_time
>= 0 )
2189 if( i_input_time
< i_seekpoint_time
+ 3000000 )
2195 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
2198 i_seekpoint
= val
.i_int
;
2200 || i_seekpoint
>= input_priv(p_input
)->master
->title
[i_title
]->i_seekpoint
)
2203 es_out_SetTime( input_priv(p_input
)->p_es_out
, -1 );
2204 demux_Control( input_priv(p_input
)->master
->p_demux
,
2205 DEMUX_SET_SEEKPOINT
, i_seekpoint
);
2206 input_SendEventSeekpoint( p_input
, i_title
, i_seekpoint
);
2210 case INPUT_CONTROL_ADD_SLAVE
:
2213 input_item_slave_t
*p_item_slave
= val
.p_address
;
2214 unsigned i_flags
= SLAVE_ADD_CANFAIL
| SLAVE_ADD_SET_TIME
;
2215 if( p_item_slave
->b_forced
)
2216 i_flags
|= SLAVE_ADD_FORCED
;
2218 if( input_SlaveSourceAdd( p_input
, p_item_slave
->i_type
,
2219 p_item_slave
->psz_uri
, i_flags
)
2222 /* Update item slaves */
2223 input_item_AddSlave( input_priv(p_input
)->p_item
, p_item_slave
);
2224 /* The slave is now owned by the item */
2225 val
.p_address
= NULL
;
2230 case INPUT_CONTROL_SET_RECORD_STATE
:
2231 if( !!input_priv(p_input
)->b_recording
!= !!val
.b_bool
)
2233 if( input_priv(p_input
)->master
->b_can_stream_record
)
2235 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
2236 DEMUX_SET_RECORD_STATE
, val
.b_bool
) )
2241 if( es_out_SetRecordState( input_priv(p_input
)->p_es_out_display
, val
.b_bool
) )
2244 input_priv(p_input
)->b_recording
= val
.b_bool
;
2246 input_SendEventRecord( p_input
, val
.b_bool
);
2248 b_force_update
= true;
2252 case INPUT_CONTROL_SET_FRAME_NEXT
:
2253 if( input_priv(p_input
)->i_state
== PAUSE_S
)
2255 es_out_SetFrameNext( input_priv(p_input
)->p_es_out
);
2257 else if( input_priv(p_input
)->i_state
== PLAYING_S
)
2259 ControlPause( p_input
, i_control_date
);
2263 msg_Err( p_input
, "invalid state for frame next" );
2265 b_force_update
= true;
2268 case INPUT_CONTROL_SET_BOOKMARK
:
2270 mtime_t time_offset
= -1;
2272 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2273 if( val
.i_int
>= 0 && val
.i_int
< input_priv(p_input
)->i_bookmark
)
2275 const seekpoint_t
*p_bookmark
= input_priv(p_input
)->pp_bookmark
[val
.i_int
];
2276 time_offset
= p_bookmark
->i_time_offset
;
2278 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2280 if( time_offset
< 0 )
2282 msg_Err( p_input
, "invalid bookmark %"PRId64
, val
.i_int
);
2286 val
.i_int
= time_offset
;
2287 b_force_update
= Control( p_input
, INPUT_CONTROL_SET_TIME
, val
);
2290 case INPUT_CONTROL_SET_RENDERER
:
2293 vlc_renderer_item_t
*p_item
= val
.p_address
;
2294 input_thread_private_t
*p_priv
= input_priv( p_input
);
2295 // We do not support switching from a renderer to another for now
2296 if ( p_item
== NULL
&& p_priv
->p_renderer
== NULL
)
2299 if ( p_priv
->p_renderer
)
2301 ControlUpdateSout( p_input
, NULL
);
2302 demux_FilterDisable( p_priv
->master
->p_demux
,
2303 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) );
2304 vlc_renderer_item_release( p_priv
->p_renderer
);
2305 p_priv
->p_renderer
= NULL
;
2307 if( p_item
!= NULL
)
2309 p_priv
->p_renderer
= vlc_renderer_item_hold( p_item
);
2310 ControlUpdateSout( p_input
, vlc_renderer_item_sout( p_item
) );
2311 if( !demux_FilterEnable( p_priv
->master
->p_demux
,
2312 vlc_renderer_item_demux_filter( p_priv
->p_renderer
) ) )
2314 ControlInsertDemuxFilter( p_input
,
2315 vlc_renderer_item_demux_filter( p_item
) );
2317 input_resource_TerminateVout( p_priv
->p_resource
);
2323 case INPUT_CONTROL_NAV_ACTIVATE
:
2324 case INPUT_CONTROL_NAV_UP
:
2325 case INPUT_CONTROL_NAV_DOWN
:
2326 case INPUT_CONTROL_NAV_LEFT
:
2327 case INPUT_CONTROL_NAV_RIGHT
:
2328 case INPUT_CONTROL_NAV_POPUP
:
2329 case INPUT_CONTROL_NAV_MENU
:
2330 ControlNav( p_input
, i_type
);
2334 msg_Err( p_input
, "not yet implemented" );
2338 ControlRelease( i_type
, val
);
2339 return b_force_update
;
2342 /*****************************************************************************
2343 * UpdateTitleSeekpoint
2344 *****************************************************************************/
2345 static int UpdateTitleSeekpoint( input_thread_t
*p_input
,
2346 int i_title
, int i_seekpoint
)
2348 int i_title_end
= input_priv(p_input
)->master
->i_title_end
-
2349 input_priv(p_input
)->master
->i_title_offset
;
2350 int i_seekpoint_end
= input_priv(p_input
)->master
->i_seekpoint_end
-
2351 input_priv(p_input
)->master
->i_seekpoint_offset
;
2353 if( i_title_end
>= 0 && i_seekpoint_end
>= 0 )
2355 if( i_title
> i_title_end
||
2356 ( i_title
== i_title_end
&& i_seekpoint
> i_seekpoint_end
) )
2357 return VLC_DEMUXER_EOF
;
2359 else if( i_seekpoint_end
>= 0 )
2361 if( i_seekpoint
> i_seekpoint_end
)
2362 return VLC_DEMUXER_EOF
;
2364 else if( i_title_end
>= 0 )
2366 if( i_title
> i_title_end
)
2367 return VLC_DEMUXER_EOF
;
2369 return VLC_DEMUXER_SUCCESS
;
2371 /*****************************************************************************
2373 *****************************************************************************/
2374 static int UpdateTitleSeekpointFromDemux( input_thread_t
*p_input
)
2376 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2378 /* TODO event-like */
2379 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_TITLE
) )
2380 input_SendEventTitle( p_input
, demux_GetTitle( p_demux
) );
2382 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_SEEKPOINT
) )
2383 input_SendEventSeekpoint( p_input
, demux_GetTitle( p_demux
),
2384 demux_GetSeekpoint( p_demux
) );
2386 return UpdateTitleSeekpoint( p_input
,
2387 demux_GetTitle( p_demux
),
2388 demux_GetSeekpoint( p_demux
) );
2391 static void UpdateGenericFromDemux( input_thread_t
*p_input
)
2393 demux_t
*p_demux
= input_priv(p_input
)->master
->p_demux
;
2395 if( demux_TestAndClearFlags( p_demux
, INPUT_UPDATE_META
) )
2396 InputUpdateMeta( p_input
, p_demux
);
2402 if( !demux_Control( p_demux
, DEMUX_GET_SIGNAL
, &quality
, &strength
) )
2403 input_SendEventSignal( p_input
, quality
, strength
);
2407 static void UpdateTitleListfromDemux( input_thread_t
*p_input
)
2409 input_thread_private_t
*priv
= input_priv(p_input
);
2410 input_source_t
*in
= priv
->master
;
2412 /* Delete the preexisting titles */
2413 if( in
->i_title
> 0 )
2415 for( int i
= 0; i
< in
->i_title
; i
++ )
2416 vlc_input_title_Delete( in
->title
[i
] );
2417 TAB_CLEAN( in
->i_title
, in
->title
);
2420 in
->b_title_demux
= false;
2423 /* Get the new title list */
2424 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2425 &in
->title
, &in
->i_title
,
2426 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2427 TAB_INIT( in
->i_title
, in
->title
);
2429 in
->b_title_demux
= true;
2431 InitTitle( p_input
);
2435 InputStreamHandleAnchor( input_source_t
*source
, stream_t
**stream
,
2436 char const *anchor
)
2439 if( stream_extractor_AttachParsed( stream
, anchor
, &extra
) )
2441 msg_Err( source
, "unable to attach stream-extractors for %s",
2442 (*stream
)->psz_url
);
2444 return VLC_EGENERIC
;
2447 if( vlc_stream_directory_Attach( stream
, NULL
) )
2448 msg_Dbg( source
, "attachment of directory-extractor failed for %s",
2449 (*stream
)->psz_url
);
2451 MRLSections( extra
? extra
: "",
2452 &source
->i_title_start
, &source
->i_title_end
,
2453 &source
->i_seekpoint_start
, &source
->i_seekpoint_end
);
2458 static demux_t
*InputDemuxNew( input_thread_t
*p_input
, input_source_t
*p_source
,
2459 const char *psz_access
, const char *psz_demux
,
2460 const char *psz_path
, const char *psz_anchor
)
2462 input_thread_private_t
*priv
= input_priv(p_input
);
2463 demux_t
*p_demux
= NULL
;
2465 /* first, try to create an access demux */
2466 p_demux
= demux_NewAdvanced( VLC_OBJECT( p_source
), p_input
,
2467 psz_access
, psz_access
, psz_path
,
2468 NULL
, priv
->p_es_out
, priv
->b_preparsing
);
2471 MRLSections( psz_anchor
,
2472 &p_source
->i_title_start
, &p_source
->i_title_end
,
2473 &p_source
->i_seekpoint_start
, &p_source
->i_seekpoint_end
);
2478 /* not an access-demux: create the underlying access stream */
2481 if( asprintf( &psz_base_mrl
, "%s://%s", psz_access
, psz_path
) < 0 )
2484 char *psz_filters
= var_InheritString( p_source
, "stream-filter" );
2485 stream_t
* p_stream
= stream_AccessNew( VLC_OBJECT( p_source
), p_input
,
2488 FREENULL( psz_base_mrl
);
2490 if( p_stream
== NULL
)
2493 /* attach explicit stream filters to stream */
2495 p_stream
= stream_FilterChainNew( p_stream
, psz_filters
);
2497 FREENULL( psz_filters
);
2499 /* handle anchors */
2500 if( InputStreamHandleAnchor( p_source
, &p_stream
, psz_anchor
) )
2503 /* attach conditional record stream-filter */
2504 if( var_InheritBool( p_source
, "input-record-native" ) )
2505 p_stream
= stream_FilterChainNew( p_stream
, "record" );
2507 /* create a regular demux with the access stream created */
2508 p_demux
= demux_NewAdvanced( VLC_OBJECT( p_source
), p_input
,
2509 psz_access
, psz_demux
, psz_path
,
2510 p_stream
, priv
->p_es_out
,
2511 priv
->b_preparsing
);
2516 free( psz_base_mrl
);
2517 free( psz_filters
);
2520 vlc_stream_Delete( p_stream
);
2525 /*****************************************************************************
2527 *****************************************************************************/
2528 static input_source_t
*InputSourceNew( input_thread_t
*p_input
,
2529 const char *psz_mrl
,
2530 const char *psz_forced_demux
,
2531 bool b_in_can_fail
)
2533 input_thread_private_t
*priv
= input_priv(p_input
);
2534 input_source_t
*in
= vlc_custom_create( p_input
, sizeof( *in
),
2536 if( unlikely(in
== NULL
) )
2539 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2542 char *psz_dup
= strdup( psz_mrl
);
2543 char *psz_demux_var
= NULL
;
2545 if( psz_dup
== NULL
)
2547 vlc_object_release( in
);
2552 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, &psz_anchor
, psz_dup
);
2554 if( psz_demux
== NULL
|| psz_demux
[0] == '\0' )
2555 psz_demux
= psz_demux_var
= var_InheritString( in
, "demux" );
2557 if( psz_forced_demux
!= NULL
)
2558 psz_demux
= psz_forced_demux
;
2560 if( psz_demux
== NULL
)
2563 msg_Dbg( p_input
, "`%s' gives access `%s' demux `%s' path `%s'",
2564 psz_mrl
, psz_access
, psz_demux
, psz_path
);
2566 if( input_priv(p_input
)->master
== NULL
/* XXX ugly */)
2567 { /* On master stream only, use input-list */
2568 char *str
= var_InheritString( p_input
, "input-list" );
2573 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2574 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2577 var_SetString( p_input
, "concat-list", list
);
2581 psz_access
= "concat";
2585 if( strcasecmp( psz_access
, "concat" ) )
2586 { /* Autodetect extra files if none specified */
2590 TAB_INIT( count
, tab
);
2591 InputGetExtraFiles( p_input
, &count
, &tab
, &psz_access
, psz_path
);
2596 for( int i
= 0; i
< count
; i
++ )
2599 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2608 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2609 if( likely(list
!= NULL
) )
2611 var_SetString( p_input
, "concat-list", list
);
2615 TAB_CLEAN( count
, tab
);
2618 in
->p_demux
= InputDemuxNew( p_input
, in
, psz_access
, psz_demux
,
2619 psz_path
, psz_anchor
);
2621 free( psz_demux_var
);
2624 if( in
->p_demux
== NULL
)
2626 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2627 vlc_dialog_display_error( p_input
, _("Your input can't be opened"),
2628 _("VLC is unable to open the MRL '%s'."
2629 " Check the log for details."), psz_mrl
);
2630 vlc_object_release( in
);
2634 char *psz_demux_chain
= NULL
;
2635 if( priv
->p_renderer
)
2637 const char* psz_renderer_demux
= vlc_renderer_item_demux_filter(
2639 if( psz_renderer_demux
)
2640 psz_demux_chain
= strdup( psz_renderer_demux
);
2642 if( !psz_demux_chain
)
2643 psz_demux_chain
= var_GetNonEmptyString(p_input
, "demux-filter");
2644 if( psz_demux_chain
!= NULL
) /* add the chain of demux filters */
2646 in
->p_demux
= demux_FilterChainNew( in
->p_demux
, psz_demux_chain
);
2647 free( psz_demux_chain
);
2649 if( in
->p_demux
== NULL
)
2651 msg_Err(p_input
, "Failed to create demux filter");
2652 vlc_object_release( in
);
2657 /* Get infos from (access_)demux */
2659 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &b_can_seek
) )
2661 var_SetBool( p_input
, "can-seek", b_can_seek
);
2663 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_PACE
,
2664 &in
->b_can_pace_control
) )
2665 in
->b_can_pace_control
= false;
2667 assert( in
->p_demux
->pf_demux
!= NULL
|| !in
->b_can_pace_control
);
2669 if( !in
->b_can_pace_control
)
2671 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_RATE
,
2672 &in
->b_can_rate_control
) )
2674 in
->b_can_rate_control
= false;
2675 in
->b_rescale_ts
= true;
2678 in
->b_rescale_ts
= !in
->b_can_rate_control
;
2682 in
->b_can_rate_control
= true;
2683 in
->b_rescale_ts
= true;
2686 demux_Control( in
->p_demux
, DEMUX_CAN_PAUSE
, &in
->b_can_pause
);
2688 var_SetBool( p_input
, "can-pause", in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2689 var_SetBool( p_input
, "can-rate", !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2690 var_SetBool( p_input
, "can-rewind", !in
->b_rescale_ts
&& !in
->b_can_pace_control
&& in
->b_can_rate_control
);
2692 /* Set record capabilities */
2693 if( demux_Control( in
->p_demux
, DEMUX_CAN_RECORD
, &in
->b_can_stream_record
) )
2694 in
->b_can_stream_record
= false;
2696 if( !var_GetBool( p_input
, "input-record-native" ) )
2697 in
->b_can_stream_record
= false;
2698 var_SetBool( p_input
, "can-record", true );
2700 var_SetBool( p_input
, "can-record", in
->b_can_stream_record
);
2704 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2705 if( !input_priv(p_input
)->b_preparsing
)
2707 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2708 &in
->title
, &in
->i_title
,
2709 &in
->i_title_offset
, &in
->i_seekpoint_offset
))
2711 TAB_INIT( in
->i_title
, in
->title
);
2715 in
->b_title_demux
= true;
2719 input_attachment_t
**attachment
;
2720 if( !demux_Control( in
->p_demux
, DEMUX_GET_ATTACHMENTS
,
2721 &attachment
, &i_attachment
) )
2723 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2724 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2725 i_attachment
, attachment
, in
->p_demux
);
2726 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2729 demux_Control( in
->p_demux
, DEMUX_GET_PTS_DELAY
, &in
->i_pts_delay
);
2730 if( in
->i_pts_delay
> INPUT_PTS_DELAY_MAX
)
2731 in
->i_pts_delay
= INPUT_PTS_DELAY_MAX
;
2732 else if( in
->i_pts_delay
< 0 )
2733 in
->i_pts_delay
= 0;
2736 if( demux_Control( in
->p_demux
, DEMUX_GET_FPS
, &in
->f_fps
) )
2739 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2740 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2745 /*****************************************************************************
2746 * InputSourceDestroy:
2747 *****************************************************************************/
2748 static void InputSourceDestroy( input_source_t
*in
)
2753 demux_Delete( in
->p_demux
);
2755 if( in
->i_title
> 0 )
2757 for( i
= 0; i
< in
->i_title
; i
++ )
2758 vlc_input_title_Delete( in
->title
[i
] );
2759 TAB_CLEAN( in
->i_title
, in
->title
);
2762 vlc_object_release( in
);
2765 /*****************************************************************************
2767 *****************************************************************************/
2768 static void InputSourceMeta( input_thread_t
*p_input
,
2769 input_source_t
*p_source
, vlc_meta_t
*p_meta
)
2771 demux_t
*p_demux
= p_source
->p_demux
;
2773 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2776 bool has_meta
= false;
2778 /* Read demux meta */
2779 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
2782 bool has_unsupported
;
2783 if( demux_Control( p_demux
, DEMUX_HAS_UNSUPPORTED_META
, &has_unsupported
) )
2784 has_unsupported
= true;
2786 /* If the demux report unsupported meta data, or if we don't have meta data
2787 * try an external "meta reader" */
2788 if( has_meta
&& !has_unsupported
)
2791 demux_meta_t
*p_demux_meta
=
2792 vlc_custom_create( p_source
, sizeof( *p_demux_meta
), "demux meta" );
2793 if( unlikely(p_demux_meta
== NULL
) )
2795 p_demux_meta
->p_item
= input_priv(p_input
)->p_item
;
2797 module_t
*p_id3
= module_need( p_demux_meta
, "meta reader", NULL
, false );
2800 if( p_demux_meta
->p_meta
)
2802 vlc_meta_Merge( p_meta
, p_demux_meta
->p_meta
);
2803 vlc_meta_Delete( p_demux_meta
->p_meta
);
2806 if( p_demux_meta
->i_attachments
> 0 )
2808 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2809 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
2810 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
, p_demux
);
2811 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
2813 module_unneed( p_demux
, p_id3
);
2815 vlc_object_release( p_demux_meta
);
2819 static void SlaveDemux( input_thread_t
*p_input
)
2824 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2826 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2830 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2832 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2838 /* Call demux_Demux until we have read enough data */
2839 if( demux_Control( in
->p_demux
, DEMUX_SET_NEXT_DEMUX_TIME
, i_time
) )
2844 if( demux_Control( in
->p_demux
, DEMUX_GET_TIME
, &i_stime
) )
2846 msg_Err( p_input
, "slave[%d] doesn't like "
2847 "DEMUX_GET_TIME -> EOF", i
);
2852 if( i_stime
>= i_time
)
2858 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2864 i_ret
= demux_Demux( in
->p_demux
);
2869 msg_Dbg( p_input
, "slave %d EOF", i
);
2875 static void SlaveSeek( input_thread_t
*p_input
)
2880 if( demux_Control( input_priv(p_input
)->master
->p_demux
, DEMUX_GET_TIME
, &i_time
) )
2882 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2886 for( i
= 0; i
< input_priv(p_input
)->i_slave
; i
++ )
2888 input_source_t
*in
= input_priv(p_input
)->slave
[i
];
2890 if( demux_Control( in
->p_demux
, DEMUX_SET_TIME
, i_time
, true ) )
2893 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2903 /*****************************************************************************
2905 *****************************************************************************/
2906 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
)
2908 static const struct { int i_meta
; const char *psz_name
; } p_list
[] = {
2909 { vlc_meta_Title
, "meta-title" },
2910 { vlc_meta_Artist
, "meta-artist" },
2911 { vlc_meta_Genre
, "meta-genre" },
2912 { vlc_meta_Copyright
, "meta-copyright" },
2913 { vlc_meta_Description
, "meta-description" },
2914 { vlc_meta_Date
, "meta-date" },
2915 { vlc_meta_URL
, "meta-url" },
2919 /* Get meta information from user */
2920 for( int i
= 0; p_list
[i
].psz_name
; i
++ )
2922 char *psz_string
= var_GetNonEmptyString( p_input
, p_list
[i
].psz_name
);
2926 EnsureUTF8( psz_string
);
2927 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, psz_string
);
2932 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
,
2933 const demux_t
***ppp_attachment_demux
,
2934 int i_new
, input_attachment_t
**pp_new
, const demux_t
*p_demux
)
2936 int i_attachment
= *pi_attachment
;
2939 input_attachment_t
**pp_att
= realloc( *ppp_attachment
,
2940 sizeof(*pp_att
) * ( i_attachment
+ i_new
) );
2941 if( likely(pp_att
) )
2943 *ppp_attachment
= pp_att
;
2944 const demux_t
**pp_attdmx
= realloc( *ppp_attachment_demux
,
2945 sizeof(*pp_attdmx
) * ( i_attachment
+ i_new
) );
2946 if( likely(pp_attdmx
) )
2948 *ppp_attachment_demux
= pp_attdmx
;
2950 for( i
= 0; i
< i_new
; i
++ )
2952 pp_att
[i_attachment
] = pp_new
[i
];
2953 pp_attdmx
[i_attachment
++] = p_demux
;
2956 *pi_attachment
= i_attachment
;
2962 /* on alloc errors */
2963 for( i
= 0; i
< i_new
; i
++ )
2964 vlc_input_attachment_Delete( pp_new
[i
] );
2968 /*****************************************************************************
2969 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
2970 * arturl and locking issue.
2971 *****************************************************************************/
2972 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
)
2974 vlc_meta_t
*p_meta
= vlc_meta_New();
2975 if( unlikely(p_meta
== NULL
) )
2978 demux_Control( p_demux
, DEMUX_GET_META
, p_meta
);
2980 /* If metadata changed, then the attachments might have changed.
2981 We need to update them in case they contain album art. */
2982 input_attachment_t
**attachment
;
2985 if( !demux_Control( p_demux
, DEMUX_GET_ATTACHMENTS
,
2986 &attachment
, &i_attachment
) )
2988 vlc_mutex_lock( &input_priv(p_input
)->p_item
->lock
);
2989 if( input_priv(p_input
)->i_attachment
> 0 )
2992 for( int i
= 0; i
< input_priv(p_input
)->i_attachment
; i
++ )
2994 if( input_priv(p_input
)->attachment_demux
[i
] == p_demux
)
2995 vlc_input_attachment_Delete( input_priv(p_input
)->attachment
[i
] );
2998 input_priv(p_input
)->attachment
[j
] = input_priv(p_input
)->attachment
[i
];
2999 input_priv(p_input
)->attachment_demux
[j
] = input_priv(p_input
)->attachment_demux
[i
];
3003 input_priv(p_input
)->i_attachment
= j
;
3005 AppendAttachment( &input_priv(p_input
)->i_attachment
, &input_priv(p_input
)->attachment
, &input_priv(p_input
)->attachment_demux
,
3006 i_attachment
, attachment
, p_demux
);
3007 vlc_mutex_unlock( &input_priv(p_input
)->p_item
->lock
);
3010 es_out_ControlSetMeta( input_priv(p_input
)->p_es_out
, p_meta
);
3011 vlc_meta_Delete( p_meta
);
3014 /*****************************************************************************
3015 * InputGetExtraFiles
3016 * Autodetect extra input list
3017 *****************************************************************************/
3018 static void InputGetExtraFilesPattern( input_thread_t
*p_input
,
3019 int *pi_list
, char ***pppsz_list
,
3020 const char *psz_path
,
3021 const char *psz_match
,
3022 const char *psz_format
,
3023 int i_start
, int i_stop
)
3027 TAB_INIT( i_list
, ppsz_list
);
3029 char *psz_base
= strdup( psz_path
);
3033 /* Remove the extension */
3034 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
3035 assert( psz_end
>= psz_base
);
3038 /* Try to list files */
3039 for( int i
= i_start
; i
<= i_stop
; i
++ )
3042 if( asprintf( &psz_probe
, psz_format
, psz_base
, i
) < 0 )
3045 char *filepath
= get_path( psz_probe
);
3048 if( filepath
== NULL
||
3049 vlc_stat( filepath
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
3056 msg_Dbg( p_input
, "Detected extra file `%s'", filepath
);
3058 char* psz_uri
= vlc_path2uri( filepath
, NULL
);
3060 TAB_APPEND( i_list
, ppsz_list
, psz_uri
);
3068 *pppsz_list
= ppsz_list
;
3071 static void InputGetExtraFiles( input_thread_t
*p_input
,
3072 int *pi_list
, char ***pppsz_list
,
3073 const char **ppsz_access
, const char *psz_path
)
3075 static const struct pattern
3077 const char *psz_access_force
;
3078 const char *psz_match
;
3079 const char *psz_format
;
3083 /* XXX the order is important */
3084 { "concat", ".001", "%s.%.3d", 2, 999 },
3085 { NULL
, ".part1.rar","%s.part%.1d.rar", 2, 9 },
3086 { NULL
, ".part01.rar","%s.part%.2d.rar", 2, 99, },
3087 { NULL
, ".part001.rar", "%s.part%.3d.rar", 2, 999 },
3088 { NULL
, ".rar", "%s.r%.2d", 0, 99 },
3091 TAB_INIT( *pi_list
, *pppsz_list
);
3093 if( ( **ppsz_access
&& strcmp( *ppsz_access
, "file" ) ) || !psz_path
)
3096 const size_t i_path
= strlen(psz_path
);
3098 for( size_t i
= 0; i
< ARRAY_SIZE( patterns
); ++i
)
3100 const struct pattern
* pat
= &patterns
[i
];
3101 const size_t i_ext
= strlen( pat
->psz_match
);
3103 if( i_path
< i_ext
)
3106 if( !strcmp( &psz_path
[i_path
-i_ext
], pat
->psz_match
) )
3108 InputGetExtraFilesPattern( p_input
, pi_list
, pppsz_list
, psz_path
,
3109 pat
->psz_match
, pat
->psz_format
, pat
->i_start
, pat
->i_stop
);
3111 if( *pi_list
> 0 && pat
->psz_access_force
)
3112 *ppsz_access
= pat
->psz_access_force
;
3119 static void input_ChangeState( input_thread_t
*p_input
, int i_state
)
3121 if( input_priv(p_input
)->i_state
== i_state
)
3124 input_priv(p_input
)->i_state
= i_state
;
3125 if( i_state
== ERROR_S
)
3126 input_item_SetErrorWhenReading( input_priv(p_input
)->p_item
, true );
3127 input_SendEventState( p_input
, i_state
);
3131 /*****************************************************************************
3132 * MRLSplit: parse the access, demux and url part of the
3133 * Media Resource Locator.
3134 *****************************************************************************/
3135 void input_SplitMRL( const char **access
, const char **demux
,
3136 const char **path
, const char **anchor
, char *buf
)
3140 /* Separate <path> from <access>[/<demux>]:// */
3141 p
= strstr( buf
, "://" );
3145 p
+= 3; /* skips "://" */
3148 /* Remove HTML anchor if present (not supported).
3149 * The hash symbol itself should be URI-encoded. */
3150 p
= strchr( p
, '#' );
3162 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
3165 /* Note: this is a valid non const pointer to "": */
3166 *path
= buf
+ strlen( buf
);
3169 /* Separate access from demux */
3170 p
= strchr( buf
, '/' );
3181 /* We really don't want module name substitution here! */
3188 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
3193 /* Look for the title */
3194 u
= strtoul( str
, &end
, 0 );
3195 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3198 /* Look for the chapter */
3202 u
= strtoul( str
, &end
, 0 );
3203 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
3213 /*****************************************************************************
3214 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
3217 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
3218 *****************************************************************************/
3219 static void MRLSections( const char *p
,
3220 int *pi_title_start
, int *pi_title_end
,
3221 int *pi_chapter_start
, int *pi_chapter_end
)
3223 *pi_title_start
= *pi_title_end
= *pi_chapter_start
= *pi_chapter_end
= -1;
3225 int title_start
, chapter_start
, title_end
, chapter_end
;
3231 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
3233 title_start
= chapter_start
= -1;
3236 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
3238 title_end
= chapter_end
= -1;
3240 if( *p
) /* syntax error */
3243 *pi_title_start
= title_start
;
3244 *pi_title_end
= title_end
;
3245 *pi_chapter_start
= chapter_start
;
3246 *pi_chapter_end
= chapter_end
;
3249 static int input_SlaveSourceAdd( input_thread_t
*p_input
,
3250 enum slave_type i_type
, const char *psz_uri
,
3255 const char *psz_forced_demux
;
3256 const bool b_can_fail
= i_flags
& SLAVE_ADD_CANFAIL
;
3257 const bool b_forced
= i_flags
& SLAVE_ADD_FORCED
;
3258 const bool b_set_time
= i_flags
& SLAVE_ADD_SET_TIME
;
3262 case SLAVE_TYPE_SPU
:
3264 psz_forced_demux
= "subtitle";
3266 case SLAVE_TYPE_AUDIO
:
3267 psz_es
= "audio-es";
3268 psz_forced_demux
= NULL
;
3271 vlc_assert_unreachable();
3275 var_Change( p_input
, psz_es
, VLC_VAR_CHOICESCOUNT
, &count
, NULL
);
3277 msg_Dbg( p_input
, "loading %s slave: %s (forced: %d)", psz_es
, psz_uri
,
3280 input_source_t
*p_source
= InputSourceNew( p_input
, psz_uri
,
3282 b_can_fail
|| psz_forced_demux
);
3284 if( psz_forced_demux
&& p_source
== NULL
)
3285 p_source
= InputSourceNew( p_input
, psz_uri
, NULL
, b_can_fail
);
3287 if( p_source
== NULL
)
3289 msg_Warn( p_input
, "failed to add %s as slave", psz_uri
);
3290 return VLC_EGENERIC
;
3293 if( i_type
== SLAVE_TYPE_AUDIO
)
3300 if( demux_Control( input_priv(p_input
)->master
->p_demux
,
3301 DEMUX_GET_TIME
, &i_time
) )
3303 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
3304 InputSourceDestroy( p_source
);
3305 return VLC_EGENERIC
;
3308 if( demux_Control( p_source
->p_demux
,
3309 DEMUX_SET_TIME
, i_time
, true ) )
3311 msg_Err( p_input
, "seek failed for new slave" );
3312 InputSourceDestroy( p_source
);
3313 return VLC_EGENERIC
;
3317 /* Get meta (access and demux) */
3318 InputUpdateMeta( p_input
, p_source
->p_demux
);
3321 TAB_APPEND( input_priv(p_input
)->i_slave
, input_priv(p_input
)->slave
, p_source
);
3329 if( var_Change( p_input
, psz_es
, VLC_VAR_GETCHOICES
, &list
, NULL
) )
3332 if( count
.i_int
== 0 )
3334 /* if it was first one, there is disable too */
3336 if( count
.i_int
< list
.p_list
->i_count
)
3338 const int i_id
= list
.p_list
->p_values
[count
.i_int
].i_int
;
3340 es_out_Control( input_priv(p_input
)->p_es_out_display
, ES_OUT_SET_ES_DEFAULT_BY_ID
, i_id
);
3341 es_out_Control( input_priv(p_input
)->p_es_out_display
, ES_OUT_SET_ES_BY_ID
, i_id
);
3343 var_FreeList( &list
, NULL
);
3348 static char *input_SubtitleFile2Uri( input_thread_t
*p_input
,
3349 const char *psz_subtitle
)
3351 /* if we are provided a subtitle.sub file,
3352 * see if we don't have a subtitle.idx and use it instead */
3353 char *psz_idxpath
= NULL
;
3354 char *psz_extension
= strrchr( psz_subtitle
, '.');
3355 if( psz_extension
&& strcmp( psz_extension
, ".sub" ) == 0 )
3357 psz_idxpath
= strdup( psz_subtitle
);
3362 psz_extension
= psz_extension
- psz_subtitle
+ psz_idxpath
;
3363 strcpy( psz_extension
, ".idx" );
3365 if( !vlc_stat( psz_idxpath
, &st
) && S_ISREG( st
.st_mode
) )
3367 msg_Dbg( p_input
, "using %s as subtitle file instead of %s",
3368 psz_idxpath
, psz_subtitle
);
3369 psz_subtitle
= psz_idxpath
;
3374 char *psz_uri
= vlc_path2uri( psz_subtitle
, NULL
);
3375 free( psz_idxpath
);
3380 /* TODO FIXME nearly the same logic that snapshot code */
3381 char *input_CreateFilename(input_thread_t
*input
, const char *dir
,
3382 const char *filenamefmt
, const char *ext
)
3385 char *filename
= str_format(input
, filenamefmt
);
3386 if (unlikely(filename
== NULL
))
3389 filename_sanitize(filename
);
3392 ? asprintf(&path
, "%s"DIR_SEP
"%s.%s", dir
, filename
, ext
)
3393 : asprintf(&path
, "%s"DIR_SEP
"%s", dir
, filename
)) < 0)