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>
40 #include "input_internal.h"
43 #include "es_out_timeshift.h"
51 #include <vlc_dialog.h>
53 #include <vlc_charset.h>
55 #include <vlc_strings.h>
56 #include <vlc_modules.h>
58 /*****************************************************************************
60 *****************************************************************************/
61 static void *Run ( void * );
63 static input_thread_t
* Create ( vlc_object_t
*, input_item_t
*,
64 const char *, bool, input_resource_t
* );
65 static int Init ( input_thread_t
*p_input
);
66 static void End ( input_thread_t
*p_input
);
67 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
);
69 static inline int ControlPop( input_thread_t
*, int *, vlc_value_t
*, mtime_t i_deadline
, bool b_postpone_seek
);
70 static void ControlRelease( int i_type
, vlc_value_t val
);
71 static bool ControlIsSeekRequest( int i_type
);
72 static bool Control( input_thread_t
*, int, vlc_value_t
);
73 static void ControlPause( input_thread_t
*, mtime_t
);
75 static int UpdateTitleSeekpointFromDemux( input_thread_t
* );
76 static void UpdateGenericFromDemux( input_thread_t
* );
77 static void UpdateTitleListfromDemux( input_thread_t
* );
79 static void MRLSections( const char *, int *, int *, int *, int *);
81 static input_source_t
*InputSourceNew( input_thread_t
*);
82 static int InputSourceInit( input_thread_t
*, input_source_t
*,
83 const char *, const char *psz_forced_demux
,
85 static void InputSourceClean( input_source_t
* );
86 static void InputSourceMeta( input_thread_t
*, input_source_t
*, vlc_meta_t
* );
89 //static void InputGetAttachments( input_thread_t *, input_source_t * );
90 static void SlaveDemux( input_thread_t
*p_input
);
91 static void SlaveSeek( input_thread_t
*p_input
);
93 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
);
94 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
);
95 static void InputGetExtraFiles( input_thread_t
*p_input
,
96 int *pi_list
, char ***pppsz_list
,
97 const char *psz_access
, const char *psz_path
);
99 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
, demux_t
***ppp_attachment_demux
,
100 int i_new
, input_attachment_t
**pp_new
, demux_t
*p_demux
);
108 static void input_SubtitleAdd( input_thread_t
*, const char *, unsigned );
109 static void input_SubtitleFileAdd( input_thread_t
*, char *, unsigned );
110 static void input_ChangeState( input_thread_t
*p_input
, int i_state
); /* TODO fix name */
114 * Create a new input_thread_t.
116 * You need to call input_Start on it when you are done
117 * adding callback on the variables/events you want to monitor.
119 * \param p_parent a vlc_object
120 * \param p_item an input item
121 * \param psz_log an optional prefix for this input logs
122 * \param p_resource an optional input ressource
123 * \return a pointer to the spawned input thread
125 input_thread_t
*input_Create( vlc_object_t
*p_parent
,
126 input_item_t
*p_item
,
127 const char *psz_log
, input_resource_t
*p_resource
)
129 return Create( p_parent
, p_item
, psz_log
, false, p_resource
);
134 * Initialize an input thread and run it until it stops by itself.
136 * \param p_parent a vlc_object
137 * \param p_item an input item
138 * \return an error code, VLC_SUCCESS on success
140 int input_Read( vlc_object_t
*p_parent
, input_item_t
*p_item
)
142 input_thread_t
*p_input
= Create( p_parent
, p_item
, NULL
, false, NULL
);
146 if( !Init( p_input
) )
148 MainLoop( p_input
, false );
152 vlc_object_release( p_input
);
157 * Initialize an input and initialize it to preparse the item
158 * This function is blocking. It will only accept parsing regular files.
160 * \param p_parent a vlc_object_t
161 * \param p_item an input item
162 * \return VLC_SUCCESS or an error
164 int input_Preparse( vlc_object_t
*p_parent
, input_item_t
*p_item
)
166 input_thread_t
*p_input
;
168 /* Allocate descriptor */
169 p_input
= Create( p_parent
, p_item
, NULL
, true, NULL
);
173 if( !Init( p_input
) ) {
174 /* if the demux is a playlist, call Mainloop that will call
175 * demux_Demux in order to fetch sub items */
176 bool b_is_playlist
= false;
178 if ( input_item_ShouldPreparseSubItems( p_item
)
179 && demux_Control( p_input
->p
->input
.p_demux
,
182 b_is_playlist
= false;
184 MainLoop( p_input
, false );
188 vlc_object_release( p_input
);
194 * Start a input_thread_t created by input_Create.
196 * You must not start an already running input_thread_t.
198 * \param the input thread to start
200 int input_Start( input_thread_t
*p_input
)
202 assert( !p_input
->p
->is_running
);
203 /* Create thread and wait for its readiness. */
204 p_input
->p
->is_running
= !vlc_clone( &p_input
->p
->thread
,
205 Run
, p_input
, VLC_THREAD_PRIORITY_INPUT
);
206 if( !p_input
->p
->is_running
)
208 input_ChangeState( p_input
, ERROR_S
);
209 msg_Err( p_input
, "cannot create input thread" );
216 * Request a running input thread to stop and die
218 * \param p_input the input thread to stop
220 void input_Stop( input_thread_t
*p_input
)
222 input_thread_private_t
*sys
= p_input
->p
;
224 vlc_mutex_lock( &sys
->lock_control
);
225 /* Discard all pending controls */
226 for( int i
= 0; i
< sys
->i_control
; i
++ )
228 input_control_t
*ctrl
= &sys
->control
[i
];
229 ControlRelease( ctrl
->i_type
, ctrl
->val
);
232 sys
->is_stopped
= true;
233 vlc_cond_signal( &sys
->wait_control
);
234 vlc_mutex_unlock( &sys
->lock_control
);
235 vlc_interrupt_kill( &sys
->interrupt
);
241 * It does not call input_Stop itself.
243 void input_Close( input_thread_t
*p_input
)
245 if( p_input
->p
->is_running
)
246 vlc_join( p_input
->p
->thread
, NULL
);
247 vlc_interrupt_deinit( &p_input
->p
->interrupt
);
248 vlc_object_release( p_input
);
252 * Input destructor (called when the object's refcount reaches 0).
254 static void input_Destructor( vlc_object_t
*obj
)
256 input_thread_t
*p_input
= (input_thread_t
*)obj
;
258 char * psz_name
= input_item_GetName( p_input
->p
->p_item
);
259 msg_Dbg( p_input
, "Destroying the input for '%s'", psz_name
);
263 if( p_input
->p
->p_es_out_display
)
264 es_out_Delete( p_input
->p
->p_es_out_display
);
266 if( p_input
->p
->p_resource
)
267 input_resource_Release( p_input
->p
->p_resource
);
268 if( p_input
->p
->p_resource_private
)
269 input_resource_Release( p_input
->p
->p_resource_private
);
271 vlc_gc_decref( p_input
->p
->p_item
);
273 vlc_mutex_destroy( &p_input
->p
->counters
.counters_lock
);
275 for( int i
= 0; i
< p_input
->p
->i_control
; i
++ )
277 input_control_t
*p_ctrl
= &p_input
->p
->control
[i
];
278 ControlRelease( p_ctrl
->i_type
, p_ctrl
->val
);
281 vlc_cond_destroy( &p_input
->p
->wait_control
);
282 vlc_mutex_destroy( &p_input
->p
->lock_control
);
287 * Get the item from an input thread
288 * FIXME it does not increase ref count of the item.
289 * if it is used after p_input is destroyed nothing prevent it from
292 input_item_t
*input_GetItem( input_thread_t
*p_input
)
294 assert( p_input
&& p_input
->p
);
295 return p_input
->p
->p_item
;
298 /*****************************************************************************
299 * This function creates a new input, and returns a pointer
300 * to its description. On error, it returns NULL.
302 * XXX Do not forget to update vlc_input.h if you add new variables.
303 *****************************************************************************/
304 static input_thread_t
*Create( vlc_object_t
*p_parent
, input_item_t
*p_item
,
305 const char *psz_header
, bool b_quick
,
306 input_resource_t
*p_resource
)
308 input_thread_t
*p_input
= NULL
; /* thread descriptor */
310 /* Allocate descriptor */
311 p_input
= vlc_custom_create( p_parent
, sizeof( *p_input
), "input" );
312 if( p_input
== NULL
)
315 /* Construct a nice name for the input timer */
316 char psz_timer_name
[255];
317 char * psz_name
= input_item_GetName( p_item
);
318 snprintf( psz_timer_name
, sizeof(psz_timer_name
),
319 "input launching for '%s'", psz_name
);
321 msg_Dbg( p_input
, "Creating an input for '%s'", psz_name
);
325 p_input
->p
= calloc( 1, sizeof( input_thread_private_t
) );
328 vlc_object_release( p_input
);
332 /* Parse input options */
333 input_item_ApplyOptions( VLC_OBJECT(p_input
), p_item
);
335 p_input
->b_preparsing
= b_quick
;
336 p_input
->psz_header
= psz_header
? strdup( psz_header
) : NULL
;
338 /* Init Common fields */
339 p_input
->p
->b_can_pace_control
= true;
340 p_input
->p
->i_start
= 0;
341 p_input
->p
->i_time
= 0;
342 p_input
->p
->i_stop
= 0;
343 p_input
->p
->i_run
= 0;
344 p_input
->p
->i_title
= 0;
345 p_input
->p
->title
= NULL
;
346 p_input
->p
->i_title_offset
= p_input
->p
->i_seekpoint_offset
= 0;
347 p_input
->p
->i_state
= INIT_S
;
348 p_input
->p
->is_running
= false;
349 p_input
->p
->is_stopped
= false;
350 p_input
->p
->b_recording
= false;
351 p_input
->p
->i_rate
= INPUT_RATE_DEFAULT
;
352 memset( &p_input
->p
->bookmark
, 0, sizeof(p_input
->p
->bookmark
) );
353 TAB_INIT( p_input
->p
->i_bookmark
, p_input
->p
->pp_bookmark
);
354 TAB_INIT( p_input
->p
->i_attachment
, p_input
->p
->attachment
);
355 p_input
->p
->attachment_demux
= NULL
;
356 p_input
->p
->p_sout
= NULL
;
357 p_input
->p
->b_out_pace_control
= false;
359 vlc_gc_incref( p_item
); /* Released in Destructor() */
360 p_input
->p
->p_item
= p_item
;
362 /* Init Input fields */
363 p_input
->p
->input
.p_demux
= NULL
;
364 p_input
->p
->input
.b_title_demux
= false;
365 p_input
->p
->input
.i_title
= 0;
366 p_input
->p
->input
.title
= NULL
;
367 p_input
->p
->input
.i_title_offset
= p_input
->p
->input
.i_seekpoint_offset
= 0;
368 p_input
->p
->input
.b_can_pace_control
= true;
369 p_input
->p
->input
.b_can_rate_control
= true;
370 p_input
->p
->input
.b_rescale_ts
= true;
371 p_input
->p
->input
.b_eof
= false;
373 vlc_mutex_lock( &p_item
->lock
);
375 if( !p_item
->p_stats
)
376 p_item
->p_stats
= stats_NewInputStats( p_input
);
378 /* setup the preparse depth of the item
379 * if we are preparsing, use the i_preparse_depth of the parent item */
380 if( !p_input
->b_preparsing
)
382 char *psz_rec
= var_InheritString( p_parent
, "recursive" );
384 if( psz_rec
!= NULL
)
386 if ( !strcasecmp( psz_rec
, "none" ) )
387 p_item
->i_preparse_depth
= 0;
388 else if ( !strcasecmp( psz_rec
, "collapse" ) )
389 p_item
->i_preparse_depth
= 1;
391 p_item
->i_preparse_depth
= -1; /* default is expand */
394 p_item
->i_preparse_depth
= -1;
397 vlc_mutex_unlock( &p_item
->lock
);
400 p_input
->p
->i_slave
= 0;
401 p_input
->p
->slave
= NULL
;
406 p_input
->p
->p_resource_private
= NULL
;
407 p_input
->p
->p_resource
= input_resource_Hold( p_resource
);
411 p_input
->p
->p_resource_private
= input_resource_New( VLC_OBJECT( p_input
) );
412 p_input
->p
->p_resource
= input_resource_Hold( p_input
->p
->p_resource_private
);
414 input_resource_SetInput( p_input
->p
->p_resource
, p_input
);
416 /* Init control buffer */
417 vlc_mutex_init( &p_input
->p
->lock_control
);
418 vlc_cond_init( &p_input
->p
->wait_control
);
419 p_input
->p
->i_control
= 0;
420 vlc_interrupt_init(&p_input
->p
->interrupt
);
422 /* Create Object Variables for private use only */
423 input_ConfigVarInit( p_input
);
425 /* Create Objects variables for public Get and Set */
426 input_ControlVarInit( p_input
);
429 if( !p_input
->b_preparsing
)
431 char *psz_bookmarks
= var_GetNonEmptyString( p_input
, "bookmarks" );
434 /* FIXME: have a common cfg parsing routine used by sout and others */
435 char *psz_parser
, *psz_start
, *psz_end
;
436 psz_parser
= psz_bookmarks
;
437 while( (psz_start
= strchr( psz_parser
, '{' ) ) )
439 seekpoint_t
*p_seekpoint
;
442 psz_end
= strchr( psz_start
, '}' );
443 if( !psz_end
) break;
444 psz_parser
= psz_end
+ 1;
445 backup
= *psz_parser
;
449 p_seekpoint
= vlc_seekpoint_New();
450 while( (psz_end
= strchr( psz_start
, ',' ) ) )
453 if( !strncmp( psz_start
, "name=", 5 ) )
455 p_seekpoint
->psz_name
= strdup(psz_start
+ 5);
457 else if( !strncmp( psz_start
, "bytes=", 6 ) )
459 p_seekpoint
->i_byte_offset
= atoll(psz_start
+ 6);
461 else if( !strncmp( psz_start
, "time=", 5 ) )
463 p_seekpoint
->i_time_offset
= atoll(psz_start
+ 5) *
466 psz_start
= psz_end
+ 1;
468 msg_Dbg( p_input
, "adding bookmark: %s, bytes=%"PRId64
", time=%"PRId64
,
469 p_seekpoint
->psz_name
, p_seekpoint
->i_byte_offset
,
470 p_seekpoint
->i_time_offset
);
471 input_Control( p_input
, INPUT_ADD_BOOKMARK
, p_seekpoint
);
472 vlc_seekpoint_Delete( p_seekpoint
);
473 *psz_parser
= backup
;
475 free( psz_bookmarks
);
479 /* Remove 'Now playing' info as it is probably outdated */
480 input_item_SetNowPlaying( p_item
, NULL
);
481 input_item_SetESNowPlaying( p_item
, NULL
);
482 input_SendEventMeta( p_input
);
485 if( p_input
->b_preparsing
)
486 p_input
->i_flags
|= OBJECT_FLAGS_QUIET
| OBJECT_FLAGS_NOINTERACT
;
488 /* Make sure the interaction option is honored */
489 if( !var_InheritBool( p_input
, "interact" ) )
490 p_input
->i_flags
|= OBJECT_FLAGS_NOINTERACT
;
493 memset( &p_input
->p
->counters
, 0, sizeof( p_input
->p
->counters
) );
494 vlc_mutex_init( &p_input
->p
->counters
.counters_lock
);
496 p_input
->p
->p_es_out_display
= input_EsOutNew( p_input
, p_input
->p
->i_rate
);
497 p_input
->p
->p_es_out
= NULL
;
499 /* Set the destructor when we are sure we are initialized */
500 vlc_object_set_destructor( p_input
, input_Destructor
);
505 /*****************************************************************************
506 * Run: main thread loop
507 * This is the "normal" thread that spawns the input processing chain,
508 * reads the stream, cleans up and waits
509 *****************************************************************************/
510 static void *Run( void *obj
)
512 input_thread_t
*p_input
= (input_thread_t
*)obj
;
514 vlc_interrupt_set(&p_input
->p
->interrupt
);
516 if( !Init( p_input
) )
518 MainLoop( p_input
, true ); /* FIXME it can be wrong (like with VLM) */
524 input_SendEventDead( p_input
);
528 bool input_Stopped( input_thread_t
*input
)
530 input_thread_private_t
*sys
= input
->p
;
533 vlc_mutex_lock( &sys
->lock_control
);
534 ret
= sys
->is_stopped
;
535 vlc_mutex_unlock( &sys
->lock_control
);
539 /*****************************************************************************
540 * Main loop: Fill buffers from access, and demux
541 *****************************************************************************/
545 * It asks the demuxer to demux some data
547 static void MainLoopDemux( input_thread_t
*p_input
, bool *pb_changed
, mtime_t i_start_mdate
)
553 if( ( p_input
->p
->i_stop
> 0 && p_input
->p
->i_time
>= p_input
->p
->i_stop
) ||
554 ( p_input
->p
->i_run
> 0 && i_start_mdate
+p_input
->p
->i_run
< mdate() ) )
557 i_ret
= demux_Demux( p_input
->p
->input
.p_demux
);
561 if( p_input
->p
->input
.p_demux
->info
.i_update
)
563 if( p_input
->p
->input
.p_demux
->info
.i_update
& INPUT_UPDATE_TITLE_LIST
)
565 UpdateTitleListfromDemux( p_input
);
566 p_input
->p
->input
.p_demux
->info
.i_update
&= ~INPUT_UPDATE_TITLE_LIST
;
568 if( p_input
->p
->input
.b_title_demux
)
570 i_ret
= UpdateTitleSeekpointFromDemux( p_input
);
573 UpdateGenericFromDemux( p_input
);
577 if( i_ret
== 0 ) /* EOF */
579 msg_Dbg( p_input
, "EOF reached" );
580 p_input
->p
->input
.b_eof
= true;
581 es_out_Eos(p_input
->p
->p_es_out
);
585 input_ChangeState( p_input
, ERROR_S
);
588 if( i_ret
> 0 && p_input
->p
->i_slave
> 0 )
589 SlaveDemux( p_input
);
592 static int MainLoopTryRepeat( input_thread_t
*p_input
, mtime_t
*pi_start_mdate
)
594 int i_repeat
= var_GetInteger( p_input
, "input-repeat" );
600 msg_Dbg( p_input
, "repeating the same input (%d)", i_repeat
);
604 var_SetInteger( p_input
, "input-repeat", i_repeat
);
607 /* Seek to start title/seekpoint */
608 val
.i_int
= p_input
->p
->input
.i_title_start
-
609 p_input
->p
->input
.i_title_offset
;
610 if( val
.i_int
< 0 || val
.i_int
>= p_input
->p
->input
.i_title
)
612 input_ControlPush( p_input
,
613 INPUT_CONTROL_SET_TITLE
, &val
);
615 val
.i_int
= p_input
->p
->input
.i_seekpoint_start
-
616 p_input
->p
->input
.i_seekpoint_offset
;
617 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
618 input_ControlPush( p_input
,
619 INPUT_CONTROL_SET_SEEKPOINT
, &val
);
621 /* Seek to start position */
622 if( p_input
->p
->i_start
> 0 )
624 val
.i_int
= p_input
->p
->i_start
;
625 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &val
);
630 input_ControlPush( p_input
, INPUT_CONTROL_SET_POSITION
, &val
);
634 *pi_start_mdate
= mdate();
639 * Update timing infos and statistics.
641 static void MainLoopStatistics( input_thread_t
*p_input
)
643 double f_position
= 0.0;
645 mtime_t i_length
= 0;
647 /* update input status variables */
648 if( demux_Control( p_input
->p
->input
.p_demux
,
649 DEMUX_GET_POSITION
, &f_position
) )
652 if( demux_Control( p_input
->p
->input
.p_demux
,
653 DEMUX_GET_TIME
, &i_time
) )
655 p_input
->p
->i_time
= i_time
;
657 if( demux_Control( p_input
->p
->input
.p_demux
,
658 DEMUX_GET_LENGTH
, &i_length
) )
661 es_out_SetTimes( p_input
->p
->p_es_out
, f_position
, i_time
, i_length
);
663 /* update current bookmark */
664 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
665 p_input
->p
->bookmark
.i_time_offset
= i_time
;
666 p_input
->p
->bookmark
.i_byte_offset
= -1;
667 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
669 stats_ComputeInputStats( p_input
, p_input
->p
->p_item
->p_stats
);
670 input_SendEventStatistics( p_input
);
675 * The main input loop.
677 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
)
679 mtime_t i_start_mdate
= mdate();
680 mtime_t i_intf_update
= 0;
681 mtime_t i_last_seek_mdate
= 0;
683 if( b_interactive
&& var_InheritBool( p_input
, "start-paused" ) )
684 ControlPause( p_input
, i_start_mdate
);
686 bool b_pause_after_eof
= b_interactive
&&
687 var_InheritBool( p_input
, "play-and-pause" );
689 while( !input_Stopped( p_input
) && p_input
->p
->i_state
!= ERROR_S
)
691 mtime_t i_wakeup
= -1;
692 bool b_paused
= p_input
->p
->i_state
== PAUSE_S
;
693 /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
694 * is paused -> this may cause problem with some of them
695 * The same problem can be seen when seeking while paused */
697 b_paused
= !es_out_GetBuffering( p_input
->p
->p_es_out
) || p_input
->p
->input
.b_eof
;
701 if( !p_input
->p
->input
.b_eof
)
703 bool b_force_update
= false;
705 MainLoopDemux( p_input
, &b_force_update
, i_start_mdate
);
706 i_wakeup
= es_out_GetWakeup( p_input
->p
->p_es_out
);
711 else if( !es_out_GetEmpty( p_input
->p
->p_es_out
) )
713 msg_Dbg( p_input
, "waiting decoder fifos to empty" );
714 i_wakeup
= mdate() + INPUT_IDLE_SLEEP
;
716 /* Pause after eof only if the input is pausable.
717 * This way we won't trigger timeshifting for nothing */
718 else if( b_pause_after_eof
&& p_input
->p
->b_can_pause
)
720 vlc_value_t val
= { .i_int
= PAUSE_S
};
722 msg_Dbg( p_input
, "pausing at EOF (pause after each)");
723 Control( p_input
, INPUT_CONTROL_SET_STATE
, val
);
729 if( MainLoopTryRepeat( p_input
, &i_start_mdate
) )
733 /* Update interface and statistics */
734 mtime_t now
= mdate();
735 if( now
>= i_intf_update
)
737 MainLoopStatistics( p_input
);
738 i_intf_update
= now
+ INT64_C(250000);
745 mtime_t i_deadline
= i_wakeup
;
747 /* Postpone seeking until ES buffering is complete or at most
749 bool b_postpone
= es_out_GetBuffering( p_input
->p
->p_es_out
)
750 && !p_input
->p
->input
.b_eof
;
753 mtime_t now
= mdate();
755 /* Recheck ES buffer level every 20 ms when seeking */
756 if( now
< i_last_seek_mdate
+ INT64_C(125000)
757 && (i_deadline
< 0 || i_deadline
> now
+ INT64_C(20000)) )
758 i_deadline
= now
+ INT64_C(20000);
766 if( ControlPop( p_input
, &i_type
, &val
, i_deadline
, b_postpone
) )
770 break; /* Wake-up time reached */
774 msg_Dbg( p_input
, "control type=%d", i_type
);
776 if( Control( p_input
, i_type
, val
) )
778 if( ControlIsSeekRequest( i_type
) )
779 i_last_seek_mdate
= mdate();
783 /* Update the wakeup time */
785 i_wakeup
= es_out_GetWakeup( p_input
->p
->p_es_out
);
790 static void InitStatistics( input_thread_t
* p_input
)
792 if( p_input
->b_preparsing
) return;
794 /* Prepare statistics */
795 #define INIT_COUNTER( c, compute ) p_input->p->counters.p_##c = \
796 stats_CounterCreate( STATS_##compute);
797 if( libvlc_stats( p_input
) )
799 INIT_COUNTER( read_bytes
, COUNTER
);
800 INIT_COUNTER( read_packets
, COUNTER
);
801 INIT_COUNTER( demux_read
, COUNTER
);
802 INIT_COUNTER( input_bitrate
, DERIVATIVE
);
803 INIT_COUNTER( demux_bitrate
, DERIVATIVE
);
804 INIT_COUNTER( demux_corrupted
, COUNTER
);
805 INIT_COUNTER( demux_discontinuity
, COUNTER
);
806 INIT_COUNTER( played_abuffers
, COUNTER
);
807 INIT_COUNTER( lost_abuffers
, COUNTER
);
808 INIT_COUNTER( displayed_pictures
, COUNTER
);
809 INIT_COUNTER( lost_pictures
, COUNTER
);
810 INIT_COUNTER( decoded_audio
, COUNTER
);
811 INIT_COUNTER( decoded_video
, COUNTER
);
812 INIT_COUNTER( decoded_sub
, COUNTER
);
813 p_input
->p
->counters
.p_sout_send_bitrate
= NULL
;
814 p_input
->p
->counters
.p_sout_sent_packets
= NULL
;
815 p_input
->p
->counters
.p_sout_sent_bytes
= NULL
;
820 static int InitSout( input_thread_t
* p_input
)
822 if( p_input
->b_preparsing
)
825 /* Find a usable sout and attach it to p_input */
826 char *psz
= var_GetNonEmptyString( p_input
, "sout" );
827 if( psz
&& strncasecmp( p_input
->p
->p_item
->psz_uri
, "vlc:", 4 ) )
829 p_input
->p
->p_sout
= input_resource_RequestSout( p_input
->p
->p_resource
, NULL
, psz
);
830 if( !p_input
->p
->p_sout
)
832 input_ChangeState( p_input
, ERROR_S
);
833 msg_Err( p_input
, "cannot start stream output instance, " \
838 if( libvlc_stats( p_input
) )
840 INIT_COUNTER( sout_sent_packets
, COUNTER
);
841 INIT_COUNTER( sout_sent_bytes
, COUNTER
);
842 INIT_COUNTER( sout_send_bitrate
, DERIVATIVE
);
847 input_resource_RequestSout( p_input
->p
->p_resource
, NULL
, NULL
);
855 static void InitTitle( input_thread_t
* p_input
)
857 input_source_t
*p_master
= &p_input
->p
->input
;
859 if( p_input
->b_preparsing
)
862 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
863 /* Create global title (from master) */
864 p_input
->p
->i_title
= p_master
->i_title
;
865 p_input
->p
->title
= p_master
->title
;
866 p_input
->p
->i_title_offset
= p_master
->i_title_offset
;
867 p_input
->p
->i_seekpoint_offset
= p_master
->i_seekpoint_offset
;
868 if( p_input
->p
->i_title
> 0 )
870 /* Setup variables */
871 input_ControlVarNavigation( p_input
);
872 input_SendEventTitle( p_input
, 0 );
876 p_input
->p
->b_can_pace_control
= p_master
->b_can_pace_control
;
877 p_input
->p
->b_can_pause
= p_master
->b_can_pause
;
878 p_input
->p
->b_can_rate_control
= p_master
->b_can_rate_control
;
879 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
882 static void StartTitle( input_thread_t
* p_input
)
886 /* Start title/chapter */
887 val
.i_int
= p_input
->p
->input
.i_title_start
-
888 p_input
->p
->input
.i_title_offset
;
889 if( val
.i_int
> 0 && val
.i_int
< p_input
->p
->input
.i_title
)
890 input_ControlPush( p_input
, INPUT_CONTROL_SET_TITLE
, &val
);
892 val
.i_int
= p_input
->p
->input
.i_seekpoint_start
-
893 p_input
->p
->input
.i_seekpoint_offset
;
894 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
895 input_ControlPush( p_input
, INPUT_CONTROL_SET_SEEKPOINT
, &val
);
897 /* Start/stop/run time */
898 p_input
->p
->i_start
= llroundf(1000000.f
899 * var_GetFloat( p_input
, "start-time" ));
900 p_input
->p
->i_stop
= llroundf(1000000.f
901 * var_GetFloat( p_input
, "stop-time" ));
902 p_input
->p
->i_run
= llroundf(1000000.f
903 * var_GetFloat( p_input
, "run-time" ));
904 if( p_input
->p
->i_run
< 0 )
906 msg_Warn( p_input
, "invalid run-time ignored" );
907 p_input
->p
->i_run
= 0;
910 if( p_input
->p
->i_start
> 0 )
914 msg_Dbg( p_input
, "starting at time: %ds",
915 (int)( p_input
->p
->i_start
/ CLOCK_FREQ
) );
917 s
.i_int
= p_input
->p
->i_start
;
918 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &s
);
920 if( p_input
->p
->i_stop
> 0 && p_input
->p
->i_stop
<= p_input
->p
->i_start
)
922 msg_Warn( p_input
, "invalid stop-time ignored" );
923 p_input
->p
->i_stop
= 0;
925 p_input
->p
->b_fast_seek
= var_GetBool( p_input
, "input-fast-seek" );
928 static void LoadSubtitles( input_thread_t
*p_input
)
931 /* Get fps and set it if not already set */
932 const float f_fps
= p_input
->p
->f_fps
;
935 var_Create( p_input
, "sub-original-fps", VLC_VAR_FLOAT
);
936 var_SetFloat( p_input
, "sub-original-fps", f_fps
);
938 float f_requested_fps
= var_CreateGetFloat( p_input
, "sub-fps" );
939 if( f_requested_fps
!= f_fps
)
941 var_Create( p_input
, "sub-fps", VLC_VAR_FLOAT
|
943 var_SetFloat( p_input
, "sub-fps", f_requested_fps
);
947 const int i_delay
= var_CreateGetInteger( p_input
, "sub-delay" );
949 var_SetInteger( p_input
, "spu-delay", (mtime_t
)i_delay
* 100000 );
951 /* Look for and add subtitle files */
952 unsigned i_flags
= SUB_FORCED
;
954 char *psz_subtitle
= var_GetNonEmptyString( p_input
, "sub-file" );
955 if( psz_subtitle
!= NULL
)
957 msg_Dbg( p_input
, "forced subtitle: %s", psz_subtitle
);
958 input_SubtitleFileAdd( p_input
, psz_subtitle
, i_flags
);
959 i_flags
= SUB_NOFLAG
;
962 if( var_GetBool( p_input
, "sub-autodetect-file" ) )
964 char *psz_autopath
= var_GetNonEmptyString( p_input
, "sub-autodetect-path" );
965 char **ppsz_subs
= subtitles_Detect( p_input
, psz_autopath
,
966 p_input
->p
->p_item
->psz_uri
);
967 free( psz_autopath
);
969 for( int i
= 0; ppsz_subs
&& ppsz_subs
[i
]; i
++ )
971 if( !psz_subtitle
|| strcmp( psz_subtitle
, ppsz_subs
[i
] ) )
973 i_flags
|= SUB_CANFAIL
;
974 input_SubtitleFileAdd( p_input
, ppsz_subs
[i
], i_flags
);
975 i_flags
= SUB_NOFLAG
;
978 free( ppsz_subs
[i
] );
982 free( psz_subtitle
);
984 /* Load subtitles from attachments */
985 int i_attachment
= 0;
986 input_attachment_t
**pp_attachment
= NULL
;
988 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
989 for( int i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
991 const input_attachment_t
*a
= p_input
->p
->attachment
[i
];
992 if( !strcmp( a
->psz_mime
, "application/x-srt" ) )
993 TAB_APPEND( i_attachment
, pp_attachment
,
994 vlc_input_attachment_New( a
->psz_name
, NULL
,
995 a
->psz_description
, NULL
, 0 ) );
997 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
999 if( i_attachment
> 0 )
1000 var_Create( p_input
, "sub-description", VLC_VAR_STRING
);
1001 for( int i
= 0; i
< i_attachment
; i
++ )
1003 input_attachment_t
*a
= pp_attachment
[i
];
1007 if( a
->psz_name
[i
] &&
1008 asprintf( &psz_mrl
, "attachment://%s", a
->psz_name
) >= 0 )
1010 var_SetString( p_input
, "sub-description", a
->psz_description
? a
->psz_description
: "");
1012 input_SubtitleAdd( p_input
, psz_mrl
, i_flags
);
1014 i_flags
= SUB_NOFLAG
;
1017 vlc_input_attachment_Delete( a
);
1019 free( pp_attachment
);
1020 if( i_attachment
> 0 )
1021 var_Destroy( p_input
, "sub-description" );
1024 static void LoadSlaves( input_thread_t
*p_input
)
1026 char *psz
= var_GetNonEmptyString( p_input
, "input-slave" );
1030 char *psz_org
= psz
;
1031 while( psz
&& *psz
)
1033 while( *psz
== ' ' || *psz
== '#' )
1036 char *psz_delim
= strchr( psz
, '#' );
1038 *psz_delim
++ = '\0';
1043 char *uri
= strstr(psz
, "://")
1044 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
1048 msg_Dbg( p_input
, "adding slave input '%s'", uri
);
1050 input_source_t
*p_slave
= InputSourceNew( p_input
);
1051 if( p_slave
&& !InputSourceInit( p_input
, p_slave
, uri
, NULL
, false ) )
1052 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, p_slave
);
1060 static void UpdatePtsDelay( input_thread_t
*p_input
)
1062 input_thread_private_t
*p_sys
= p_input
->p
;
1064 /* Get max pts delay from input source */
1065 mtime_t i_pts_delay
= p_sys
->input
.i_pts_delay
;
1066 for( int i
= 0; i
< p_sys
->i_slave
; i
++ )
1067 i_pts_delay
= __MAX( i_pts_delay
, p_sys
->slave
[i
]->i_pts_delay
);
1069 if( i_pts_delay
< 0 )
1072 /* Take care of audio/spu delay */
1073 const mtime_t i_audio_delay
= var_GetInteger( p_input
, "audio-delay" );
1074 const mtime_t i_spu_delay
= var_GetInteger( p_input
, "spu-delay" );
1075 const mtime_t i_extra_delay
= __MIN( i_audio_delay
, i_spu_delay
);
1076 if( i_extra_delay
< 0 )
1077 i_pts_delay
-= i_extra_delay
;
1079 /* Update cr_average depending on the caching */
1080 const int i_cr_average
= var_GetInteger( p_input
, "cr-average" ) * i_pts_delay
/ DEFAULT_PTS_DELAY
;
1083 es_out_SetDelay( p_input
->p
->p_es_out_display
, AUDIO_ES
, i_audio_delay
);
1084 es_out_SetDelay( p_input
->p
->p_es_out_display
, SPU_ES
, i_spu_delay
);
1085 es_out_SetJitter( p_input
->p
->p_es_out
, i_pts_delay
, 0, i_cr_average
);
1088 static void InitPrograms( input_thread_t
* p_input
)
1093 /* Compute correct pts_delay */
1094 UpdatePtsDelay( p_input
);
1097 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1098 if( p_input
->p
->p_sout
)
1102 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1106 TAB_INIT( list
.i_count
, list
.p_values
);
1107 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1109 prgm
= strtok_r( NULL
, ",", &buf
) )
1111 vlc_value_t val
= { .i_int
= atoi( prgm
) };
1112 INSERT_ELEM( list
.p_values
, list
.i_count
, list
.i_count
, val
);
1115 if( list
.i_count
> 0 )
1116 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1117 /* Note : we should remove the "program" callback. */
1121 else if( var_GetBool( p_input
, "sout-all" ) )
1123 i_es_out_mode
= ES_OUT_MODE_ALL
;
1126 es_out_SetMode( p_input
->p
->p_es_out
, i_es_out_mode
);
1128 /* Inform the demuxer about waited group (needed only for DVB) */
1129 if( i_es_out_mode
== ES_OUT_MODE_ALL
)
1131 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, -1, NULL
);
1133 else if( i_es_out_mode
== ES_OUT_MODE_PARTIAL
)
1135 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, -1,
1137 TAB_CLEAN( list
.i_count
, list
.p_values
);
1141 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
,
1142 es_out_GetGroupForced( p_input
->p
->p_es_out
), NULL
);
1146 static int Init( input_thread_t
* p_input
)
1148 for( int i
= 0; i
< p_input
->p
->p_item
->i_options
; i
++ )
1150 if( !strncmp( p_input
->p
->p_item
->ppsz_options
[i
], "meta-file", 9 ) )
1152 msg_Dbg( p_input
, "Input is a meta file: disabling unneeded options" );
1153 var_SetString( p_input
, "sout", "" );
1154 var_SetBool( p_input
, "sout-all", false );
1155 var_SetString( p_input
, "input-slave", "" );
1156 var_SetInteger( p_input
, "input-repeat", 0 );
1157 var_SetString( p_input
, "sub-file", "" );
1158 var_SetBool( p_input
, "sub-autodetect-file", false );
1162 InitStatistics( p_input
);
1164 if( InitSout( p_input
) )
1169 p_input
->p
->p_es_out
= input_EsOutTimeshiftNew( p_input
, p_input
->p
->p_es_out_display
, p_input
->p
->i_rate
);
1172 input_ChangeState( p_input
, OPENING_S
);
1173 input_SendEventCache( p_input
, 0.0 );
1176 if( InputSourceInit( p_input
, &p_input
->p
->input
,
1177 p_input
->p
->p_item
->psz_uri
, NULL
, false ) )
1182 InitTitle( p_input
);
1184 /* Load master infos */
1187 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_LENGTH
,
1191 i_length
= input_item_GetDuration( p_input
->p
->p_item
);
1192 input_SendEventLength( p_input
, i_length
);
1194 input_SendEventPosition( p_input
, 0.0, 0 );
1196 if( !p_input
->b_preparsing
)
1198 StartTitle( p_input
);
1199 LoadSubtitles( p_input
);
1200 LoadSlaves( p_input
);
1201 InitPrograms( p_input
);
1203 double f_rate
= var_InheritFloat( p_input
, "rate" );
1204 if( f_rate
!= 0.0 && f_rate
!= 1.0 )
1206 vlc_value_t val
= { .i_int
= INPUT_RATE_DEFAULT
/ f_rate
};
1207 input_ControlPush( p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1211 if( !p_input
->b_preparsing
&& p_input
->p
->p_sout
)
1213 p_input
->p
->b_out_pace_control
= (p_input
->p
->p_sout
->i_out_pace_nocontrol
> 0);
1215 if( p_input
->p
->b_can_pace_control
&& p_input
->p
->b_out_pace_control
)
1217 /* We don't want a high input priority here or we'll
1218 * end-up sucking up all the CPU time */
1219 vlc_set_priority( p_input
->p
->thread
, VLC_THREAD_PRIORITY_LOW
);
1222 msg_Dbg( p_input
, "starting in %s mode",
1223 p_input
->p
->b_out_pace_control
? "async" : "sync" );
1226 vlc_meta_t
*p_meta
= vlc_meta_New();
1227 if( p_meta
!= NULL
)
1229 /* Get meta data from users */
1230 InputMetaUser( p_input
, p_meta
);
1232 /* Get meta data from master input */
1233 InputSourceMeta( p_input
, &p_input
->p
->input
, p_meta
);
1235 /* And from slave */
1236 for( int i
= 0; i
< p_input
->p
->i_slave
; i
++ )
1237 InputSourceMeta( p_input
, p_input
->p
->slave
[i
], p_meta
);
1239 es_out_ControlSetMeta( p_input
->p
->p_es_out
, p_meta
);
1240 vlc_meta_Delete( p_meta
);
1243 msg_Dbg( p_input
, "`%s' successfully opened",
1244 p_input
->p
->p_item
->psz_uri
);
1246 /* initialization is complete */
1247 input_ChangeState( p_input
, PLAYING_S
);
1252 input_ChangeState( p_input
, ERROR_S
);
1254 if( p_input
->p
->p_es_out
)
1255 es_out_Delete( p_input
->p
->p_es_out
);
1256 es_out_SetMode( p_input
->p
->p_es_out_display
, ES_OUT_MODE_END
);
1257 if( p_input
->p
->p_resource
)
1259 if( p_input
->p
->p_sout
)
1260 input_resource_RequestSout( p_input
->p
->p_resource
,
1261 p_input
->p
->p_sout
, NULL
);
1262 input_resource_SetInput( p_input
->p
->p_resource
, NULL
);
1263 if( p_input
->p
->p_resource_private
)
1264 input_resource_Terminate( p_input
->p
->p_resource_private
);
1267 if( !p_input
->b_preparsing
&& libvlc_stats( p_input
) )
1269 #define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \
1270 stats_CounterClean( p_input->p->counters.p_##c );\
1271 p_input->p->counters.p_##c = NULL; } while(0)
1272 EXIT_COUNTER( read_bytes
);
1273 EXIT_COUNTER( read_packets
);
1274 EXIT_COUNTER( demux_read
);
1275 EXIT_COUNTER( input_bitrate
);
1276 EXIT_COUNTER( demux_bitrate
);
1277 EXIT_COUNTER( demux_corrupted
);
1278 EXIT_COUNTER( demux_discontinuity
);
1279 EXIT_COUNTER( played_abuffers
);
1280 EXIT_COUNTER( lost_abuffers
);
1281 EXIT_COUNTER( displayed_pictures
);
1282 EXIT_COUNTER( lost_pictures
);
1283 EXIT_COUNTER( decoded_audio
);
1284 EXIT_COUNTER( decoded_video
);
1285 EXIT_COUNTER( decoded_sub
);
1287 if( p_input
->p
->p_sout
)
1289 EXIT_COUNTER( sout_sent_packets
);
1290 EXIT_COUNTER( sout_sent_bytes
);
1291 EXIT_COUNTER( sout_send_bitrate
);
1296 /* Mark them deleted */
1297 p_input
->p
->input
.p_demux
= NULL
;
1298 p_input
->p
->p_es_out
= NULL
;
1299 p_input
->p
->p_sout
= NULL
;
1301 return VLC_EGENERIC
;
1304 /*****************************************************************************
1305 * End: end the input thread
1306 *****************************************************************************/
1307 static void End( input_thread_t
* p_input
)
1311 /* We are at the end */
1312 input_ChangeState( p_input
, END_S
);
1314 /* Clean control variables */
1315 input_ControlVarStop( p_input
);
1317 /* Stop es out activity */
1318 es_out_SetMode( p_input
->p
->p_es_out
, ES_OUT_MODE_NONE
);
1320 /* Clean up master */
1321 InputSourceClean( &p_input
->p
->input
);
1324 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
1326 InputSourceClean( p_input
->p
->slave
[i
] );
1327 free( p_input
->p
->slave
[i
] );
1329 free( p_input
->p
->slave
);
1331 /* Unload all modules */
1332 if( p_input
->p
->p_es_out
)
1333 es_out_Delete( p_input
->p
->p_es_out
);
1334 es_out_SetMode( p_input
->p
->p_es_out_display
, ES_OUT_MODE_END
);
1336 if( !p_input
->b_preparsing
)
1338 #define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL;
1339 if( libvlc_stats( p_input
) )
1341 /* make sure we are up to date */
1342 stats_ComputeInputStats( p_input
, p_input
->p
->p_item
->p_stats
);
1343 CL_CO( read_bytes
);
1344 CL_CO( read_packets
);
1345 CL_CO( demux_read
);
1346 CL_CO( input_bitrate
);
1347 CL_CO( demux_bitrate
);
1348 CL_CO( demux_corrupted
);
1349 CL_CO( demux_discontinuity
);
1350 CL_CO( played_abuffers
);
1351 CL_CO( lost_abuffers
);
1352 CL_CO( displayed_pictures
);
1353 CL_CO( lost_pictures
);
1354 CL_CO( decoded_audio
) ;
1355 CL_CO( decoded_video
);
1356 CL_CO( decoded_sub
) ;
1359 /* Close optional stream output instance */
1360 if( p_input
->p
->p_sout
)
1362 CL_CO( sout_sent_packets
);
1363 CL_CO( sout_sent_bytes
);
1364 CL_CO( sout_send_bitrate
);
1369 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
1370 if( p_input
->p
->i_attachment
> 0 )
1372 for( i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
1373 vlc_input_attachment_Delete( p_input
->p
->attachment
[i
] );
1374 TAB_CLEAN( p_input
->p
->i_attachment
, p_input
->p
->attachment
);
1375 free( p_input
->p
->attachment_demux
);
1376 p_input
->p
->attachment_demux
= NULL
;
1378 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
1381 input_resource_RequestSout( p_input
->p
->p_resource
,
1382 p_input
->p
->p_sout
, NULL
);
1383 input_resource_SetInput( p_input
->p
->p_resource
, NULL
);
1384 if( p_input
->p
->p_resource_private
)
1385 input_resource_Terminate( p_input
->p
->p_resource_private
);
1388 /*****************************************************************************
1390 *****************************************************************************/
1391 void input_ControlPush( input_thread_t
*p_input
,
1392 int i_type
, vlc_value_t
*p_val
)
1394 input_thread_private_t
*sys
= p_input
->p
;
1396 vlc_mutex_lock( &sys
->lock_control
);
1397 if( sys
->is_stopped
)
1399 else if( sys
->i_control
>= INPUT_CONTROL_FIFO_SIZE
)
1401 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1404 ControlRelease( i_type
, *p_val
);
1413 memset( &c
.val
, 0, sizeof(c
.val
) );
1415 sys
->control
[sys
->i_control
++] = c
;
1417 vlc_cond_signal( &sys
->wait_control
);
1418 vlc_mutex_unlock( &sys
->lock_control
);
1421 static int ControlGetReducedIndexLocked( input_thread_t
*p_input
)
1423 const int i_lt
= p_input
->p
->control
[0].i_type
;
1425 for( i
= 1; i
< p_input
->p
->i_control
; i
++ )
1427 const int i_ct
= p_input
->p
->control
[i
].i_type
;
1430 ( i_ct
== INPUT_CONTROL_SET_STATE
||
1431 i_ct
== INPUT_CONTROL_SET_RATE
||
1432 i_ct
== INPUT_CONTROL_SET_POSITION
||
1433 i_ct
== INPUT_CONTROL_SET_TIME
||
1434 i_ct
== INPUT_CONTROL_SET_PROGRAM
||
1435 i_ct
== INPUT_CONTROL_SET_TITLE
||
1436 i_ct
== INPUT_CONTROL_SET_SEEKPOINT
||
1437 i_ct
== INPUT_CONTROL_SET_BOOKMARK
) )
1443 /* TODO but that's not that important
1444 - merge SET_X with SET_X_CMD
1445 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1446 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1456 static inline int ControlPop( input_thread_t
*p_input
,
1457 int *pi_type
, vlc_value_t
*p_val
,
1458 mtime_t i_deadline
, bool b_postpone_seek
)
1460 input_thread_private_t
*p_sys
= p_input
->p
;
1462 vlc_mutex_lock( &p_sys
->lock_control
);
1463 while( p_sys
->i_control
<= 0 ||
1464 ( b_postpone_seek
&& ControlIsSeekRequest( p_sys
->control
[0].i_type
) ) )
1466 if( p_sys
->is_stopped
)
1468 vlc_mutex_unlock( &p_sys
->lock_control
);
1469 return VLC_EGENERIC
;
1472 if( i_deadline
>= 0 )
1474 if( vlc_cond_timedwait( &p_sys
->wait_control
, &p_sys
->lock_control
,
1477 vlc_mutex_unlock( &p_sys
->lock_control
);
1478 return VLC_EGENERIC
;
1482 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
1486 const int i_index
= ControlGetReducedIndexLocked( p_input
);
1489 *pi_type
= p_sys
->control
[i_index
].i_type
;
1490 *p_val
= p_sys
->control
[i_index
].val
;
1492 p_sys
->i_control
-= i_index
+ 1;
1493 if( p_sys
->i_control
> 0 )
1494 memmove( &p_sys
->control
[0], &p_sys
->control
[i_index
+1],
1495 sizeof(*p_sys
->control
) * p_sys
->i_control
);
1496 vlc_mutex_unlock( &p_sys
->lock_control
);
1500 static bool ControlIsSeekRequest( int i_type
)
1504 case INPUT_CONTROL_SET_POSITION
:
1505 case INPUT_CONTROL_SET_TIME
:
1506 case INPUT_CONTROL_SET_TITLE
:
1507 case INPUT_CONTROL_SET_TITLE_NEXT
:
1508 case INPUT_CONTROL_SET_TITLE_PREV
:
1509 case INPUT_CONTROL_SET_SEEKPOINT
:
1510 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1511 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1512 case INPUT_CONTROL_SET_BOOKMARK
:
1513 case INPUT_CONTROL_NAV_ACTIVATE
:
1514 case INPUT_CONTROL_NAV_UP
:
1515 case INPUT_CONTROL_NAV_DOWN
:
1516 case INPUT_CONTROL_NAV_LEFT
:
1517 case INPUT_CONTROL_NAV_RIGHT
:
1524 static void ControlRelease( int i_type
, vlc_value_t val
)
1528 case INPUT_CONTROL_ADD_SUBTITLE
:
1529 case INPUT_CONTROL_ADD_SLAVE
:
1530 free( val
.psz_string
);
1539 static void ControlPause( input_thread_t
*p_input
, mtime_t i_control_date
)
1541 int i_ret
= VLC_SUCCESS
;
1542 int i_state
= PAUSE_S
;
1544 if( p_input
->p
->b_can_pause
)
1546 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1548 if( p_demux
->s
!= NULL
)
1549 i_ret
= stream_Control( p_demux
->s
, STREAM_SET_PAUSE_STATE
, true );
1551 i_ret
= demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, true );
1555 msg_Warn( p_input
, "cannot set pause state" );
1561 i_ret
= es_out_SetPauseState( p_input
->p
->p_es_out
,
1562 p_input
->p
->b_can_pause
, true,
1566 msg_Warn( p_input
, "cannot set pause state at es_out level" );
1570 /* Switch to new state */
1571 input_ChangeState( p_input
, i_state
);
1574 static void ControlUnpause( input_thread_t
*p_input
, mtime_t i_control_date
)
1576 if( p_input
->p
->b_can_pause
)
1578 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1581 if( p_demux
->s
!= NULL
)
1582 ret
= stream_Control( p_demux
->s
, STREAM_SET_PAUSE_STATE
, false );
1584 ret
= demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, false );
1585 if( ret
!= VLC_SUCCESS
)
1587 msg_Err( p_input
, "cannot resume" );
1588 input_ChangeState( p_input
, ERROR_S
);
1593 /* Switch to play */
1594 input_ChangeState( p_input
, PLAYING_S
);
1595 es_out_SetPauseState( p_input
->p
->p_es_out
, false, false, i_control_date
);
1598 static bool Control( input_thread_t
*p_input
,
1599 int i_type
, vlc_value_t val
)
1601 const mtime_t i_control_date
= mdate();
1602 /* FIXME b_force_update is abused, it should be carefully checked */
1603 bool b_force_update
= false;
1606 return b_force_update
;
1610 case INPUT_CONTROL_SET_POSITION
:
1612 if( p_input
->p
->b_recording
)
1614 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
1618 float f_pos
= val
.f_float
;
1621 else if( f_pos
> 1.f
)
1623 /* Reset the decoders states and clock sync (before calling the demuxer */
1624 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1625 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_POSITION
,
1626 (double) f_pos
, !p_input
->p
->b_fast_seek
) )
1628 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
1629 "%2.1f%% failed", (double)(f_pos
* 100.f
) );
1633 if( p_input
->p
->i_slave
> 0 )
1634 SlaveSeek( p_input
);
1635 p_input
->p
->input
.b_eof
= false;
1637 b_force_update
= true;
1642 case INPUT_CONTROL_SET_TIME
:
1647 if( p_input
->p
->b_recording
)
1649 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
1657 /* Reset the decoders states and clock sync (before calling the demuxer */
1658 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1660 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1661 DEMUX_SET_TIME
, i_time
,
1662 !p_input
->p
->b_fast_seek
);
1667 /* Emulate it with a SET_POS */
1668 if( !demux_Control( p_input
->p
->input
.p_demux
,
1669 DEMUX_GET_LENGTH
, &i_length
) && i_length
> 0 )
1671 double f_pos
= (double)i_time
/ (double)i_length
;
1672 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1673 DEMUX_SET_POSITION
, f_pos
,
1674 !p_input
->p
->b_fast_seek
);
1679 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64
1680 " failed or not possible", i_time
);
1684 if( p_input
->p
->i_slave
> 0 )
1685 SlaveSeek( p_input
);
1686 p_input
->p
->input
.b_eof
= false;
1688 b_force_update
= true;
1693 case INPUT_CONTROL_SET_STATE
:
1697 if( p_input
->p
->i_state
== PAUSE_S
)
1699 ControlUnpause( p_input
, i_control_date
);
1700 b_force_update
= true;
1704 if( p_input
->p
->i_state
== PLAYING_S
)
1706 ControlPause( p_input
, i_control_date
);
1707 b_force_update
= true;
1711 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
1715 case INPUT_CONTROL_SET_RATE
:
1717 /* Get rate and direction */
1718 int i_rate
= abs( val
.i_int
);
1719 int i_rate_sign
= val
.i_int
< 0 ? -1 : 1;
1721 /* Check rate bound */
1722 if( i_rate
< INPUT_RATE_MIN
)
1724 msg_Dbg( p_input
, "cannot set rate faster" );
1725 i_rate
= INPUT_RATE_MIN
;
1727 else if( i_rate
> INPUT_RATE_MAX
)
1729 msg_Dbg( p_input
, "cannot set rate slower" );
1730 i_rate
= INPUT_RATE_MAX
;
1733 /* Apply direction */
1734 if( i_rate_sign
< 0 )
1736 if( p_input
->p
->input
.b_rescale_ts
)
1738 msg_Dbg( p_input
, "cannot set negative rate" );
1739 i_rate
= p_input
->p
->i_rate
;
1740 assert( i_rate
> 0 );
1744 i_rate
*= i_rate_sign
;
1748 if( i_rate
!= INPUT_RATE_DEFAULT
&&
1749 ( ( !p_input
->p
->b_can_rate_control
&& !p_input
->p
->input
.b_rescale_ts
) ||
1750 ( p_input
->p
->p_sout
&& !p_input
->p
->b_out_pace_control
) ) )
1752 msg_Dbg( p_input
, "cannot change rate" );
1753 i_rate
= INPUT_RATE_DEFAULT
;
1755 if( i_rate
!= p_input
->p
->i_rate
&&
1756 !p_input
->p
->b_can_pace_control
&& p_input
->p
->b_can_rate_control
)
1758 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1759 int i_ret
= VLC_EGENERIC
;
1761 if( p_demux
->s
== NULL
)
1763 if( !p_input
->p
->input
.b_rescale_ts
)
1764 es_out_Control( p_input
->p
->p_es_out
, ES_OUT_RESET_PCR
);
1766 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1767 DEMUX_SET_RATE
, &i_rate
);
1771 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
1772 i_rate
= p_input
->p
->i_rate
;
1777 if( i_rate
!= p_input
->p
->i_rate
)
1779 p_input
->p
->i_rate
= i_rate
;
1780 input_SendEventRate( p_input
, i_rate
);
1782 if( p_input
->p
->input
.b_rescale_ts
)
1784 const int i_rate_source
= (p_input
->p
->b_can_pace_control
|| p_input
->p
->b_can_rate_control
) ? i_rate
: INPUT_RATE_DEFAULT
;
1785 es_out_SetRate( p_input
->p
->p_es_out
, i_rate_source
, i_rate
);
1788 b_force_update
= true;
1793 case INPUT_CONTROL_SET_PROGRAM
:
1794 /* No need to force update, es_out does it if needed */
1795 es_out_Control( p_input
->p
->p_es_out
,
1796 ES_OUT_SET_GROUP
, val
.i_int
);
1798 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, val
.i_int
,
1802 case INPUT_CONTROL_SET_ES
:
1803 /* No need to force update, es_out does it if needed */
1804 es_out_Control( p_input
->p
->p_es_out_display
,
1805 ES_OUT_SET_ES_BY_ID
, (int)val
.i_int
, 0);
1807 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_ES
, (int)val
.i_int
);
1809 case INPUT_CONTROL_SET_ES2
:
1810 es_out_Control( p_input
->p
->p_es_out_display
,
1811 ES_OUT_SET_ES_BY_ID
, (int)val
.i_int
, 1);
1813 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_ES
, (int)val
.i_int
);
1816 case INPUT_CONTROL_RESTART_ES
:
1817 es_out_Control( p_input
->p
->p_es_out_display
,
1818 ES_OUT_RESTART_ES_BY_ID
, (int)val
.i_int
, 0);
1820 case INPUT_CONTROL_RESTART_ES2
:
1821 es_out_Control( p_input
->p
->p_es_out_display
,
1822 ES_OUT_RESTART_ES_BY_ID
, (int)val
.i_int
, 1);
1825 case INPUT_CONTROL_SET_AUDIO_DELAY
:
1826 input_SendEventAudioDelay( p_input
, val
.i_int
);
1827 UpdatePtsDelay( p_input
);
1830 case INPUT_CONTROL_SET_SPU_DELAY
:
1831 input_SendEventSubtitleDelay( p_input
, val
.i_int
);
1832 UpdatePtsDelay( p_input
);
1835 case INPUT_CONTROL_SET_TITLE
:
1836 case INPUT_CONTROL_SET_TITLE_NEXT
:
1837 case INPUT_CONTROL_SET_TITLE_PREV
:
1839 if( p_input
->p
->b_recording
)
1841 msg_Err( p_input
, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
1844 if( p_input
->p
->input
.i_title
<= 0 )
1847 int i_title
= p_input
->p
->input
.p_demux
->info
.i_title
;
1848 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
1850 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
1853 i_title
= val
.i_int
;
1854 if( i_title
< 0 || i_title
>= p_input
->p
->input
.i_title
)
1857 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1858 demux_Control( p_input
->p
->input
.p_demux
,
1859 DEMUX_SET_TITLE
, i_title
);
1860 input_SendEventTitle( p_input
, i_title
);
1863 case INPUT_CONTROL_SET_SEEKPOINT
:
1864 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1865 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1867 if( p_input
->p
->b_recording
)
1869 msg_Err( p_input
, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
1872 if( p_input
->p
->input
.i_title
<= 0 )
1875 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1877 int i_title
= p_demux
->info
.i_title
;
1878 int i_seekpoint
= p_demux
->info
.i_seekpoint
;
1880 if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_PREV
)
1882 int64_t i_seekpoint_time
= p_input
->p
->input
.title
[i_title
]->seekpoint
[i_seekpoint
]->i_time_offset
;
1883 int64_t i_input_time
= var_GetInteger( p_input
, "time" );
1884 if( i_seekpoint_time
>= 0 && i_input_time
>= 0 )
1886 if( i_input_time
< i_seekpoint_time
+ 3000000 )
1892 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
1895 i_seekpoint
= val
.i_int
;
1897 || i_seekpoint
>= p_input
->p
->input
.title
[i_title
]->i_seekpoint
)
1900 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1901 demux_Control( p_input
->p
->input
.p_demux
,
1902 DEMUX_SET_SEEKPOINT
, i_seekpoint
);
1903 input_SendEventSeekpoint( p_input
, i_title
, i_seekpoint
);
1907 case INPUT_CONTROL_ADD_SUBTITLE
:
1908 if( val
.psz_string
)
1909 input_SubtitleFileAdd( p_input
, val
.psz_string
, true );
1912 case INPUT_CONTROL_ADD_SLAVE
:
1913 if( val
.psz_string
)
1915 const char *uri
= val
.psz_string
;
1916 input_source_t
*slave
= InputSourceNew( p_input
);
1918 if( slave
&& !InputSourceInit( p_input
, slave
, uri
, NULL
, false ) )
1923 msg_Dbg( p_input
, "adding %s as slave on the fly", uri
);
1926 if( demux_Control( p_input
->p
->input
.p_demux
,
1927 DEMUX_GET_TIME
, &i_time
) )
1929 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
1930 InputSourceClean( slave
);
1934 if( demux_Control( slave
->p_demux
,
1935 DEMUX_SET_TIME
, i_time
, true ) )
1937 msg_Err( p_input
, "seek failed for new slave" );
1938 InputSourceClean( slave
);
1943 /* Get meta (access and demux) */
1944 InputUpdateMeta( p_input
, slave
->p_demux
);
1946 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, slave
);
1951 msg_Warn( p_input
, "failed to add %s as slave", uri
);
1956 case INPUT_CONTROL_SET_RECORD_STATE
:
1957 if( !!p_input
->p
->b_recording
!= !!val
.b_bool
)
1959 if( p_input
->p
->input
.b_can_stream_record
)
1961 if( demux_Control( p_input
->p
->input
.p_demux
,
1962 DEMUX_SET_RECORD_STATE
, val
.b_bool
) )
1967 if( es_out_SetRecordState( p_input
->p
->p_es_out_display
, val
.b_bool
) )
1970 p_input
->p
->b_recording
= val
.b_bool
;
1972 input_SendEventRecord( p_input
, val
.b_bool
);
1974 b_force_update
= true;
1978 case INPUT_CONTROL_SET_FRAME_NEXT
:
1979 if( p_input
->p
->i_state
== PAUSE_S
)
1981 es_out_SetFrameNext( p_input
->p
->p_es_out
);
1983 else if( p_input
->p
->i_state
== PLAYING_S
)
1985 ControlPause( p_input
, i_control_date
);
1989 msg_Err( p_input
, "invalid state for frame next" );
1991 b_force_update
= true;
1994 case INPUT_CONTROL_SET_BOOKMARK
:
1996 mtime_t time_offset
= -1;
1998 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
1999 if( val
.i_int
>= 0 && val
.i_int
< p_input
->p
->i_bookmark
)
2001 const seekpoint_t
*p_bookmark
= p_input
->p
->pp_bookmark
[val
.i_int
];
2002 time_offset
= p_bookmark
->i_time_offset
;
2004 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2006 if( time_offset
< 0 )
2008 msg_Err( p_input
, "invalid bookmark %"PRId64
, val
.i_int
);
2012 val
.i_int
= time_offset
;
2013 b_force_update
= Control( p_input
, INPUT_CONTROL_SET_TIME
, val
);
2017 case INPUT_CONTROL_NAV_ACTIVATE
:
2018 case INPUT_CONTROL_NAV_UP
:
2019 case INPUT_CONTROL_NAV_DOWN
:
2020 case INPUT_CONTROL_NAV_LEFT
:
2021 case INPUT_CONTROL_NAV_RIGHT
:
2022 demux_Control( p_input
->p
->input
.p_demux
, i_type
2023 - INPUT_CONTROL_NAV_ACTIVATE
+ DEMUX_NAV_ACTIVATE
);
2027 msg_Err( p_input
, "not yet implemented" );
2031 ControlRelease( i_type
, val
);
2032 return b_force_update
;
2035 /*****************************************************************************
2036 * UpdateTitleSeekpoint
2037 *****************************************************************************/
2038 static int UpdateTitleSeekpoint( input_thread_t
*p_input
,
2039 int i_title
, int i_seekpoint
)
2041 int i_title_end
= p_input
->p
->input
.i_title_end
-
2042 p_input
->p
->input
.i_title_offset
;
2043 int i_seekpoint_end
= p_input
->p
->input
.i_seekpoint_end
-
2044 p_input
->p
->input
.i_seekpoint_offset
;
2046 if( i_title_end
>= 0 && i_seekpoint_end
>= 0 )
2048 if( i_title
> i_title_end
||
2049 ( i_title
== i_title_end
&& i_seekpoint
> i_seekpoint_end
) )
2052 else if( i_seekpoint_end
>= 0 )
2054 if( i_seekpoint
> i_seekpoint_end
)
2057 else if( i_title_end
>= 0 )
2059 if( i_title
> i_title_end
)
2064 /*****************************************************************************
2066 *****************************************************************************/
2067 static int UpdateTitleSeekpointFromDemux( input_thread_t
*p_input
)
2069 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
2071 /* TODO event-like */
2072 if( p_demux
->info
.i_update
& INPUT_UPDATE_TITLE
)
2074 input_SendEventTitle( p_input
, p_demux
->info
.i_title
);
2076 p_demux
->info
.i_update
&= ~INPUT_UPDATE_TITLE
;
2078 if( p_demux
->info
.i_update
& INPUT_UPDATE_SEEKPOINT
)
2080 input_SendEventSeekpoint( p_input
,
2081 p_demux
->info
.i_title
, p_demux
->info
.i_seekpoint
);
2083 p_demux
->info
.i_update
&= ~INPUT_UPDATE_SEEKPOINT
;
2086 /* Hmmm only works with master input */
2087 if( p_input
->p
->input
.p_demux
== p_demux
)
2088 return UpdateTitleSeekpoint( p_input
,
2089 p_demux
->info
.i_title
,
2090 p_demux
->info
.i_seekpoint
);
2094 static void UpdateGenericFromDemux( input_thread_t
*p_input
)
2096 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
2098 if( p_demux
->info
.i_update
& INPUT_UPDATE_META
)
2100 InputUpdateMeta( p_input
, p_demux
);
2101 p_demux
->info
.i_update
&= ~INPUT_UPDATE_META
;
2107 if( !demux_Control( p_demux
, DEMUX_GET_SIGNAL
, &quality
, &strength
) )
2108 input_SendEventSignal( p_input
, quality
, strength
);
2112 static void UpdateTitleListfromDemux( input_thread_t
*p_input
)
2114 input_source_t
*in
= &p_input
->p
->input
;
2116 /* Delete the preexisting titles */
2117 if( in
->i_title
> 0 )
2119 for( int i
= 0; i
< in
->i_title
; i
++ )
2120 vlc_input_title_Delete( in
->title
[i
] );
2121 TAB_CLEAN( in
->i_title
, in
->title
);
2122 in
->b_title_demux
= false;
2125 /* Get the new title list */
2126 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2127 &in
->title
, &in
->i_title
,
2128 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2129 TAB_INIT( in
->i_title
, in
->title
);
2131 in
->b_title_demux
= true;
2133 InitTitle( p_input
);
2137 /*****************************************************************************
2139 *****************************************************************************/
2140 static input_source_t
*InputSourceNew( input_thread_t
*p_input
)
2142 VLC_UNUSED(p_input
);
2144 return calloc( 1, sizeof( input_source_t
) );
2147 /*****************************************************************************
2149 *****************************************************************************/
2150 static int InputSourceInit( input_thread_t
*p_input
,
2151 input_source_t
*in
, const char *psz_mrl
,
2152 const char *psz_forced_demux
, bool b_in_can_fail
)
2154 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2155 char *psz_var_demux
= NULL
;
2159 char *psz_dup
= strdup( psz_mrl
);
2161 if( psz_dup
== NULL
)
2165 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, &psz_anchor
, psz_dup
);
2167 msg_Dbg( p_input
, "`%s' gives access `%s' demux `%s' path `%s'",
2168 psz_mrl
, psz_access
, psz_demux
, psz_path
);
2169 if( !p_input
->b_preparsing
)
2171 /* Find optional titles and seekpoints */
2172 MRLSections( psz_anchor
, &in
->i_title_start
, &in
->i_title_end
,
2173 &in
->i_seekpoint_start
, &in
->i_seekpoint_end
);
2174 if( psz_forced_demux
&& *psz_forced_demux
)
2176 psz_demux
= psz_forced_demux
;
2178 else if( *psz_demux
== '\0' )
2180 /* special hack for forcing a demuxer with --demux=module
2181 * (and do nothing with a list) */
2182 psz_var_demux
= var_GetNonEmptyString( p_input
, "demux" );
2183 psz_demux
= (psz_var_demux
!= NULL
) ? psz_var_demux
: "any";
2184 msg_Dbg( p_input
, "specified demux `%s'", psz_demux
);
2187 /* Try access_demux first */
2188 in
->p_demux
= demux_New( p_input
, p_input
, psz_access
, psz_demux
,
2189 psz_path
, NULL
, p_input
->p
->p_es_out
, false );
2195 msg_Dbg( p_input
, "trying to pre-parse %s", psz_path
);
2198 mtime_t i_pts_delay
;
2202 /* Get infos from access_demux */
2203 in
->b_title_demux
= true;
2204 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2205 &in
->title
, &in
->i_title
,
2206 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2208 TAB_INIT( in
->i_title
, in
->title
);
2210 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_PACE
,
2211 &in
->b_can_pace_control
) )
2212 in
->b_can_pace_control
= false;
2214 assert( in
->p_demux
->pf_demux
!= NULL
|| !in
->b_can_pace_control
);
2216 if( !in
->b_can_pace_control
)
2218 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_RATE
,
2219 &in
->b_can_rate_control
, &in
->b_rescale_ts
) )
2221 in
->b_can_rate_control
= false;
2222 in
->b_rescale_ts
= true; /* not used */
2227 in
->b_can_rate_control
= true;
2228 in
->b_rescale_ts
= true;
2230 if( demux_Control( in
->p_demux
, DEMUX_CAN_PAUSE
,
2231 &in
->b_can_pause
) )
2232 in
->b_can_pause
= false;
2233 var_SetBool( p_input
, "can-pause", in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2234 var_SetBool( p_input
, "can-rate", !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2235 var_SetBool( p_input
, "can-rewind", !in
->b_rescale_ts
&& !in
->b_can_pace_control
&& in
->b_can_rate_control
);
2238 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &b_can_seek
) )
2240 var_SetBool( p_input
, "can-seek", b_can_seek
);
2243 { /* Now try a real access */
2244 if( &p_input
->p
->input
== in
)
2245 { /* On master stream only, use input-list */
2246 char *str
= var_InheritString( p_input
, "input-list" );
2251 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2252 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2255 var_SetString( p_input
, "concat-list", list
);
2259 psz_access
= "concat";
2263 if( strcasecmp( psz_access
, "concat" ) )
2264 { /* Autodetect extra files if none specified */
2268 TAB_INIT( count
, tab
);
2269 InputGetExtraFiles( p_input
, &count
, &tab
, psz_access
, psz_path
);
2274 for( int i
= 0; i
< count
; i
++ )
2277 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2286 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2287 if( likely(list
!= NULL
) )
2289 var_SetString( p_input
, "concat-list", list
);
2292 psz_access
= "concat";
2294 TAB_CLEAN( count
, tab
);
2298 access_t
*p_access
= access_New( p_input
, p_input
,
2299 psz_access
, psz_path
);
2300 if( p_access
== NULL
)
2302 msg_Err( p_input
, "open of `%s' failed", psz_mrl
);
2303 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2304 dialog_Fatal( p_input
, _("Your input can't be opened"),
2305 _("VLC is unable to open the MRL '%s'."
2306 " Check the log for details."), psz_mrl
);
2310 /* Access-forced demuxer (PARENTAL ADVISORY: EXPLICIT HACK) */
2311 #warning FIXME: parse content type
2313 /* Create the stream_t */
2314 stream_t
*p_stream
= stream_AccessNew( p_access
);
2315 if( p_stream
== NULL
)
2317 msg_Warn( p_input
, "cannot create a stream_t from access" );
2321 /* Add stream filters */
2322 char *psz_stream_filter
= var_GetNonEmptyString( p_input
,
2324 p_stream
= stream_FilterChainNew( p_stream
, psz_stream_filter
,
2325 var_GetBool( p_input
, "input-record-native" ) );
2326 free( psz_stream_filter
);
2328 if( !p_input
->b_preparsing
)
2332 stream_Control( p_stream
, STREAM_CAN_CONTROL_PACE
,
2333 &in
->b_can_pace_control
);
2334 in
->b_can_rate_control
= in
->b_can_pace_control
;
2335 in
->b_rescale_ts
= true;
2337 stream_Control( p_stream
, STREAM_CAN_PAUSE
, &in
->b_can_pause
);
2338 var_SetBool( p_input
, "can-pause",
2339 in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2340 var_SetBool( p_input
, "can-rate",
2341 !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2342 var_SetBool( p_input
, "can-rewind",
2343 !in
->b_rescale_ts
&& !in
->b_can_pace_control
);
2345 stream_Control( p_stream
, STREAM_CAN_SEEK
, &b
);
2346 var_SetBool( p_input
, "can-seek", b
);
2348 in
->b_title_demux
= false;
2350 stream_Control( p_stream
, STREAM_GET_PTS_DELAY
, &i_pts_delay
);
2353 if( p_stream
->psz_url
!= NULL
)
2354 /* Take access/stream redirections into account: */
2355 psz_path
= strstr( p_stream
->psz_url
, "://" );
2356 if( psz_path
== NULL
)
2359 in
->p_demux
= demux_New( p_input
, p_input
, psz_access
, psz_demux
,
2360 psz_path
, p_stream
, p_input
->p
->p_es_out
,
2361 p_input
->b_preparsing
);
2363 if( in
->p_demux
== NULL
)
2365 msg_Err( p_input
, "no suitable demux module for `%s/%s://%s'",
2366 psz_access
, psz_demux
, psz_path
);
2367 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2368 dialog_Fatal( VLC_OBJECT( p_input
),
2369 _("VLC can't recognize the input's format"),
2370 _("The format of '%s' cannot be detected. "
2371 "Have a look at the log for details."),
2373 stream_Delete( p_stream
);
2376 assert( in
->p_demux
->pf_demux
!= NULL
);
2378 /* Get title from demux */
2379 if( !p_input
->b_preparsing
&& in
->i_title
<= 0 )
2381 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2382 &in
->title
, &in
->i_title
,
2383 &in
->i_title_offset
, &in
->i_seekpoint_offset
))
2385 TAB_INIT( in
->i_title
, in
->title
);
2389 in
->b_title_demux
= true;
2394 free( psz_var_demux
);
2397 /* Set record capabilities */
2398 if( demux_Control( in
->p_demux
, DEMUX_CAN_RECORD
, &in
->b_can_stream_record
) )
2399 in
->b_can_stream_record
= false;
2401 if( !var_GetBool( p_input
, "input-record-native" ) )
2402 in
->b_can_stream_record
= false;
2403 var_SetBool( p_input
, "can-record", true );
2405 var_SetBool( p_input
, "can-record", in
->b_can_stream_record
);
2409 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2410 if( !p_input
->b_preparsing
)
2413 input_attachment_t
**attachment
;
2414 if( !demux_Control( in
->p_demux
, DEMUX_GET_ATTACHMENTS
,
2415 &attachment
, &i_attachment
) )
2417 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2418 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2419 i_attachment
, attachment
, in
->p_demux
);
2420 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2423 /* PTS delay: request from demux first. This is required for
2424 * access_demux and some special cases like SDP demux. Otherwise,
2425 * fallback to access */
2426 if( demux_Control( in
->p_demux
, DEMUX_GET_PTS_DELAY
,
2427 &in
->i_pts_delay
) )
2428 in
->i_pts_delay
= i_pts_delay
;
2429 if( in
->i_pts_delay
> INPUT_PTS_DELAY_MAX
)
2430 in
->i_pts_delay
= INPUT_PTS_DELAY_MAX
;
2431 else if( in
->i_pts_delay
< 0 )
2432 in
->i_pts_delay
= 0;
2435 if( !demux_Control( in
->p_demux
, DEMUX_GET_FPS
, &f_fps
) && f_fps
> 0.0 )
2437 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2438 p_input
->p
->f_fps
= f_fps
;
2439 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2442 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2443 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2449 demux_Delete( in
->p_demux
);
2451 free( psz_var_demux
);
2454 return VLC_EGENERIC
;
2457 /*****************************************************************************
2459 *****************************************************************************/
2460 static void InputSourceClean( input_source_t
*in
)
2465 demux_Delete( in
->p_demux
);
2467 if( in
->i_title
> 0 )
2469 for( i
= 0; i
< in
->i_title
; i
++ )
2470 vlc_input_title_Delete( in
->title
[i
] );
2471 TAB_CLEAN( in
->i_title
, in
->title
);
2475 /*****************************************************************************
2477 *****************************************************************************/
2478 static void InputSourceMeta( input_thread_t
*p_input
,
2479 input_source_t
*p_source
, vlc_meta_t
*p_meta
)
2481 demux_t
*p_demux
= p_source
->p_demux
;
2483 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2486 bool has_meta
= false;
2488 /* Read demux meta */
2489 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
2492 bool has_unsupported
;
2493 if( demux_Control( p_demux
, DEMUX_HAS_UNSUPPORTED_META
, &has_unsupported
) )
2494 has_unsupported
= true;
2496 /* If the demux report unsupported meta data, or if we don't have meta data
2497 * try an external "meta reader" */
2498 if( has_meta
&& !has_unsupported
)
2501 demux_meta_t
*p_demux_meta
=
2502 vlc_custom_create( p_input
, sizeof( *p_demux_meta
), "demux meta" );
2503 if( unlikely(p_demux_meta
== NULL
) )
2505 p_demux_meta
->p_item
= p_input
->p
->p_item
;
2507 module_t
*p_id3
= module_need( p_demux_meta
, "meta reader", NULL
, false );
2510 if( p_demux_meta
->p_meta
)
2512 vlc_meta_Merge( p_meta
, p_demux_meta
->p_meta
);
2513 vlc_meta_Delete( p_demux_meta
->p_meta
);
2516 if( p_demux_meta
->i_attachments
> 0 )
2518 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2519 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2520 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
, p_demux
);
2521 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2523 module_unneed( p_demux
, p_id3
);
2525 vlc_object_release( p_demux_meta
);
2529 static void SlaveDemux( input_thread_t
*p_input
)
2534 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_TIME
, &i_time
) )
2536 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2540 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
2542 input_source_t
*in
= p_input
->p
->slave
[i
];
2548 /* Call demux_Demux until we have read enough data */
2549 if( demux_Control( in
->p_demux
, DEMUX_SET_NEXT_DEMUX_TIME
, i_time
) )
2554 if( demux_Control( in
->p_demux
, DEMUX_GET_TIME
, &i_stime
) )
2556 msg_Err( p_input
, "slave[%d] doesn't like "
2557 "DEMUX_GET_TIME -> EOF", i
);
2562 if( i_stime
>= i_time
)
2568 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2574 i_ret
= demux_Demux( in
->p_demux
);
2579 msg_Dbg( p_input
, "slave %d EOF", i
);
2585 static void SlaveSeek( input_thread_t
*p_input
)
2590 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_TIME
, &i_time
) )
2592 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2596 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
2598 input_source_t
*in
= p_input
->p
->slave
[i
];
2600 if( demux_Control( in
->p_demux
, DEMUX_SET_TIME
, i_time
, true ) )
2603 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2613 /*****************************************************************************
2615 *****************************************************************************/
2616 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
)
2618 static const struct { int i_meta
; const char *psz_name
; } p_list
[] = {
2619 { vlc_meta_Title
, "meta-title" },
2620 { vlc_meta_Artist
, "meta-artist" },
2621 { vlc_meta_Genre
, "meta-genre" },
2622 { vlc_meta_Copyright
, "meta-copyright" },
2623 { vlc_meta_Description
, "meta-description" },
2624 { vlc_meta_Date
, "meta-date" },
2625 { vlc_meta_URL
, "meta-url" },
2629 /* Get meta information from user */
2630 for( int i
= 0; p_list
[i
].psz_name
; i
++ )
2632 char *psz_string
= var_GetNonEmptyString( p_input
, p_list
[i
].psz_name
);
2636 EnsureUTF8( psz_string
);
2637 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, psz_string
);
2642 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
, demux_t
***ppp_attachment_demux
,
2643 int i_new
, input_attachment_t
**pp_new
, demux_t
*p_demux
)
2645 int i_attachment
= *pi_attachment
;
2646 input_attachment_t
**attachment
= *ppp_attachment
;
2647 demux_t
**attachment_demux
= *ppp_attachment_demux
;
2650 attachment
= xrealloc( attachment
,
2651 sizeof(*attachment
) * ( i_attachment
+ i_new
) );
2652 attachment_demux
= xrealloc( attachment_demux
,
2653 sizeof(*attachment_demux
) * ( i_attachment
+ i_new
) );
2654 for( i
= 0; i
< i_new
; i
++ )
2656 attachment
[i_attachment
] = pp_new
[i
];
2657 attachment_demux
[i_attachment
++] = p_demux
;
2662 *pi_attachment
= i_attachment
;
2663 *ppp_attachment
= attachment
;
2664 *ppp_attachment_demux
= attachment_demux
;
2667 /*****************************************************************************
2668 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
2669 * arturl and locking issue.
2670 *****************************************************************************/
2671 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
)
2673 vlc_meta_t
*p_meta
= vlc_meta_New();
2674 if( unlikely(p_meta
== NULL
) )
2677 demux_Control( p_demux
, DEMUX_GET_META
, p_meta
);
2679 /* If metadata changed, then the attachments might have changed.
2680 We need to update them in case they contain album art. */
2681 input_attachment_t
**attachment
;
2684 if( !demux_Control( p_demux
, DEMUX_GET_ATTACHMENTS
,
2685 &attachment
, &i_attachment
) )
2687 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2688 if( p_input
->p
->i_attachment
> 0 )
2691 for( int i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
2693 if( p_input
->p
->attachment_demux
[i
] == p_demux
)
2694 vlc_input_attachment_Delete( p_input
->p
->attachment
[i
] );
2697 p_input
->p
->attachment
[j
] = p_input
->p
->attachment
[i
];
2698 p_input
->p
->attachment_demux
[j
] = p_input
->p
->attachment_demux
[i
];
2702 p_input
->p
->i_attachment
= j
;
2704 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2705 i_attachment
, attachment
, p_demux
);
2706 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2709 es_out_ControlSetMeta( p_input
->p
->p_es_out
, p_meta
);
2710 vlc_meta_Delete( p_meta
);
2713 /*****************************************************************************
2714 * InputGetExtraFiles
2715 * Autodetect extra input list
2716 *****************************************************************************/
2717 static void InputGetExtraFilesPattern( input_thread_t
*p_input
,
2718 int *pi_list
, char ***pppsz_list
,
2719 const char *psz_path
,
2720 const char *psz_match
,
2721 const char *psz_format
,
2722 int i_start
, int i_stop
)
2727 TAB_INIT( i_list
, ppsz_list
);
2729 char *psz_base
= strdup( psz_path
);
2733 /* Remove the extension */
2734 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
2735 assert( psz_end
>= psz_base
);
2738 /* Try to list files */
2739 for( int i
= i_start
; i
<= i_stop
; i
++ )
2744 if( asprintf( &psz_file
, psz_format
, psz_base
, i
) < 0 )
2747 char *psz_tmp_path
= get_path( psz_file
);
2749 if( vlc_stat( psz_tmp_path
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
2752 free( psz_tmp_path
);
2756 msg_Dbg( p_input
, "Detected extra file `%s'", psz_file
);
2757 TAB_APPEND( i_list
, ppsz_list
, psz_file
);
2758 free( psz_tmp_path
);
2763 *pppsz_list
= ppsz_list
;
2766 static void InputGetExtraFiles( input_thread_t
*p_input
,
2767 int *pi_list
, char ***pppsz_list
,
2768 const char *psz_access
, const char *psz_path
)
2772 const char *psz_match
;
2773 const char *psz_format
;
2777 /* XXX the order is important */
2778 { ".001", "%s.%.3d", 2, 999 },
2779 { NULL
, NULL
, 0, 0 }
2782 TAB_INIT( *pi_list
, *pppsz_list
);
2784 if( ( psz_access
&& *psz_access
&& strcmp( psz_access
, "file" ) ) || !psz_path
)
2787 const size_t i_path
= strlen(psz_path
);
2789 for( int i
= 0; p_pattern
[i
].psz_match
!= NULL
; i
++ )
2791 const size_t i_ext
= strlen(p_pattern
[i
].psz_match
);
2793 if( i_path
< i_ext
)
2795 if( !strcmp( &psz_path
[i_path
-i_ext
], p_pattern
[i
].psz_match
) )
2797 InputGetExtraFilesPattern( p_input
, pi_list
, pppsz_list
,
2799 p_pattern
[i
].psz_match
, p_pattern
[i
].psz_format
,
2800 p_pattern
[i
].i_start
, p_pattern
[i
].i_stop
);
2807 static void input_ChangeState( input_thread_t
*p_input
, int i_state
)
2809 if( p_input
->p
->i_state
== i_state
)
2812 p_input
->p
->i_state
= i_state
;
2813 if( i_state
== ERROR_S
)
2814 input_item_SetErrorWhenReading( p_input
->p
->p_item
, true );
2815 input_SendEventState( p_input
, i_state
);
2819 /*****************************************************************************
2820 * MRLSplit: parse the access, demux and url part of the
2821 * Media Resource Locator.
2822 *****************************************************************************/
2823 void input_SplitMRL( const char **access
, const char **demux
,
2824 const char **path
, const char **anchor
, char *buf
)
2828 /* Separate <path> from <access>[/<demux>]:// */
2829 p
= strstr( buf
, "://" );
2833 p
+= 3; /* skips "://" */
2836 /* Remove HTML anchor if present (not supported).
2837 * The hash symbol itself should be URI-encoded. */
2838 p
= strchr( p
, '#' );
2850 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
2853 /* Note: this is a valid non const pointer to "": */
2854 *path
= buf
+ strlen( buf
);
2857 /* Separate access from demux */
2858 p
= strchr( buf
, '/' );
2869 /* We really don't want module name substitution here! */
2876 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
2881 /* Look for the title */
2882 u
= strtoul( str
, &end
, 0 );
2883 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
2886 /* Look for the chapter */
2890 u
= strtoul( str
, &end
, 0 );
2891 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
2901 /*****************************************************************************
2902 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
2905 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
2906 *****************************************************************************/
2907 static void MRLSections( const char *p
,
2908 int *pi_title_start
, int *pi_title_end
,
2909 int *pi_chapter_start
, int *pi_chapter_end
)
2911 *pi_title_start
= *pi_title_end
= *pi_chapter_start
= *pi_chapter_end
= -1;
2913 int title_start
, chapter_start
, title_end
, chapter_end
;
2919 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
2921 title_start
= chapter_start
= -1;
2924 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
2926 title_end
= chapter_end
= -1;
2928 if( *p
) /* syntax error */
2931 *pi_title_start
= title_start
;
2932 *pi_title_end
= title_end
;
2933 *pi_chapter_start
= chapter_start
;
2934 *pi_chapter_end
= chapter_end
;
2937 /*****************************************************************************
2938 * input_AddSubtitles: add a subtitle file and enable it
2939 *****************************************************************************/
2940 static void input_SubtitleAdd( input_thread_t
*p_input
,
2941 const char *url
, unsigned i_flags
)
2943 input_source_t
*sub
= InputSourceNew( p_input
);
2949 var_Change( p_input
, "spu-es", VLC_VAR_CHOICESCOUNT
, &count
, NULL
);
2951 if( InputSourceInit( p_input
, sub
, url
, "subtitle",
2952 (i_flags
& SUB_CANFAIL
) ) )
2957 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, sub
);
2959 if( !(i_flags
& SUB_FORCED
) )//one of them is false
2965 if( var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
, &list
, NULL
) )
2967 if( count
.i_int
== 0 )
2969 /* if it was first one, there is disable too */
2971 if( count
.i_int
< list
.p_list
->i_count
)//the count must have been increased by other thread, in EsOutAdd
2973 const int i_id
= list
.p_list
->p_values
[count
.i_int
].i_int
;
2975 es_out_Control( p_input
->p
->p_es_out_display
, ES_OUT_SET_ES_DEFAULT_BY_ID
, i_id
);
2976 es_out_Control( p_input
->p
->p_es_out_display
, ES_OUT_SET_ES_BY_ID
, i_id
);
2978 var_FreeList( &list
, NULL
);
2981 static void input_SubtitleFileAdd( input_thread_t
*p_input
, char *psz_subtitle
,
2984 /* if we are provided a subtitle.sub file,
2985 * see if we don't have a subtitle.idx and use it instead */
2986 char *psz_path
= strdup( psz_subtitle
);
2987 if( likely(psz_path
!= NULL
) )
2989 char *psz_extension
= strrchr( psz_path
, '.');
2990 if( psz_extension
&& strcmp( psz_extension
, ".sub" ) == 0 )
2994 strcpy( psz_extension
, ".idx" );
2996 if( !vlc_stat( psz_path
, &st
) && S_ISREG( st
.st_mode
) )
2998 msg_Dbg( p_input
, "using %s as subtitle file instead of %s",
2999 psz_path
, psz_subtitle
);
3000 strcpy( psz_subtitle
, psz_path
); /* <- FIXME! constify */
3006 char *url
= vlc_path2uri( psz_subtitle
, NULL
);
3010 input_SubtitleAdd( p_input
, url
, i_flags
);
3014 /*****************************************************************************
3016 *****************************************************************************/
3017 void input_UpdateStatistic( input_thread_t
*p_input
,
3018 input_statistic_t i_type
, int i_delta
)
3020 assert( p_input
->p
->i_state
!= INIT_S
);
3022 vlc_mutex_lock( &p_input
->p
->counters
.counters_lock
);
3025 #define I(c) stats_Update( p_input->p->counters.c, i_delta, NULL )
3026 case INPUT_STATISTIC_DECODED_VIDEO
:
3029 case INPUT_STATISTIC_DECODED_AUDIO
:
3032 case INPUT_STATISTIC_DECODED_SUBTITLE
:
3035 case INPUT_STATISTIC_SENT_PACKET
:
3036 I(p_sout_sent_packets
);
3039 case INPUT_STATISTIC_SENT_BYTE
:
3043 stats_Update( p_input
->p
->counters
.p_sout_sent_bytes
, i_delta
, &bytes
);
3044 stats_Update( p_input
->p
->counters
.p_sout_send_bitrate
, bytes
, NULL
);
3048 msg_Err( p_input
, "Invalid statistic type %d (internal error)", i_type
);
3051 vlc_mutex_unlock( &p_input
->p
->counters
.counters_lock
);
3055 /* TODO FIXME nearly the same logic that snapshot code */
3056 char *input_CreateFilename( input_thread_t
*input
, const char *psz_path
, const char *psz_prefix
, const char *psz_extension
)
3061 path
= vlc_opendir( psz_path
);
3066 char *psz_tmp
= str_format( input
, psz_prefix
);
3070 filename_sanitize( psz_tmp
);
3072 if( asprintf( &psz_file
, "%s"DIR_SEP
"%s%s%s",
3074 psz_extension
? "." : "",
3075 psz_extension
? psz_extension
: "" ) < 0 )
3082 psz_file
= str_format( input
, psz_path
);
3083 path_sanitize( psz_file
);