+ case INPUT_CONTROL_SET_STATE:
+ if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) ||
+ ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) )
+ {
+ int i_ret;
+ if( p_input->input.p_access )
+ i_ret = access2_Control( p_input->input.p_access,
+ ACCESS_SET_PAUSE_STATE, VLC_FALSE );
+ else
+ i_ret = demux2_Control( p_input->input.p_demux,
+ DEMUX_SET_PAUSE_STATE, VLC_FALSE );
+
+ if( i_ret )
+ {
+ /* FIXME What to do ? */
+ msg_Warn( p_input, "cannot unset pause -> EOF" );
+ input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
+ }
+
+ b_force_update = VLC_TRUE;
+
+ /* Switch to play */
+ p_input->i_state = PLAYING_S;
+ val.i_int = PLAYING_S;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+
+ /* Reset clock */
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ }
+ else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S &&
+ p_input->b_can_pause )
+ {
+ int i_ret;
+ if( p_input->input.p_access )
+ i_ret = access2_Control( p_input->input.p_access,
+ ACCESS_SET_PAUSE_STATE, VLC_TRUE );
+ else
+ i_ret = demux2_Control( p_input->input.p_demux,
+ DEMUX_SET_PAUSE_STATE, VLC_TRUE );
+
+ b_force_update = VLC_TRUE;
+
+ if( i_ret )
+ {
+ msg_Warn( p_input, "cannot set pause state" );
+ val.i_int = p_input->i_state;
+ }
+ else
+ {
+ val.i_int = PAUSE_S;
+ }
+
+ /* Switch to new state */
+ p_input->i_state = val.i_int;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+ }
+ else if( val.i_int == PAUSE_S && !p_input->b_can_pause )
+ {
+ b_force_update = VLC_TRUE;
+
+ /* Correct "state" value */
+ val.i_int = p_input->i_state;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+ }
+ else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
+ {
+ msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" );
+ }
+ break;
+
+ case INPUT_CONTROL_SET_RATE:
+ case INPUT_CONTROL_SET_RATE_SLOWER:
+ case INPUT_CONTROL_SET_RATE_FASTER:
+ {
+ int i_rate;
+
+ if( i_type == INPUT_CONTROL_SET_RATE_SLOWER )
+ i_rate = p_input->i_rate * 2;
+ else if( i_type == INPUT_CONTROL_SET_RATE_FASTER )
+ i_rate = p_input->i_rate / 2;
+ else
+ i_rate = val.i_int;
+
+ if( i_rate < INPUT_RATE_MIN )
+ {
+ msg_Dbg( p_input, "cannot set rate faster" );
+ i_rate = INPUT_RATE_MIN;
+ }
+ else if( i_rate > INPUT_RATE_MAX )
+ {
+ msg_Dbg( p_input, "cannot set rate slower" );
+ i_rate = INPUT_RATE_MAX;
+ }
+ if( i_rate != INPUT_RATE_DEFAULT &&
+ ( !p_input->b_can_pace_control ||
+ !p_input->b_out_pace_control ) )
+ {
+ msg_Dbg( p_input, "cannot change rate" );
+ i_rate = INPUT_RATE_DEFAULT;
+ }
+ if( i_rate != p_input->i_rate )
+ {
+ p_input->i_rate = i_rate;
+ val.i_int = i_rate;
+ var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
+
+ /* We haven't send data to decoder when rate != default */
+ if( i_rate == INPUT_RATE_DEFAULT )
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_TRUE );
+
+ /* Reset clock */
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+
+ b_force_update = VLC_TRUE;
+ }
+ break;
+ }
+
+ case INPUT_CONTROL_SET_PROGRAM:
+ /* No need to force update, es_out does it if needed */
+ es_out_Control( p_input->p_es_out,
+ ES_OUT_SET_GROUP, val.i_int );
+ break;
+
+ case INPUT_CONTROL_SET_ES:
+ /* No need to force update, es_out does it if needed */
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
+ input_EsOutGetFromID( p_input->p_es_out,
+ val.i_int ) );
+ break;
+
+ case INPUT_CONTROL_SET_AUDIO_DELAY:
+ input_EsOutSetDelay( p_input->p_es_out,
+ AUDIO_ES, val.i_time );
+ var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, &val, NULL );
+ break;
+
+ case INPUT_CONTROL_SET_SPU_DELAY:
+ input_EsOutSetDelay( p_input->p_es_out,
+ SPU_ES, val.i_time );
+ var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL );
+ break;
+
+ case INPUT_CONTROL_SET_TITLE:
+ case INPUT_CONTROL_SET_TITLE_NEXT:
+ case INPUT_CONTROL_SET_TITLE_PREV:
+ if( p_input->input.b_title_demux &&
+ p_input->input.i_title > 0 )
+ {
+ /* TODO */
+ /* FIXME handle demux title */
+ demux_t *p_demux = p_input->input.p_demux;
+ int i_title;
+
+ if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
+ i_title = p_demux->info.i_title - 1;
+ else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
+ i_title = p_demux->info.i_title + 1;
+ else
+ i_title = val.i_int;
+
+ if( i_title >= 0 && i_title < p_input->input.i_title )
+ {
+ demux2_Control( p_demux, DEMUX_SET_TITLE, i_title );
+
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+
+ input_ControlVarTitle( p_input, i_title );
+ }
+ }
+ else if( p_input->input.i_title > 0 )
+ {
+ access_t *p_access = p_input->input.p_access;
+ int i_title;
+
+ if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
+ i_title = p_access->info.i_title - 1;
+ else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
+ i_title = p_access->info.i_title + 1;
+ else
+ i_title = val.i_int;
+
+ if( i_title >= 0 && i_title < p_input->input.i_title )
+ {
+ access2_Control( p_access, ACCESS_SET_TITLE, i_title );
+ stream_AccessReset( p_input->input.p_stream );
+
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ }
+ }
+ break;
+ case INPUT_CONTROL_SET_SEEKPOINT:
+ case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
+ case INPUT_CONTROL_SET_SEEKPOINT_PREV:
+ if( p_input->input.b_title_demux &&
+ p_input->input.i_title > 0 )
+ {
+ demux_t *p_demux = p_input->input.p_demux;
+ int i_seekpoint;
+
+ if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
+ i_seekpoint = p_demux->info.i_seekpoint - 1;
+ else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT )
+ i_seekpoint = p_demux->info.i_seekpoint + 1;
+ else
+ i_seekpoint = val.i_int;
+
+ if( i_seekpoint >= 0 && i_seekpoint <
+ p_input->input.title[p_demux->info.i_title]->i_seekpoint )
+ {
+ demux2_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
+
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ }
+ }
+ else if( p_input->input.i_title > 0 )
+ {
+ access_t *p_access = p_input->input.p_access;
+ int i_seekpoint;
+
+ if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
+ i_seekpoint = p_access->info.i_seekpoint - 1;
+ else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
+ i_seekpoint = p_access->info.i_seekpoint + 1;
+ else
+ i_seekpoint = val.i_int;
+
+ if( i_seekpoint >= 0 && i_seekpoint <
+ p_input->input.title[p_access->info.i_title]->i_seekpoint )
+ {
+ access2_Control( p_access, ACCESS_SET_SEEKPOINT, i_seekpoint );
+ stream_AccessReset( p_input->input.p_stream );
+
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ }
+ }
+ break;
+
+ case INPUT_CONTROL_SET_BOOKMARK:
+ default:
+ msg_Err( p_input, "not yet implemented" );
+ break;
+ }
+
+ return b_force_update;
+}
+
+/*****************************************************************************
+ * UpdateFromDemux:
+ *****************************************************************************/
+static void UpdateFromDemux( input_thread_t *p_input )
+{
+ demux_t *p_demux = p_input->input.p_demux;
+ vlc_value_t v;
+
+ if( p_demux->info.i_update & INPUT_UPDATE_TITLE )
+ {
+ v.i_int = p_demux->info.i_title;
+ var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
+
+ input_ControlVarTitle( p_input, p_demux->info.i_title );
+
+ p_demux->info.i_update &= ~INPUT_UPDATE_TITLE;
+ }
+ if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT )
+ {
+ v.i_int = p_demux->info.i_seekpoint;
+ var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
+
+ p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
+ }
+ p_demux->info.i_update &= ~INPUT_UPDATE_SIZE;
+}
+
+/*****************************************************************************
+ * UpdateFromAccess:
+ *****************************************************************************/
+static void UpdateFromAccess( input_thread_t *p_input )
+{
+ access_t *p_access = p_input->input.p_access;
+ vlc_value_t v;
+
+ if( p_access->info.i_update & INPUT_UPDATE_TITLE )
+ {
+ v.i_int = p_access->info.i_title;
+ var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
+
+ input_ControlVarTitle( p_input, p_access->info.i_title );
+
+ p_access->info.i_update &= ~INPUT_UPDATE_TITLE;
+ }
+ if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT )
+ {
+ v.i_int = p_access->info.i_seekpoint;
+ var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
+
+ p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
+ }
+ p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
+}
+
+/*****************************************************************************
+ * InputSourceNew:
+ *****************************************************************************/
+static input_source_t *InputSourceNew( input_thread_t *p_input )
+{
+ input_source_t *in = malloc( sizeof( input_source_t ) );
+
+ in->p_item = NULL;
+ in->p_access = NULL;
+ in->p_stream = NULL;
+ in->p_demux = NULL;
+ in->b_title_demux = VLC_FALSE;
+ in->i_title = 0;
+ in->title = NULL;
+ in->b_can_pace_control = VLC_TRUE;
+ in->b_eof = VLC_FALSE;
+ in->i_cr_average = 0;
+
+ return in;
+}
+
+/*****************************************************************************
+ * InputSourceInit:
+ *****************************************************************************/
+static int InputSourceInit( input_thread_t *p_input,
+ input_source_t *in, char *psz_mrl,
+ char *psz_forced_demux )
+{
+ char *psz_dup = strdup( psz_mrl );
+ char *psz_access;
+ char *psz_demux;
+ char *psz_path;
+ vlc_value_t val;
+
+ /* Split uri */
+ MRLSplit( p_input, psz_dup, &psz_access, &psz_demux, &psz_path );
+
+ msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
+ psz_mrl, psz_access, psz_demux, psz_path );
+
+ if( psz_forced_demux && *psz_forced_demux )
+ psz_demux = psz_forced_demux;
+
+ /* Try access_demux if no demux given */
+ if( *psz_access && *psz_demux == '\0' )
+ {
+ in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
+ NULL, p_input->p_es_out );
+ }
+
+ if( in->p_demux )
+ {
+ int64_t i_pts_delay;
+
+ /* Get infos from access_demux */
+ demux2_Control( in->p_demux,
+ DEMUX_GET_PTS_DELAY, &i_pts_delay );
+ p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
+
+ in->b_title_demux = VLC_TRUE;
+ if( demux2_Control( in->p_demux,
+ DEMUX_GET_TITLE_INFO,
+ &in->title, &in->i_title ) )
+ {
+ in->i_title = 0;
+ in->title = NULL;
+ }
+ demux2_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE,
+ &in->b_can_pace_control );
+ demux2_Control( in->p_demux, DEMUX_CAN_PAUSE,
+ &in->b_can_pause );
+
+ /* FIXME todo
+ demux2_Control( in->p_demux, DEMUX_CAN_SEEK,
+ &val.b_bool );
+ */
+ }
+ else
+ {
+ int64_t i_pts_delay;
+
+ /* Now try a real access */
+ in->p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
+
+ /* Access failed, URL encoded ? */
+ if( in->p_access == NULL && strchr( psz_path, '%' ) )
+ {
+ DecodeUrl( psz_path );
+
+ msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'",
+ psz_access, psz_demux, psz_path );
+
+ in->p_access = access2_New( p_input,
+ psz_access, psz_demux, psz_path );
+ }
+#ifndef WIN32 /* Remove this gross hack from the win32 build as colons
+ * are forbidden in filenames on Win32. */
+
+ /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
+ if( in->p_access == NULL &&
+ *psz_access == '\0' && ( *psz_demux || *psz_path ) )
+ {
+ free( psz_dup );
+ psz_dup = strdup( psz_mrl );
+ psz_access = "";
+ psz_demux = "";
+ psz_path = psz_dup;
+
+ in->p_access = access2_New( p_input,
+ psz_access, psz_demux, psz_path );
+ }
+#endif
+
+ if( in->p_access == NULL )
+ {
+ msg_Err( p_input, "no suitable access module for `%s'", psz_mrl );
+ goto error;
+ }
+
+ /* Get infos from access */
+ access2_Control( in->p_access,
+ ACCESS_GET_PTS_DELAY, &i_pts_delay );
+ p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay );
+
+ in->b_title_demux = VLC_FALSE;
+ if( access2_Control( in->p_access,
+ ACCESS_GET_TITLE_INFO,
+ &in->title, &in->i_title ) )
+ {
+ in->i_title = 0;
+ in->title = NULL;
+ }
+ access2_Control( in->p_access, ACCESS_CAN_CONTROL_PACE,
+ &in->b_can_pace_control );
+ access2_Control( in->p_access, ACCESS_CAN_PAUSE,
+ &in->b_can_pause );
+ access2_Control( in->p_access, ACCESS_CAN_SEEK,
+ &val.b_bool );
+ var_Set( p_input, "seekable", val );
+
+ /* Create the stream_t */
+ in->p_stream = stream_AccessNew( in->p_access );
+ if( in->p_stream == NULL )
+ {
+ msg_Warn( p_input, "cannot create a stream_t from access" );
+ goto error;
+ }
+
+ /* Open a demuxer */
+ if( *psz_demux == '\0' && *in->p_access->psz_demux )
+ {
+ psz_demux = in->p_access->psz_demux;
+ }
+ in->p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
+ in->p_stream, p_input->p_es_out );
+ if( in->p_demux == NULL )
+ {
+ msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
+ psz_access, psz_demux, psz_path );
+ goto error;
+ }
+
+ /* TODO get title from demux */
+ if( in->i_title <= 0 )
+ {
+ if( demux2_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
+ &in->title, &in->i_title ) )
+ {
+ in->i_title = 0;
+ in->title = NULL;
+ }
+ else
+ {
+ in->b_title_demux = VLC_TRUE;
+ }
+ }
+ }
+ free( psz_dup );
+ return VLC_SUCCESS;
+
+error:
+ if( in->p_demux )
+ demux2_Delete( in->p_demux );
+
+ if( in->p_stream )
+ stream_AccessDelete( in->p_stream );
+
+ if( in->p_access )
+ access2_Delete( in->p_access );
+ free( psz_dup );
+
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * InputSourceClean:
+ *****************************************************************************/
+static void InputSourceClean( input_thread_t *p_input, input_source_t *in )
+{
+ if( in->p_demux )
+ demux2_Delete( in->p_demux );
+
+ if( in->p_stream )
+ stream_AccessDelete( in->p_stream );
+
+ if( in->p_access )
+ access2_Delete( in->p_access );
+
+ if( in->i_title > 0 )
+ {
+ int i;
+ for( i = 0; i < in->i_title; i++ )
+ {
+ vlc_input_title_Delete( in->title[i] );
+ }
+ free( in->title );
+ }
+}
+
+static void SlaveDemux( input_thread_t *p_input )
+{
+ int64_t i_time;
+ int i;
+ if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
+ {
+ msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+ return;
+ }
+
+ for( i = 0; i < p_input->i_slave; i++ )
+ {
+ input_source_t *in = p_input->slave[i];
+ int i_ret = 1;
+
+ if( in->b_eof )
+ continue;
+
+ if( demux2_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) )
+ {
+ for( ;; )
+ {
+ int64_t i_stime;
+ if( demux2_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) )
+ {
+ msg_Err( p_input, "slave[%d] doesn't like "
+ "DEMUX_GET_TIME -> EOF", i );
+ i_ret = 0;
+ break;
+ }
+
+ if( i_stime >= i_time )
+ break;
+
+ if( ( i_ret = in->p_demux->pf_demux( in->p_demux ) ) <= 0 )
+ break;
+ }
+ }
+ else
+ {
+ i_ret = in->p_demux->pf_demux( in->p_demux );
+ }
+
+ if( i_ret <= 0 )
+ {
+ msg_Dbg( p_input, "slave %d EOF", i );
+ in->b_eof = VLC_TRUE;
+ }
+ }
+}
+
+static void SlaveSeek( input_thread_t *p_input )
+{
+ int64_t i_time;
+ int i;
+
+ if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
+ {
+ msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+ return;
+ }
+
+ for( i = 0; i < p_input->i_slave; i++ )
+ {
+ input_source_t *in = p_input->slave[i];
+
+ if( demux2_Control( in->p_demux, DEMUX_SET_TIME, i_time ) )
+ {
+ msg_Err( p_input, "seek failed for slave %d -> EOF", i );
+ in->b_eof = VLC_TRUE;
+ }
+ }
+}
+/*****************************************************************************
+ * InputMetaUser:
+ *****************************************************************************/
+static vlc_meta_t *InputMetaUser( input_thread_t *p_input )
+{
+ vlc_meta_t *p_meta;
+ vlc_value_t val;
+
+ if( ( p_meta = vlc_meta_New() ) == NULL )
+ return NULL;
+
+ /* Get meta information from user */
+#define GET_META( c, s ) \
+ var_Get( p_input, (s), &val ); \
+ if( *val.psz_string ) \
+ vlc_meta_Add( p_meta, c, val.psz_string ); \
+ free( val.psz_string )
+
+ GET_META( VLC_META_TITLE, "meta-title" );
+ GET_META( VLC_META_AUTHOR, "meta-author" );
+ GET_META( VLC_META_ARTIST, "meta-artist" );
+ GET_META( VLC_META_GENRE, "meta-genre" );
+ GET_META( VLC_META_COPYRIGHT, "meta-copyright" );
+ GET_META( VLC_META_DESCRIPTION, "meta-description" );
+ GET_META( VLC_META_DATE, "meta-date" );
+ GET_META( VLC_META_URL, "meta-url" );
+#undef GET_META
+
+ return p_meta;
+}
+
+/*****************************************************************************
+ * DecodeUrl: decode a given encoded url
+ *****************************************************************************/
+static void DecodeUrl( char *psz )
+{
+ char *dup = strdup( psz );
+ char *p = dup;
+
+ while( *p )
+ {
+ if( *p == '%' )
+ {
+ char val[3];
+ p++;
+ if( !*p )
+ {
+ break;
+ }
+
+ val[0] = *p++;
+ val[1] = *p++;
+ val[2] = '\0';
+
+ *psz++ = strtol( val, NULL, 16 );
+ }
+ else if( *p == '+' )
+ {
+ *psz++ = ' ';
+ p++;
+ }
+ else
+ {
+ *psz++ = *p++;
+ }
+ }
+ *psz++ ='\0';
+ free( dup );