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"
50 #include <vlc_dialog.h>
52 #include <vlc_charset.h>
54 #include <vlc_strings.h>
55 #include <vlc_modules.h>
57 /*****************************************************************************
59 *****************************************************************************/
60 static void *Run ( void * );
62 static input_thread_t
* Create ( vlc_object_t
*, input_item_t
*,
63 const char *, bool, input_resource_t
* );
64 static int Init ( input_thread_t
*p_input
);
65 static void End ( input_thread_t
*p_input
);
66 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
);
68 static inline int ControlPop( input_thread_t
*, int *, vlc_value_t
*, mtime_t i_deadline
, bool b_postpone_seek
);
69 static void ControlRelease( int i_type
, vlc_value_t val
);
70 static bool ControlIsSeekRequest( int i_type
);
71 static bool Control( input_thread_t
*, int, vlc_value_t
);
72 static void ControlPause( input_thread_t
*, mtime_t
);
74 static int UpdateTitleSeekpointFromDemux( input_thread_t
* );
75 static void UpdateGenericFromDemux( input_thread_t
* );
76 static void UpdateTitleListfromDemux( input_thread_t
* );
78 static void MRLSections( const char *, int *, int *, int *, int *);
80 static input_source_t
*InputSourceNew( input_thread_t
*);
81 static int InputSourceInit( input_thread_t
*, input_source_t
*,
82 const char *, const char *psz_forced_demux
,
84 static void InputSourceClean( input_source_t
* );
85 static void InputSourceMeta( input_thread_t
*, input_source_t
*, vlc_meta_t
* );
88 //static void InputGetAttachments( input_thread_t *, input_source_t * );
89 static void SlaveDemux( input_thread_t
*p_input
);
90 static void SlaveSeek( input_thread_t
*p_input
);
92 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
);
93 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
);
94 static void InputGetExtraFiles( input_thread_t
*p_input
,
95 int *pi_list
, char ***pppsz_list
,
96 const char *psz_access
, const char *psz_path
);
98 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
, demux_t
***ppp_attachment_demux
,
99 int i_new
, input_attachment_t
**pp_new
, demux_t
*p_demux
);
107 static void input_SubtitleAdd( input_thread_t
*, const char *, unsigned );
108 static void input_SubtitleFileAdd( input_thread_t
*, char *, unsigned );
109 static void input_ChangeState( input_thread_t
*p_input
, int i_state
); /* TODO fix name */
113 * Create a new input_thread_t.
115 * You need to call input_Start on it when you are done
116 * adding callback on the variables/events you want to monitor.
118 * \param p_parent a vlc_object
119 * \param p_item an input item
120 * \param psz_log an optional prefix for this input logs
121 * \param p_resource an optional input ressource
122 * \return a pointer to the spawned input thread
124 input_thread_t
*input_Create( vlc_object_t
*p_parent
,
125 input_item_t
*p_item
,
126 const char *psz_log
, input_resource_t
*p_resource
)
128 return Create( p_parent
, p_item
, psz_log
, false, p_resource
);
133 * Initialize an input thread and run it until it stops by itself.
135 * \param p_parent a vlc_object
136 * \param p_item an input item
137 * \return an error code, VLC_SUCCESS on success
139 int input_Read( vlc_object_t
*p_parent
, input_item_t
*p_item
)
141 input_thread_t
*p_input
= Create( p_parent
, p_item
, NULL
, false, NULL
);
145 if( !Init( p_input
) )
147 MainLoop( p_input
, false );
151 vlc_object_release( p_input
);
156 * Initialize an input and initialize it to preparse the item
157 * This function is blocking. It will only accept parsing regular files.
159 * \param p_parent a vlc_object_t
160 * \param p_item an input item
161 * \return VLC_SUCCESS or an error
163 int input_Preparse( vlc_object_t
*p_parent
, input_item_t
*p_item
)
165 input_thread_t
*p_input
;
167 /* Allocate descriptor */
168 p_input
= Create( p_parent
, p_item
, NULL
, true, NULL
);
172 if( !Init( p_input
) ) {
173 /* if the demux is a playlist, call Mainloop that will call
174 * demux_Demux in order to fetch sub items */
175 bool b_is_playlist
= false;
177 if ( input_item_ShouldPreparseSubItems( p_item
)
178 && demux_Control( p_input
->p
->input
.p_demux
,
181 b_is_playlist
= false;
183 MainLoop( p_input
, false );
187 vlc_object_release( p_input
);
193 * Start a input_thread_t created by input_Create.
195 * You must not start an already running input_thread_t.
197 * \param the input thread to start
199 int input_Start( input_thread_t
*p_input
)
201 assert( !p_input
->p
->is_running
);
202 /* Create thread and wait for its readiness. */
203 p_input
->p
->is_running
= !vlc_clone( &p_input
->p
->thread
,
204 Run
, p_input
, VLC_THREAD_PRIORITY_INPUT
);
205 if( !p_input
->p
->is_running
)
207 input_ChangeState( p_input
, ERROR_S
);
208 msg_Err( p_input
, "cannot create input thread" );
215 * Request a running input thread to stop and die
217 * \param p_input the input thread to stop
219 void input_Stop( input_thread_t
*p_input
)
221 input_thread_private_t
*sys
= p_input
->p
;
223 vlc_mutex_lock( &sys
->lock_control
);
224 /* Discard all pending controls */
225 for( int i
= 0; i
< sys
->i_control
; i
++ )
227 input_control_t
*ctrl
= &sys
->control
[i
];
228 ControlRelease( ctrl
->i_type
, ctrl
->val
);
231 sys
->is_stopped
= true;
232 vlc_cond_signal( &sys
->wait_control
);
233 vlc_mutex_unlock( &sys
->lock_control
);
234 vlc_interrupt_kill( &sys
->interrupt
);
240 * It does not call input_Stop itself.
242 void input_Close( input_thread_t
*p_input
)
244 if( p_input
->p
->is_running
)
245 vlc_join( p_input
->p
->thread
, NULL
);
246 vlc_interrupt_deinit( &p_input
->p
->interrupt
);
247 vlc_object_release( p_input
);
251 * Input destructor (called when the object's refcount reaches 0).
253 static void input_Destructor( vlc_object_t
*obj
)
255 input_thread_t
*p_input
= (input_thread_t
*)obj
;
257 char * psz_name
= input_item_GetName( p_input
->p
->p_item
);
258 msg_Dbg( p_input
, "Destroying the input for '%s'", psz_name
);
262 if( p_input
->p
->p_es_out_display
)
263 es_out_Delete( p_input
->p
->p_es_out_display
);
265 if( p_input
->p
->p_resource
)
266 input_resource_Release( p_input
->p
->p_resource
);
267 if( p_input
->p
->p_resource_private
)
268 input_resource_Release( p_input
->p
->p_resource_private
);
270 vlc_gc_decref( p_input
->p
->p_item
);
272 vlc_mutex_destroy( &p_input
->p
->counters
.counters_lock
);
274 for( int i
= 0; i
< p_input
->p
->i_control
; i
++ )
276 input_control_t
*p_ctrl
= &p_input
->p
->control
[i
];
277 ControlRelease( p_ctrl
->i_type
, p_ctrl
->val
);
280 vlc_cond_destroy( &p_input
->p
->wait_control
);
281 vlc_mutex_destroy( &p_input
->p
->lock_control
);
286 * Get the item from an input thread
287 * FIXME it does not increase ref count of the item.
288 * if it is used after p_input is destroyed nothing prevent it from
291 input_item_t
*input_GetItem( input_thread_t
*p_input
)
293 assert( p_input
&& p_input
->p
);
294 return p_input
->p
->p_item
;
297 /*****************************************************************************
298 * This function creates a new input, and returns a pointer
299 * to its description. On error, it returns NULL.
301 * XXX Do not forget to update vlc_input.h if you add new variables.
302 *****************************************************************************/
303 static input_thread_t
*Create( vlc_object_t
*p_parent
, input_item_t
*p_item
,
304 const char *psz_header
, bool b_quick
,
305 input_resource_t
*p_resource
)
307 input_thread_t
*p_input
= NULL
; /* thread descriptor */
309 /* Allocate descriptor */
310 p_input
= vlc_custom_create( p_parent
, sizeof( *p_input
), "input" );
311 if( p_input
== NULL
)
314 /* Construct a nice name for the input timer */
315 char psz_timer_name
[255];
316 char * psz_name
= input_item_GetName( p_item
);
317 snprintf( psz_timer_name
, sizeof(psz_timer_name
),
318 "input launching for '%s'", psz_name
);
320 msg_Dbg( p_input
, "Creating an input for '%s'", psz_name
);
324 p_input
->p
= calloc( 1, sizeof( input_thread_private_t
) );
327 vlc_object_release( p_input
);
331 /* Parse input options */
332 input_item_ApplyOptions( VLC_OBJECT(p_input
), p_item
);
334 p_input
->b_preparsing
= b_quick
;
335 p_input
->psz_header
= psz_header
? strdup( psz_header
) : NULL
;
337 /* Init Common fields */
338 p_input
->p
->b_can_pace_control
= true;
339 p_input
->p
->i_start
= 0;
340 p_input
->p
->i_time
= 0;
341 p_input
->p
->i_stop
= 0;
342 p_input
->p
->i_run
= 0;
343 p_input
->p
->i_title
= 0;
344 p_input
->p
->title
= NULL
;
345 p_input
->p
->i_title_offset
= p_input
->p
->i_seekpoint_offset
= 0;
346 p_input
->p
->i_state
= INIT_S
;
347 p_input
->p
->is_running
= false;
348 p_input
->p
->is_stopped
= false;
349 p_input
->p
->b_recording
= false;
350 p_input
->p
->i_rate
= INPUT_RATE_DEFAULT
;
351 memset( &p_input
->p
->bookmark
, 0, sizeof(p_input
->p
->bookmark
) );
352 TAB_INIT( p_input
->p
->i_bookmark
, p_input
->p
->pp_bookmark
);
353 TAB_INIT( p_input
->p
->i_attachment
, p_input
->p
->attachment
);
354 p_input
->p
->attachment_demux
= NULL
;
355 p_input
->p
->p_sout
= NULL
;
356 p_input
->p
->b_out_pace_control
= false;
358 vlc_gc_incref( p_item
); /* Released in Destructor() */
359 p_input
->p
->p_item
= p_item
;
361 /* Init Input fields */
362 p_input
->p
->input
.p_demux
= NULL
;
363 p_input
->p
->input
.b_title_demux
= false;
364 p_input
->p
->input
.i_title
= 0;
365 p_input
->p
->input
.title
= NULL
;
366 p_input
->p
->input
.i_title_offset
= p_input
->p
->input
.i_seekpoint_offset
= 0;
367 p_input
->p
->input
.b_can_pace_control
= true;
368 p_input
->p
->input
.b_can_rate_control
= true;
369 p_input
->p
->input
.b_rescale_ts
= true;
370 p_input
->p
->input
.b_eof
= false;
372 vlc_mutex_lock( &p_item
->lock
);
374 if( !p_item
->p_stats
)
375 p_item
->p_stats
= stats_NewInputStats( p_input
);
377 /* setup the preparse depth of the item
378 * if we are preparsing, use the i_preparse_depth of the parent item */
379 if( !p_input
->b_preparsing
)
381 char *psz_rec
= var_InheritString( p_parent
, "recursive" );
383 if( psz_rec
!= NULL
)
385 if ( !strcasecmp( psz_rec
, "none" ) )
386 p_item
->i_preparse_depth
= 0;
387 else if ( !strcasecmp( psz_rec
, "collapse" ) )
388 p_item
->i_preparse_depth
= 1;
390 p_item
->i_preparse_depth
= -1; /* default is expand */
393 p_item
->i_preparse_depth
= -1;
396 vlc_mutex_unlock( &p_item
->lock
);
399 p_input
->p
->i_slave
= 0;
400 p_input
->p
->slave
= NULL
;
405 p_input
->p
->p_resource_private
= NULL
;
406 p_input
->p
->p_resource
= input_resource_Hold( p_resource
);
410 p_input
->p
->p_resource_private
= input_resource_New( VLC_OBJECT( p_input
) );
411 p_input
->p
->p_resource
= input_resource_Hold( p_input
->p
->p_resource_private
);
413 input_resource_SetInput( p_input
->p
->p_resource
, p_input
);
415 /* Init control buffer */
416 vlc_mutex_init( &p_input
->p
->lock_control
);
417 vlc_cond_init( &p_input
->p
->wait_control
);
418 p_input
->p
->i_control
= 0;
419 vlc_interrupt_init(&p_input
->p
->interrupt
);
421 /* Create Object Variables for private use only */
422 input_ConfigVarInit( p_input
);
424 /* Create Objects variables for public Get and Set */
425 input_ControlVarInit( p_input
);
428 if( !p_input
->b_preparsing
)
430 char *psz_bookmarks
= var_GetNonEmptyString( p_input
, "bookmarks" );
433 /* FIXME: have a common cfg parsing routine used by sout and others */
434 char *psz_parser
, *psz_start
, *psz_end
;
435 psz_parser
= psz_bookmarks
;
436 while( (psz_start
= strchr( psz_parser
, '{' ) ) )
438 seekpoint_t
*p_seekpoint
;
441 psz_end
= strchr( psz_start
, '}' );
442 if( !psz_end
) break;
443 psz_parser
= psz_end
+ 1;
444 backup
= *psz_parser
;
448 p_seekpoint
= vlc_seekpoint_New();
449 while( (psz_end
= strchr( psz_start
, ',' ) ) )
452 if( !strncmp( psz_start
, "name=", 5 ) )
454 p_seekpoint
->psz_name
= strdup(psz_start
+ 5);
456 else if( !strncmp( psz_start
, "bytes=", 6 ) )
458 p_seekpoint
->i_byte_offset
= atoll(psz_start
+ 6);
460 else if( !strncmp( psz_start
, "time=", 5 ) )
462 p_seekpoint
->i_time_offset
= atoll(psz_start
+ 5) *
465 psz_start
= psz_end
+ 1;
467 msg_Dbg( p_input
, "adding bookmark: %s, bytes=%"PRId64
", time=%"PRId64
,
468 p_seekpoint
->psz_name
, p_seekpoint
->i_byte_offset
,
469 p_seekpoint
->i_time_offset
);
470 input_Control( p_input
, INPUT_ADD_BOOKMARK
, p_seekpoint
);
471 vlc_seekpoint_Delete( p_seekpoint
);
472 *psz_parser
= backup
;
474 free( psz_bookmarks
);
478 /* Remove 'Now playing' info as it is probably outdated */
479 input_item_SetNowPlaying( p_item
, NULL
);
480 input_item_SetESNowPlaying( p_item
, NULL
);
481 input_SendEventMeta( p_input
);
484 if( p_input
->b_preparsing
)
485 p_input
->i_flags
|= OBJECT_FLAGS_QUIET
| OBJECT_FLAGS_NOINTERACT
;
487 /* Make sure the interaction option is honored */
488 if( !var_InheritBool( p_input
, "interact" ) )
489 p_input
->i_flags
|= OBJECT_FLAGS_NOINTERACT
;
492 memset( &p_input
->p
->counters
, 0, sizeof( p_input
->p
->counters
) );
493 vlc_mutex_init( &p_input
->p
->counters
.counters_lock
);
495 p_input
->p
->p_es_out_display
= input_EsOutNew( p_input
, p_input
->p
->i_rate
);
496 p_input
->p
->p_es_out
= NULL
;
498 /* Set the destructor when we are sure we are initialized */
499 vlc_object_set_destructor( p_input
, input_Destructor
);
504 /*****************************************************************************
505 * Run: main thread loop
506 * This is the "normal" thread that spawns the input processing chain,
507 * reads the stream, cleans up and waits
508 *****************************************************************************/
509 static void *Run( void *obj
)
511 input_thread_t
*p_input
= (input_thread_t
*)obj
;
513 vlc_interrupt_set(&p_input
->p
->interrupt
);
515 if( !Init( p_input
) )
517 MainLoop( p_input
, true ); /* FIXME it can be wrong (like with VLM) */
523 input_SendEventDead( p_input
);
527 bool input_Stopped( input_thread_t
*input
)
529 input_thread_private_t
*sys
= input
->p
;
532 vlc_mutex_lock( &sys
->lock_control
);
533 ret
= sys
->is_stopped
;
534 vlc_mutex_unlock( &sys
->lock_control
);
538 /*****************************************************************************
539 * Main loop: Fill buffers from access, and demux
540 *****************************************************************************/
544 * It asks the demuxer to demux some data
546 static void MainLoopDemux( input_thread_t
*p_input
, bool *pb_changed
, mtime_t i_start_mdate
)
552 if( ( p_input
->p
->i_stop
> 0 && p_input
->p
->i_time
>= p_input
->p
->i_stop
) ||
553 ( p_input
->p
->i_run
> 0 && i_start_mdate
+p_input
->p
->i_run
< mdate() ) )
556 i_ret
= demux_Demux( p_input
->p
->input
.p_demux
);
560 if( p_input
->p
->input
.p_demux
->info
.i_update
)
562 if( p_input
->p
->input
.p_demux
->info
.i_update
& INPUT_UPDATE_TITLE_LIST
)
564 UpdateTitleListfromDemux( p_input
);
565 p_input
->p
->input
.p_demux
->info
.i_update
&= ~INPUT_UPDATE_TITLE_LIST
;
567 if( p_input
->p
->input
.b_title_demux
)
569 i_ret
= UpdateTitleSeekpointFromDemux( p_input
);
572 UpdateGenericFromDemux( p_input
);
576 if( i_ret
== 0 ) /* EOF */
578 msg_Dbg( p_input
, "EOF reached" );
579 p_input
->p
->input
.b_eof
= true;
580 es_out_Eos(p_input
->p
->p_es_out
);
584 input_ChangeState( p_input
, ERROR_S
);
587 if( i_ret
> 0 && p_input
->p
->i_slave
> 0 )
588 SlaveDemux( p_input
);
591 static int MainLoopTryRepeat( input_thread_t
*p_input
, mtime_t
*pi_start_mdate
)
593 int i_repeat
= var_GetInteger( p_input
, "input-repeat" );
599 msg_Dbg( p_input
, "repeating the same input (%d)", i_repeat
);
603 var_SetInteger( p_input
, "input-repeat", i_repeat
);
606 /* Seek to start title/seekpoint */
607 val
.i_int
= p_input
->p
->input
.i_title_start
-
608 p_input
->p
->input
.i_title_offset
;
609 if( val
.i_int
< 0 || val
.i_int
>= p_input
->p
->input
.i_title
)
611 input_ControlPush( p_input
,
612 INPUT_CONTROL_SET_TITLE
, &val
);
614 val
.i_int
= p_input
->p
->input
.i_seekpoint_start
-
615 p_input
->p
->input
.i_seekpoint_offset
;
616 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
617 input_ControlPush( p_input
,
618 INPUT_CONTROL_SET_SEEKPOINT
, &val
);
620 /* Seek to start position */
621 if( p_input
->p
->i_start
> 0 )
623 val
.i_int
= p_input
->p
->i_start
;
624 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &val
);
629 input_ControlPush( p_input
, INPUT_CONTROL_SET_POSITION
, &val
);
633 *pi_start_mdate
= mdate();
638 * Update timing infos and statistics.
640 static void MainLoopStatistics( input_thread_t
*p_input
)
642 double f_position
= 0.0;
644 mtime_t i_length
= 0;
646 /* update input status variables */
647 if( demux_Control( p_input
->p
->input
.p_demux
,
648 DEMUX_GET_POSITION
, &f_position
) )
651 if( demux_Control( p_input
->p
->input
.p_demux
,
652 DEMUX_GET_TIME
, &i_time
) )
654 p_input
->p
->i_time
= i_time
;
656 if( demux_Control( p_input
->p
->input
.p_demux
,
657 DEMUX_GET_LENGTH
, &i_length
) )
660 es_out_SetTimes( p_input
->p
->p_es_out
, f_position
, i_time
, i_length
);
662 /* update current bookmark */
663 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
664 p_input
->p
->bookmark
.i_time_offset
= i_time
;
665 p_input
->p
->bookmark
.i_byte_offset
= -1;
666 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
668 stats_ComputeInputStats( p_input
, p_input
->p
->p_item
->p_stats
);
669 input_SendEventStatistics( p_input
);
674 * The main input loop.
676 static void MainLoop( input_thread_t
*p_input
, bool b_interactive
)
678 mtime_t i_start_mdate
= mdate();
679 mtime_t i_intf_update
= 0;
680 mtime_t i_last_seek_mdate
= 0;
682 if( b_interactive
&& var_InheritBool( p_input
, "start-paused" ) )
683 ControlPause( p_input
, i_start_mdate
);
685 bool b_pause_after_eof
= b_interactive
&&
686 var_InheritBool( p_input
, "play-and-pause" );
688 while( !input_Stopped( p_input
) && p_input
->p
->i_state
!= ERROR_S
)
690 mtime_t i_wakeup
= -1;
691 bool b_paused
= p_input
->p
->i_state
== PAUSE_S
;
692 /* FIXME if p_input->p->i_state == PAUSE_S the access/access_demux
693 * is paused -> this may cause problem with some of them
694 * The same problem can be seen when seeking while paused */
696 b_paused
= !es_out_GetBuffering( p_input
->p
->p_es_out
) || p_input
->p
->input
.b_eof
;
700 if( !p_input
->p
->input
.b_eof
)
702 bool b_force_update
= false;
704 MainLoopDemux( p_input
, &b_force_update
, i_start_mdate
);
705 i_wakeup
= es_out_GetWakeup( p_input
->p
->p_es_out
);
710 else if( !es_out_GetEmpty( p_input
->p
->p_es_out
) )
712 msg_Dbg( p_input
, "waiting decoder fifos to empty" );
713 i_wakeup
= mdate() + INPUT_IDLE_SLEEP
;
715 /* Pause after eof only if the input is pausable.
716 * This way we won't trigger timeshifting for nothing */
717 else if( b_pause_after_eof
&& p_input
->p
->b_can_pause
)
719 vlc_value_t val
= { .i_int
= PAUSE_S
};
721 msg_Dbg( p_input
, "pausing at EOF (pause after each)");
722 Control( p_input
, INPUT_CONTROL_SET_STATE
, val
);
728 if( MainLoopTryRepeat( p_input
, &i_start_mdate
) )
732 /* Update interface and statistics */
733 mtime_t now
= mdate();
734 if( now
>= i_intf_update
)
736 MainLoopStatistics( p_input
);
737 i_intf_update
= now
+ INT64_C(250000);
744 mtime_t i_deadline
= i_wakeup
;
746 /* Postpone seeking until ES buffering is complete or at most
748 bool b_postpone
= es_out_GetBuffering( p_input
->p
->p_es_out
)
749 && !p_input
->p
->input
.b_eof
;
752 mtime_t now
= mdate();
754 /* Recheck ES buffer level every 20 ms when seeking */
755 if( now
< i_last_seek_mdate
+ INT64_C(125000)
756 && (i_deadline
< 0 || i_deadline
> now
+ INT64_C(20000)) )
757 i_deadline
= now
+ INT64_C(20000);
765 if( ControlPop( p_input
, &i_type
, &val
, i_deadline
, b_postpone
) )
769 break; /* Wake-up time reached */
773 msg_Dbg( p_input
, "control type=%d", i_type
);
775 if( Control( p_input
, i_type
, val
) )
777 if( ControlIsSeekRequest( i_type
) )
778 i_last_seek_mdate
= mdate();
782 /* Update the wakeup time */
784 i_wakeup
= es_out_GetWakeup( p_input
->p
->p_es_out
);
789 static void InitStatistics( input_thread_t
* p_input
)
791 if( p_input
->b_preparsing
) return;
793 /* Prepare statistics */
794 #define INIT_COUNTER( c, compute ) p_input->p->counters.p_##c = \
795 stats_CounterCreate( STATS_##compute);
796 if( libvlc_stats( p_input
) )
798 INIT_COUNTER( read_bytes
, COUNTER
);
799 INIT_COUNTER( read_packets
, COUNTER
);
800 INIT_COUNTER( demux_read
, COUNTER
);
801 INIT_COUNTER( input_bitrate
, DERIVATIVE
);
802 INIT_COUNTER( demux_bitrate
, DERIVATIVE
);
803 INIT_COUNTER( demux_corrupted
, COUNTER
);
804 INIT_COUNTER( demux_discontinuity
, COUNTER
);
805 INIT_COUNTER( played_abuffers
, COUNTER
);
806 INIT_COUNTER( lost_abuffers
, COUNTER
);
807 INIT_COUNTER( displayed_pictures
, COUNTER
);
808 INIT_COUNTER( lost_pictures
, COUNTER
);
809 INIT_COUNTER( decoded_audio
, COUNTER
);
810 INIT_COUNTER( decoded_video
, COUNTER
);
811 INIT_COUNTER( decoded_sub
, COUNTER
);
812 p_input
->p
->counters
.p_sout_send_bitrate
= NULL
;
813 p_input
->p
->counters
.p_sout_sent_packets
= NULL
;
814 p_input
->p
->counters
.p_sout_sent_bytes
= NULL
;
819 static int InitSout( input_thread_t
* p_input
)
821 if( p_input
->b_preparsing
)
824 /* Find a usable sout and attach it to p_input */
825 char *psz
= var_GetNonEmptyString( p_input
, "sout" );
826 if( psz
&& strncasecmp( p_input
->p
->p_item
->psz_uri
, "vlc:", 4 ) )
828 p_input
->p
->p_sout
= input_resource_RequestSout( p_input
->p
->p_resource
, NULL
, psz
);
829 if( !p_input
->p
->p_sout
)
831 input_ChangeState( p_input
, ERROR_S
);
832 msg_Err( p_input
, "cannot start stream output instance, " \
837 if( libvlc_stats( p_input
) )
839 INIT_COUNTER( sout_sent_packets
, COUNTER
);
840 INIT_COUNTER( sout_sent_bytes
, COUNTER
);
841 INIT_COUNTER( sout_send_bitrate
, DERIVATIVE
);
846 input_resource_RequestSout( p_input
->p
->p_resource
, NULL
, NULL
);
854 static void InitTitle( input_thread_t
* p_input
)
856 input_source_t
*p_master
= &p_input
->p
->input
;
858 if( p_input
->b_preparsing
)
861 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
862 /* Create global title (from master) */
863 p_input
->p
->i_title
= p_master
->i_title
;
864 p_input
->p
->title
= p_master
->title
;
865 p_input
->p
->i_title_offset
= p_master
->i_title_offset
;
866 p_input
->p
->i_seekpoint_offset
= p_master
->i_seekpoint_offset
;
867 if( p_input
->p
->i_title
> 0 )
869 /* Setup variables */
870 input_ControlVarNavigation( p_input
);
871 input_SendEventTitle( p_input
, 0 );
875 p_input
->p
->b_can_pace_control
= p_master
->b_can_pace_control
;
876 p_input
->p
->b_can_pause
= p_master
->b_can_pause
;
877 p_input
->p
->b_can_rate_control
= p_master
->b_can_rate_control
;
878 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
881 static void StartTitle( input_thread_t
* p_input
)
885 /* Start title/chapter */
886 val
.i_int
= p_input
->p
->input
.i_title_start
-
887 p_input
->p
->input
.i_title_offset
;
888 if( val
.i_int
> 0 && val
.i_int
< p_input
->p
->input
.i_title
)
889 input_ControlPush( p_input
, INPUT_CONTROL_SET_TITLE
, &val
);
891 val
.i_int
= p_input
->p
->input
.i_seekpoint_start
-
892 p_input
->p
->input
.i_seekpoint_offset
;
893 if( val
.i_int
> 0 /* TODO: check upper boundary */ )
894 input_ControlPush( p_input
, INPUT_CONTROL_SET_SEEKPOINT
, &val
);
896 /* Start/stop/run time */
897 p_input
->p
->i_start
= llroundf(1000000.f
898 * var_GetFloat( p_input
, "start-time" ));
899 p_input
->p
->i_stop
= llroundf(1000000.f
900 * var_GetFloat( p_input
, "stop-time" ));
901 p_input
->p
->i_run
= llroundf(1000000.f
902 * var_GetFloat( p_input
, "run-time" ));
903 if( p_input
->p
->i_run
< 0 )
905 msg_Warn( p_input
, "invalid run-time ignored" );
906 p_input
->p
->i_run
= 0;
909 if( p_input
->p
->i_start
> 0 )
913 msg_Dbg( p_input
, "starting at time: %ds",
914 (int)( p_input
->p
->i_start
/ CLOCK_FREQ
) );
916 s
.i_int
= p_input
->p
->i_start
;
917 input_ControlPush( p_input
, INPUT_CONTROL_SET_TIME
, &s
);
919 if( p_input
->p
->i_stop
> 0 && p_input
->p
->i_stop
<= p_input
->p
->i_start
)
921 msg_Warn( p_input
, "invalid stop-time ignored" );
922 p_input
->p
->i_stop
= 0;
924 p_input
->p
->b_fast_seek
= var_GetBool( p_input
, "input-fast-seek" );
927 static void LoadSubtitles( input_thread_t
*p_input
)
930 /* Get fps and set it if not already set */
931 const float f_fps
= p_input
->p
->f_fps
;
934 var_Create( p_input
, "sub-original-fps", VLC_VAR_FLOAT
);
935 var_SetFloat( p_input
, "sub-original-fps", f_fps
);
937 float f_requested_fps
= var_CreateGetFloat( p_input
, "sub-fps" );
938 if( f_requested_fps
!= f_fps
)
940 var_Create( p_input
, "sub-fps", VLC_VAR_FLOAT
|
942 var_SetFloat( p_input
, "sub-fps", f_requested_fps
);
946 const int i_delay
= var_CreateGetInteger( p_input
, "sub-delay" );
948 var_SetInteger( p_input
, "spu-delay", (mtime_t
)i_delay
* 100000 );
950 /* Look for and add subtitle files */
951 unsigned i_flags
= SUB_FORCED
;
953 char *psz_subtitle
= var_GetNonEmptyString( p_input
, "sub-file" );
954 if( psz_subtitle
!= NULL
)
956 msg_Dbg( p_input
, "forced subtitle: %s", psz_subtitle
);
957 input_SubtitleFileAdd( p_input
, psz_subtitle
, i_flags
);
958 i_flags
= SUB_NOFLAG
;
961 if( var_GetBool( p_input
, "sub-autodetect-file" ) )
963 char *psz_autopath
= var_GetNonEmptyString( p_input
, "sub-autodetect-path" );
964 char **ppsz_subs
= subtitles_Detect( p_input
, psz_autopath
,
965 p_input
->p
->p_item
->psz_uri
);
966 free( psz_autopath
);
968 for( int i
= 0; ppsz_subs
&& ppsz_subs
[i
]; i
++ )
970 if( !psz_subtitle
|| strcmp( psz_subtitle
, ppsz_subs
[i
] ) )
972 i_flags
|= SUB_CANFAIL
;
973 input_SubtitleFileAdd( p_input
, ppsz_subs
[i
], i_flags
);
974 i_flags
= SUB_NOFLAG
;
977 free( ppsz_subs
[i
] );
981 free( psz_subtitle
);
983 /* Load subtitles from attachments */
984 int i_attachment
= 0;
985 input_attachment_t
**pp_attachment
= NULL
;
987 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
988 for( int i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
990 const input_attachment_t
*a
= p_input
->p
->attachment
[i
];
991 if( !strcmp( a
->psz_mime
, "application/x-srt" ) )
992 TAB_APPEND( i_attachment
, pp_attachment
,
993 vlc_input_attachment_New( a
->psz_name
, NULL
,
994 a
->psz_description
, NULL
, 0 ) );
996 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
998 if( i_attachment
> 0 )
999 var_Create( p_input
, "sub-description", VLC_VAR_STRING
);
1000 for( int i
= 0; i
< i_attachment
; i
++ )
1002 input_attachment_t
*a
= pp_attachment
[i
];
1006 if( a
->psz_name
[0] &&
1007 asprintf( &psz_mrl
, "attachment://%s", a
->psz_name
) >= 0 )
1009 var_SetString( p_input
, "sub-description", a
->psz_description
? a
->psz_description
: "");
1011 input_SubtitleAdd( p_input
, psz_mrl
, i_flags
);
1013 i_flags
= SUB_NOFLAG
;
1016 vlc_input_attachment_Delete( a
);
1018 free( pp_attachment
);
1019 if( i_attachment
> 0 )
1020 var_Destroy( p_input
, "sub-description" );
1023 static void LoadSlaves( input_thread_t
*p_input
)
1025 char *psz
= var_GetNonEmptyString( p_input
, "input-slave" );
1029 char *psz_org
= psz
;
1030 while( psz
&& *psz
)
1032 while( *psz
== ' ' || *psz
== '#' )
1035 char *psz_delim
= strchr( psz
, '#' );
1037 *psz_delim
++ = '\0';
1042 char *uri
= strstr(psz
, "://")
1043 ? strdup( psz
) : vlc_path2uri( psz
, NULL
);
1047 msg_Dbg( p_input
, "adding slave input '%s'", uri
);
1049 input_source_t
*p_slave
= InputSourceNew( p_input
);
1050 if( p_slave
&& !InputSourceInit( p_input
, p_slave
, uri
, NULL
, false ) )
1051 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, p_slave
);
1059 static void UpdatePtsDelay( input_thread_t
*p_input
)
1061 input_thread_private_t
*p_sys
= p_input
->p
;
1063 /* Get max pts delay from input source */
1064 mtime_t i_pts_delay
= p_sys
->input
.i_pts_delay
;
1065 for( int i
= 0; i
< p_sys
->i_slave
; i
++ )
1066 i_pts_delay
= __MAX( i_pts_delay
, p_sys
->slave
[i
]->i_pts_delay
);
1068 if( i_pts_delay
< 0 )
1071 /* Take care of audio/spu delay */
1072 const mtime_t i_audio_delay
= var_GetInteger( p_input
, "audio-delay" );
1073 const mtime_t i_spu_delay
= var_GetInteger( p_input
, "spu-delay" );
1074 const mtime_t i_extra_delay
= __MIN( i_audio_delay
, i_spu_delay
);
1075 if( i_extra_delay
< 0 )
1076 i_pts_delay
-= i_extra_delay
;
1078 /* Update cr_average depending on the caching */
1079 const int i_cr_average
= var_GetInteger( p_input
, "cr-average" ) * i_pts_delay
/ DEFAULT_PTS_DELAY
;
1082 es_out_SetDelay( p_input
->p
->p_es_out_display
, AUDIO_ES
, i_audio_delay
);
1083 es_out_SetDelay( p_input
->p
->p_es_out_display
, SPU_ES
, i_spu_delay
);
1084 es_out_SetJitter( p_input
->p
->p_es_out
, i_pts_delay
, 0, i_cr_average
);
1087 static void InitPrograms( input_thread_t
* p_input
)
1092 /* Compute correct pts_delay */
1093 UpdatePtsDelay( p_input
);
1096 i_es_out_mode
= ES_OUT_MODE_AUTO
;
1097 if( p_input
->p
->p_sout
)
1101 if( (prgms
= var_GetNonEmptyString( p_input
, "programs" )) != NULL
)
1105 TAB_INIT( list
.i_count
, list
.p_values
);
1106 for( const char *prgm
= strtok_r( prgms
, ",", &buf
);
1108 prgm
= strtok_r( NULL
, ",", &buf
) )
1110 vlc_value_t val
= { .i_int
= atoi( prgm
) };
1111 INSERT_ELEM( list
.p_values
, list
.i_count
, list
.i_count
, val
);
1114 if( list
.i_count
> 0 )
1115 i_es_out_mode
= ES_OUT_MODE_PARTIAL
;
1116 /* Note : we should remove the "program" callback. */
1120 else if( var_GetBool( p_input
, "sout-all" ) )
1122 i_es_out_mode
= ES_OUT_MODE_ALL
;
1125 es_out_SetMode( p_input
->p
->p_es_out
, i_es_out_mode
);
1127 /* Inform the demuxer about waited group (needed only for DVB) */
1128 if( i_es_out_mode
== ES_OUT_MODE_ALL
)
1130 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, -1, NULL
);
1132 else if( i_es_out_mode
== ES_OUT_MODE_PARTIAL
)
1134 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, -1,
1136 TAB_CLEAN( list
.i_count
, list
.p_values
);
1140 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
,
1141 es_out_GetGroupForced( p_input
->p
->p_es_out
), NULL
);
1145 static int Init( input_thread_t
* p_input
)
1147 for( int i
= 0; i
< p_input
->p
->p_item
->i_options
; i
++ )
1149 if( !strncmp( p_input
->p
->p_item
->ppsz_options
[i
], "meta-file", 9 ) )
1151 msg_Dbg( p_input
, "Input is a meta file: disabling unneeded options" );
1152 var_SetString( p_input
, "sout", "" );
1153 var_SetBool( p_input
, "sout-all", false );
1154 var_SetString( p_input
, "input-slave", "" );
1155 var_SetInteger( p_input
, "input-repeat", 0 );
1156 var_SetString( p_input
, "sub-file", "" );
1157 var_SetBool( p_input
, "sub-autodetect-file", false );
1161 InitStatistics( p_input
);
1163 if( InitSout( p_input
) )
1168 p_input
->p
->p_es_out
= input_EsOutTimeshiftNew( p_input
, p_input
->p
->p_es_out_display
, p_input
->p
->i_rate
);
1171 input_ChangeState( p_input
, OPENING_S
);
1172 input_SendEventCache( p_input
, 0.0 );
1175 if( InputSourceInit( p_input
, &p_input
->p
->input
,
1176 p_input
->p
->p_item
->psz_uri
, NULL
, false ) )
1181 InitTitle( p_input
);
1183 /* Load master infos */
1186 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_LENGTH
,
1190 i_length
= input_item_GetDuration( p_input
->p
->p_item
);
1191 input_SendEventLength( p_input
, i_length
);
1193 input_SendEventPosition( p_input
, 0.0, 0 );
1195 if( !p_input
->b_preparsing
)
1197 StartTitle( p_input
);
1198 LoadSubtitles( p_input
);
1199 LoadSlaves( p_input
);
1200 InitPrograms( p_input
);
1202 double f_rate
= var_InheritFloat( p_input
, "rate" );
1203 if( f_rate
!= 0.0 && f_rate
!= 1.0 )
1205 vlc_value_t val
= { .i_int
= INPUT_RATE_DEFAULT
/ f_rate
};
1206 input_ControlPush( p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1210 if( !p_input
->b_preparsing
&& p_input
->p
->p_sout
)
1212 p_input
->p
->b_out_pace_control
= (p_input
->p
->p_sout
->i_out_pace_nocontrol
> 0);
1214 if( p_input
->p
->b_can_pace_control
&& p_input
->p
->b_out_pace_control
)
1216 /* We don't want a high input priority here or we'll
1217 * end-up sucking up all the CPU time */
1218 vlc_set_priority( p_input
->p
->thread
, VLC_THREAD_PRIORITY_LOW
);
1221 msg_Dbg( p_input
, "starting in %s mode",
1222 p_input
->p
->b_out_pace_control
? "async" : "sync" );
1225 vlc_meta_t
*p_meta
= vlc_meta_New();
1226 if( p_meta
!= NULL
)
1228 /* Get meta data from users */
1229 InputMetaUser( p_input
, p_meta
);
1231 /* Get meta data from master input */
1232 InputSourceMeta( p_input
, &p_input
->p
->input
, p_meta
);
1234 /* And from slave */
1235 for( int i
= 0; i
< p_input
->p
->i_slave
; i
++ )
1236 InputSourceMeta( p_input
, p_input
->p
->slave
[i
], p_meta
);
1238 es_out_ControlSetMeta( p_input
->p
->p_es_out
, p_meta
);
1239 vlc_meta_Delete( p_meta
);
1242 msg_Dbg( p_input
, "`%s' successfully opened",
1243 p_input
->p
->p_item
->psz_uri
);
1245 /* initialization is complete */
1246 input_ChangeState( p_input
, PLAYING_S
);
1251 input_ChangeState( p_input
, ERROR_S
);
1253 if( p_input
->p
->p_es_out
)
1254 es_out_Delete( p_input
->p
->p_es_out
);
1255 es_out_SetMode( p_input
->p
->p_es_out_display
, ES_OUT_MODE_END
);
1256 if( p_input
->p
->p_resource
)
1258 if( p_input
->p
->p_sout
)
1259 input_resource_RequestSout( p_input
->p
->p_resource
,
1260 p_input
->p
->p_sout
, NULL
);
1261 input_resource_SetInput( p_input
->p
->p_resource
, NULL
);
1262 if( p_input
->p
->p_resource_private
)
1263 input_resource_Terminate( p_input
->p
->p_resource_private
);
1266 if( !p_input
->b_preparsing
&& libvlc_stats( p_input
) )
1268 #define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \
1269 stats_CounterClean( p_input->p->counters.p_##c );\
1270 p_input->p->counters.p_##c = NULL; } while(0)
1271 EXIT_COUNTER( read_bytes
);
1272 EXIT_COUNTER( read_packets
);
1273 EXIT_COUNTER( demux_read
);
1274 EXIT_COUNTER( input_bitrate
);
1275 EXIT_COUNTER( demux_bitrate
);
1276 EXIT_COUNTER( demux_corrupted
);
1277 EXIT_COUNTER( demux_discontinuity
);
1278 EXIT_COUNTER( played_abuffers
);
1279 EXIT_COUNTER( lost_abuffers
);
1280 EXIT_COUNTER( displayed_pictures
);
1281 EXIT_COUNTER( lost_pictures
);
1282 EXIT_COUNTER( decoded_audio
);
1283 EXIT_COUNTER( decoded_video
);
1284 EXIT_COUNTER( decoded_sub
);
1286 if( p_input
->p
->p_sout
)
1288 EXIT_COUNTER( sout_sent_packets
);
1289 EXIT_COUNTER( sout_sent_bytes
);
1290 EXIT_COUNTER( sout_send_bitrate
);
1295 /* Mark them deleted */
1296 p_input
->p
->input
.p_demux
= NULL
;
1297 p_input
->p
->p_es_out
= NULL
;
1298 p_input
->p
->p_sout
= NULL
;
1300 return VLC_EGENERIC
;
1303 /*****************************************************************************
1304 * End: end the input thread
1305 *****************************************************************************/
1306 static void End( input_thread_t
* p_input
)
1310 /* We are at the end */
1311 input_ChangeState( p_input
, END_S
);
1313 /* Clean control variables */
1314 input_ControlVarStop( p_input
);
1316 /* Stop es out activity */
1317 es_out_SetMode( p_input
->p
->p_es_out
, ES_OUT_MODE_NONE
);
1319 /* Clean up master */
1320 InputSourceClean( &p_input
->p
->input
);
1323 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
1325 InputSourceClean( p_input
->p
->slave
[i
] );
1326 free( p_input
->p
->slave
[i
] );
1328 free( p_input
->p
->slave
);
1330 /* Unload all modules */
1331 if( p_input
->p
->p_es_out
)
1332 es_out_Delete( p_input
->p
->p_es_out
);
1333 es_out_SetMode( p_input
->p
->p_es_out_display
, ES_OUT_MODE_END
);
1335 if( !p_input
->b_preparsing
)
1337 #define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL;
1338 if( libvlc_stats( p_input
) )
1340 /* make sure we are up to date */
1341 stats_ComputeInputStats( p_input
, p_input
->p
->p_item
->p_stats
);
1342 CL_CO( read_bytes
);
1343 CL_CO( read_packets
);
1344 CL_CO( demux_read
);
1345 CL_CO( input_bitrate
);
1346 CL_CO( demux_bitrate
);
1347 CL_CO( demux_corrupted
);
1348 CL_CO( demux_discontinuity
);
1349 CL_CO( played_abuffers
);
1350 CL_CO( lost_abuffers
);
1351 CL_CO( displayed_pictures
);
1352 CL_CO( lost_pictures
);
1353 CL_CO( decoded_audio
) ;
1354 CL_CO( decoded_video
);
1355 CL_CO( decoded_sub
) ;
1358 /* Close optional stream output instance */
1359 if( p_input
->p
->p_sout
)
1361 CL_CO( sout_sent_packets
);
1362 CL_CO( sout_sent_bytes
);
1363 CL_CO( sout_send_bitrate
);
1368 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
1369 if( p_input
->p
->i_attachment
> 0 )
1371 for( i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
1372 vlc_input_attachment_Delete( p_input
->p
->attachment
[i
] );
1373 TAB_CLEAN( p_input
->p
->i_attachment
, p_input
->p
->attachment
);
1374 free( p_input
->p
->attachment_demux
);
1375 p_input
->p
->attachment_demux
= NULL
;
1377 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
1380 input_resource_RequestSout( p_input
->p
->p_resource
,
1381 p_input
->p
->p_sout
, NULL
);
1382 input_resource_SetInput( p_input
->p
->p_resource
, NULL
);
1383 if( p_input
->p
->p_resource_private
)
1384 input_resource_Terminate( p_input
->p
->p_resource_private
);
1387 /*****************************************************************************
1389 *****************************************************************************/
1390 void input_ControlPush( input_thread_t
*p_input
,
1391 int i_type
, vlc_value_t
*p_val
)
1393 input_thread_private_t
*sys
= p_input
->p
;
1395 vlc_mutex_lock( &sys
->lock_control
);
1396 if( sys
->is_stopped
)
1398 else if( sys
->i_control
>= INPUT_CONTROL_FIFO_SIZE
)
1400 msg_Err( p_input
, "input control fifo overflow, trashing type=%d",
1403 ControlRelease( i_type
, *p_val
);
1412 memset( &c
.val
, 0, sizeof(c
.val
) );
1414 sys
->control
[sys
->i_control
++] = c
;
1416 vlc_cond_signal( &sys
->wait_control
);
1417 vlc_mutex_unlock( &sys
->lock_control
);
1420 static int ControlGetReducedIndexLocked( input_thread_t
*p_input
)
1422 const int i_lt
= p_input
->p
->control
[0].i_type
;
1424 for( i
= 1; i
< p_input
->p
->i_control
; i
++ )
1426 const int i_ct
= p_input
->p
->control
[i
].i_type
;
1429 ( i_ct
== INPUT_CONTROL_SET_STATE
||
1430 i_ct
== INPUT_CONTROL_SET_RATE
||
1431 i_ct
== INPUT_CONTROL_SET_POSITION
||
1432 i_ct
== INPUT_CONTROL_SET_TIME
||
1433 i_ct
== INPUT_CONTROL_SET_PROGRAM
||
1434 i_ct
== INPUT_CONTROL_SET_TITLE
||
1435 i_ct
== INPUT_CONTROL_SET_SEEKPOINT
||
1436 i_ct
== INPUT_CONTROL_SET_BOOKMARK
) )
1442 /* TODO but that's not that important
1443 - merge SET_X with SET_X_CMD
1444 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
1445 - ignore SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
1455 static inline int ControlPop( input_thread_t
*p_input
,
1456 int *pi_type
, vlc_value_t
*p_val
,
1457 mtime_t i_deadline
, bool b_postpone_seek
)
1459 input_thread_private_t
*p_sys
= p_input
->p
;
1461 vlc_mutex_lock( &p_sys
->lock_control
);
1462 while( p_sys
->i_control
<= 0 ||
1463 ( b_postpone_seek
&& ControlIsSeekRequest( p_sys
->control
[0].i_type
) ) )
1465 if( p_sys
->is_stopped
)
1467 vlc_mutex_unlock( &p_sys
->lock_control
);
1468 return VLC_EGENERIC
;
1471 if( i_deadline
>= 0 )
1473 if( vlc_cond_timedwait( &p_sys
->wait_control
, &p_sys
->lock_control
,
1476 vlc_mutex_unlock( &p_sys
->lock_control
);
1477 return VLC_EGENERIC
;
1481 vlc_cond_wait( &p_sys
->wait_control
, &p_sys
->lock_control
);
1485 const int i_index
= ControlGetReducedIndexLocked( p_input
);
1488 *pi_type
= p_sys
->control
[i_index
].i_type
;
1489 *p_val
= p_sys
->control
[i_index
].val
;
1491 p_sys
->i_control
-= i_index
+ 1;
1492 if( p_sys
->i_control
> 0 )
1493 memmove( &p_sys
->control
[0], &p_sys
->control
[i_index
+1],
1494 sizeof(*p_sys
->control
) * p_sys
->i_control
);
1495 vlc_mutex_unlock( &p_sys
->lock_control
);
1499 static bool ControlIsSeekRequest( int i_type
)
1503 case INPUT_CONTROL_SET_POSITION
:
1504 case INPUT_CONTROL_SET_TIME
:
1505 case INPUT_CONTROL_SET_TITLE
:
1506 case INPUT_CONTROL_SET_TITLE_NEXT
:
1507 case INPUT_CONTROL_SET_TITLE_PREV
:
1508 case INPUT_CONTROL_SET_SEEKPOINT
:
1509 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1510 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1511 case INPUT_CONTROL_SET_BOOKMARK
:
1512 case INPUT_CONTROL_NAV_ACTIVATE
:
1513 case INPUT_CONTROL_NAV_UP
:
1514 case INPUT_CONTROL_NAV_DOWN
:
1515 case INPUT_CONTROL_NAV_LEFT
:
1516 case INPUT_CONTROL_NAV_RIGHT
:
1523 static void ControlRelease( int i_type
, vlc_value_t val
)
1527 case INPUT_CONTROL_ADD_SUBTITLE
:
1528 case INPUT_CONTROL_ADD_SLAVE
:
1529 free( val
.psz_string
);
1538 static void ControlPause( input_thread_t
*p_input
, mtime_t i_control_date
)
1540 int i_ret
= VLC_SUCCESS
;
1541 int i_state
= PAUSE_S
;
1543 if( p_input
->p
->b_can_pause
)
1545 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1547 if( p_demux
->s
!= NULL
)
1548 i_ret
= stream_Control( p_demux
->s
, STREAM_SET_PAUSE_STATE
, true );
1550 i_ret
= demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, true );
1554 msg_Warn( p_input
, "cannot set pause state" );
1560 i_ret
= es_out_SetPauseState( p_input
->p
->p_es_out
,
1561 p_input
->p
->b_can_pause
, true,
1565 msg_Warn( p_input
, "cannot set pause state at es_out level" );
1569 /* Switch to new state */
1570 input_ChangeState( p_input
, i_state
);
1573 static void ControlUnpause( input_thread_t
*p_input
, mtime_t i_control_date
)
1575 if( p_input
->p
->b_can_pause
)
1577 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1580 if( p_demux
->s
!= NULL
)
1581 ret
= stream_Control( p_demux
->s
, STREAM_SET_PAUSE_STATE
, false );
1583 ret
= demux_Control( p_demux
, DEMUX_SET_PAUSE_STATE
, false );
1584 if( ret
!= VLC_SUCCESS
)
1586 msg_Err( p_input
, "cannot resume" );
1587 input_ChangeState( p_input
, ERROR_S
);
1592 /* Switch to play */
1593 input_ChangeState( p_input
, PLAYING_S
);
1594 es_out_SetPauseState( p_input
->p
->p_es_out
, false, false, i_control_date
);
1597 static bool Control( input_thread_t
*p_input
,
1598 int i_type
, vlc_value_t val
)
1600 const mtime_t i_control_date
= mdate();
1601 /* FIXME b_force_update is abused, it should be carefully checked */
1602 bool b_force_update
= false;
1605 return b_force_update
;
1609 case INPUT_CONTROL_SET_POSITION
:
1611 if( p_input
->p
->b_recording
)
1613 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
1617 float f_pos
= val
.f_float
;
1620 else if( f_pos
> 1.f
)
1622 /* Reset the decoders states and clock sync (before calling the demuxer */
1623 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1624 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_POSITION
,
1625 (double) f_pos
, !p_input
->p
->b_fast_seek
) )
1627 msg_Err( p_input
, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
1628 "%2.1f%% failed", (double)(f_pos
* 100.f
) );
1632 if( p_input
->p
->i_slave
> 0 )
1633 SlaveSeek( p_input
);
1634 p_input
->p
->input
.b_eof
= false;
1636 b_force_update
= true;
1641 case INPUT_CONTROL_SET_TIME
:
1646 if( p_input
->p
->b_recording
)
1648 msg_Err( p_input
, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
1656 /* Reset the decoders states and clock sync (before calling the demuxer */
1657 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1659 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1660 DEMUX_SET_TIME
, i_time
,
1661 !p_input
->p
->b_fast_seek
);
1666 /* Emulate it with a SET_POS */
1667 if( !demux_Control( p_input
->p
->input
.p_demux
,
1668 DEMUX_GET_LENGTH
, &i_length
) && i_length
> 0 )
1670 double f_pos
= (double)i_time
/ (double)i_length
;
1671 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1672 DEMUX_SET_POSITION
, f_pos
,
1673 !p_input
->p
->b_fast_seek
);
1678 msg_Warn( p_input
, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64
1679 " failed or not possible", i_time
);
1683 if( p_input
->p
->i_slave
> 0 )
1684 SlaveSeek( p_input
);
1685 p_input
->p
->input
.b_eof
= false;
1687 b_force_update
= true;
1692 case INPUT_CONTROL_SET_STATE
:
1696 if( p_input
->p
->i_state
== PAUSE_S
)
1698 ControlUnpause( p_input
, i_control_date
);
1699 b_force_update
= true;
1703 if( p_input
->p
->i_state
== PLAYING_S
)
1705 ControlPause( p_input
, i_control_date
);
1706 b_force_update
= true;
1710 msg_Err( p_input
, "invalid INPUT_CONTROL_SET_STATE" );
1714 case INPUT_CONTROL_SET_RATE
:
1716 /* Get rate and direction */
1717 int i_rate
= abs( val
.i_int
);
1718 int i_rate_sign
= val
.i_int
< 0 ? -1 : 1;
1720 /* Check rate bound */
1721 if( i_rate
< INPUT_RATE_MIN
)
1723 msg_Dbg( p_input
, "cannot set rate faster" );
1724 i_rate
= INPUT_RATE_MIN
;
1726 else if( i_rate
> INPUT_RATE_MAX
)
1728 msg_Dbg( p_input
, "cannot set rate slower" );
1729 i_rate
= INPUT_RATE_MAX
;
1732 /* Apply direction */
1733 if( i_rate_sign
< 0 )
1735 if( p_input
->p
->input
.b_rescale_ts
)
1737 msg_Dbg( p_input
, "cannot set negative rate" );
1738 i_rate
= p_input
->p
->i_rate
;
1739 assert( i_rate
> 0 );
1743 i_rate
*= i_rate_sign
;
1747 if( i_rate
!= INPUT_RATE_DEFAULT
&&
1748 ( ( !p_input
->p
->b_can_rate_control
&& !p_input
->p
->input
.b_rescale_ts
) ||
1749 ( p_input
->p
->p_sout
&& !p_input
->p
->b_out_pace_control
) ) )
1751 msg_Dbg( p_input
, "cannot change rate" );
1752 i_rate
= INPUT_RATE_DEFAULT
;
1754 if( i_rate
!= p_input
->p
->i_rate
&&
1755 !p_input
->p
->b_can_pace_control
&& p_input
->p
->b_can_rate_control
)
1757 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1758 int i_ret
= VLC_EGENERIC
;
1760 if( p_demux
->s
== NULL
)
1762 if( !p_input
->p
->input
.b_rescale_ts
)
1763 es_out_Control( p_input
->p
->p_es_out
, ES_OUT_RESET_PCR
);
1765 i_ret
= demux_Control( p_input
->p
->input
.p_demux
,
1766 DEMUX_SET_RATE
, &i_rate
);
1770 msg_Warn( p_input
, "ACCESS/DEMUX_SET_RATE failed" );
1771 i_rate
= p_input
->p
->i_rate
;
1776 if( i_rate
!= p_input
->p
->i_rate
)
1778 p_input
->p
->i_rate
= i_rate
;
1779 input_SendEventRate( p_input
, i_rate
);
1781 if( p_input
->p
->input
.b_rescale_ts
)
1783 const int i_rate_source
= (p_input
->p
->b_can_pace_control
|| p_input
->p
->b_can_rate_control
) ? i_rate
: INPUT_RATE_DEFAULT
;
1784 es_out_SetRate( p_input
->p
->p_es_out
, i_rate_source
, i_rate
);
1787 b_force_update
= true;
1792 case INPUT_CONTROL_SET_PROGRAM
:
1793 /* No need to force update, es_out does it if needed */
1794 es_out_Control( p_input
->p
->p_es_out
,
1795 ES_OUT_SET_GROUP
, val
.i_int
);
1797 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_GROUP
, val
.i_int
,
1801 case INPUT_CONTROL_SET_ES
:
1802 /* No need to force update, es_out does it if needed */
1803 es_out_Control( p_input
->p
->p_es_out_display
,
1804 ES_OUT_SET_ES_BY_ID
, (int)val
.i_int
);
1806 demux_Control( p_input
->p
->input
.p_demux
, DEMUX_SET_ES
, (int)val
.i_int
);
1809 case INPUT_CONTROL_RESTART_ES
:
1810 es_out_Control( p_input
->p
->p_es_out_display
,
1811 ES_OUT_RESTART_ES_BY_ID
, (int)val
.i_int
);
1814 case INPUT_CONTROL_SET_AUDIO_DELAY
:
1815 input_SendEventAudioDelay( p_input
, val
.i_int
);
1816 UpdatePtsDelay( p_input
);
1819 case INPUT_CONTROL_SET_SPU_DELAY
:
1820 input_SendEventSubtitleDelay( p_input
, val
.i_int
);
1821 UpdatePtsDelay( p_input
);
1824 case INPUT_CONTROL_SET_TITLE
:
1825 case INPUT_CONTROL_SET_TITLE_NEXT
:
1826 case INPUT_CONTROL_SET_TITLE_PREV
:
1828 if( p_input
->p
->b_recording
)
1830 msg_Err( p_input
, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
1833 if( p_input
->p
->input
.i_title
<= 0 )
1836 int i_title
= p_input
->p
->input
.p_demux
->info
.i_title
;
1837 if( i_type
== INPUT_CONTROL_SET_TITLE_PREV
)
1839 else if( i_type
== INPUT_CONTROL_SET_TITLE_NEXT
)
1842 i_title
= val
.i_int
;
1843 if( i_title
< 0 || i_title
>= p_input
->p
->input
.i_title
)
1846 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1847 demux_Control( p_input
->p
->input
.p_demux
,
1848 DEMUX_SET_TITLE
, i_title
);
1849 input_SendEventTitle( p_input
, i_title
);
1852 case INPUT_CONTROL_SET_SEEKPOINT
:
1853 case INPUT_CONTROL_SET_SEEKPOINT_NEXT
:
1854 case INPUT_CONTROL_SET_SEEKPOINT_PREV
:
1856 if( p_input
->p
->b_recording
)
1858 msg_Err( p_input
, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
1861 if( p_input
->p
->input
.i_title
<= 0 )
1864 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
1866 int i_title
= p_demux
->info
.i_title
;
1867 int i_seekpoint
= p_demux
->info
.i_seekpoint
;
1869 if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_PREV
)
1871 int64_t i_seekpoint_time
= p_input
->p
->input
.title
[i_title
]->seekpoint
[i_seekpoint
]->i_time_offset
;
1872 int64_t i_input_time
= var_GetInteger( p_input
, "time" );
1873 if( i_seekpoint_time
>= 0 && i_input_time
>= 0 )
1875 if( i_input_time
< i_seekpoint_time
+ 3000000 )
1881 else if( i_type
== INPUT_CONTROL_SET_SEEKPOINT_NEXT
)
1884 i_seekpoint
= val
.i_int
;
1886 || i_seekpoint
>= p_input
->p
->input
.title
[i_title
]->i_seekpoint
)
1889 es_out_SetTime( p_input
->p
->p_es_out
, -1 );
1890 demux_Control( p_input
->p
->input
.p_demux
,
1891 DEMUX_SET_SEEKPOINT
, i_seekpoint
);
1892 input_SendEventSeekpoint( p_input
, i_title
, i_seekpoint
);
1896 case INPUT_CONTROL_ADD_SUBTITLE
:
1897 if( val
.psz_string
)
1898 input_SubtitleFileAdd( p_input
, val
.psz_string
, true );
1901 case INPUT_CONTROL_ADD_SLAVE
:
1902 if( val
.psz_string
)
1904 const char *uri
= val
.psz_string
;
1905 input_source_t
*slave
= InputSourceNew( p_input
);
1907 if( slave
&& !InputSourceInit( p_input
, slave
, uri
, NULL
, false ) )
1912 msg_Dbg( p_input
, "adding %s as slave on the fly", uri
);
1915 if( demux_Control( p_input
->p
->input
.p_demux
,
1916 DEMUX_GET_TIME
, &i_time
) )
1918 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
1919 InputSourceClean( slave
);
1923 if( demux_Control( slave
->p_demux
,
1924 DEMUX_SET_TIME
, i_time
, true ) )
1926 msg_Err( p_input
, "seek failed for new slave" );
1927 InputSourceClean( slave
);
1932 /* Get meta (access and demux) */
1933 InputUpdateMeta( p_input
, slave
->p_demux
);
1935 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, slave
);
1940 msg_Warn( p_input
, "failed to add %s as slave", uri
);
1945 case INPUT_CONTROL_SET_RECORD_STATE
:
1946 if( !!p_input
->p
->b_recording
!= !!val
.b_bool
)
1948 if( p_input
->p
->input
.b_can_stream_record
)
1950 if( demux_Control( p_input
->p
->input
.p_demux
,
1951 DEMUX_SET_RECORD_STATE
, val
.b_bool
) )
1956 if( es_out_SetRecordState( p_input
->p
->p_es_out_display
, val
.b_bool
) )
1959 p_input
->p
->b_recording
= val
.b_bool
;
1961 input_SendEventRecord( p_input
, val
.b_bool
);
1963 b_force_update
= true;
1967 case INPUT_CONTROL_SET_FRAME_NEXT
:
1968 if( p_input
->p
->i_state
== PAUSE_S
)
1970 es_out_SetFrameNext( p_input
->p
->p_es_out
);
1972 else if( p_input
->p
->i_state
== PLAYING_S
)
1974 ControlPause( p_input
, i_control_date
);
1978 msg_Err( p_input
, "invalid state for frame next" );
1980 b_force_update
= true;
1983 case INPUT_CONTROL_SET_BOOKMARK
:
1985 mtime_t time_offset
= -1;
1987 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
1988 if( val
.i_int
>= 0 && val
.i_int
< p_input
->p
->i_bookmark
)
1990 const seekpoint_t
*p_bookmark
= p_input
->p
->pp_bookmark
[val
.i_int
];
1991 time_offset
= p_bookmark
->i_time_offset
;
1993 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
1995 if( time_offset
< 0 )
1997 msg_Err( p_input
, "invalid bookmark %"PRId64
, val
.i_int
);
2001 val
.i_int
= time_offset
;
2002 b_force_update
= Control( p_input
, INPUT_CONTROL_SET_TIME
, val
);
2006 case INPUT_CONTROL_NAV_ACTIVATE
:
2007 case INPUT_CONTROL_NAV_UP
:
2008 case INPUT_CONTROL_NAV_DOWN
:
2009 case INPUT_CONTROL_NAV_LEFT
:
2010 case INPUT_CONTROL_NAV_RIGHT
:
2011 demux_Control( p_input
->p
->input
.p_demux
, i_type
2012 - INPUT_CONTROL_NAV_ACTIVATE
+ DEMUX_NAV_ACTIVATE
);
2016 msg_Err( p_input
, "not yet implemented" );
2020 ControlRelease( i_type
, val
);
2021 return b_force_update
;
2024 /*****************************************************************************
2025 * UpdateTitleSeekpoint
2026 *****************************************************************************/
2027 static int UpdateTitleSeekpoint( input_thread_t
*p_input
,
2028 int i_title
, int i_seekpoint
)
2030 int i_title_end
= p_input
->p
->input
.i_title_end
-
2031 p_input
->p
->input
.i_title_offset
;
2032 int i_seekpoint_end
= p_input
->p
->input
.i_seekpoint_end
-
2033 p_input
->p
->input
.i_seekpoint_offset
;
2035 if( i_title_end
>= 0 && i_seekpoint_end
>= 0 )
2037 if( i_title
> i_title_end
||
2038 ( i_title
== i_title_end
&& i_seekpoint
> i_seekpoint_end
) )
2041 else if( i_seekpoint_end
>= 0 )
2043 if( i_seekpoint
> i_seekpoint_end
)
2046 else if( i_title_end
>= 0 )
2048 if( i_title
> i_title_end
)
2053 /*****************************************************************************
2055 *****************************************************************************/
2056 static int UpdateTitleSeekpointFromDemux( input_thread_t
*p_input
)
2058 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
2060 /* TODO event-like */
2061 if( p_demux
->info
.i_update
& INPUT_UPDATE_TITLE
)
2063 input_SendEventTitle( p_input
, p_demux
->info
.i_title
);
2065 p_demux
->info
.i_update
&= ~INPUT_UPDATE_TITLE
;
2067 if( p_demux
->info
.i_update
& INPUT_UPDATE_SEEKPOINT
)
2069 input_SendEventSeekpoint( p_input
,
2070 p_demux
->info
.i_title
, p_demux
->info
.i_seekpoint
);
2072 p_demux
->info
.i_update
&= ~INPUT_UPDATE_SEEKPOINT
;
2075 /* Hmmm only works with master input */
2076 if( p_input
->p
->input
.p_demux
== p_demux
)
2077 return UpdateTitleSeekpoint( p_input
,
2078 p_demux
->info
.i_title
,
2079 p_demux
->info
.i_seekpoint
);
2083 static void UpdateGenericFromDemux( input_thread_t
*p_input
)
2085 demux_t
*p_demux
= p_input
->p
->input
.p_demux
;
2087 if( p_demux
->info
.i_update
& INPUT_UPDATE_META
)
2089 InputUpdateMeta( p_input
, p_demux
);
2090 p_demux
->info
.i_update
&= ~INPUT_UPDATE_META
;
2096 if( !demux_Control( p_demux
, DEMUX_GET_SIGNAL
, &quality
, &strength
) )
2097 input_SendEventSignal( p_input
, quality
, strength
);
2101 static void UpdateTitleListfromDemux( input_thread_t
*p_input
)
2103 input_source_t
*in
= &p_input
->p
->input
;
2105 /* Delete the preexisting titles */
2106 if( in
->i_title
> 0 )
2108 for( int i
= 0; i
< in
->i_title
; i
++ )
2109 vlc_input_title_Delete( in
->title
[i
] );
2110 TAB_CLEAN( in
->i_title
, in
->title
);
2111 in
->b_title_demux
= false;
2114 /* Get the new title list */
2115 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2116 &in
->title
, &in
->i_title
,
2117 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2118 TAB_INIT( in
->i_title
, in
->title
);
2120 in
->b_title_demux
= true;
2122 InitTitle( p_input
);
2126 /*****************************************************************************
2128 *****************************************************************************/
2129 static input_source_t
*InputSourceNew( input_thread_t
*p_input
)
2131 VLC_UNUSED(p_input
);
2133 return calloc( 1, sizeof( input_source_t
) );
2136 /*****************************************************************************
2138 *****************************************************************************/
2139 static int InputSourceInit( input_thread_t
*p_input
,
2140 input_source_t
*in
, const char *psz_mrl
,
2141 const char *psz_forced_demux
, bool b_in_can_fail
)
2143 const char *psz_access
, *psz_demux
, *psz_path
, *psz_anchor
= NULL
;
2144 char *psz_var_demux
= NULL
;
2148 char *psz_dup
= strdup( psz_mrl
);
2150 if( psz_dup
== NULL
)
2154 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, &psz_anchor
, psz_dup
);
2156 msg_Dbg( p_input
, "`%s' gives access `%s' demux `%s' path `%s'",
2157 psz_mrl
, psz_access
, psz_demux
, psz_path
);
2158 if( !p_input
->b_preparsing
)
2160 /* Find optional titles and seekpoints */
2161 MRLSections( psz_anchor
, &in
->i_title_start
, &in
->i_title_end
,
2162 &in
->i_seekpoint_start
, &in
->i_seekpoint_end
);
2163 if( psz_forced_demux
&& *psz_forced_demux
)
2165 psz_demux
= psz_forced_demux
;
2167 else if( *psz_demux
== '\0' )
2169 /* special hack for forcing a demuxer with --demux=module
2170 * (and do nothing with a list) */
2171 psz_var_demux
= var_GetNonEmptyString( p_input
, "demux" );
2172 psz_demux
= (psz_var_demux
!= NULL
) ? psz_var_demux
: "any";
2173 msg_Dbg( p_input
, "specified demux `%s'", psz_demux
);
2176 /* Try access_demux first */
2177 in
->p_demux
= demux_New( p_input
, p_input
, psz_access
, psz_demux
,
2178 psz_path
, NULL
, p_input
->p
->p_es_out
, false );
2184 msg_Dbg( p_input
, "trying to pre-parse %s", psz_path
);
2187 mtime_t i_pts_delay
;
2191 /* Get infos from access_demux */
2192 in
->b_title_demux
= true;
2193 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2194 &in
->title
, &in
->i_title
,
2195 &in
->i_title_offset
, &in
->i_seekpoint_offset
) )
2197 TAB_INIT( in
->i_title
, in
->title
);
2199 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_PACE
,
2200 &in
->b_can_pace_control
) )
2201 in
->b_can_pace_control
= false;
2203 assert( in
->p_demux
->pf_demux
!= NULL
|| !in
->b_can_pace_control
);
2205 if( !in
->b_can_pace_control
)
2207 if( demux_Control( in
->p_demux
, DEMUX_CAN_CONTROL_RATE
,
2208 &in
->b_can_rate_control
, &in
->b_rescale_ts
) )
2210 in
->b_can_rate_control
= false;
2211 in
->b_rescale_ts
= true; /* not used */
2216 in
->b_can_rate_control
= true;
2217 in
->b_rescale_ts
= true;
2219 if( demux_Control( in
->p_demux
, DEMUX_CAN_PAUSE
,
2220 &in
->b_can_pause
) )
2221 in
->b_can_pause
= false;
2222 var_SetBool( p_input
, "can-pause", in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2223 var_SetBool( p_input
, "can-rate", !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2224 var_SetBool( p_input
, "can-rewind", !in
->b_rescale_ts
&& !in
->b_can_pace_control
&& in
->b_can_rate_control
);
2227 if( demux_Control( in
->p_demux
, DEMUX_CAN_SEEK
, &b_can_seek
) )
2229 var_SetBool( p_input
, "can-seek", b_can_seek
);
2232 { /* Now try a real access */
2233 if( &p_input
->p
->input
== in
)
2234 { /* On master stream only, use input-list */
2235 char *str
= var_InheritString( p_input
, "input-list" );
2240 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2241 if( likely(asprintf( &list
, "%s://%s,%s", psz_access
, psz_path
,
2244 var_SetString( p_input
, "concat-list", list
);
2248 psz_access
= "concat";
2252 if( strcasecmp( psz_access
, "concat" ) )
2253 { /* Autodetect extra files if none specified */
2257 TAB_INIT( count
, tab
);
2258 InputGetExtraFiles( p_input
, &count
, &tab
, psz_access
, psz_path
);
2263 for( int i
= 0; i
< count
; i
++ )
2266 if( asprintf( &str
, "%s,%s", list
? list
: psz_mrl
,
2275 var_Create( p_input
, "concat-list", VLC_VAR_STRING
);
2276 if( likely(list
!= NULL
) )
2278 var_SetString( p_input
, "concat-list", list
);
2281 psz_access
= "concat";
2283 TAB_CLEAN( count
, tab
);
2286 /* Create the stream_t */
2287 stream_t
*p_stream
= NULL
;
2290 if( likely(asprintf( &url
, "%s://%s", psz_access
, psz_path
) >= 0) )
2292 p_stream
= stream_AccessNew( VLC_OBJECT(p_input
), p_input
, url
);
2295 if( p_stream
== NULL
)
2297 msg_Err( p_input
, "open of `%s' failed", psz_mrl
);
2298 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2299 dialog_Fatal( p_input
, _("Your input can't be opened"),
2300 _("VLC is unable to open the MRL '%s'."
2301 " Check the log for details."), psz_mrl
);
2305 /* Add stream filters */
2306 p_stream
= stream_FilterAutoNew( p_stream
);
2308 char *filters
= var_GetNonEmptyString( p_input
, "stream-filter" );
2309 if( filters
!= NULL
)
2311 p_stream
= stream_FilterChainNew( p_stream
, filters
);
2315 if( var_GetBool( p_input
, "input-record-native" ) )
2316 p_stream
= stream_FilterChainNew( p_stream
, "record" );
2318 if( !p_input
->b_preparsing
)
2322 stream_Control( p_stream
, STREAM_CAN_CONTROL_PACE
,
2323 &in
->b_can_pace_control
);
2324 in
->b_can_rate_control
= in
->b_can_pace_control
;
2325 in
->b_rescale_ts
= true;
2327 stream_Control( p_stream
, STREAM_CAN_PAUSE
, &in
->b_can_pause
);
2328 var_SetBool( p_input
, "can-pause",
2329 in
->b_can_pause
|| !in
->b_can_pace_control
); /* XXX temporary because of es_out_timeshift*/
2330 var_SetBool( p_input
, "can-rate",
2331 !in
->b_can_pace_control
|| in
->b_can_rate_control
); /* XXX temporary because of es_out_timeshift*/
2332 var_SetBool( p_input
, "can-rewind",
2333 !in
->b_rescale_ts
&& !in
->b_can_pace_control
);
2335 stream_Control( p_stream
, STREAM_CAN_SEEK
, &b
);
2336 var_SetBool( p_input
, "can-seek", b
);
2338 in
->b_title_demux
= false;
2340 stream_Control( p_stream
, STREAM_GET_PTS_DELAY
, &i_pts_delay
);
2343 if( p_stream
->psz_url
!= NULL
)
2344 /* Take access/stream redirections into account: */
2345 psz_path
= strstr( p_stream
->psz_url
, "://" );
2346 if( psz_path
== NULL
)
2349 in
->p_demux
= demux_New( p_input
, p_input
, psz_access
, psz_demux
,
2350 psz_path
, p_stream
, p_input
->p
->p_es_out
,
2351 p_input
->b_preparsing
);
2353 if( in
->p_demux
== NULL
)
2355 msg_Err( p_input
, "no suitable demux module for `%s/%s://%s'",
2356 psz_access
, psz_demux
, psz_path
);
2357 if( !b_in_can_fail
&& !input_Stopped( p_input
) )
2358 dialog_Fatal( VLC_OBJECT( p_input
),
2359 _("VLC can't recognize the input's format"),
2360 _("The format of '%s' cannot be detected. "
2361 "Have a look at the log for details."),
2363 stream_Delete( p_stream
);
2366 assert( in
->p_demux
->pf_demux
!= NULL
);
2368 /* Get title from demux */
2369 if( !p_input
->b_preparsing
&& in
->i_title
<= 0 )
2371 if( demux_Control( in
->p_demux
, DEMUX_GET_TITLE_INFO
,
2372 &in
->title
, &in
->i_title
,
2373 &in
->i_title_offset
, &in
->i_seekpoint_offset
))
2375 TAB_INIT( in
->i_title
, in
->title
);
2379 in
->b_title_demux
= true;
2384 free( psz_var_demux
);
2387 /* Set record capabilities */
2388 if( demux_Control( in
->p_demux
, DEMUX_CAN_RECORD
, &in
->b_can_stream_record
) )
2389 in
->b_can_stream_record
= false;
2391 if( !var_GetBool( p_input
, "input-record-native" ) )
2392 in
->b_can_stream_record
= false;
2393 var_SetBool( p_input
, "can-record", true );
2395 var_SetBool( p_input
, "can-record", in
->b_can_stream_record
);
2399 * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */
2400 if( !p_input
->b_preparsing
)
2403 input_attachment_t
**attachment
;
2404 if( !demux_Control( in
->p_demux
, DEMUX_GET_ATTACHMENTS
,
2405 &attachment
, &i_attachment
) )
2407 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2408 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2409 i_attachment
, attachment
, in
->p_demux
);
2410 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2413 /* PTS delay: request from demux first. This is required for
2414 * access_demux and some special cases like SDP demux. Otherwise,
2415 * fallback to access */
2416 if( demux_Control( in
->p_demux
, DEMUX_GET_PTS_DELAY
,
2417 &in
->i_pts_delay
) )
2418 in
->i_pts_delay
= i_pts_delay
;
2419 if( in
->i_pts_delay
> INPUT_PTS_DELAY_MAX
)
2420 in
->i_pts_delay
= INPUT_PTS_DELAY_MAX
;
2421 else if( in
->i_pts_delay
< 0 )
2422 in
->i_pts_delay
= 0;
2425 if( !demux_Control( in
->p_demux
, DEMUX_GET_FPS
, &f_fps
) && f_fps
> 0.0 )
2427 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2428 p_input
->p
->f_fps
= f_fps
;
2429 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2432 if( var_GetInteger( p_input
, "clock-synchro" ) != -1 )
2433 in
->b_can_pace_control
= !var_GetInteger( p_input
, "clock-synchro" );
2439 demux_Delete( in
->p_demux
);
2441 free( psz_var_demux
);
2444 return VLC_EGENERIC
;
2447 /*****************************************************************************
2449 *****************************************************************************/
2450 static void InputSourceClean( input_source_t
*in
)
2455 demux_Delete( in
->p_demux
);
2457 if( in
->i_title
> 0 )
2459 for( i
= 0; i
< in
->i_title
; i
++ )
2460 vlc_input_title_Delete( in
->title
[i
] );
2461 TAB_CLEAN( in
->i_title
, in
->title
);
2465 /*****************************************************************************
2467 *****************************************************************************/
2468 static void InputSourceMeta( input_thread_t
*p_input
,
2469 input_source_t
*p_source
, vlc_meta_t
*p_meta
)
2471 demux_t
*p_demux
= p_source
->p_demux
;
2473 /* XXX Remember that checking against p_item->p_meta->i_status & ITEM_PREPARSED
2476 bool has_meta
= false;
2478 /* Read demux meta */
2479 if( !demux_Control( p_demux
, DEMUX_GET_META
, p_meta
) )
2482 bool has_unsupported
;
2483 if( demux_Control( p_demux
, DEMUX_HAS_UNSUPPORTED_META
, &has_unsupported
) )
2484 has_unsupported
= true;
2486 /* If the demux report unsupported meta data, or if we don't have meta data
2487 * try an external "meta reader" */
2488 if( has_meta
&& !has_unsupported
)
2491 demux_meta_t
*p_demux_meta
=
2492 vlc_custom_create( p_input
, sizeof( *p_demux_meta
), "demux meta" );
2493 if( unlikely(p_demux_meta
== NULL
) )
2495 p_demux_meta
->p_item
= p_input
->p
->p_item
;
2497 module_t
*p_id3
= module_need( p_demux_meta
, "meta reader", NULL
, false );
2500 if( p_demux_meta
->p_meta
)
2502 vlc_meta_Merge( p_meta
, p_demux_meta
->p_meta
);
2503 vlc_meta_Delete( p_demux_meta
->p_meta
);
2506 if( p_demux_meta
->i_attachments
> 0 )
2508 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2509 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2510 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
, p_demux
);
2511 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2513 module_unneed( p_demux
, p_id3
);
2515 vlc_object_release( p_demux_meta
);
2519 static void SlaveDemux( input_thread_t
*p_input
)
2524 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_TIME
, &i_time
) )
2526 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2530 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
2532 input_source_t
*in
= p_input
->p
->slave
[i
];
2538 /* Call demux_Demux until we have read enough data */
2539 if( demux_Control( in
->p_demux
, DEMUX_SET_NEXT_DEMUX_TIME
, i_time
) )
2544 if( demux_Control( in
->p_demux
, DEMUX_GET_TIME
, &i_stime
) )
2546 msg_Err( p_input
, "slave[%d] doesn't like "
2547 "DEMUX_GET_TIME -> EOF", i
);
2552 if( i_stime
>= i_time
)
2558 if( ( i_ret
= demux_Demux( in
->p_demux
) ) <= 0 )
2564 i_ret
= demux_Demux( in
->p_demux
);
2569 msg_Dbg( p_input
, "slave %d EOF", i
);
2575 static void SlaveSeek( input_thread_t
*p_input
)
2580 if( demux_Control( p_input
->p
->input
.p_demux
, DEMUX_GET_TIME
, &i_time
) )
2582 msg_Err( p_input
, "demux doesn't like DEMUX_GET_TIME" );
2586 for( i
= 0; i
< p_input
->p
->i_slave
; i
++ )
2588 input_source_t
*in
= p_input
->p
->slave
[i
];
2590 if( demux_Control( in
->p_demux
, DEMUX_SET_TIME
, i_time
, true ) )
2593 msg_Err( p_input
, "seek failed for slave %d -> EOF", i
);
2603 /*****************************************************************************
2605 *****************************************************************************/
2606 static void InputMetaUser( input_thread_t
*p_input
, vlc_meta_t
*p_meta
)
2608 static const struct { int i_meta
; const char *psz_name
; } p_list
[] = {
2609 { vlc_meta_Title
, "meta-title" },
2610 { vlc_meta_Artist
, "meta-artist" },
2611 { vlc_meta_Genre
, "meta-genre" },
2612 { vlc_meta_Copyright
, "meta-copyright" },
2613 { vlc_meta_Description
, "meta-description" },
2614 { vlc_meta_Date
, "meta-date" },
2615 { vlc_meta_URL
, "meta-url" },
2619 /* Get meta information from user */
2620 for( int i
= 0; p_list
[i
].psz_name
; i
++ )
2622 char *psz_string
= var_GetNonEmptyString( p_input
, p_list
[i
].psz_name
);
2626 EnsureUTF8( psz_string
);
2627 vlc_meta_Set( p_meta
, p_list
[i
].i_meta
, psz_string
);
2632 static void AppendAttachment( int *pi_attachment
, input_attachment_t
***ppp_attachment
, demux_t
***ppp_attachment_demux
,
2633 int i_new
, input_attachment_t
**pp_new
, demux_t
*p_demux
)
2635 int i_attachment
= *pi_attachment
;
2636 input_attachment_t
**attachment
= *ppp_attachment
;
2637 demux_t
**attachment_demux
= *ppp_attachment_demux
;
2640 attachment
= xrealloc( attachment
,
2641 sizeof(*attachment
) * ( i_attachment
+ i_new
) );
2642 attachment_demux
= xrealloc( attachment_demux
,
2643 sizeof(*attachment_demux
) * ( i_attachment
+ i_new
) );
2644 for( i
= 0; i
< i_new
; i
++ )
2646 attachment
[i_attachment
] = pp_new
[i
];
2647 attachment_demux
[i_attachment
++] = p_demux
;
2652 *pi_attachment
= i_attachment
;
2653 *ppp_attachment
= attachment
;
2654 *ppp_attachment_demux
= attachment_demux
;
2657 /*****************************************************************************
2658 * InputUpdateMeta: merge p_item meta data with p_meta taking care of
2659 * arturl and locking issue.
2660 *****************************************************************************/
2661 static void InputUpdateMeta( input_thread_t
*p_input
, demux_t
*p_demux
)
2663 vlc_meta_t
*p_meta
= vlc_meta_New();
2664 if( unlikely(p_meta
== NULL
) )
2667 demux_Control( p_demux
, DEMUX_GET_META
, p_meta
);
2669 /* If metadata changed, then the attachments might have changed.
2670 We need to update them in case they contain album art. */
2671 input_attachment_t
**attachment
;
2674 if( !demux_Control( p_demux
, DEMUX_GET_ATTACHMENTS
,
2675 &attachment
, &i_attachment
) )
2677 vlc_mutex_lock( &p_input
->p
->p_item
->lock
);
2678 if( p_input
->p
->i_attachment
> 0 )
2681 for( int i
= 0; i
< p_input
->p
->i_attachment
; i
++ )
2683 if( p_input
->p
->attachment_demux
[i
] == p_demux
)
2684 vlc_input_attachment_Delete( p_input
->p
->attachment
[i
] );
2687 p_input
->p
->attachment
[j
] = p_input
->p
->attachment
[i
];
2688 p_input
->p
->attachment_demux
[j
] = p_input
->p
->attachment_demux
[i
];
2692 p_input
->p
->i_attachment
= j
;
2694 AppendAttachment( &p_input
->p
->i_attachment
, &p_input
->p
->attachment
, &p_input
->p
->attachment_demux
,
2695 i_attachment
, attachment
, p_demux
);
2696 vlc_mutex_unlock( &p_input
->p
->p_item
->lock
);
2699 es_out_ControlSetMeta( p_input
->p
->p_es_out
, p_meta
);
2700 vlc_meta_Delete( p_meta
);
2703 /*****************************************************************************
2704 * InputGetExtraFiles
2705 * Autodetect extra input list
2706 *****************************************************************************/
2707 static void InputGetExtraFilesPattern( input_thread_t
*p_input
,
2708 int *pi_list
, char ***pppsz_list
,
2709 const char *psz_path
,
2710 const char *psz_match
,
2711 const char *psz_format
,
2712 int i_start
, int i_stop
)
2717 TAB_INIT( i_list
, ppsz_list
);
2719 char *psz_base
= strdup( psz_path
);
2723 /* Remove the extension */
2724 char *psz_end
= &psz_base
[strlen(psz_base
)-strlen(psz_match
)];
2725 assert( psz_end
>= psz_base
);
2728 /* Try to list files */
2729 for( int i
= i_start
; i
<= i_stop
; i
++ )
2734 if( asprintf( &psz_file
, psz_format
, psz_base
, i
) < 0 )
2737 char *psz_tmp_path
= get_path( psz_file
);
2739 if( vlc_stat( psz_tmp_path
, &st
) || !S_ISREG( st
.st_mode
) || !st
.st_size
)
2742 free( psz_tmp_path
);
2746 msg_Dbg( p_input
, "Detected extra file `%s'", psz_file
);
2747 TAB_APPEND( i_list
, ppsz_list
, psz_file
);
2748 free( psz_tmp_path
);
2753 *pppsz_list
= ppsz_list
;
2756 static void InputGetExtraFiles( input_thread_t
*p_input
,
2757 int *pi_list
, char ***pppsz_list
,
2758 const char *psz_access
, const char *psz_path
)
2762 const char *psz_match
;
2763 const char *psz_format
;
2767 /* XXX the order is important */
2768 { ".001", "%s.%.3d", 2, 999 },
2769 { NULL
, NULL
, 0, 0 }
2772 TAB_INIT( *pi_list
, *pppsz_list
);
2774 if( ( psz_access
&& *psz_access
&& strcmp( psz_access
, "file" ) ) || !psz_path
)
2777 const size_t i_path
= strlen(psz_path
);
2779 for( int i
= 0; p_pattern
[i
].psz_match
!= NULL
; i
++ )
2781 const size_t i_ext
= strlen(p_pattern
[i
].psz_match
);
2783 if( i_path
< i_ext
)
2785 if( !strcmp( &psz_path
[i_path
-i_ext
], p_pattern
[i
].psz_match
) )
2787 InputGetExtraFilesPattern( p_input
, pi_list
, pppsz_list
,
2789 p_pattern
[i
].psz_match
, p_pattern
[i
].psz_format
,
2790 p_pattern
[i
].i_start
, p_pattern
[i
].i_stop
);
2797 static void input_ChangeState( input_thread_t
*p_input
, int i_state
)
2799 if( p_input
->p
->i_state
== i_state
)
2802 p_input
->p
->i_state
= i_state
;
2803 if( i_state
== ERROR_S
)
2804 input_item_SetErrorWhenReading( p_input
->p
->p_item
, true );
2805 input_SendEventState( p_input
, i_state
);
2809 /*****************************************************************************
2810 * MRLSplit: parse the access, demux and url part of the
2811 * Media Resource Locator.
2812 *****************************************************************************/
2813 void input_SplitMRL( const char **access
, const char **demux
,
2814 const char **path
, const char **anchor
, char *buf
)
2818 /* Separate <path> from <access>[/<demux>]:// */
2819 p
= strstr( buf
, "://" );
2823 p
+= 3; /* skips "://" */
2826 /* Remove HTML anchor if present (not supported).
2827 * The hash symbol itself should be URI-encoded. */
2828 p
= strchr( p
, '#' );
2840 fprintf( stderr
, "%s(\"%s\") probably not a valid URI!\n", __func__
,
2843 /* Note: this is a valid non const pointer to "": */
2844 *path
= buf
+ strlen( buf
);
2847 /* Separate access from demux */
2848 p
= strchr( buf
, '/' );
2859 /* We really don't want module name substitution here! */
2866 static const char *MRLSeekPoint( const char *str
, int *title
, int *chapter
)
2871 /* Look for the title */
2872 u
= strtoul( str
, &end
, 0 );
2873 *title
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
2876 /* Look for the chapter */
2880 u
= strtoul( str
, &end
, 0 );
2881 *chapter
= (str
== end
|| u
> (unsigned long)INT_MAX
) ? -1 : (int)u
;
2891 /*****************************************************************************
2892 * MRLSections: parse title and seekpoint info from the Media Resource Locator.
2895 * [url][@[title_start][:chapter_start][-[title_end][:chapter_end]]]
2896 *****************************************************************************/
2897 static void MRLSections( const char *p
,
2898 int *pi_title_start
, int *pi_title_end
,
2899 int *pi_chapter_start
, int *pi_chapter_end
)
2901 *pi_title_start
= *pi_title_end
= *pi_chapter_start
= *pi_chapter_end
= -1;
2903 int title_start
, chapter_start
, title_end
, chapter_end
;
2909 p
= MRLSeekPoint( p
, &title_start
, &chapter_start
);
2911 title_start
= chapter_start
= -1;
2914 p
= MRLSeekPoint( p
+ 1, &title_end
, &chapter_end
);
2916 title_end
= chapter_end
= -1;
2918 if( *p
) /* syntax error */
2921 *pi_title_start
= title_start
;
2922 *pi_title_end
= title_end
;
2923 *pi_chapter_start
= chapter_start
;
2924 *pi_chapter_end
= chapter_end
;
2927 /*****************************************************************************
2928 * input_AddSubtitles: add a subtitle file and enable it
2929 *****************************************************************************/
2930 static void input_SubtitleAdd( input_thread_t
*p_input
,
2931 const char *url
, unsigned i_flags
)
2933 input_source_t
*sub
= InputSourceNew( p_input
);
2939 var_Change( p_input
, "spu-es", VLC_VAR_CHOICESCOUNT
, &count
, NULL
);
2941 if( InputSourceInit( p_input
, sub
, url
, "subtitle",
2942 (i_flags
& SUB_CANFAIL
) ) )
2947 TAB_APPEND( p_input
->p
->i_slave
, p_input
->p
->slave
, sub
);
2949 if( !(i_flags
& SUB_FORCED
) )
2955 if( var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
, &list
, NULL
) )
2957 if( count
.i_int
== 0 )
2959 /* if it was first one, there is disable too */
2961 if( count
.i_int
< list
.p_list
->i_count
)
2963 const int i_id
= list
.p_list
->p_values
[count
.i_int
].i_int
;
2965 es_out_Control( p_input
->p
->p_es_out_display
, ES_OUT_SET_ES_DEFAULT_BY_ID
, i_id
);
2966 es_out_Control( p_input
->p
->p_es_out_display
, ES_OUT_SET_ES_BY_ID
, i_id
);
2968 var_FreeList( &list
, NULL
);
2971 static void input_SubtitleFileAdd( input_thread_t
*p_input
, char *psz_subtitle
,
2974 /* if we are provided a subtitle.sub file,
2975 * see if we don't have a subtitle.idx and use it instead */
2976 char *psz_path
= strdup( psz_subtitle
);
2977 if( likely(psz_path
!= NULL
) )
2979 char *psz_extension
= strrchr( psz_path
, '.');
2980 if( psz_extension
&& strcmp( psz_extension
, ".sub" ) == 0 )
2984 strcpy( psz_extension
, ".idx" );
2986 if( !vlc_stat( psz_path
, &st
) && S_ISREG( st
.st_mode
) )
2988 msg_Dbg( p_input
, "using %s as subtitle file instead of %s",
2989 psz_path
, psz_subtitle
);
2990 strcpy( psz_subtitle
, psz_path
); /* <- FIXME! constify */
2996 char *url
= vlc_path2uri( psz_subtitle
, NULL
);
3000 input_SubtitleAdd( p_input
, url
, i_flags
);
3004 /*****************************************************************************
3006 *****************************************************************************/
3007 void input_UpdateStatistic( input_thread_t
*p_input
,
3008 input_statistic_t i_type
, int i_delta
)
3010 assert( p_input
->p
->i_state
!= INIT_S
);
3012 vlc_mutex_lock( &p_input
->p
->counters
.counters_lock
);
3015 #define I(c) stats_Update( p_input->p->counters.c, i_delta, NULL )
3016 case INPUT_STATISTIC_DECODED_VIDEO
:
3019 case INPUT_STATISTIC_DECODED_AUDIO
:
3022 case INPUT_STATISTIC_DECODED_SUBTITLE
:
3025 case INPUT_STATISTIC_SENT_PACKET
:
3026 I(p_sout_sent_packets
);
3029 case INPUT_STATISTIC_SENT_BYTE
:
3033 stats_Update( p_input
->p
->counters
.p_sout_sent_bytes
, i_delta
, &bytes
);
3034 stats_Update( p_input
->p
->counters
.p_sout_send_bitrate
, bytes
, NULL
);
3038 msg_Err( p_input
, "Invalid statistic type %d (internal error)", i_type
);
3041 vlc_mutex_unlock( &p_input
->p
->counters
.counters_lock
);
3045 /* TODO FIXME nearly the same logic that snapshot code */
3046 char *input_CreateFilename( input_thread_t
*input
, const char *psz_path
, const char *psz_prefix
, const char *psz_extension
)
3051 path
= vlc_opendir( psz_path
);
3056 char *psz_tmp
= str_format( input
, psz_prefix
);
3060 filename_sanitize( psz_tmp
);
3062 if( asprintf( &psz_file
, "%s"DIR_SEP
"%s%s%s",
3064 psz_extension
? "." : "",
3065 psz_extension
? psz_extension
: "" ) < 0 )
3072 psz_file
= str_format( input
, psz_path
);
3073 path_sanitize( psz_file
);