/***************************************************************************** * var.c: object variables for input thread ***************************************************************************** * Copyright (C) 2004-2007 VLC authors and VideoLAN * * Authors: Laurent Aimar * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include "input_internal.h" /***************************************************************************** * Callbacks *****************************************************************************/ static int StateCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int RateCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int TimeCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int TimeOffsetCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int ProgramCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int TitleCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int SeekpointCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int EsVideoCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int EsAudioCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int EsSpuCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void * ); static int RecordCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ); static int FrameNextCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ); static int SubFpsCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ); static void input_LegacyVarTitle( input_thread_t *p_input, int i_title ); static void input_LegacyVarNavigation( input_thread_t *p_input ); typedef struct { const char *psz_name; vlc_callback_t callback; } vlc_input_callback_t; static void InputAddCallbacks( input_thread_t *, const vlc_input_callback_t * ); #ifdef CALLBACK /* For windows */ # undef CALLBACK /* We don't care of this one here */ #endif /* List all callbacks added by input */ #define CALLBACK(name,cb) { name, cb } static const vlc_input_callback_t p_input_callbacks[] = { CALLBACK( "state", StateCallback ), CALLBACK( "rate", RateCallback ), CALLBACK( "position", PositionCallback ), CALLBACK( "time", TimeCallback ), CALLBACK( "time-offset", TimeOffsetCallback ), CALLBACK( "bookmark", BookmarkCallback ), CALLBACK( "program", ProgramCallback ), CALLBACK( "title", TitleCallback ), CALLBACK( "chapter", SeekpointCallback ), CALLBACK( "audio-delay", EsDelayCallback ), CALLBACK( "spu-delay", EsDelayCallback ), CALLBACK( "video-es", EsVideoCallback ), CALLBACK( "audio-es", EsAudioCallback ), CALLBACK( "spu-es", EsSpuCallback ), CALLBACK( "record", RecordCallback ), CALLBACK( "frame-next", FrameNextCallback ), CALLBACK( "sub-fps", SubFpsCallback ), CALLBACK( NULL, NULL ) }; #undef CALLBACK static void Trigger( input_thread_t *p_input, int i_type ) { var_SetInteger( p_input, "intf-event", i_type ); } static void VarListAdd( input_thread_t *p_input, const char *psz_variable, int i_value, const char *psz_text ) { vlc_value_t val; val.i_int = i_value; var_Change( p_input, psz_variable, VLC_VAR_ADDCHOICE, val, psz_text ); } static void VarListDel( input_thread_t *p_input, const char *psz_variable, int i_value ) { vlc_value_t val; if( i_value >= 0 ) { val.i_int = i_value; var_Change( p_input, psz_variable, VLC_VAR_DELCHOICE, val ); } else { var_Change( p_input, psz_variable, VLC_VAR_CLEARCHOICES ); } } static void VarListSelect( input_thread_t *p_input, const char *psz_variable, int i_value ) { vlc_value_t val; val.i_int = i_value; var_Change( p_input, psz_variable, VLC_VAR_SETVALUE, val ); } static const char *GetEsVarName( enum es_format_category_e i_cat ) { switch( i_cat ) { case VIDEO_ES: return "video-es"; case AUDIO_ES: return "audio-es"; case SPU_ES: return "spu-es"; default: return NULL; } } static void UpdateBookmarksOption( input_thread_t *p_input ) { input_thread_private_t *priv = input_priv(p_input); input_item_t* item = priv->p_item; struct vlc_memstream vstr; vlc_memstream_open( &vstr ); vlc_memstream_puts( &vstr, "bookmarks=" ); vlc_mutex_lock( &item->lock ); var_Change( p_input, "bookmark", VLC_VAR_CLEARCHOICES ); for( int i = 0; i < priv->i_bookmark; i++ ) { seekpoint_t const* sp = priv->pp_bookmark[i]; /* Add bookmark to choice-list */ var_Change( p_input, "bookmark", VLC_VAR_ADDCHOICE, (vlc_value_t){ .i_int = i }, sp->psz_name ); /* Append bookmark to option-buffer */ /* TODO: escape inappropriate values */ vlc_memstream_printf( &vstr, "%s{name=%s,time=%.3f}", i > 0 ? "," : "", sp->psz_name, secf_from_vlc_tick(sp->i_time_offset) ); } if( vlc_memstream_close( &vstr ) ) { vlc_mutex_unlock( &item->lock ); return; } /* XXX: The below is ugly and should be fixed elsewhere, but in order to * not have more than one "bookmarks=" option associated with the item, we * need to remove any existing ones before adding the new one. This logic * should exist in input_item_AddOption with "OPTION_UNIQUE & , but until then we handle it here. */ char** const orig_beg = &item->ppsz_options[0]; char** const orig_end = orig_beg + item->i_options; char** end = orig_end; for( char** option = orig_beg; option != end; ) { if( strncmp( *option, "bookmarks=", 10 ) ) ++option; else { free( *option ); /* It might be tempting to optimize the below by overwriting * *option with the value of the last element, however; we want to * preserve the order of the other options (as behavior might * depend on it) */ memmove( option, option + 1, ( --end - option ) * sizeof *end ); } } if( end != orig_end ) /* we removed at least 1 option */ { *end = vstr.ptr; item->i_options = end - orig_beg + 1; vlc_mutex_unlock( &item->lock ); } else /* nothing removed, add the usual way */ { vlc_mutex_unlock( &item->lock ); input_item_AddOption( item, vstr.ptr, VLC_INPUT_OPTION_UNIQUE ); free( vstr.ptr ); } } static inline bool EsFmtIsTeletext( const es_format_t *p_fmt ) { return p_fmt->i_cat == SPU_ES && p_fmt->i_codec == VLC_CODEC_TELETEXT; } void input_LegacyEvents( input_thread_t *p_input, const struct vlc_input_event *event, void *user_data ) { (void) user_data; vlc_value_t val; switch( event->type ) { case INPUT_EVENT_STATE: val.i_int = event->state; var_Change( p_input, "state", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_DEAD: break; case INPUT_EVENT_RATE: val.f_float = event->rate; var_Change( p_input, "rate", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_CAPABILITIES: var_SetBool( p_input, "can-seek", event->capabilities & VLC_INPUT_CAPABILITIES_SEEKABLE ); var_SetBool( p_input, "can-pause", event->capabilities & VLC_INPUT_CAPABILITIES_PAUSEABLE ); var_SetBool( p_input, "can-rate", event->capabilities & VLC_INPUT_CAPABILITIES_CHANGE_RATE ); var_SetBool( p_input, "can-rewind", event->capabilities & VLC_INPUT_CAPABILITIES_REWINDABLE ); var_SetBool( p_input, "can-record", event->capabilities & VLC_INPUT_CAPABILITIES_RECORDABLE ); break; case INPUT_EVENT_POSITION: val.f_float = event->position.percentage; var_Change( p_input, "position", VLC_VAR_SETVALUE, val ); val.i_int = event->position.ms; var_Change( p_input, "time", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_LENGTH: /* FIXME ugly + what about meta change event ? */ if( var_GetInteger( p_input, "length" ) == event->length ) return; val.i_int = event->length; var_Change( p_input, "length", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_TITLE: val.i_int = event->title.action == VLC_INPUT_TITLE_NEW_LIST ? 0 : event->title.selected_idx; var_Change( p_input, "title", VLC_VAR_SETVALUE, val ); input_LegacyVarNavigation( p_input ); input_LegacyVarTitle( p_input, val.i_int ); break; case INPUT_EVENT_CHAPTER: /* "chapter" */ val.i_int = event->chapter.seekpoint; var_Change( p_input, "chapter", VLC_VAR_SETVALUE, val ); /* "title %2u" */ char psz_title[sizeof ("title ") + 3 * sizeof (int)]; sprintf( psz_title, "title %2u", event->chapter.title ); var_Change( p_input, psz_title, VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_PROGRAM: switch (event->program.action) { case VLC_INPUT_PROGRAM_ADDED: VarListAdd( p_input, "program", event->program.id, event->program.title ); break; case VLC_INPUT_PROGRAM_DELETED: VarListDel( p_input, "program", event->program.id ); break; case VLC_INPUT_PROGRAM_UPDATED: VarListDel( p_input, "program", event->program.id ); VarListAdd( p_input, "program", event->program.id, event->program.title ); break; case VLC_INPUT_PROGRAM_SELECTED: VarListSelect( p_input, "program", event->program.id ); break; case VLC_INPUT_PROGRAM_SCRAMBLED: if( var_GetInteger( p_input, "program" ) != event->program.id ) return; var_SetBool( p_input, "program-scrambled", event->program.scrambled ); break; } break; case INPUT_EVENT_ES: switch (event->es.action) { case VLC_INPUT_ES_ADDED: { const char *varname = GetEsVarName( event->es.fmt->i_cat ); if( varname ) { size_t count; var_Change( p_input, varname, VLC_VAR_CHOICESCOUNT, &count ); if( count == 0 ) { /* First one, we need to add the "Disable" choice */ VarListAdd( p_input, varname, -1, _("Disable") ); } VarListAdd( p_input, varname, event->es.fmt->i_id, event->es.title ); } if( EsFmtIsTeletext( event->es.fmt ) ) { char psz_page[3+1]; snprintf( psz_page, sizeof(psz_page), "%d%2.2x", event->es.fmt->subs.teletext.i_magazine, event->es.fmt->subs.teletext.i_page ); VarListAdd( p_input, "teletext-es", event->es.fmt->i_id, event->es.fmt->subs.teletext.i_magazine >= 0 ? psz_page : "" ); } break; } case VLC_INPUT_ES_DELETED: { const char *varname = GetEsVarName( event->es.fmt->i_cat ); if( varname ) VarListDel( p_input, varname, event->es.fmt->i_id ); if( EsFmtIsTeletext( event->es.fmt ) ) VarListDel( p_input, "teletext-es", event->es.fmt->i_id ); break; } case VLC_INPUT_ES_SELECTED: case VLC_INPUT_ES_UNSELECTED: { int i_id = event->es.action == VLC_INPUT_ES_SELECTED ? event->es.fmt->i_id : -1; const char *varname = GetEsVarName( event->es.fmt->i_cat ); if( varname ) VarListSelect( p_input, varname, i_id ); if( EsFmtIsTeletext( event->es.fmt ) ) VarListSelect( p_input, "teletext-es", i_id ); break; } case VLC_INPUT_ES_UPDATED: break; } break; case INPUT_EVENT_RECORD: val.b_bool = event->record; var_Change( p_input, "record", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_ITEM_META: break; case INPUT_EVENT_ITEM_INFO: break; case INPUT_EVENT_ITEM_EPG: break; case INPUT_EVENT_STATISTICS: break; case INPUT_EVENT_SIGNAL: val.f_float = event->signal.quality; var_Change( p_input, "signal-quality", VLC_VAR_SETVALUE, val ); val.f_float = event->signal.strength; var_Change( p_input, "signal-strength", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_AUDIO_DELAY: val.i_int = event->audio_delay; var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_SUBTITLE_DELAY: val.i_int = event->subtitle_delay; var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_BOOKMARK: UpdateBookmarksOption( p_input ); break; case INPUT_EVENT_CACHE: val.f_float = event->cache; var_Change( p_input, "cache", VLC_VAR_SETVALUE, val ); break; case INPUT_EVENT_VOUT: case INPUT_EVENT_SUBITEMS: case INPUT_EVENT_THUMBNAIL_READY: case INPUT_EVENT_VBI_PAGE: case INPUT_EVENT_VBI_TRANSPARENCY: case INPUT_EVENT_SUBS_FPS: break; } Trigger( p_input, event->type ); } /***************************************************************************** * input_LegacyVarInit: * Create all control object variables with their callbacks *****************************************************************************/ void input_LegacyVarInit ( input_thread_t *p_input ) { vlc_value_t val; var_Create( p_input, "can-seek", VLC_VAR_BOOL ); var_SetBool( p_input, "can-seek", true ); /* Fixed later*/ var_Create( p_input, "can-pause", VLC_VAR_BOOL ); var_SetBool( p_input, "can-pause", true ); /* Fixed later*/ var_Create( p_input, "can-rate", VLC_VAR_BOOL ); var_SetBool( p_input, "can-rate", false ); var_Create( p_input, "can-rewind", VLC_VAR_BOOL ); var_SetBool( p_input, "can-rewind", false ); var_Create( p_input, "can-record", VLC_VAR_BOOL ); var_SetBool( p_input, "can-record", false ); /* Fixed later*/ var_Create( p_input, "record", VLC_VAR_BOOL ); var_SetBool( p_input, "record", false ); var_Create( p_input, "teletext-es", VLC_VAR_INTEGER ); var_SetInteger( p_input, "teletext-es", -1 ); var_Create( p_input, "cache", VLC_VAR_FLOAT ); var_SetFloat( p_input, "cache", 0.0 ); var_Create( p_input, "signal-quality", VLC_VAR_FLOAT ); var_SetFloat( p_input, "signal-quality", -1 ); var_Create( p_input, "signal-strength", VLC_VAR_FLOAT ); var_SetFloat( p_input, "signal-strength", -1 ); var_Create( p_input, "program-scrambled", VLC_VAR_BOOL ); var_SetBool( p_input, "program-scrambled", false ); /* State */ var_Create( p_input, "state", VLC_VAR_INTEGER ); val.i_int = input_priv(p_input)->i_state; var_Change( p_input, "state", VLC_VAR_SETVALUE, val ); var_Create( p_input, "frame-next", VLC_VAR_VOID ); /* Position */ var_Create( p_input, "position", VLC_VAR_FLOAT ); /* Time */ var_Create( p_input, "time", VLC_VAR_INTEGER ); var_Create( p_input, "time-offset", VLC_VAR_INTEGER ); /* relative */ /* Bookmark */ var_Create( p_input, "bookmark", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); var_Change( p_input, "bookmark", VLC_VAR_SETTEXT, _("Bookmark") ); /* Program */ var_Get( p_input, "program", &val ); if( val.i_int <= 0 ) var_Change( p_input, "program", VLC_VAR_DELCHOICE, val ); var_Change( p_input, "program", VLC_VAR_SETTEXT, _("Program") ); /* Programs */ var_Change( p_input, "programs", VLC_VAR_SETTEXT, _("Programs") ); /* Title */ var_Create( p_input, "title", VLC_VAR_INTEGER ); var_Change( p_input, "title", VLC_VAR_SETTEXT, _("Title") ); /* Chapter */ var_Create( p_input, "chapter", VLC_VAR_INTEGER ); var_Change( p_input, "chapter", VLC_VAR_SETTEXT, _("Chapter") ); /* Delay */ var_Create( p_input, "audio-delay", VLC_VAR_INTEGER ); var_SetInteger( p_input, "audio-delay", VLC_TICK_FROM_MS( var_GetInteger( p_input, "audio-desync" ) ) ); var_Create( p_input, "spu-delay", VLC_VAR_INTEGER ); val.i_int = -1; /* Video ES */ var_Create( p_input, "video-es", VLC_VAR_INTEGER ); var_Change( p_input, "video-es", VLC_VAR_SETVALUE, val ); var_Change( p_input, "video-es", VLC_VAR_SETTEXT, _("Video Track") ); /* Audio ES */ var_Create( p_input, "audio-es", VLC_VAR_INTEGER ); var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, val ); var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, _("Audio Track") ); /* Spu ES */ var_Create( p_input, "spu-es", VLC_VAR_INTEGER ); var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, val ); var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, _("Subtitle Track") ); var_Create( p_input, "spu-choice", VLC_VAR_INTEGER ); var_SetInteger( p_input, "spu-choice", -1 ); var_Create( p_input, "length", VLC_VAR_INTEGER ); var_Create( p_input, "bit-rate", VLC_VAR_INTEGER ); var_Create( p_input, "sample-rate", VLC_VAR_INTEGER ); /* Special "intf-event" variable. */ var_Create( p_input, "intf-event", VLC_VAR_INTEGER ); var_Create( p_input, "sub-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); /* Add all callbacks * XXX we put callback only in non preparsing mode. We need to create the variable * unless someone want to check all var_Get/var_Change return value ... */ if( !input_priv(p_input)->b_preparsing ) InputAddCallbacks( p_input, p_input_callbacks ); } /***************************************************************************** * input_LegacyVarNavigation: * Create all remaining control object variables *****************************************************************************/ static void input_LegacyVarNavigation( input_thread_t *p_input ) { /* Create more command variables */ if( input_priv(p_input)->i_title > 1 ) { if( var_Type( p_input, "next-title" ) == 0 ) { var_Create( p_input, "next-title", VLC_VAR_VOID ); var_Change( p_input, "next-title", VLC_VAR_SETTEXT, _("Next title") ); var_AddCallback( p_input, "next-title", TitleCallback, NULL ); } if( var_Type( p_input, "prev-title" ) == 0 ) { var_Create( p_input, "prev-title", VLC_VAR_VOID ); var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, _("Previous title") ); var_AddCallback( p_input, "prev-title", TitleCallback, NULL ); } if( var_Type( p_input, "menu-title" ) == 0 ) { var_Create( p_input, "menu-title", VLC_VAR_VOID ); var_Change( p_input, "menu-title", VLC_VAR_SETTEXT, _("Menu title") ); var_AddCallback( p_input, "menu-title", TitleCallback, NULL ); } if( var_Type( p_input, "menu-popup" ) == 0 ) { var_Create( p_input, "menu-popup", VLC_VAR_VOID ); var_Change( p_input, "menu-popup", VLC_VAR_SETTEXT, _("Menu popup") ); var_AddCallback( p_input, "menu-popup", TitleCallback, NULL ); } } /* Create titles and chapters */ var_Change( p_input, "title", VLC_VAR_CLEARCHOICES ); for( int i = 0; i < input_priv(p_input)->i_title; i++ ) { vlc_value_t val2, text2; char title[sizeof("title ") + 3 * sizeof (int)]; /* Add Navigation entries */ sprintf( title, "title %2u", i ); var_Destroy( p_input, title ); var_Create( p_input, title, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); var_AddCallback( p_input, title, NavigationCallback, NULL ); char psz_length[MSTRTIME_MAX_SIZE + sizeof(" []")]; if( input_priv(p_input)->title[i]->i_length > 0 ) { strcpy( psz_length, " [" ); secstotimestr( &psz_length[2], SEC_FROM_VLC_TICK(input_priv(p_input)->title[i]->i_length) ); strcat( psz_length, "]" ); } else psz_length[0] = '\0'; char *titlestr; if( input_priv(p_input)->title[i]->psz_name == NULL || *input_priv(p_input)->title[i]->psz_name == '\0' ) { if( asprintf( &titlestr, _("Title %i%s"), i + input_priv(p_input)->i_title_offset, psz_length ) == -1 ) continue; } else { if( asprintf( &titlestr, "%s%s", input_priv(p_input)->title[i]->psz_name, psz_length ) == -1 ) continue; } /* Add title choice */ val2.i_int = i; var_Change( p_input, "title", VLC_VAR_ADDCHOICE, val2, (const char *)titlestr ); free( titlestr ); for( int j = 0; j < input_priv(p_input)->title[i]->i_seekpoint; j++ ) { val2.i_int = j; if( input_priv(p_input)->title[i]->seekpoint[j]->psz_name == NULL || *input_priv(p_input)->title[i]->seekpoint[j]->psz_name == '\0' ) { /* Default value */ if( asprintf( &text2.psz_string, _("Chapter %i"), j + input_priv(p_input)->i_seekpoint_offset ) == -1 ) continue; } else { text2.psz_string = strdup( input_priv(p_input)->title[i]->seekpoint[j]->psz_name ); } var_Change( p_input, title, VLC_VAR_ADDCHOICE, val2, (const char *)text2.psz_string ); free( text2.psz_string ); } } } /***************************************************************************** * input_LegacyVarTitle: * Create all variables for a title *****************************************************************************/ static void input_LegacyVarTitle( input_thread_t *p_input, int i_title ) { const input_title_t *t = input_priv(p_input)->title[i_title]; vlc_value_t text; int i; /* Create/Destroy command variables */ if( t->i_seekpoint <= 1 ) { var_Destroy( p_input, "next-chapter" ); var_Destroy( p_input, "prev-chapter" ); } else if( var_Type( p_input, "next-chapter" ) == 0 ) { var_Create( p_input, "next-chapter", VLC_VAR_VOID ); var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, _("Next chapter") ); var_AddCallback( p_input, "next-chapter", SeekpointCallback, NULL ); var_Create( p_input, "prev-chapter", VLC_VAR_VOID ); var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, _("Previous chapter") ); var_AddCallback( p_input, "prev-chapter", SeekpointCallback, NULL ); } /* Build chapter list */ var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES ); for( i = 0; i < t->i_seekpoint; i++ ) { vlc_value_t val; val.i_int = i; if( t->seekpoint[i]->psz_name == NULL || *t->seekpoint[i]->psz_name == '\0' ) { /* Default value */ if( asprintf( &text.psz_string, _("Chapter %i"), i + input_priv(p_input)->i_seekpoint_offset ) == -1 ) continue; } else { text.psz_string = strdup( t->seekpoint[i]->psz_name ); } var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, val, (const char *)text.psz_string ); free( text.psz_string ); } } /***************************************************************************** * input_ConfigVarInit: * Create all config object variables *****************************************************************************/ void input_ConfigVarInit ( input_thread_t *p_input ) { /* Create Object Variables for private use only */ if( !input_priv(p_input)->b_preparsing ) { var_Create( p_input, "video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "video-track", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "audio-track", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-track", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "audio-language", VLC_VAR_STRING|VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-language", VLC_VAR_STRING|VLC_VAR_DOINHERIT ); var_Create( p_input, "menu-language", VLC_VAR_STRING|VLC_VAR_DOINHERIT ); var_Create( p_input, "video-track-id", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "audio-track-id", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-track-id", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-file", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-autodetect-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "sub-autodetect-fuzzy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout-all", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout-spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "sout-keep", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "input-repeat", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); var_Create( p_input, "start-time", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT ); var_Create( p_input, "stop-time", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT ); var_Create( p_input, "run-time", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT ); var_Create( p_input, "input-fast-seek", VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); var_Create( p_input, "input-slave", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "audio-desync", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_input, "cr-average", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_input, "clock-synchro", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "programs", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_input, "rate", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); } /* */ var_Create( p_input, "input-record-native", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); /* */ var_Create( p_input, "access", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "demux", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "demux-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "stream-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); /* Meta */ var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "meta-artist", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "meta-genre", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "meta-copyright", VLC_VAR_STRING | VLC_VAR_DOINHERIT); var_Create( p_input, "meta-description", VLC_VAR_STRING|VLC_VAR_DOINHERIT); var_Create( p_input, "meta-date", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Create( p_input, "meta-url", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); /* Inherited by demux/subtitle.c */ var_Create( p_input, "sub-original-fps", VLC_VAR_FLOAT ); } /***************************************************************************** * Callbacks managements: *****************************************************************************/ static void InputAddCallbacks( input_thread_t *p_input, const vlc_input_callback_t *p_callbacks ) { int i; for( i = 0; p_callbacks[i].psz_name != NULL; i++ ) var_AddCallback( p_input, p_callbacks[i].psz_name, p_callbacks[i].callback, NULL ); } /***************************************************************************** * All Callbacks: *****************************************************************************/ static int StateCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); if( newval.i_int == PLAYING_S || newval.i_int == PAUSE_S ) { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_STATE, &newval ); return VLC_SUCCESS; } return VLC_EGENERIC; } static int RateCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(oldval); VLC_UNUSED(p_data); VLC_UNUSED(psz_cmd); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_RATE, &newval ); return VLC_SUCCESS; } static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); /* Update "length" for better intf behaviour */ const vlc_tick_t i_length = var_GetInteger( p_input, "length" ); if( i_length > 0 && newval.f_float >= 0.f && newval.f_float <= 1.f ) { vlc_value_t val; val.i_int = i_length * newval.f_float; var_Change( p_input, "time", VLC_VAR_SETVALUE, val ); } input_SetPosition( p_input, newval.f_float, var_GetBool( p_input, "input-fast-seek" ) ); return VLC_SUCCESS; } static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); /* Update "position" for better intf behaviour */ const vlc_tick_t i_length = var_GetInteger( p_input, "length" ); if( i_length > 0 && newval.i_int >= 0 && newval.i_int <= i_length ) { vlc_value_t val; val.f_float = (double)newval.i_int/(double)i_length; var_Change( p_input, "position", VLC_VAR_SETVALUE, val ); /* * Notify the intf that a new event has been occurred. * XXX this is a bit hackish but it's the only way to do it now. */ var_SetInteger( p_input, "intf-event", INPUT_EVENT_POSITION ); } input_SetTime( p_input, newval.i_int, var_GetBool( p_input, "input-fast-seek" ) ); return VLC_SUCCESS; } static int TimeOffsetCallback( vlc_object_t *obj, char const *varname, vlc_value_t prev, vlc_value_t cur, void *data ) { VLC_UNUSED(varname); VLC_UNUSED(prev); VLC_UNUSED(data); vlc_tick_t i_time = var_GetInteger( obj, "time" ) + cur.i_int; if( i_time < 0 ) i_time = 0; var_SetInteger( obj, "time", i_time ); return VLC_SUCCESS; } static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_PROGRAM, &newval ); return VLC_SUCCESS; } static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; vlc_value_t val; size_t count; VLC_UNUSED(oldval); VLC_UNUSED(p_data); if( !strcmp( psz_cmd, "next-title" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE_NEXT, NULL ); val.i_int = var_GetInteger( p_input, "title" ) + 1; var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &count ); if( (size_t)val.i_int < count ) var_Change( p_input, "title", VLC_VAR_SETVALUE, val ); } else if( !strcmp( psz_cmd, "prev-title" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE_PREV, NULL ); val.i_int = var_GetInteger( p_input, "title" ) - 1; if( val.i_int >= 0 ) var_Change( p_input, "title", VLC_VAR_SETVALUE, val ); } else if( !strcmp( psz_cmd, "menu-title" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_NAV_MENU, NULL ); } else if( !strcmp( psz_cmd, "menu-popup" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_NAV_POPUP, NULL ); } else { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE, &newval ); } return VLC_SUCCESS; } static int SeekpointCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; vlc_value_t val; size_t count; VLC_UNUSED(oldval); VLC_UNUSED(p_data); if( !strcmp( psz_cmd, "next-chapter" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL ); val.i_int = var_GetInteger( p_input, "chapter" ) + 1; var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &count ); if( (size_t)val.i_int < count ) var_Change( p_input, "chapter", VLC_VAR_SETVALUE, val ); } else if( !strcmp( psz_cmd, "prev-chapter" ) ) { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL ); val.i_int = var_GetInteger( p_input, "chapter" ) - 1; if( val.i_int >= 0 ) var_Change( p_input, "chapter", VLC_VAR_SETVALUE, val ); } else { input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT, &newval ); } return VLC_SUCCESS; } static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; vlc_value_t val; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); /* Issue a title change */ sscanf(psz_cmd, "title %"SCNu64, &val.i_int); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_TITLE, &val ); var_Change( p_input, "title", VLC_VAR_SETVALUE, val ); /* And a chapter change */ input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SEEKPOINT, &newval ); var_Change( p_input, "chapter", VLC_VAR_SETVALUE, newval ); (void) p_data; return VLC_SUCCESS; } static int EsVideoCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED( psz_cmd); VLC_UNUSED( oldval ); VLC_UNUSED( p_data ); if( newval.i_int < 0 ) newval.i_int = -VIDEO_ES; /* disable video es */ input_ControlPushHelper( p_input, INPUT_CONTROL_SET_ES_BY_ID, &newval ); return VLC_SUCCESS; } static int EsAudioCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED( psz_cmd); VLC_UNUSED( oldval ); VLC_UNUSED( p_data ); if( newval.i_int < 0 ) newval.i_int = -AUDIO_ES; /* disable audio es */ input_ControlPushHelper( p_input, INPUT_CONTROL_SET_ES_BY_ID, &newval ); return VLC_SUCCESS; } static int EsSpuCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED( psz_cmd); VLC_UNUSED( oldval ); VLC_UNUSED( p_data ); if( newval.i_int < 0 ) newval.i_int = -SPU_ES; /* disable spu es */ input_ControlPushHelper( p_input, INPUT_CONTROL_SET_ES_BY_ID, &newval ); return VLC_SUCCESS; } static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(oldval); VLC_UNUSED(p_data); input_control_param_t param = { .delay = { .b_absolute = true, .i_val = newval.i_int, }, }; if( !strcmp( psz_cmd, "audio-delay" ) ) input_ControlPush( p_input, INPUT_CONTROL_SET_AUDIO_DELAY, ¶m ); else if( !strcmp( psz_cmd, "spu-delay" ) ) input_ControlPush( p_input, INPUT_CONTROL_SET_SPU_DELAY, ¶m ); return VLC_SUCCESS; } static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_BOOKMARK, &newval ); return VLC_SUCCESS; } static int RecordCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_RECORD_STATE, &newval ); return VLC_SUCCESS; } static int FrameNextCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); VLC_UNUSED(newval); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_FRAME_NEXT, NULL ); return VLC_SUCCESS; } static int SubFpsCallback( vlc_object_t *p_this, char const *psz_cmd, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { input_thread_t *p_input = (input_thread_t*)p_this; VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); VLC_UNUSED(newval); input_ControlPushHelper( p_input, INPUT_CONTROL_SET_SUBS_FPS, &newval ); return VLC_SUCCESS; }