#include <vlc_interface.h>
#include <vlc_url.h>
#include <vlc_charset.h>
+#include <vlc_strings.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
*****************************************************************************/
static void Destructor( input_thread_t * p_input );
-static int Run ( input_thread_t *p_input );
-static int RunAndDestroy ( input_thread_t *p_input );
+static void* Run ( vlc_object_t *p_this );
+static void* RunAndDestroy ( vlc_object_t *p_this );
static input_thread_t * Create ( vlc_object_t *, input_item_t *,
const char *, bool, sout_instance_t * );
static int Init ( input_thread_t *p_input );
-static void Error ( input_thread_t *p_input );
+static void WaitDie ( input_thread_t *p_input );
static void End ( input_thread_t *p_input );
static void MainLoop( input_thread_t *p_input );
* - seekable (if you can seek, it doesn't say if 'bar display' has be shown
* or not, for that check position != 0.0)
* - can-pause
+ * - can-record (if a stream can be recorded while playing)
+ * - teletext-es to get the index of spu track that is teletext --1 if no teletext)
* * For intf callback upon changes
* - intf-change
* - rate-change for when playback rate changes
char * psz_name = input_item_GetName( p_item );
snprintf( psz_timer_name, sizeof(psz_timer_name),
"input launching for '%s'", psz_name );
+
+ msg_Dbg( p_input, "Creating an input for '%s'", psz_name);
+
free( psz_name );
/* Start a timer to mesure how long it takes
p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
p_input->i_state = INIT_S;
p_input->p->i_rate = INPUT_RATE_DEFAULT;
+ p_input->p->b_recording = false;
TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark );
TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
p_input->p->p_es_out = NULL;
/* Parse input options */
vlc_mutex_lock( &p_item->lock );
- assert( p_item->optflagc == p_item->i_options );
+ assert( (int)p_item->optflagc == p_item->i_options );
for( i = 0; i < p_item->i_options; i++ )
var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i],
!!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) );
/* Create Objects variables for public Get and Set */
input_ControlVarInit( p_input );
+ /* */
+ p_input->p->pts_adjust.b_auto_adjust = var_GetBool( p_input, "auto-adjust-pts-delay" );
p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
if( !p_input->b_preparsing )
memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) );
vlc_mutex_init( &p_input->p->counters.counters_lock );
- /* Attach only once we are ready */
- vlc_object_attach( p_input, p_parent );
-
/* Set the destructor when we are sure we are initialized */
vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor );
+ /* Attach only once we are ready */
+ vlc_object_attach( p_input, p_parent );
+
return p_input;
}
{
input_thread_private_t *priv = p_input->p;
+#ifndef NDEBUG
+ char * psz_name = input_item_GetName( p_input->p->input.p_item );
+ msg_Dbg( p_input, "Destroying the input for '%s'", psz_name);
+ free( psz_name );
+#endif
+
vlc_event_manager_fini( &p_input->p->event_manager );
stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING );
if( b_block )
{
- RunAndDestroy( p_input );
+ RunAndDestroy( VLC_OBJECT(p_input) );
return VLC_SUCCESS;
}
else
*
* \param the input thread to stop
*/
-static void ObjectKillChildrens( vlc_object_t *p_obj )
+static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj )
{
vlc_list_t *p_list;
int i;
+
+ if( p_obj->i_object_type == VLC_OBJECT_VOUT ||
+ p_obj->i_object_type == VLC_OBJECT_AOUT ||
+ p_obj == VLC_OBJECT(p_input->p->p_sout) )
+ return;
+
vlc_object_kill( p_obj );
p_list = vlc_list_children( p_obj );
for( i = 0; i < p_list->i_count; i++ )
- ObjectKillChildrens( p_list->p_values[i].p_object );
+ ObjectKillChildrens( p_input, p_list->p_values[i].p_object );
vlc_list_release( p_list );
}
void input_StopThread( input_thread_t *p_input )
{
- vlc_list_t *p_list;
- int i;
-
/* Set die for input and ALL of this childrens (even (grand-)grand-childrens)
* It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to
* unlock the control loop */
- ObjectKillChildrens( VLC_OBJECT(p_input) );
+ ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}
* This is the "normal" thread that spawns the input processing chain,
* reads the stream, cleans up and waits
*****************************************************************************/
-static int Run( input_thread_t *p_input )
+static void* Run( vlc_object_t *p_this )
{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ int canc = vlc_savecancel ();
+
/* Signal that the thread is launched */
vlc_thread_ready( p_input );
/* If we failed, wait before we are killed, and exit */
p_input->b_error = true;
- Error( p_input );
+ WaitDie( p_input );
/* Tell we're dead */
p_input->b_dead = true;
- return 0;
+ return NULL;
}
MainLoop( p_input );
/* Wait until we are asked to die */
if( !p_input->b_die )
{
- Error( p_input );
+ WaitDie( p_input );
}
/* Clean up */
End( p_input );
-
- return 0;
+ vlc_restorecancel (canc);
+ return NULL;
}
/*****************************************************************************
* This is the "just forget me" thread that spawns the input processing chain,
* reads the stream, cleans up and releases memory
*****************************************************************************/
-static int RunAndDestroy( input_thread_t *p_input )
+static void* RunAndDestroy( vlc_object_t *p_this )
{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ int canc;
+
/* Signal that the thread is launched */
vlc_thread_ready( p_input );
+ canc = vlc_savecancel ();
if( Init( p_input ) )
goto exit;
exit:
/* Release memory */
vlc_object_release( p_input );
+ vlc_restorecancel (canc);
return 0;
}
}
/*****************************************************************************
- * Error: RunThread() error loop
+ * WaitDie: Wait until we are asked to die.
*****************************************************************************
* This function is called when an error occurred during thread main's loop.
*****************************************************************************/
-static void Error( input_thread_t *p_input )
+static void WaitDie( input_thread_t *p_input )
{
- input_ChangeState( p_input, ERROR_S );
+ input_ChangeState( p_input, p_input->b_error ? ERROR_S : END_S );
while( !p_input->b_die )
{
/* Sleep a while */
msg_Dbg( p_input, "control: stopping input" );
/* Mark all submodules to die */
- ObjectKillChildrens( p_input );
+ ObjectKillChildrens( p_input, VLC_OBJECT(p_input) );
break;
case INPUT_CONTROL_SET_POSITION:
case INPUT_CONTROL_SET_POSITION_OFFSET:
{
double f_pos;
+
+ if( p_input->p->b_recording )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
+ break;
+ }
if( i_type == INPUT_CONTROL_SET_POSITION )
{
f_pos = val.f_float;
int64_t i_time;
int i_ret;
+ if( p_input->p->b_recording )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
+ break;
+ }
+
if( i_type == INPUT_CONTROL_SET_TIME )
{
i_time = val.i_time;
case INPUT_CONTROL_SET_TITLE:
case INPUT_CONTROL_SET_TITLE_NEXT:
case INPUT_CONTROL_SET_TITLE_PREV:
+ if( p_input->p->b_recording )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
+ break;
+ }
if( p_input->p->input.b_title_demux &&
p_input->p->input.i_title > 0 )
{
case INPUT_CONTROL_SET_SEEKPOINT:
case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
case INPUT_CONTROL_SET_SEEKPOINT_PREV:
+ if( p_input->p->b_recording )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
+ break;
+ }
+
if( p_input->p->input.b_title_demux &&
p_input->p->input.i_title > 0 )
{
}
break;
+ case INPUT_CONTROL_SET_RECORD_STATE:
+ if( !!p_input->p->b_recording != !!val.b_bool )
+ {
+ if( p_input->p->input.b_can_stream_record )
+ {
+ if( demux_Control( p_input->p->input.p_demux,
+ DEMUX_SET_RECORD_STATE, val.b_bool ) )
+ val.b_bool = false;
+ }
+ else
+ {
+ if( input_EsOutSetRecord( p_input->p->p_es_out, val.b_bool ) )
+ val.b_bool = false;
+ }
+ p_input->p->b_recording = val.b_bool;
+
+ var_Change( p_input, "record", VLC_VAR_SETVALUE, &val, NULL );
+
+ b_force_update = true;
+ }
+ break;
+
case INPUT_CONTROL_SET_BOOKMARK:
default:
msg_Err( p_input, "not yet implemented" );
*****************************************************************************/
static input_source_t *InputSourceNew( input_thread_t *p_input )
{
- input_source_t *in = (input_source_t*) malloc( sizeof( input_source_t ) );
+ VLC_UNUSED(p_input);
+ input_source_t *in = malloc( sizeof( input_source_t ) );
if( in )
memset( in, 0, sizeof( input_source_t ) );
return in;
input_source_t *in, const char *psz_mrl,
const char *psz_forced_demux )
{
+ const bool b_master = in == &p_input->p->input;
+
char psz_dup[strlen (psz_mrl) + 1];
const char *psz_access;
const char *psz_demux;
if( !p_input ) return VLC_EGENERIC;
/* Split uri */
- MRLSplit( psz_dup, &psz_access, &psz_demux, &psz_path );
+ input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup );
msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
psz_mrl, psz_access, psz_demux, psz_path );
{
int64_t i_pts_delay;
- input_ChangeState( p_input, OPENING_S );
+ if( b_master )
+ input_ChangeState( p_input, OPENING_S );
/* Now try a real access */
in->p_access = access_New( p_input, psz_access, psz_demux, psz_path );
var_Set( p_input, "seekable", val );
}
- input_ChangeState( p_input, BUFFERING_S );
+ if( b_master )
+ input_ChangeState( p_input, BUFFERING_S );
/* Create the stream_t */
in->p_stream = stream_AccessNew( in->p_access, p_input->b_preparsing );
{
const char *psz_a, *psz_d;
psz_buf = strdup( in->p_access->psz_path );
- MRLSplit( psz_buf, &psz_a, &psz_d, &psz_real_path );
+ input_SplitMRL( &psz_a, &psz_d, &psz_real_path, psz_buf );
}
else
{
intf_UserFatal( VLC_OBJECT( p_input ), false,
_("VLC can't recognize the input's format"),
_("The format of '%s' cannot be detected. "
- "Have a look the log for details."), psz_mrl );
+ "Have a look at the log for details."), psz_mrl );
goto error;
}
+ if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
+ in->b_can_stream_record = false;
+#ifdef ENABLE_SOUT
+ if( !var_CreateGetBool( p_input, "input-record-native" ) )
+ in->b_can_stream_record = false;
+ var_SetBool( p_input, "can-record", true );
+#else
+ var_SetBool( p_input, "can-record", in->b_can_stream_record );
+#endif
/* Get title from demux */
if( !p_input->b_preparsing && in->i_title <= 0 )
{
return VLC_SUCCESS;
error:
- input_ChangeState( p_input, ERROR_S );
+ if( b_master )
+ input_ChangeState( p_input, ERROR_S );
if( in->p_demux )
demux_Delete( in->p_demux );
* MRLSplit: parse the access, demux and url part of the
* Media Resource Locator.
*****************************************************************************/
-void MRLSplit( char *psz_dup, const char **ppsz_access, const char **ppsz_demux,
- char **ppsz_path )
+void input_SplitMRL( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path,
+ char *psz_dup )
{
char *psz_access = NULL;
char *psz_demux = NULL;
{
psz_path = psz_dup;
}
+ *ppsz_access = psz_access ? psz_access : (char*)"";
+ *ppsz_demux = psz_demux ? psz_demux : (char*)"";
+ *ppsz_path = psz_path;
+}
- *ppsz_access = psz_access ? psz_access : "";
- *ppsz_demux = psz_demux ? psz_demux : "";
- *ppsz_path = psz_path ? psz_path : "";
+static inline bool next(char ** src)
+{
+ char *end;
+ errno = 0;
+ long result = strtol( *src, &end, 0 );
+ if( errno != 0 || result >= LONG_MAX || result <= LONG_MIN ||
+ end == *src )
+ {
+ return false;
+ }
+ *src = end;
+ return true;
}
/*****************************************************************************
/* Start by parsing titles and chapters */
if( !psz_source || !( psz = strrchr( psz_source, '@' ) ) ) return;
+
/* Check we are really dealing with a title/chapter section */
psz_check = psz + 1;
if( !*psz_check ) return;
- if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+ if( isdigit(*psz_check) )
+ if(!next(&psz_check)) return;
if( *psz_check != ':' && *psz_check != '-' && *psz_check ) return;
if( *psz_check == ':' && ++psz_check )
- if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+ {
+ if( isdigit(*psz_check) )
+ if(!next(&psz_check)) return;
+ }
if( *psz_check != '-' && *psz_check ) return;
if( *psz_check == '-' && ++psz_check )
- if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+ {
+ if( isdigit(*psz_check) )
+ if(!next(&psz_check)) return;
+ }
if( *psz_check != ':' && *psz_check ) return;
if( *psz_check == ':' && ++psz_check )
- if( isdigit(*psz_check) ) strtol( psz_check, &psz_check, 0 );
+ {
+ if( isdigit(*psz_check) )
+ if(!next(&psz_check)) return;
+ }
if( *psz_check ) return;
/* Separate start and end */
/*****************************************************************************
* input_get_event_manager
*****************************************************************************/
-vlc_event_manager_t *
-input_get_event_manager( input_thread_t *p_input )
+vlc_event_manager_t *input_get_event_manager( input_thread_t *p_input )
{
return &p_input->p->event_manager;
}
+
+/**/
+/* TODO FIXME nearly the same logic that snapshot code */
+char *input_CreateFilename( vlc_object_t *p_obj, const char *psz_path, const char *psz_prefix, const char *psz_extension )
+{
+ char *psz_file;
+ DIR *path;
+
+ path = utf8_opendir( psz_path );
+ if( path )
+ {
+ closedir( path );
+
+ char *psz_tmp = str_format( p_obj, psz_prefix );
+ if( !psz_tmp )
+ return NULL;
+
+ filename_sanitize( psz_tmp );
+ if( asprintf( &psz_file, "%s"DIR_SEP"%s%s%s",
+ psz_path, psz_tmp,
+ psz_extension ? "." : "",
+ psz_extension ? psz_extension : "" ) < 0 )
+ psz_file = NULL;
+ free( psz_tmp );
+ return psz_file;
+ }
+ else
+ {
+ psz_file = str_format( p_obj, psz_path );
+ path_sanitize( psz_file );
+ return psz_file;
+ }
+}
+