+ if( p_meta == NULL )
+ {
+ p_meta = p_meta_tmp;
+ }
+ else if( p_meta_tmp )
+ {
+ vlc_meta_Merge( p_meta, p_meta_tmp );
+ vlc_meta_Delete( p_meta_tmp );
+ }
+
+ /* Get meta data from slave input */
+ for( i = 0; i < p_input->i_slave; i++ )
+ {
+ vlc_meta_t *p_meta_slave;
+
+ if( !demux2_Control( p_input->slave[i]->p_demux,
+ DEMUX_GET_META, &p_meta_slave ) )
+ {
+ if( p_meta == NULL )
+ {
+ p_meta = p_meta_slave;
+ }
+ else if( p_meta_slave )
+ {
+ vlc_meta_Merge( p_meta, p_meta_slave );
+ vlc_meta_Delete( p_meta_slave );
+ }
+ }
+
+ if( p_input->slave[i]->p_access &&
+ !access2_Control( p_input->slave[i]->p_access,
+ ACCESS_GET_META, &p_meta_slave ) )
+ {
+ if( p_meta == NULL )
+ {
+ p_meta = p_meta_slave;
+ }
+ else if( p_meta_slave )
+ {
+ vlc_meta_Merge( p_meta, p_meta_slave );
+ vlc_meta_Delete( p_meta_slave );
+ }
+ }
+ }
+ }
+
+ p_input->p_meta = p_meta;
+ UpdateMeta( p_input, b_quick );
+
+ if( !b_quick )
+ {
+ msg_Dbg( p_input, "`%s' sucessfully opened",
+ p_input->input.p_item->psz_uri );
+
+ }
+
+ /* Trigger intf update for this item */
+ /* Playlist has a callback on this variable and will forward
+ * it to intf */
+ var_SetInteger( p_input, "item-change", p_input->input.p_item->i_id );
+
+ /* initialization is complete */
+ p_input->i_state = PLAYING_S;
+
+ val.i_int = PLAYING_S;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+
+ return VLC_SUCCESS;
+
+error:
+ if( p_input->p_es_out )
+ input_EsOutDelete( p_input->p_es_out );
+
+ if( p_input->p_sout )
+ sout_DeleteInstance( p_input->p_sout );
+
+ /* Mark them deleted */
+ p_input->input.p_demux = NULL;
+ p_input->input.p_stream = NULL;
+ p_input->input.p_access = NULL;
+ p_input->p_es_out = NULL;
+ p_input->p_sout = NULL;
+
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Error: RunThread() error loop
+ *****************************************************************************
+ * This function is called when an error occurred during thread main's loop.
+ *****************************************************************************/
+static void Error( input_thread_t *p_input )
+{
+ while( !p_input->b_die )
+ {
+ /* Sleep a while */
+ msleep( INPUT_IDLE_SLEEP );
+ }
+}
+
+/*****************************************************************************
+ * End: end the input thread
+ *****************************************************************************/
+static void End( input_thread_t * p_input )
+{
+ vlc_value_t val;
+ int i;
+
+ msg_Dbg( p_input, "closing input" );
+
+ /* We are at the end */
+ p_input->i_state = END_S;
+
+ val.i_int = END_S;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+
+ /* Clean control variables */
+ input_ControlVarClean( p_input );
+
+ /* Clean up master */
+ InputSourceClean( p_input, &p_input->input );
+
+ /* Delete slave */
+ for( i = 0; i < p_input->i_slave; i++ )
+ {
+ InputSourceClean( p_input, p_input->slave[i] );
+ free( p_input->slave[i] );
+ }
+ if( p_input->slave ) free( p_input->slave );
+
+ /* Unload all modules */
+ if( p_input->p_es_out )
+ input_EsOutDelete( p_input->p_es_out );
+
+ /* Close optional stream output instance */
+ if( p_input->p_sout )
+ {
+ vlc_object_t *p_pl =
+ vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+ vlc_value_t keep;
+
+ if( var_Get( p_input, "sout-keep", &keep ) >= 0 && keep.b_bool && p_pl )
+ {
+ /* attach sout to the playlist */
+ msg_Warn( p_input, "keeping sout" );
+ vlc_object_detach( p_input->p_sout );
+ vlc_object_attach( p_input->p_sout, p_pl );
+ }
+ else
+ {
+ msg_Warn( p_input, "destroying sout" );
+ sout_DeleteInstance( p_input->p_sout );
+ }
+ if( p_pl )
+ vlc_object_release( p_pl );
+ }
+
+ /* Delete meta */
+ if( p_input->p_meta )
+ vlc_meta_Delete( p_input->p_meta );
+
+ /* Tell we're dead */
+ p_input->b_dead = VLC_TRUE;
+}
+
+/*****************************************************************************
+ * Control
+ *****************************************************************************/
+static inline int ControlPopNoLock( input_thread_t *p_input,
+ int *pi_type, vlc_value_t *p_val )
+{
+ if( p_input->i_control <= 0 )
+ {
+ return VLC_EGENERIC;
+ }
+
+ *pi_type = p_input->control[0].i_type;
+ *p_val = p_input->control[0].val;
+
+ p_input->i_control--;
+ if( p_input->i_control > 0 )
+ {
+ int i;
+
+ for( i = 0; i < p_input->i_control; i++ )
+ {
+ p_input->control[i].i_type = p_input->control[i+1].i_type;
+ p_input->control[i].val = p_input->control[i+1].val;
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+
+static void ControlReduce( input_thread_t *p_input )
+{
+ int i;
+ for( i = 1; i < p_input->i_control; i++ )
+ {
+ const int i_lt = p_input->control[i-1].i_type;
+ const int i_ct = p_input->control[i].i_type;
+
+ /* XXX We can't merge INPUT_CONTROL_SET_ES */
+/* msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control,
+ i_lt, i_ct );
+*/
+ if( i_lt == i_ct &&
+ ( i_ct == INPUT_CONTROL_SET_STATE ||
+ i_ct == INPUT_CONTROL_SET_RATE ||
+ i_ct == INPUT_CONTROL_SET_POSITION ||
+ i_ct == INPUT_CONTROL_SET_TIME ||
+ i_ct == INPUT_CONTROL_SET_PROGRAM ||
+ i_ct == INPUT_CONTROL_SET_TITLE ||
+ i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
+ i_ct == INPUT_CONTROL_SET_BOOKMARK ) )
+ {
+ int j;
+// msg_Dbg( p_input, "merged at %d", i );
+ /* Remove the i-1 */
+ for( j = i; j < p_input->i_control; j++ )
+ p_input->control[j-1] = p_input->control[j];
+ p_input->i_control--;
+ }
+ else
+ {
+ /* TODO but that's not that important
+ - merge SET_X with SET_X_CMD
+ - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
+ - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
+ - ?
+ */
+ }
+ }
+}
+
+static vlc_bool_t Control( input_thread_t *p_input, int i_type,
+ vlc_value_t val )
+{
+ vlc_bool_t b_force_update = VLC_FALSE;
+
+ switch( i_type )
+ {
+ case INPUT_CONTROL_SET_DIE:
+ msg_Dbg( p_input, "control: stopping input" );
+ /* Mark all submodules to die */
+ if( p_input->input.p_access )
+ p_input->input.p_access->b_die = VLC_TRUE;
+ if( p_input->input.p_stream )
+ p_input->input.p_stream->b_die = VLC_TRUE;
+ p_input->input.p_demux->b_die = VLC_TRUE;
+
+ p_input->b_die = VLC_TRUE;
+ break;
+
+ case INPUT_CONTROL_SET_POSITION:
+ case INPUT_CONTROL_SET_POSITION_OFFSET:
+ {
+ double f_pos;
+ if( i_type == INPUT_CONTROL_SET_POSITION )
+ {
+ f_pos = val.f_float;
+ }
+ else
+ {
+ /* Should not fail */
+ demux2_Control( p_input->input.p_demux,
+ DEMUX_GET_POSITION, &f_pos );
+ f_pos += val.f_float;
+ }
+ if( f_pos < 0.0 ) f_pos = 0.0;
+ if( f_pos > 1.0 ) f_pos = 1.0;
+ /* Reset the decoders states and clock synch (before calling the demuxer */
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION,
+ f_pos ) )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) "
+ "%2.1f%% failed", f_pos * 100 );
+ }
+ else
+ {
+ if( p_input->i_slave > 0 )
+ SlaveSeek( p_input );
+
+ //input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ //es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ b_force_update = VLC_TRUE;
+ }
+ break;
+ }
+
+ case INPUT_CONTROL_SET_TIME:
+ case INPUT_CONTROL_SET_TIME_OFFSET:
+ {
+ int64_t i_time;
+ int i_ret;
+
+ if( i_type == INPUT_CONTROL_SET_TIME )
+ {
+ i_time = val.i_time;
+ }
+ else
+ {
+ /* Should not fail */
+ demux2_Control( p_input->input.p_demux,
+ DEMUX_GET_TIME, &i_time );
+ i_time += val.i_time;
+ }
+ if( i_time < 0 ) i_time = 0;
+
+ /* Reset the decoders states and clock synch (before calling the demuxer */
+ es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+
+ i_ret = demux2_Control( p_input->input.p_demux,
+ DEMUX_SET_TIME, i_time );
+ if( i_ret )
+ {
+ int64_t i_length;
+ /* Emulate it with a SET_POS */
+
+ demux2_Control( p_input->input.p_demux,
+ DEMUX_GET_LENGTH, &i_length );
+ if( i_length > 0 )
+ {
+ double f_pos = (double)i_time / (double)i_length;
+ i_ret = demux2_Control( p_input->input.p_demux,
+ DEMUX_SET_POSITION, f_pos );
+ }
+ }
+ if( i_ret )
+ {
+ msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) "I64Fd
+ " failed", i_time );
+ }
+ else
+ {
+ if( p_input->i_slave > 0 )
+ SlaveSeek( p_input );
+
+ //input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
+ //es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
+ b_force_update = VLC_TRUE;
+ }
+ break;
+ }
+
+ 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" );
+ vlc_mutex_unlock( &p_input->lock_control );
+ input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
+ vlc_mutex_lock( &p_input->lock_control );
+ }
+
+ 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->p_sout && !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 );
+
+ demux2_Control( p_input->input.p_demux, DEMUX_SET_GROUP, val.i_int,
+ NULL );
+ 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;
+ int64_t i_input_time;
+ int64_t i_seekpoint_time;
+
+ if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
+ {
+ i_seekpoint = p_demux->info.i_seekpoint;
+ i_seekpoint_time = p_input->input.title[p_demux->info.i_title]->seekpoint[i_seekpoint]->i_time_offset;
+ if( i_seekpoint_time >= 0 &&
+ !demux2_Control( p_demux,
+ DEMUX_GET_TIME, &i_input_time ) )
+ {
+ if ( i_input_time < i_seekpoint_time + 3000000 )
+ i_seekpoint--;
+ }
+ else
+ i_seekpoint--;
+ }
+ 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 )
+ {
+ demux_t *p_demux = p_input->input.p_demux;
+ access_t *p_access = p_input->input.p_access;
+ int i_seekpoint;
+ int64_t i_input_time;
+ int64_t i_seekpoint_time;
+
+ if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
+ {
+ i_seekpoint = p_access->info.i_seekpoint;
+ i_seekpoint_time = p_input->input.title[p_access->info.i_title]->seekpoint[i_seekpoint]->i_time_offset;
+ if( i_seekpoint_time >= 0 &&
+ demux2_Control( p_demux,
+ DEMUX_GET_TIME, &i_input_time ) )
+ {
+ if ( i_input_time < i_seekpoint_time + 3000000 )
+ i_seekpoint--;
+ }
+ else
+ i_seekpoint--;
+ }
+ else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_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_ADD_SLAVE:
+ if( val.psz_string )
+ {
+ input_source_t *slave = InputSourceNew( p_input );
+
+ if( !InputSourceInit( p_input, slave, val.psz_string, NULL,
+ VLC_FALSE ) )
+ {
+ vlc_meta_t *p_meta_new = NULL;
+ vlc_meta_t *p_meta;
+ int64_t i_time;
+
+ /* Add the slave */
+ msg_Dbg( p_input, "adding %s as slave on the fly",
+ val.psz_string );
+
+ /* Set position */
+ if( demux2_Control( p_input->input.p_demux,
+ DEMUX_GET_TIME, &i_time ) )
+ {
+ msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" );
+ InputSourceClean( p_input, slave );
+ free( slave );
+ break;
+ }
+ if( demux2_Control( slave->p_demux,
+ DEMUX_SET_TIME, i_time ) )
+ {
+ msg_Err( p_input, "seek failed for new slave" );
+ InputSourceClean( p_input, slave );
+ free( slave );
+ break;
+ }
+
+
+ /* Get meta (access and demux) */
+ if( access2_Control( slave->p_access,
+ ACCESS_GET_META, &p_meta_new ) )
+ p_meta_new = NULL;
+ if( !demux2_Control( slave->p_demux,
+ DEMUX_GET_META, &p_meta ) )
+ {
+ if( p_meta_new )
+ {
+ vlc_meta_Merge( p_meta_new, p_meta );
+ vlc_meta_Delete( p_meta );
+ }
+ else
+ {
+ p_meta_new = p_meta;
+ }
+ }
+ /* Update meta */
+ if( p_meta_new )
+ {
+ if( p_input->p_meta )
+ {
+ vlc_meta_Merge( p_input->p_meta, p_meta_new );
+ vlc_meta_Delete( p_meta_new );
+ }
+ else
+ {
+ p_input->p_meta = p_meta_new;
+ }
+ UpdateMeta( p_input, VLC_FALSE );
+ }
+
+ TAB_APPEND( p_input->i_slave, p_input->slave, slave );
+ }
+ else
+ {
+ msg_Warn( p_input, "failed to add %s as slave",
+ val.psz_string );
+ }
+
+ free( val.psz_string );
+ }
+ break;
+
+ case INPUT_CONTROL_SET_BOOKMARK:
+ default:
+ msg_Err( p_input, "not yet implemented" );
+ break;
+ }
+
+ return b_force_update;
+}
+
+/*****************************************************************************
+ * UpdateFromDemux:
+ *****************************************************************************/
+static int 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;
+
+ /* Hmmm only works with master input */
+ if( p_input->input.p_demux == p_demux )
+ {
+ int i_title_end = p_input->input.i_title_end -
+ p_input->input.i_title_offset;
+ int i_seekpoint_end = p_input->input.i_seekpoint_end -
+ p_input->input.i_seekpoint_offset;
+
+ if( i_title_end >= 0 && i_seekpoint_end >= 0 )
+ {
+ if( p_demux->info.i_title > i_title_end ||
+ ( p_demux->info.i_title == i_title_end &&
+ p_demux->info.i_seekpoint > i_seekpoint_end ) ) return 0;
+ }
+ else if( i_seekpoint_end >=0 )
+ {
+ if( p_demux->info.i_seekpoint > i_seekpoint_end ) return 0;
+ }
+ else if( i_title_end >= 0 )
+ {
+ if( p_demux->info.i_title > i_title_end ) return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*****************************************************************************
+ * UpdateFromAccess:
+ *****************************************************************************/
+static int 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 );
+
+ stream_AccessUpdate( p_input->input.p_stream );
+
+ 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;
+ }
+ if( p_access->info.i_update & INPUT_UPDATE_META )
+ {
+ /* TODO maybe multi - access ? */
+ vlc_meta_t *p_meta;
+ if( !access2_Control( p_input->input.p_access,ACCESS_GET_META,&p_meta))
+ {
+ if( p_input->p_meta )
+ {
+ vlc_meta_Merge( p_input->p_meta, p_meta );
+ vlc_meta_Delete( p_meta );
+ }
+ else
+ {
+ p_input->p_meta = p_meta;
+ }
+
+ UpdateMeta( p_input, VLC_FALSE );
+ var_SetBool( p_input, "item-change", p_input->input.p_item->i_id );
+ }
+ p_access->info.i_update &= ~INPUT_UPDATE_META;
+ }
+
+ p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
+
+ /* Hmmm only works with master input */
+ if( p_input->input.p_access == p_access )
+ {
+ int i_title_end = p_input->input.i_title_end -
+ p_input->input.i_title_offset;
+ int i_seekpoint_end = p_input->input.i_seekpoint_end -
+ p_input->input.i_seekpoint_offset;
+
+ if( i_title_end >= 0 && i_seekpoint_end >=0 )
+ {
+ if( p_access->info.i_title > i_title_end ||
+ ( p_access->info.i_title == i_title_end &&
+ p_access->info.i_seekpoint > i_seekpoint_end ) ) return 0;
+ }
+ else if( i_seekpoint_end >=0 )
+ {
+ if( p_access->info.i_seekpoint > i_seekpoint_end ) return 0;
+ }
+ else if( i_title_end >= 0 )
+ {
+ if( p_access->info.i_title > i_title_end ) return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*****************************************************************************
+ * UpdateMeta:
+ *****************************************************************************/
+static int UpdateMeta( input_thread_t *p_input, vlc_bool_t b_quick )
+{
+ vlc_meta_t *p_meta = p_input->p_meta;
+ int i;
+
+ if( !p_meta || p_meta->i_meta == 0 )
+ return VLC_SUCCESS;
+
+ if( !b_quick ) msg_Dbg( p_input, "meta information:" );
+ for( i = 0; i < p_meta->i_meta; i++ )
+ {
+ if( !b_quick )
+ msg_Dbg( p_input, " - '%s' = '%s'",
+ _(p_meta->name[i]), p_meta->value[i] );
+
+ if( !strcmp(p_meta->name[i], VLC_META_TITLE) && p_meta->value[i] &&
+ !p_input->input.p_item->b_fixed_name )
+ input_Control( p_input, INPUT_SET_NAME, p_meta->value[i] );
+
+ if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
+ input_Control( p_input, INPUT_ADD_INFO, _("General"),
+ _("Author"), p_meta->value[i] );
+
+ input_Control( p_input, INPUT_ADD_INFO, _("Meta-information"),
+ _(p_meta->name[i]), "%s", p_meta->value[i] );
+ }
+
+ for( i = 0; i < p_meta->i_track; i++ )
+ {
+ vlc_meta_t *tk = p_meta->track[i];
+ int j;