From 16011d374563533a6b473eeeecdcdb827f3834e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi@remlab.net> Date: Mon, 27 May 2019 20:15:11 +0300 Subject: [PATCH] rc: reorder to avoid forward declarations --- modules/control/oldrc.c | 2828 +++++++++++++++++++-------------------- 1 file changed, 1406 insertions(+), 1422 deletions(-) diff --git a/modules/control/oldrc.c b/modules/control/oldrc.c index bcf9210c3f4f..336327610d55 100644 --- a/modules/control/oldrc.c +++ b/modules/control/oldrc.c @@ -64,37 +64,6 @@ #define MAX_LINE_LENGTH 1024 #define STATUS_CHANGE "status change: " -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Activate ( vlc_object_t * ); -static void Deactivate ( vlc_object_t * ); -static void *Run ( void * ); - -static void Help ( intf_thread_t * ); - -static bool ReadCommand( intf_thread_t *, char *, int * ); - -static input_item_t *parse_MRL( const char * ); - -static void Input ( vlc_object_t *, char const *, vlc_value_t ); -static void Playlist ( vlc_object_t *, char const *, vlc_value_t ); -static void Intf ( vlc_object_t *, char const *, vlc_value_t ); -static void Volume ( vlc_object_t *, char const *, vlc_value_t ); -static void VolumeMove ( vlc_object_t *, char const *, vlc_value_t ); -static void VideoConfig ( vlc_object_t *, char const *, vlc_value_t ); -static void AudioDevice ( vlc_object_t *, char const *, vlc_value_t ); -static void AudioChannel ( vlc_object_t *, char const *, vlc_value_t ); -static void Statistics(intf_thread_t *); - -static void player_on_state_changed(vlc_player_t *, - enum vlc_player_state, void *); -static void player_on_buffering_changed(vlc_player_t *, float, void *); -static void player_on_rate_changed(vlc_player_t *, float, void *); -static void player_on_position_changed(vlc_player_t *, - vlc_tick_t, float, void*); -static void player_aout_on_volume_changed(vlc_player_t *, float, void *); - struct intf_sys_t { int *pi_socket_listen; @@ -146,1670 +115,1685 @@ static void msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... ) } #define msg_rc( ... ) msg_rc( p_intf, __VA_ARGS__ ) -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -#define POS_TEXT N_("Show stream position") -#define POS_LONGTEXT N_("Show the current position in seconds within the " \ - "stream from time to time." ) - -#define TTY_TEXT N_("Fake TTY") -#define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.") - -#define UNIX_TEXT N_("UNIX socket command input") -#define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \ - "stdin." ) - -#define HOST_TEXT N_("TCP command input") -#define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \ - "You can set the address and port the interface will bind to." ) - -#ifdef _WIN32 -#define QUIET_TEXT N_("Do not open a DOS command box interface") -#define QUIET_LONGTEXT N_( \ - "By default the rc interface plugin will start a DOS command box. " \ - "Enabling the quiet mode will not bring this command box but can also " \ - "be pretty annoying when you want to stop VLC and no video window is " \ - "open." ) -#if !VLC_WINSTORE_APP -#include "intromsg.h" -#endif -#endif - -vlc_module_begin () - set_shortname( N_("RC")) - set_category( CAT_INTERFACE ) - set_subcategory( SUBCAT_INTERFACE_MAIN ) - set_description( N_("Remote control interface") ) - add_bool( "rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true ) - -#ifdef _WIN32 - add_bool( "rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false ) -#else -#if defined (HAVE_ISATTY) - add_bool( "rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true ) -#endif - add_string( "rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true ) +#if defined (_WIN32) && !VLC_WINSTORE_APP +# include "intromsg.h" #endif - add_string( "rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true ) - set_capability( "interface", 20 ) - - set_callbacks( Activate, Deactivate ) -#ifdef _WIN32 - add_shortcut( "rc" ) -#endif -vlc_module_end () /***************************************************************************** - * Activate: initialize and create stuff + * parse_MRL: build a input item from a full mrl + ***************************************************************************** + * MRL format: "simplified-mrl [:option-name[=option-value]]" + * We don't check for '"' or '\'', we just assume that a ':' that follows a + * space is a new option. Should be good enough for our purpose. *****************************************************************************/ -static int Activate( vlc_object_t *p_this ) +static input_item_t *parse_MRL(const char *mrl) { - /* FIXME: This function is full of memory leaks and bugs in error paths. */ - intf_thread_t *p_intf = (intf_thread_t*)p_this; - char *psz_host, *psz_unix_path = NULL; - int *pi_socket = NULL; - -#ifndef _WIN32 -#if defined(HAVE_ISATTY) - /* Check that stdin is a TTY */ - if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) ) - { - msg_Warn( p_intf, "fd 0 is not a TTY" ); - return VLC_EGENERIC; - } -#endif - - psz_unix_path = var_InheritString( p_intf, "rc-unix" ); - if( psz_unix_path ) - { - int i_socket; - -#ifndef AF_LOCAL - msg_Warn( p_intf, "your OS doesn't support filesystem sockets" ); - free( psz_unix_path ); - return VLC_EGENERIC; -#else - struct sockaddr_un addr; +#define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; } +#define SKIPTRAILINGSPACE( p, d ) \ + { char *e = d; while (e > p && (*(e-1)==' ' || *(e-1)=='\t')) {e--; *e=0 ;} } - memset( &addr, 0, sizeof(struct sockaddr_un) ); + input_item_t *p_item = NULL; + char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl; + char **ppsz_options = NULL; + int i_options = 0; - msg_Dbg( p_intf, "trying UNIX socket" ); + if (mrl == NULL) + return 0; - if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 ) - { - msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) ); - free( psz_unix_path ); - return VLC_EGENERIC; - } + psz_mrl = psz_orig = strdup( mrl ); + if (psz_mrl == NULL) + return NULL; - addr.sun_family = AF_LOCAL; - strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) ); - addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0'; + while (*psz_mrl) + { + SKIPSPACE(psz_mrl); + psz_item = psz_mrl; - if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)) - && (errno == EADDRINUSE) - && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr)) - && (errno == ECONNREFUSED)) + for (; *psz_mrl; psz_mrl++) { - msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path); - unlink (psz_unix_path); - - if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))) + if ((*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':') { - msg_Err (p_intf, "cannot bind UNIX socket at %s: %s", - psz_unix_path, vlc_strerror_c(errno)); - free (psz_unix_path); - net_Close (i_socket); - return VLC_EGENERIC; + /* We have a complete item */ + break; + } + if ((*psz_mrl == ' ' || *psz_mrl == '\t') && + (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':') + { + /* We have a complete item */ + break; } } - if( listen( i_socket, 1 ) ) + if (*psz_mrl) { - msg_Warn (p_intf, "can't listen on socket: %s", - vlc_strerror_c(errno)); - free( psz_unix_path ); - net_Close( i_socket ); - return VLC_EGENERIC; + *psz_mrl = 0; + psz_mrl++; } + SKIPTRAILINGSPACE(psz_item, psz_item + strlen(psz_item)); - /* FIXME: we need a core function to merge listening sockets sets */ - pi_socket = calloc( 2, sizeof( int ) ); - if( pi_socket == NULL ) + /* Remove '"' and '\'' if necessary */ + if (*psz_item == '"' && psz_item[strlen(psz_item)-1] == '"') { - free( psz_unix_path ); - net_Close( i_socket ); - return VLC_ENOMEM; + psz_item++; + psz_item[strlen(psz_item) - 1] = 0; + } + if (*psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'') + { + psz_item++; + psz_item[strlen(psz_item)-1] = 0; } - pi_socket[0] = i_socket; - pi_socket[1] = -1; -#endif /* AF_LOCAL */ - } -#endif /* !_WIN32 */ - - if( ( pi_socket == NULL ) && - ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL ) - { - vlc_url_t url; - vlc_UrlParse( &url, psz_host ); - if( url.psz_host == NULL ) + if (psz_item_mrl == NULL) { - vlc_UrlClean( &url ); - char *psz_backward_compat_host; - if( asprintf( &psz_backward_compat_host, "//%s", psz_host ) < 0 ) + if (strstr( psz_item, "://" ) != NULL) + psz_item_mrl = strdup(psz_item); + else + psz_item_mrl = vlc_path2uri(psz_item, NULL); + if (psz_item_mrl == NULL) { - free( psz_host ); - return VLC_EGENERIC; + free(psz_orig); + return NULL; } - free( psz_host ); - psz_host = psz_backward_compat_host; - vlc_UrlParse( &url, psz_host ); } - - msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port ); - - pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port); - if( pi_socket == NULL ) + else if (*psz_item) { - msg_Warn( p_intf, "can't listen to %s port %i", - url.psz_host, url.i_port ); - vlc_UrlClean( &url ); - free( psz_host ); - return VLC_EGENERIC; + i_options++; + ppsz_options = xrealloc(ppsz_options, i_options * sizeof(char *)); + ppsz_options[i_options - 1] = &psz_item[1]; } - vlc_UrlClean( &url ); - free( psz_host ); - } - - intf_sys_t *p_sys = malloc( sizeof( *p_sys ) ); - if( unlikely(p_sys == NULL) ) - { - net_ListenClose( pi_socket ); - free( psz_unix_path ); - return VLC_ENOMEM; + if (*psz_mrl) + SKIPSPACE(psz_mrl); } - p_intf->p_sys = p_sys; - p_sys->pi_socket_listen = pi_socket; - p_sys->i_socket = -1; - p_sys->psz_unix_path = psz_unix_path; - vlc_mutex_init( &p_sys->status_lock ); - p_sys->last_state = VLC_PLAYER_STATE_STOPPED; - p_sys->b_input_buffering = false; - p_sys->playlist = vlc_intf_GetMainPlaylist(p_intf);; - vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); - - /* Non-buffered stdout */ - setvbuf( stdout, (char *)NULL, _IOLBF, 0 ); - -#if VLC_WINSTORE_APP - p_sys->b_quiet = true; -#elif defined(_WIN32) - p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" ); - if( !p_sys->b_quiet ) - intf_consoleIntroMsg( p_intf ); -#endif - - if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) ) - goto error; - - msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") ); - - static struct vlc_player_cbs const player_cbs = - { - .on_state_changed = player_on_state_changed, - .on_buffering_changed = player_on_buffering_changed, - .on_rate_changed = player_on_rate_changed, - .on_position_changed = player_on_position_changed, - }; - vlc_player_Lock(player); - p_sys->player_listener = - vlc_player_AddListener(player, &player_cbs, p_intf); - if (!p_sys->player_listener) + /* Now create a playlist item */ + if (psz_item_mrl != NULL) { - vlc_player_Unlock(player); - goto error; + p_item = input_item_New(psz_item_mrl, NULL); + for (int i = 0; i < i_options; i++) + input_item_AddOption(p_item, ppsz_options[i], + VLC_INPUT_OPTION_TRUSTED); + free(psz_item_mrl); } - static struct vlc_player_aout_cbs const player_aout_cbs = - { - .on_volume_changed = player_aout_on_volume_changed, - }; - p_sys->player_aout_listener = - vlc_player_aout_AddListener(player, &player_aout_cbs, p_intf); - vlc_player_Unlock(player); - if (!p_sys->player_aout_listener) - goto error; - - return VLC_SUCCESS; + if (i_options) + free(ppsz_options); + free(psz_orig); -error: - if (p_sys->player_listener) - { - vlc_player_Lock(player); - vlc_player_RemoveListener(player, p_sys->player_listener); - vlc_player_Unlock(player); - } - net_ListenClose( pi_socket ); - free( psz_unix_path ); - vlc_mutex_destroy( &p_sys->status_lock ); - free( p_sys ); - return VLC_EGENERIC; + return p_item; } -/***************************************************************************** - * Deactivate: uninitialize and cleanup - *****************************************************************************/ -static void Deactivate( vlc_object_t *p_this ) +static void Help( intf_thread_t *p_intf) { - intf_thread_t *p_intf = (intf_thread_t*)p_this; - intf_sys_t *p_sys = p_intf->p_sys; - - vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); - vlc_player_Lock(player); - vlc_player_aout_RemoveListener(player, p_sys->player_aout_listener); - vlc_player_RemoveListener(player, p_sys->player_listener); - vlc_player_Unlock(player); - - vlc_cancel( p_sys->thread ); - vlc_join( p_sys->thread, NULL ); - - net_ListenClose( p_sys->pi_socket_listen ); - if( p_sys->i_socket != -1 ) - net_Close( p_sys->i_socket ); - if( p_sys->psz_unix_path != NULL ) + msg_rc("%s", _("+----[ Remote control commands ]")); + msg_rc( "| "); + msg_rc("%s", _("| add XYZ . . . . . . . . . . . . add XYZ to playlist")); + msg_rc("%s", _("| enqueue XYZ . . . . . . . . . queue XYZ to playlist")); + msg_rc("%s", _("| playlist . . . . . show items currently in playlist")); + msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream")); + msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream")); + msg_rc("%s", _("| next . . . . . . . . . . . . . . next playlist item")); + msg_rc("%s", _("| prev . . . . . . . . . . . . previous playlist item")); + msg_rc("%s", _("| goto . . . . . . . . . . . . . . goto item at index")); + msg_rc("%s", _("| repeat [on|off] . . . . toggle playlist item repeat")); + msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop")); + msg_rc("%s", _("| random [on|off] . . . . . . . toggle random jumping")); + msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist")); + msg_rc("%s", _("| status . . . . . . . . . . . current playlist status")); + msg_rc("%s", _("| title [X] . . . . . . set/get title in current item")); + msg_rc("%s", _("| title_n . . . . . . . . next title in current item")); + msg_rc("%s", _("| title_p . . . . . . previous title in current item")); + msg_rc("%s", _("| chapter [X] . . . . set/get chapter in current item")); + msg_rc("%s", _("| chapter_n . . . . . . next chapter in current item")); + msg_rc("%s", _("| chapter_p . . . . previous chapter in current item")); + msg_rc( "| "); + msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'")); + msg_rc("%s", _("| pause . . . . . . . . . . . . . . . . toggle pause")); + msg_rc("%s", _("| fastforward . . . . . . . . . set to maximum rate")); + msg_rc("%s", _("| rewind . . . . . . . . . . . . set to minimum rate")); + msg_rc("%s", _("| faster . . . . . . . . . . faster playing of stream")); + msg_rc("%s", _("| slower . . . . . . . . . . slower playing of stream")); + msg_rc("%s", _("| normal . . . . . . . . . . normal playing of stream")); + msg_rc("%s", _("| frame. . . . . . . . . . play frame by frame")); + msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen")); + msg_rc("%s", _("| info . . . . . information about the current stream")); + msg_rc("%s", _("| stats . . . . . . . . show statistical information")); + msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning")); + msg_rc("%s", _("| is_playing . . . . 1 if a stream plays, 0 otherwise")); + msg_rc("%s", _("| get_title . . . . . the title of the current stream")); + msg_rc("%s", _("| get_length . . . . the length of the current stream")); + msg_rc( "| "); + msg_rc("%s", _("| volume [X] . . . . . . . . . . set/get audio volume")); + msg_rc("%s", _("| volup [X] . . . . . . . raise audio volume X steps")); + msg_rc("%s", _("| voldown [X] . . . . . . lower audio volume X steps")); + msg_rc("%s", _("| adev [device] . . . . . . . . set/get audio device")); + msg_rc("%s", _("| achan [X]. . . . . . . . . . set/get audio channels")); + msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track")); + msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track")); + msg_rc("%s", _("| vratio [X] . . . . . . . set/get video aspect ratio")); + msg_rc("%s", _("| vcrop [X] . . . . . . . . . . . set/get video crop")); + msg_rc("%s", _("| vzoom [X] . . . . . . . . . . . set/get video zoom")); + msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot")); + msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitle track")); + msg_rc("%s", _("| key [hotkey name] . . . . . . simulate hotkey press")); + msg_rc( "| "); + msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message")); + msg_rc("%s", _("| logout . . . . . . . exit (if in socket connection)")); + msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . . quit vlc")); + msg_rc( "| "); + msg_rc("%s", _("+----[ end of help ]")); +} + +/******************************************************************** + * Status callback routines + ********************************************************************/ +static void +player_on_state_changed(vlc_player_t *player, + enum vlc_player_state state, void *data) +{ VLC_UNUSED(player); + char const *psz_cmd; + switch (state) { -#if defined(AF_LOCAL) && !defined(_WIN32) - unlink( p_sys->psz_unix_path ); -#endif - free( p_sys->psz_unix_path ); + case VLC_PLAYER_STATE_STOPPING: + case VLC_PLAYER_STATE_STOPPED: + psz_cmd = "stop"; + break; + case VLC_PLAYER_STATE_PLAYING: + psz_cmd = "play"; + break; + case VLC_PLAYER_STATE_PAUSED: + psz_cmd = "pause"; + break; + default: + psz_cmd = ""; + break; } - vlc_mutex_destroy( &p_sys->status_lock ); - free( p_sys ); + intf_thread_t *p_intf = data; + msg_rc(STATUS_CHANGE "( %s state: %d )", psz_cmd, state); } -/***************************************************************************** - * Run: rc thread - ***************************************************************************** - * This part of the interface is in a separate thread so that we can call - * exec() from within it without annoying the rest of the program. - *****************************************************************************/ -static void *Run( void *data ) -{ +static void +player_on_buffering_changed(vlc_player_t *player, + float new_buffering, void *data) +{ VLC_UNUSED(player); VLC_UNUSED(new_buffering); + intf_thread_t *intf = data; + intf_sys_t *sys = intf->p_sys; + vlc_mutex_lock(&sys->status_lock); + sys->b_input_buffering = true; + vlc_mutex_unlock(&sys->status_lock); +} + +static void +player_on_rate_changed(vlc_player_t *player, float new_rate, void *data) +{ VLC_UNUSED(player); intf_thread_t *p_intf = data; - intf_sys_t *p_sys = p_intf->p_sys; - vlc_object_t *vlc = VLC_OBJECT(vlc_object_instance(p_intf)); + intf_sys_t *sys = p_intf->p_sys; + vlc_mutex_lock(&sys->status_lock); + msg_rc(STATUS_CHANGE "( new rate: %.3f )", new_rate); + vlc_mutex_unlock(&sys->status_lock); +} - char p_buffer[ MAX_LINE_LENGTH + 1 ]; - bool b_showpos = var_InheritBool( p_intf, "rc-show-pos" ); +static void +player_on_position_changed(vlc_player_t *player, + vlc_tick_t new_time, float new_pos, void *data) +{ VLC_UNUSED(player); VLC_UNUSED(new_pos); + intf_thread_t *p_intf = data; + intf_sys_t *sys = p_intf->p_sys; + vlc_mutex_lock(&sys->status_lock); + if (sys->b_input_buffering) + msg_rc(STATUS_CHANGE "( time: %"PRId64"s )", + SEC_FROM_VLC_TICK(new_time)); + sys->b_input_buffering = false; + vlc_mutex_unlock(&sys->status_lock); +} - int i_size = 0; - int i_oldpos = 0; - int i_newpos; - int canc = vlc_savecancel( ); +static void +player_aout_on_volume_changed(vlc_player_t *player, float volume, void *data) +{ VLC_UNUSED(player); + intf_thread_t *p_intf = data; + vlc_mutex_lock(&p_intf->p_sys->status_lock); + msg_rc(STATUS_CHANGE "( audio volume: %ld )", + lroundf(volume * 100)); + vlc_mutex_unlock(&p_intf->p_sys->status_lock); +} - p_buffer[0] = 0; +/******************************************************************** + * Command routines + ********************************************************************/ +static void Input( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) +{ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); -#if defined(_WIN32) && !VLC_WINSTORE_APP - /* Get the file descriptor of the console input */ - p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE); - if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE ) + vlc_player_Lock(player); + if( vlc_player_IsPaused(player) && + ( strcmp( psz_cmd, "pause" ) != 0 ) && (strcmp( psz_cmd,"frame") != 0 ) ) + msg_rc( "%s", _("Press pause to continue.") ); + else + /* Parse commands that only require an input */ + if( !strcmp( psz_cmd, "pause" ) ) + vlc_player_TogglePause(player); + else if( !strcmp( psz_cmd, "seek" ) ) { - msg_Err( p_intf, "couldn't find user input handle" ); - return NULL; + if( strlen( newval.psz_string ) > 0 && + newval.psz_string[strlen( newval.psz_string ) - 1] == '%' ) + { + float f = atof( newval.psz_string ) / 100.0; + vlc_player_SetPosition(player, f); + } + else + { + int t = atoi( newval.psz_string ); + vlc_player_SetTime(player, vlc_tick_from_sec(t)); + } } -#endif - - /* Register commands that will be cleaned up upon object destruction */ - vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); - input_item_t *item = NULL; - - /* status callbacks */ - - for( ;; ) + else if ( !strcmp( psz_cmd, "fastforward" ) ) { - char *psz_cmd, *psz_arg; - bool b_complete; - - vlc_restorecancel( canc ); - - if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 ) + if (vlc_player_CanChangeRate(player)) { - p_sys->i_socket = - net_Accept( p_intf, p_sys->pi_socket_listen ); - if( p_sys->i_socket == -1 ) continue; + float rate = vlc_player_GetRate(player); + vlc_player_ChangeRate(player, rate > 0 ? rate * 2.f : -rate); } - - b_complete = ReadCommand( p_intf, p_buffer, &i_size ); - canc = vlc_savecancel( ); - - /* Manage the input part */ - if( item == NULL ) + else + var_SetInteger( vlc_object_instance(p_this), "key-action", ACTIONID_JUMP_FORWARD_EXTRASHORT ); + } + else if ( !strcmp( psz_cmd, "rewind" ) ) + { + if (vlc_player_CanRewind(player)) { - vlc_player_Lock(player); - item = vlc_player_HoldCurrentMedia(player); - vlc_player_Unlock(player); - /* New input has been registered */ - if( item ) - { - char *psz_uri = input_item_GetURI( item ); - msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri ); - free( psz_uri ); - } + float rate = vlc_player_GetRate(player); + vlc_player_ChangeRate(player, rate < 0 ? rate * 2.f : -rate); } - - vlc_player_Lock(player); - if( !vlc_player_IsStarted( player ) ) + else + var_SetInteger( vlc_object_instance(p_this), "key-action", ACTIONID_JUMP_BACKWARD_EXTRASHORT ); + } + else if ( !strcmp( psz_cmd, "faster" ) ) + vlc_player_IncrementRate(player); + else if ( !strcmp( psz_cmd, "slower" ) ) + vlc_player_DecrementRate(player); + else if ( !strcmp( psz_cmd, "normal" ) ) + vlc_player_ChangeRate(player, 1.f); + else if ( !strcmp( psz_cmd, "frame" ) ) + vlc_player_NextVideoFrame(player); + else if( !strcmp( psz_cmd, "chapter" ) || + !strcmp( psz_cmd, "chapter_n" ) || + !strcmp( psz_cmd, "chapter_p" ) ) + { + if( !strcmp( psz_cmd, "chapter" ) ) { - if (item) + if ( *newval.psz_string ) { - input_item_Release( item ); - item = NULL; + /* Set. */ + vlc_player_SelectChapterIdx(player, atoi(newval.psz_string)); } - - p_sys->last_state = VLC_PLAYER_STATE_STOPPED; - msg_rc( STATUS_CHANGE "( stop state: 0 )" ); - } - vlc_player_Unlock(player); - - if( item != NULL ) - { - vlc_player_Lock(player); - enum vlc_player_state state = vlc_player_GetState(player); - vlc_player_Unlock(player); - - if (p_sys->last_state != state) + else { - switch (state) - { - case VLC_PLAYER_STATE_STOPPING: - case VLC_PLAYER_STATE_STOPPED: - msg_rc(STATUS_CHANGE "( stop state: 5 )"); - break; - case VLC_PLAYER_STATE_PLAYING: - msg_rc(STATUS_CHANGE "( play state: 3 )"); - break; - case VLC_PLAYER_STATE_PAUSED: - msg_rc(STATUS_CHANGE "( pause state: 4 )"); - break; - default: - break; - } - p_sys->last_state = state; + /* Get. */ + struct vlc_player_title const *title = vlc_player_GetSelectedTitle(player); + ssize_t chapter = -1; + if (title != NULL) + chapter = vlc_player_GetSelectedChapterIdx(player); + if (chapter != -1) + msg_rc("Currently playing chapter %zd/%zu.", + chapter, title->chapter_count); + else + msg_rc("No chapter selected."); } } - - if( item && b_showpos ) + else if( !strcmp( psz_cmd, "chapter_n" ) ) + vlc_player_SelectNextChapter(player); + else if( !strcmp( psz_cmd, "chapter_p" ) ) + vlc_player_SelectPrevChapter(player); + } + else if( !strcmp( psz_cmd, "title" ) || + !strcmp( psz_cmd, "title_n" ) || + !strcmp( psz_cmd, "title_p" ) ) + { + if( !strcmp( psz_cmd, "title" ) ) { - vlc_player_Lock(player); - i_newpos = 100 * vlc_player_GetPosition( player ); - vlc_player_Unlock(player); - if( i_oldpos != i_newpos ) + if ( *newval.psz_string ) { - i_oldpos = i_newpos; - msg_rc( "pos: %d%%", i_newpos ); + /* Set. */ + int idx = atoi(newval.psz_string); + if (idx >= 0) + vlc_player_SelectTitleIdx(player, (size_t)idx); + } + else + { + /* Get. */ + ssize_t title = vlc_player_GetSelectedTitleIdx(player); + vlc_player_title_list *titles = + vlc_player_GetTitleList(player); + size_t count = 0; + if (titles != NULL) + count = vlc_player_title_list_GetCount(titles); + if (title != -1 && count != 0) + msg_rc("Currently playing title %zd/%zu.", title, count); + else + msg_rc("No title selected."); } } - - /* Is there something to do? */ - if( !b_complete ) continue; - - /* Skip heading spaces */ - psz_cmd = p_buffer; - while( *psz_cmd == ' ' ) + else if( !strcmp( psz_cmd, "title_n" ) ) + vlc_player_SelectNextTitle(player); + else if( !strcmp( psz_cmd, "title_p" ) ) + vlc_player_SelectPrevTitle(player); + } + else if( !strcmp( psz_cmd, "atrack" ) + || !strcmp( psz_cmd, "vtrack" ) + || !strcmp( psz_cmd, "strack" ) ) + { + enum es_format_category_e cat; + if( !strcmp( psz_cmd, "atrack" ) ) + cat = AUDIO_ES; + else if( !strcmp( psz_cmd, "vtrack" ) ) + cat = VIDEO_ES; + else + cat = SPU_ES; + if( newval.psz_string && *newval.psz_string ) { - psz_cmd++; + int idx = atoi(newval.psz_string); + if (idx < 0) + goto out; + size_t track_count = vlc_player_GetTrackCount(player, cat); + if ((unsigned)idx >= track_count) + goto out; + struct vlc_player_track const *track = + vlc_player_GetTrackAt(player, cat, (size_t)idx); + if (!track) + goto out; + vlc_player_SelectTrack(player, track->es_id); } + else + { + struct vlc_player_track const *cur_track = + vlc_player_GetSelectedTrack(player, cat); + char const *name = cur_track ? cur_track->name : psz_cmd; + msg_rc("+----[ %s ]", name); + size_t count = vlc_player_GetTrackCount(player, cat); + for (size_t i = 0; i < count; ++i) + { + struct vlc_player_track const *track = + vlc_player_GetTrackAt(player, cat, i); + msg_rc("| %zu - %s%s", + i, track->name, track == cur_track ? " *" : ""); + } + msg_rc("+----[ end of %s ]", name); + } + } +out: + vlc_player_Unlock(player); +} - /* Split psz_cmd at the first space and make sure that - * psz_arg is valid */ - psz_arg = strchr( psz_cmd, ' ' ); - if( psz_arg ) +static void print_playlist(intf_thread_t *p_intf, vlc_playlist_t *playlist) +{ + size_t count = vlc_playlist_Count(playlist); + for (size_t i = 0; i < count; ++i) + { + vlc_playlist_item_t *plitem = vlc_playlist_Get(playlist, i); + input_item_t *item = vlc_playlist_item_GetMedia(plitem); + vlc_tick_t len = item->i_duration; + if (len != INPUT_DURATION_INDEFINITE && len != VLC_TICK_INVALID) { - *psz_arg++ = 0; - while( *psz_arg == ' ' ) - { - psz_arg++; - } + char buf[MSTRTIME_MAX_SIZE]; + secstotimestr(buf, SEC_FROM_VLC_TICK(len)); + msg_rc("|-- %s (%s)", item->psz_name, buf); } else - { - psz_arg = (char*)""; - } - - if( !strcmp( psz_cmd, "quit" ) ) - libvlc_Quit( vlc_object_instance(p_intf) ); - -#define VOID(name, func) \ - if (strcmp(psz_cmd, name) == 0) { \ - vlc_value_t n; \ - func(VLC_OBJECT(p_intf), psz_cmd, n); \ - } else - -#define STRING(name, func) \ - if (strcmp(psz_cmd, name) == 0) { \ - vlc_value_t n = { .psz_string = psz_arg }; \ - func(VLC_OBJECT(p_intf), psz_cmd, n); \ - } else - - - STRING("intf", Intf) - - STRING("add", Playlist) - STRING("repeat", Playlist) - STRING("loop", Playlist) - STRING("random", Playlist) - STRING("enqueue", Playlist) - VOID("playlist", Playlist) - VOID("sort", Playlist) - VOID("play", Playlist) - VOID("stop", Playlist) - VOID("clear", Playlist) - VOID("prev", Playlist) - VOID("next", Playlist) - STRING("goto", Playlist) - STRING("status", Playlist) - - /* DVD commands */ - VOID("pause", Input) - STRING("seek", Input) - STRING("title", Input) - VOID("title_n", Input) - VOID("title_p", Input) - STRING("chapter", Input) - VOID("chapter_n", Input) - VOID("chapter_p", Input) - - VOID("fastforward", Input) - VOID("rewind", Input) - VOID("faster", Input) - VOID("slower", Input) - VOID("normal", Input) - VOID("frame", Input) + msg_rc("|-- %s", item->psz_name); + } +} - STRING("atrack", Input) - STRING("vtrack", Input) - STRING("strack", Input) +static void Playlist( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) +{ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + vlc_playlist_t *playlist = p_intf->p_sys->playlist; + vlc_player_t *player = vlc_playlist_GetPlayer(playlist); - /* video commands */ - STRING("vratio", VideoConfig) - STRING("vcrop", VideoConfig) - STRING("vzoom", VideoConfig) - VOID("snapshot", VideoConfig) + vlc_playlist_Lock(playlist); - /* audio commands */ - STRING("volume", Volume) - STRING("volup", VolumeMove) - STRING("voldown", VolumeMove) - STRING("adev", AudioDevice) - STRING("achan", AudioChannel) + if (vlc_playlist_GetCurrentIndex(playlist) != -1 && + vlc_player_IsPaused(player)) + { + msg_rc("%s", _("Type 'pause' to continue.")); + goto end; + } -#undef STRING -#undef VOID + /* Parse commands that require a playlist */ + if( !strcmp( psz_cmd, "prev" ) ) + vlc_playlist_Prev(playlist); + else if( !strcmp( psz_cmd, "next" ) ) + vlc_playlist_Next(playlist); + else if( !strcmp( psz_cmd, "play" ) ) + vlc_playlist_Start(playlist); + else if( !strcmp( psz_cmd, "repeat" ) ) + { + bool b_update = true; + enum vlc_playlist_playback_repeat repeat_mode = + vlc_playlist_GetPlaybackRepeat(playlist); + bool b_value = repeat_mode == VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT; - /* misc menu commands */ - if( !strcmp( psz_cmd, "stats" ) ) - Statistics( p_intf ); - else if( !strcmp( psz_cmd, "logout" ) ) + if( strlen( newval.psz_string ) > 0 ) { - /* Close connection */ - if( p_sys->i_socket != -1 ) + if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || + ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) { - net_Close( p_sys->i_socket ); - p_sys->i_socket = -1; + b_update = false; } } - else if( !strcmp( psz_cmd, "info" ) ) - { - if( item ) - { - int i; - vlc_mutex_lock( &item->lock ); - for ( i = 0; i < item->i_categories; i++ ) - { - info_category_t *p_category = item->pp_categories[i]; - info_t *p_info; - msg_rc( "+----[ %s ]", p_category->psz_name ); - msg_rc( "| " ); - info_foreach(p_info, &p_category->infos) - msg_rc( "| %s: %s", p_info->psz_name, - p_info->psz_value ); - msg_rc( "| " ); - } - msg_rc( "+----[ end of stream info ]" ); - vlc_mutex_unlock( &item->lock ); - } - else - { - msg_rc( "no input" ); - } + if ( b_update ) + { + b_value = !b_value; + repeat_mode = b_value + ? VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT + : VLC_PLAYLIST_PLAYBACK_REPEAT_NONE; + vlc_playlist_SetPlaybackRepeat(playlist, repeat_mode); } - else if( !strcmp( psz_cmd, "is_playing" ) ) + msg_rc( "Setting repeat to %s", b_value ? "true" : "false" ); + } + else if( !strcmp( psz_cmd, "loop" ) ) + { + bool b_update = true; + enum vlc_playlist_playback_repeat repeat_mode = + vlc_playlist_GetPlaybackRepeat(playlist); + bool b_value = repeat_mode == VLC_PLAYLIST_PLAYBACK_REPEAT_ALL; + + if( strlen( newval.psz_string ) > 0 ) { - if (p_sys->last_state != VLC_PLAYER_STATE_PLAYING && - p_sys->last_state != VLC_PLAYER_STATE_PAUSED) - { - msg_rc( "0" ); - } - else + if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || + ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) { - msg_rc( "1" ); + b_update = false; } } - else if( !strcmp( psz_cmd, "get_time" ) ) - { - vlc_player_Lock(player); - vlc_tick_t t = vlc_player_GetTime(player); - vlc_player_Unlock(player); - if (t != VLC_TICK_INVALID) - msg_rc("%"PRIu64, SEC_FROM_VLC_TICK(t)); - } - else if( !strcmp( psz_cmd, "get_length" ) ) + + if ( b_update ) { - vlc_player_Lock(player); - vlc_tick_t l = vlc_player_GetLength(player); - vlc_player_Unlock(player); - if (l != VLC_TICK_INVALID) - msg_rc("%"PRIu64, SEC_FROM_VLC_TICK(l)); + b_value = !b_value; + repeat_mode = b_value + ? VLC_PLAYLIST_PLAYBACK_REPEAT_ALL + : VLC_PLAYLIST_PLAYBACK_REPEAT_NONE; + vlc_playlist_SetPlaybackRepeat(playlist, repeat_mode); } - else if( !strcmp( psz_cmd, "get_title" ) ) + msg_rc( "Setting loop to %s", b_value ? "true" : "false" ); + } + else if( !strcmp( psz_cmd, "random" ) ) + { + bool b_update = true; + enum vlc_playlist_playback_order order_mode = + vlc_playlist_GetPlaybackOrder(playlist); + bool b_value = order_mode == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM; + + if( strlen( newval.psz_string ) > 0 ) { - vlc_player_Lock(player); - struct vlc_player_title const *title = - vlc_player_GetSelectedTitle(player); - vlc_player_Unlock(player); - msg_rc("%s", title ? title->name : ""); + if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || + ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) + { + b_update = false; + } } - else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 ) - || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) ) + + if ( b_update ) { - Help( p_intf ); + b_value = !b_value; + order_mode = b_value + ? VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM + : VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL; + vlc_playlist_SetPlaybackOrder(playlist, order_mode); } - else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) ) + msg_rc( "Setting random to %s", b_value ? "true" : "false" ); + } + else if (!strcmp( psz_cmd, "goto" ) ) + { + long long llindex = atoll(newval.psz_string); + size_t index = (size_t)llindex; + size_t count = vlc_playlist_Count(playlist); + if (llindex < 0) + msg_rc("%s", _("Error: `goto' needs an argument greater or equal to zero.")); + else if (index < count) + vlc_playlist_PlayAt(playlist, index); + else + msg_rc(vlc_ngettext("Playlist has only %zu element", + "Playlist has only %zu elements", count), + count); + } + else if( !strcmp( psz_cmd, "stop" ) ) + vlc_playlist_Stop(playlist); + else if( !strcmp( psz_cmd, "clear" ) ) + { + vlc_playlist_Stop(playlist); + vlc_playlist_Clear(playlist); + } + else if ((!strcmp(psz_cmd, "add") || !strcmp(psz_cmd, "enqueue")) && + newval.psz_string && *newval.psz_string) + { + input_item_t *p_item = parse_MRL( newval.psz_string ); + + if( p_item ) { - var_SetInteger( vlc, "key-action", vlc_actions_get_id( psz_arg ) ); + msg_rc("Trying to %s %s to playlist.", psz_cmd, newval.psz_string); + + size_t count = vlc_playlist_Count(playlist); + int ret = vlc_playlist_InsertOne(playlist, count, p_item); + input_item_Release(p_item); + if (ret != VLC_SUCCESS) + goto end; + + if (!strcmp(psz_cmd, "add")) + vlc_playlist_PlayAt(playlist, count); } - else switch( psz_cmd[0] ) + } + else if( !strcmp( psz_cmd, "playlist" ) ) + { + msg_rc( "+----[ Playlist ]" ); + print_playlist(p_intf, playlist); + msg_rc( "+----[ End of playlist ]" ); + } + + else if( !strcmp( psz_cmd, "sort" )) + { + struct vlc_playlist_sort_criterion criteria = { - case 'f': - case 'F': + .key = VLC_PLAYLIST_SORT_KEY_ARTIST, + .order = VLC_PLAYLIST_SORT_ORDER_ASCENDING + }; + vlc_playlist_Sort(playlist, &criteria, 1); + } + else if( !strcmp( psz_cmd, "status" ) ) + { + input_item_t *item = vlc_player_GetCurrentMedia(player); + if (item) { - bool fs; - if( !strncasecmp( psz_arg, "on", 2 ) ) - fs = true; - else if( !strncasecmp( psz_arg, "off", 3 ) ) - fs = false; - else - fs = !vlc_player_vout_IsFullscreen(player); - vlc_player_vout_SetFullscreen(player, fs); - break; + char *psz_uri = input_item_GetURI(item); + if( likely(psz_uri != NULL) ) + { + msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri ); + free( psz_uri ); + } } - case 's': - case 'S': - ; - break; - case '\0': - /* Ignore empty lines */ - break; + float volume = vlc_player_aout_GetVolume(player); + if( volume >= 0.f ) + msg_rc( STATUS_CHANGE "( audio volume: %ld )", + lroundf(volume * 100.f ) ); - default: - msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd); - break; + enum vlc_player_state state = vlc_player_GetState(player); + switch (state) + { + case VLC_PLAYER_STATE_STOPPING: + case VLC_PLAYER_STATE_STOPPED: + msg_rc( STATUS_CHANGE "( stop state: 5 )" ); + break; + case VLC_PLAYER_STATE_PLAYING: + msg_rc( STATUS_CHANGE "( play state: 3 )" ); + break; + case VLC_PLAYER_STATE_PAUSED: + msg_rc( STATUS_CHANGE "( pause state: 4 )" ); + break; + default: + msg_rc( STATUS_CHANGE "( unknown state: -1 )" ); + break; } - - /* Command processed */ - i_size = 0; p_buffer[0] = 0; } - msg_rc( STATUS_CHANGE "( stop state: 0 )" ); - msg_rc( STATUS_CHANGE "( quit )" ); - - vlc_restorecancel( canc ); + /* + * sanity check + */ + else + msg_rc( "unknown command!" ); - return NULL; +end: + vlc_playlist_Unlock(playlist); } -static void Help( intf_thread_t *p_intf) +static void Intf( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) { - msg_rc("%s", _("+----[ Remote control commands ]")); - msg_rc( "| "); - msg_rc("%s", _("| add XYZ . . . . . . . . . . . . add XYZ to playlist")); - msg_rc("%s", _("| enqueue XYZ . . . . . . . . . queue XYZ to playlist")); - msg_rc("%s", _("| playlist . . . . . show items currently in playlist")); - msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream")); - msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream")); - msg_rc("%s", _("| next . . . . . . . . . . . . . . next playlist item")); - msg_rc("%s", _("| prev . . . . . . . . . . . . previous playlist item")); - msg_rc("%s", _("| goto . . . . . . . . . . . . . . goto item at index")); - msg_rc("%s", _("| repeat [on|off] . . . . toggle playlist item repeat")); - msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop")); - msg_rc("%s", _("| random [on|off] . . . . . . . toggle random jumping")); - msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist")); - msg_rc("%s", _("| status . . . . . . . . . . . current playlist status")); - msg_rc("%s", _("| title [X] . . . . . . set/get title in current item")); - msg_rc("%s", _("| title_n . . . . . . . . next title in current item")); - msg_rc("%s", _("| title_p . . . . . . previous title in current item")); - msg_rc("%s", _("| chapter [X] . . . . set/get chapter in current item")); - msg_rc("%s", _("| chapter_n . . . . . . next chapter in current item")); - msg_rc("%s", _("| chapter_p . . . . previous chapter in current item")); - msg_rc( "| "); - msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'")); - msg_rc("%s", _("| pause . . . . . . . . . . . . . . . . toggle pause")); - msg_rc("%s", _("| fastforward . . . . . . . . . set to maximum rate")); - msg_rc("%s", _("| rewind . . . . . . . . . . . . set to minimum rate")); - msg_rc("%s", _("| faster . . . . . . . . . . faster playing of stream")); - msg_rc("%s", _("| slower . . . . . . . . . . slower playing of stream")); - msg_rc("%s", _("| normal . . . . . . . . . . normal playing of stream")); - msg_rc("%s", _("| frame. . . . . . . . . . play frame by frame")); - msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen")); - msg_rc("%s", _("| info . . . . . information about the current stream")); - msg_rc("%s", _("| stats . . . . . . . . show statistical information")); - msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning")); - msg_rc("%s", _("| is_playing . . . . 1 if a stream plays, 0 otherwise")); - msg_rc("%s", _("| get_title . . . . . the title of the current stream")); - msg_rc("%s", _("| get_length . . . . the length of the current stream")); - msg_rc( "| "); - msg_rc("%s", _("| volume [X] . . . . . . . . . . set/get audio volume")); - msg_rc("%s", _("| volup [X] . . . . . . . raise audio volume X steps")); - msg_rc("%s", _("| voldown [X] . . . . . . lower audio volume X steps")); - msg_rc("%s", _("| adev [device] . . . . . . . . set/get audio device")); - msg_rc("%s", _("| achan [X]. . . . . . . . . . set/get audio channels")); - msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track")); - msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track")); - msg_rc("%s", _("| vratio [X] . . . . . . . set/get video aspect ratio")); - msg_rc("%s", _("| vcrop [X] . . . . . . . . . . . set/get video crop")); - msg_rc("%s", _("| vzoom [X] . . . . . . . . . . . set/get video zoom")); - msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot")); - msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitle track")); - msg_rc("%s", _("| key [hotkey name] . . . . . . simulate hotkey press")); - msg_rc( "| "); - msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message")); - msg_rc("%s", _("| logout . . . . . . . exit (if in socket connection)")); - msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . . quit vlc")); - msg_rc( "| "); - msg_rc("%s", _("+----[ end of help ]")); + VLC_UNUSED(psz_cmd); + intf_Create(vlc_object_instance(p_this), newval.psz_string); } -/******************************************************************** - * Status callback routines - ********************************************************************/ -static void -player_on_state_changed(vlc_player_t *player, - enum vlc_player_state state, void *data) -{ VLC_UNUSED(player); - char const *psz_cmd; - switch (state) +static void Volume( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) +{ + VLC_UNUSED(psz_cmd); + intf_thread_t *p_intf = (intf_thread_t*)p_this; + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + vlc_player_Lock(player); + if ( *newval.psz_string ) { - case VLC_PLAYER_STATE_STOPPING: - case VLC_PLAYER_STATE_STOPPED: - psz_cmd = "stop"; - break; - case VLC_PLAYER_STATE_PLAYING: - psz_cmd = "play"; - break; - case VLC_PLAYER_STATE_PAUSED: - psz_cmd = "pause"; - break; - default: - psz_cmd = ""; - break; + /* Set. */ + float volume = atol(newval.psz_string) / 100.f; + vlc_player_aout_SetVolume(player, volume); } - intf_thread_t *p_intf = data; - msg_rc(STATUS_CHANGE "( %s state: %d )", psz_cmd, state); -} - -static void -player_on_buffering_changed(vlc_player_t *player, - float new_buffering, void *data) -{ VLC_UNUSED(player); VLC_UNUSED(new_buffering); - intf_thread_t *intf = data; - intf_sys_t *sys = intf->p_sys; - vlc_mutex_lock(&sys->status_lock); - sys->b_input_buffering = true; - vlc_mutex_unlock(&sys->status_lock); + else + { + /* Get. */ + long int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f); + msg_rc(STATUS_CHANGE "( audio volume: %ld )", volume); + } + vlc_player_Unlock(player); } -static void -player_on_rate_changed(vlc_player_t *player, float new_rate, void *data) -{ VLC_UNUSED(player); - intf_thread_t *p_intf = data; - intf_sys_t *sys = p_intf->p_sys; - vlc_mutex_lock(&sys->status_lock); - msg_rc(STATUS_CHANGE "( new rate: %.3f )", new_rate); - vlc_mutex_unlock(&sys->status_lock); -} +static void VolumeMove( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) +{ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); -static void -player_on_position_changed(vlc_player_t *player, - vlc_tick_t new_time, float new_pos, void *data) -{ VLC_UNUSED(player); VLC_UNUSED(new_pos); - intf_thread_t *p_intf = data; - intf_sys_t *sys = p_intf->p_sys; - vlc_mutex_lock(&sys->status_lock); - if (sys->b_input_buffering) - msg_rc(STATUS_CHANGE "( time: %"PRId64"s )", - SEC_FROM_VLC_TICK(new_time)); - sys->b_input_buffering = false; - vlc_mutex_unlock(&sys->status_lock); -} + float volume; + int i_nb_steps = atoi(newval.psz_string); -static void -player_aout_on_volume_changed(vlc_player_t *player, float volume, void *data) -{ VLC_UNUSED(player); - intf_thread_t *p_intf = data; - vlc_mutex_lock(&p_intf->p_sys->status_lock); - msg_rc(STATUS_CHANGE "( audio volume: %ld )", - lroundf(volume * 100)); - vlc_mutex_unlock(&p_intf->p_sys->status_lock); -} + if( !strcmp(psz_cmd, "voldown") ) + i_nb_steps *= -1; -/******************************************************************** - * Command routines - ********************************************************************/ -static void Input( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) + vlc_player_Lock(player); + vlc_player_aout_IncrementVolume(player, i_nb_steps, &volume); + vlc_player_Unlock(player); +} + +static void VideoConfig( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t newval ) { intf_thread_t *p_intf = (intf_thread_t*)p_this; vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + vout_thread_t *p_vout = vlc_player_vout_Hold(player); + const char * psz_variable = NULL; - vlc_player_Lock(player); - if( vlc_player_IsPaused(player) && - ( strcmp( psz_cmd, "pause" ) != 0 ) && (strcmp( psz_cmd,"frame") != 0 ) ) - msg_rc( "%s", _("Press pause to continue.") ); + if( !strcmp( psz_cmd, "vcrop" ) ) + psz_variable = "crop"; + else if( !strcmp( psz_cmd, "vratio" ) ) + psz_variable = "aspect-ratio"; + else if( !strcmp( psz_cmd, "vzoom" ) ) + psz_variable = "zoom"; + else if( !strcmp( psz_cmd, "snapshot" ) ) + psz_variable = "video-snapshot"; else - /* Parse commands that only require an input */ - if( !strcmp( psz_cmd, "pause" ) ) - vlc_player_TogglePause(player); - else if( !strcmp( psz_cmd, "seek" ) ) + /* This case can't happen */ + vlc_assert_unreachable(); + + if( newval.psz_string && *newval.psz_string ) { - if( strlen( newval.psz_string ) > 0 && - newval.psz_string[strlen( newval.psz_string ) - 1] == '%' ) + /* set */ + if( !strcmp( psz_variable, "zoom" ) ) { - float f = atof( newval.psz_string ) / 100.0; - vlc_player_SetPosition(player, f); + float f_float = atof( newval.psz_string ); + var_SetFloat( p_vout, psz_variable, f_float ); } + else + var_SetString( p_vout, psz_variable, newval.psz_string ); + } + else if( !strcmp( psz_cmd, "snapshot" ) ) + vlc_player_vout_Snapshot(player); + else + { + /* get */ + char *name; + vlc_value_t *val; + char **text; + float f_value = 0.; + char *psz_value = NULL; + size_t count; + + if( !strcmp( psz_variable, "zoom" ) ) + f_value = var_GetFloat( p_vout, "zoom" ); else { - int t = atoi( newval.psz_string ); - vlc_player_SetTime(player, vlc_tick_from_sec(t)); + psz_value = var_GetString( p_vout, psz_variable ); + if( psz_value == NULL ) + { + vout_Release(p_vout); + return; + } + } + + if ( var_Change( p_vout, psz_variable, VLC_VAR_GETCHOICES, + &count, &val, &text ) < 0 ) + { + vout_Release(p_vout); + free( psz_value ); + return; + } + + /* Get the descriptive name of the variable */ + var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT, &name ); + if( !name ) name = strdup(psz_variable); + + msg_rc( "+----[ %s ]", name ); + if( !strcmp( psz_variable, "zoom" ) ) + { + for ( size_t i = 0; i < count; i++ ) + { + msg_rc( "| %f - %s%s", val[i].f_float, text[i], + f_value == val[i].f_float ? " *" : "" ); + free(text[i]); + } + } + else + { + for ( size_t i = 0; i < count; i++ ) + { + msg_rc( "| %s - %s%s", val[i].psz_string, text[i], + strcmp(psz_value, val[i].psz_string) + ? "" : " *" ); + free(text[i]); + free(val[i].psz_string); + } + free( psz_value ); } + free(text); + free(val); + msg_rc( "+----[ end of %s ]", name ); + + free( name ); } - else if ( !strcmp( psz_cmd, "fastforward" ) ) + vout_Release(p_vout); +} + +static void AudioDevice( vlc_object_t *obj, char const *cmd, vlc_value_t cur ) +{ + intf_thread_t *p_intf = (intf_thread_t *)obj; + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + audio_output_t *aout = vlc_player_aout_Hold(player); + if (aout == NULL) + return; + + char **ids, **names; + int n = aout_DevicesList(aout, &ids, &names); + if (n < 0) + goto out; + + bool setdev = cur.psz_string && *cur.psz_string; + if (setdev) + aout_DeviceSet(aout, cur.psz_string); + + if (setdev) { - if (vlc_player_CanChangeRate(player)) + int i; + for (i = 0; i < n; ++i) + if (!strcmp(cur.psz_string, ids[i])) + break; + if (i < n) + vlc_player_vout_OSDMessage(player, + _("Audio device: %s"), names[i]); + } + else + { + char *dev = aout_DeviceGet(aout); + const char *devstr = (dev != NULL) ? dev : ""; + + msg_rc( "+----[ %s ]", cmd ); + for ( int i = 0; i < n; i++ ) { - float rate = vlc_player_GetRate(player); - vlc_player_ChangeRate(player, rate > 0 ? rate * 2.f : -rate); + const char *fmt = "| %s - %s"; + + if( !strcmp(devstr, ids[i]) ) + fmt = "| %s - %s *"; + msg_rc( fmt, ids[i], names[i] ); + free( names[i] ); + free( ids[i] ); } - else - var_SetInteger( vlc_object_instance(p_this), "key-action", ACTIONID_JUMP_FORWARD_EXTRASHORT ); + msg_rc( "+----[ end of %s ]", cmd ); + + free( dev ); } - else if ( !strcmp( psz_cmd, "rewind" ) ) + + free(ids); + free(names); +out: + aout_Release(aout); +} + +static void AudioChannel( vlc_object_t *obj, char const *cmd, vlc_value_t cur ) +{ + intf_thread_t *p_intf = (intf_thread_t*)obj; + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + audio_output_t *p_aout = vlc_player_aout_Hold(player); + if ( p_aout == NULL ) + return; + + if ( !*cur.psz_string ) { - if (vlc_player_CanRewind(player)) + /* Retrieve all registered ***. */ + vlc_value_t *val; + char **text; + size_t count; + + if ( var_Change( p_aout, "stereo-mode", VLC_VAR_GETCHOICES, + &count, &val, &text ) < 0 ) + goto out; + + int i_value = var_GetInteger( p_aout, "stereo-mode" ); + + msg_rc( "+----[ %s ]", cmd ); + for ( size_t i = 0; i < count; i++ ) { - float rate = vlc_player_GetRate(player); - vlc_player_ChangeRate(player, rate < 0 ? rate * 2.f : -rate); + msg_rc( "| %"PRId64" - %s%s", val[i].i_int, text[i], + i_value == val[i].i_int ? " *" : "" ); + free(text[i]); } - else - var_SetInteger( vlc_object_instance(p_this), "key-action", ACTIONID_JUMP_BACKWARD_EXTRASHORT ); + free(text); + free(val); + msg_rc( "+----[ end of %s ]", cmd ); } - else if ( !strcmp( psz_cmd, "faster" ) ) - vlc_player_IncrementRate(player); - else if ( !strcmp( psz_cmd, "slower" ) ) - vlc_player_DecrementRate(player); - else if ( !strcmp( psz_cmd, "normal" ) ) - vlc_player_ChangeRate(player, 1.f); - else if ( !strcmp( psz_cmd, "frame" ) ) - vlc_player_NextVideoFrame(player); - else if( !strcmp( psz_cmd, "chapter" ) || - !strcmp( psz_cmd, "chapter_n" ) || - !strcmp( psz_cmd, "chapter_p" ) ) + else + var_SetInteger( p_aout, "stereo-mode", atoi( cur.psz_string ) ); +out: + aout_Release(p_aout); +} + +static void Statistics( intf_thread_t *p_intf ) +{ + vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + vlc_player_Lock(player); + input_item_t *p_item = vlc_player_GetCurrentMedia(player); + vlc_player_Unlock(player); + + if (p_item == NULL) + return; + + vlc_mutex_lock( &p_item->lock ); + msg_rc( "+----[ begin of statistical info ]" ); + + /* Input */ + msg_rc("%s", _("+-[Incoming]")); + msg_rc(_("| input bytes read : %8.0f KiB"), + (float)(p_item->p_stats->i_read_bytes)/1024 ); + msg_rc(_("| input bitrate : %6.0f kb/s"), + (float)(p_item->p_stats->f_input_bitrate)*8000 ); + msg_rc(_("| demux bytes read : %8.0f KiB"), + (float)(p_item->p_stats->i_demux_read_bytes)/1024 ); + msg_rc(_("| demux bitrate : %6.0f kb/s"), + (float)(p_item->p_stats->f_demux_bitrate)*8000 ); + msg_rc(_("| demux corrupted : %5"PRIi64), + p_item->p_stats->i_demux_corrupted ); + msg_rc(_("| discontinuities : %5"PRIi64), + p_item->p_stats->i_demux_discontinuity ); + msg_rc("|"); + /* Video */ + msg_rc("%s", _("+-[Video Decoding]")); + msg_rc(_("| video decoded : %5"PRIi64), + p_item->p_stats->i_decoded_video ); + msg_rc(_("| frames displayed : %5"PRIi64), + p_item->p_stats->i_displayed_pictures ); + msg_rc(_("| frames lost : %5"PRIi64), + p_item->p_stats->i_lost_pictures ); + msg_rc("|"); + /* Audio*/ + msg_rc("%s", _("+-[Audio Decoding]")); + msg_rc(_("| audio decoded : %5"PRIi64), + p_item->p_stats->i_decoded_audio ); + msg_rc(_("| buffers played : %5"PRIi64), + p_item->p_stats->i_played_abuffers ); + msg_rc(_("| buffers lost : %5"PRIi64), + p_item->p_stats->i_lost_abuffers ); + msg_rc("|"); + msg_rc( "+----[ end of statistical info ]" ); + vlc_mutex_unlock( &p_item->lock ); +} + +#if defined(_WIN32) && !VLC_WINSTORE_APP +static bool ReadWin32( intf_thread_t *p_intf, unsigned char *p_buffer, int *pi_size ) +{ + INPUT_RECORD input_record; + DWORD i_dw; + + /* On Win32, select() only works on socket descriptors */ + while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn, + MS_FROM_VLC_TICK(INTF_IDLE_SLEEP), TRUE ) == WAIT_OBJECT_0 ) { - if( !strcmp( psz_cmd, "chapter" ) ) + // Prefer to fail early when there's not enough space to store a 4 bytes + // UTF8 character. The function will be immediatly called again and we won't + // lose an input + while( *pi_size < MAX_LINE_LENGTH - 4 && + ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record, 1, &i_dw ) ) { - if ( *newval.psz_string ) - { - /* Set. */ - vlc_player_SelectChapterIdx(player, atoi(newval.psz_string)); - } - else + if( input_record.EventType != KEY_EVENT || + !input_record.Event.KeyEvent.bKeyDown || + input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || + input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL|| + input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU || + input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL ) { - /* Get. */ - struct vlc_player_title const *title = vlc_player_GetSelectedTitle(player); - ssize_t chapter = -1; - if (title != NULL) - chapter = vlc_player_GetSelectedChapterIdx(player); - if (chapter != -1) - msg_rc("Currently playing chapter %zd/%zu.", - chapter, title->chapter_count); - else - msg_rc("No chapter selected."); + /* nothing interesting */ + continue; } - } - else if( !strcmp( psz_cmd, "chapter_n" ) ) - vlc_player_SelectNextChapter(player); - else if( !strcmp( psz_cmd, "chapter_p" ) ) - vlc_player_SelectPrevChapter(player); - } - else if( !strcmp( psz_cmd, "title" ) || - !strcmp( psz_cmd, "title_n" ) || - !strcmp( psz_cmd, "title_p" ) ) - { - if( !strcmp( psz_cmd, "title" ) ) - { - if ( *newval.psz_string ) + if( input_record.Event.KeyEvent.uChar.AsciiChar == '\n' || + input_record.Event.KeyEvent.uChar.AsciiChar == '\r' ) { - /* Set. */ - int idx = atoi(newval.psz_string); - if (idx >= 0) - vlc_player_SelectTitleIdx(player, (size_t)idx); + putc( '\n', stdout ); + break; } - else + switch( input_record.Event.KeyEvent.uChar.AsciiChar ) { - /* Get. */ - ssize_t title = vlc_player_GetSelectedTitleIdx(player); - vlc_player_title_list *titles = - vlc_player_GetTitleList(player); - size_t count = 0; - if (titles != NULL) - count = vlc_player_title_list_GetCount(titles); - if (title != -1 && count != 0) - msg_rc("Currently playing title %zd/%zu.", title, count); + case '\b': + if ( *pi_size == 0 ) + break; + if ( *pi_size > 1 && (p_buffer[*pi_size - 1] & 0xC0) == 0x80 ) + { + // pi_size currently points to the character to be written, so + // we need to roll back from 2 bytes to start erasing the previous + // character + (*pi_size) -= 2; + unsigned int nbBytes = 1; + while( *pi_size > 0 && (p_buffer[*pi_size] & 0xC0) == 0x80 ) + { + (*pi_size)--; + nbBytes++; + } + assert( clz( (unsigned char)~(p_buffer[*pi_size]) ) == nbBytes + 1 ); + // The first utf8 byte will be overriden by a \0 + } else - msg_rc("No title selected."); + (*pi_size)--; + p_buffer[*pi_size] = 0; + + fputs( "\b \b", stdout ); + break; + default: + { + WCHAR psz_winput[] = { input_record.Event.KeyEvent.uChar.UnicodeChar, L'\0' }; + char* psz_input = FromWide( psz_winput ); + int input_size = strlen(psz_input); + if ( *pi_size + input_size > MAX_LINE_LENGTH ) + { + p_buffer[ *pi_size ] = 0; + return false; + } + strcpy( (char*)&p_buffer[*pi_size], psz_input ); + utf8_fprintf( stdout, "%s", psz_input ); + free(psz_input); + *pi_size += input_size; + } } } - else if( !strcmp( psz_cmd, "title_n" ) ) - vlc_player_SelectNextTitle(player); - else if( !strcmp( psz_cmd, "title_p" ) ) - vlc_player_SelectPrevTitle(player); + + p_buffer[ *pi_size ] = 0; + return true; } - else if( !strcmp( psz_cmd, "atrack" ) - || !strcmp( psz_cmd, "vtrack" ) - || !strcmp( psz_cmd, "strack" ) ) + + vlc_testcancel (); + + return false; +} +#endif + +bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size ) +{ +#if defined(_WIN32) && !VLC_WINSTORE_APP + if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet ) + return ReadWin32( p_intf, (unsigned char*)p_buffer, pi_size ); + else if( p_intf->p_sys->i_socket == -1 ) { - enum es_format_category_e cat; - if( !strcmp( psz_cmd, "atrack" ) ) - cat = AUDIO_ES; - else if( !strcmp( psz_cmd, "vtrack" ) ) - cat = VIDEO_ES; - else - cat = SPU_ES; - if( newval.psz_string && *newval.psz_string ) + vlc_tick_sleep( INTF_IDLE_SLEEP ); + return false; + } +#endif + + while( *pi_size < MAX_LINE_LENGTH ) + { + if( p_intf->p_sys->i_socket == -1 ) { - int idx = atoi(newval.psz_string); - if (idx < 0) - goto out; - size_t track_count = vlc_player_GetTrackCount(player, cat); - if ((unsigned)idx >= track_count) - goto out; - struct vlc_player_track const *track = - vlc_player_GetTrackAt(player, cat, (size_t)idx); - if (!track) - goto out; - vlc_player_SelectTrack(player, track->es_id); + if( read( 0/*STDIN_FILENO*/, p_buffer + *pi_size, 1 ) <= 0 ) + { /* Standard input closed: exit */ + libvlc_Quit( vlc_object_instance(p_intf) ); + p_buffer[*pi_size] = 0; + return true; + } } else - { - struct vlc_player_track const *cur_track = - vlc_player_GetSelectedTrack(player, cat); - char const *name = cur_track ? cur_track->name : psz_cmd; - msg_rc("+----[ %s ]", name); - size_t count = vlc_player_GetTrackCount(player, cat); - for (size_t i = 0; i < count; ++i) + { /* Connection closed */ + if( net_Read( p_intf, p_intf->p_sys->i_socket, p_buffer + *pi_size, + 1 ) <= 0 ) { - struct vlc_player_track const *track = - vlc_player_GetTrackAt(player, cat, i); - msg_rc("| %zu - %s%s", - i, track->name, track == cur_track ? " *" : ""); + net_Close( p_intf->p_sys->i_socket ); + p_intf->p_sys->i_socket = -1; + p_buffer[*pi_size] = 0; + return true; } - msg_rc("+----[ end of %s ]", name); } + + if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' ) + break; + + (*pi_size)++; } -out: - vlc_player_Unlock(player); -} -static void print_playlist(intf_thread_t *p_intf, vlc_playlist_t *playlist) -{ - size_t count = vlc_playlist_Count(playlist); - for (size_t i = 0; i < count; ++i) + if( *pi_size == MAX_LINE_LENGTH || + p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' ) { - vlc_playlist_item_t *plitem = vlc_playlist_Get(playlist, i); - input_item_t *item = vlc_playlist_item_GetMedia(plitem); - vlc_tick_t len = item->i_duration; - if (len != INPUT_DURATION_INDEFINITE && len != VLC_TICK_INVALID) - { - char buf[MSTRTIME_MAX_SIZE]; - secstotimestr(buf, SEC_FROM_VLC_TICK(len)); - msg_rc("|-- %s (%s)", item->psz_name, buf); - } - else - msg_rc("|-- %s", item->psz_name); + p_buffer[ *pi_size ] = 0; + return true; } + + return false; } -static void Playlist( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) +/***************************************************************************** + * Run: rc thread + ***************************************************************************** + * This part of the interface is in a separate thread so that we can call + * exec() from within it without annoying the rest of the program. + *****************************************************************************/ +static void *Run( void *data ) { - intf_thread_t *p_intf = (intf_thread_t*)p_this; - vlc_playlist_t *playlist = p_intf->p_sys->playlist; - vlc_player_t *player = vlc_playlist_GetPlayer(playlist); + intf_thread_t *p_intf = data; + intf_sys_t *p_sys = p_intf->p_sys; + vlc_object_t *vlc = VLC_OBJECT(vlc_object_instance(p_intf)); + + char p_buffer[ MAX_LINE_LENGTH + 1 ]; + bool b_showpos = var_InheritBool( p_intf, "rc-show-pos" ); + + int i_size = 0; + int i_oldpos = 0; + int i_newpos; + int canc = vlc_savecancel( ); + + p_buffer[0] = 0; + +#if defined(_WIN32) && !VLC_WINSTORE_APP + /* Get the file descriptor of the console input */ + p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE); + if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE ) + { + msg_Err( p_intf, "couldn't find user input handle" ); + return NULL; + } +#endif + + /* Register commands that will be cleaned up upon object destruction */ + vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); + input_item_t *item = NULL; - vlc_playlist_Lock(playlist); + /* status callbacks */ - if (vlc_playlist_GetCurrentIndex(playlist) != -1 && - vlc_player_IsPaused(player)) + for( ;; ) { - msg_rc("%s", _("Type 'pause' to continue.")); - goto end; - } + char *psz_cmd, *psz_arg; + bool b_complete; - /* Parse commands that require a playlist */ - if( !strcmp( psz_cmd, "prev" ) ) - vlc_playlist_Prev(playlist); - else if( !strcmp( psz_cmd, "next" ) ) - vlc_playlist_Next(playlist); - else if( !strcmp( psz_cmd, "play" ) ) - vlc_playlist_Start(playlist); - else if( !strcmp( psz_cmd, "repeat" ) ) - { - bool b_update = true; - enum vlc_playlist_playback_repeat repeat_mode = - vlc_playlist_GetPlaybackRepeat(playlist); - bool b_value = repeat_mode == VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT; + vlc_restorecancel( canc ); - if( strlen( newval.psz_string ) > 0 ) + if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 ) { - if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || - ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) - { - b_update = false; - } + p_sys->i_socket = + net_Accept( p_intf, p_sys->pi_socket_listen ); + if( p_sys->i_socket == -1 ) continue; } - if ( b_update ) - { - b_value = !b_value; - repeat_mode = b_value - ? VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT - : VLC_PLAYLIST_PLAYBACK_REPEAT_NONE; - vlc_playlist_SetPlaybackRepeat(playlist, repeat_mode); - } - msg_rc( "Setting repeat to %s", b_value ? "true" : "false" ); - } - else if( !strcmp( psz_cmd, "loop" ) ) - { - bool b_update = true; - enum vlc_playlist_playback_repeat repeat_mode = - vlc_playlist_GetPlaybackRepeat(playlist); - bool b_value = repeat_mode == VLC_PLAYLIST_PLAYBACK_REPEAT_ALL; + b_complete = ReadCommand( p_intf, p_buffer, &i_size ); + canc = vlc_savecancel( ); - if( strlen( newval.psz_string ) > 0 ) + /* Manage the input part */ + if( item == NULL ) { - if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || - ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) + vlc_player_Lock(player); + item = vlc_player_HoldCurrentMedia(player); + vlc_player_Unlock(player); + /* New input has been registered */ + if( item ) { - b_update = false; + char *psz_uri = input_item_GetURI( item ); + msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri ); + free( psz_uri ); } } - if ( b_update ) + vlc_player_Lock(player); + if( !vlc_player_IsStarted( player ) ) { - b_value = !b_value; - repeat_mode = b_value - ? VLC_PLAYLIST_PLAYBACK_REPEAT_ALL - : VLC_PLAYLIST_PLAYBACK_REPEAT_NONE; - vlc_playlist_SetPlaybackRepeat(playlist, repeat_mode); + if (item) + { + input_item_Release( item ); + item = NULL; + } + + p_sys->last_state = VLC_PLAYER_STATE_STOPPED; + msg_rc( STATUS_CHANGE "( stop state: 0 )" ); } - msg_rc( "Setting loop to %s", b_value ? "true" : "false" ); - } - else if( !strcmp( psz_cmd, "random" ) ) - { - bool b_update = true; - enum vlc_playlist_playback_order order_mode = - vlc_playlist_GetPlaybackOrder(playlist); - bool b_value = order_mode == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM; + vlc_player_Unlock(player); - if( strlen( newval.psz_string ) > 0 ) + if( item != NULL ) { - if ( ( !strncmp( newval.psz_string, "on", 2 ) && b_value ) || - ( !strncmp( newval.psz_string, "off", 3 ) && !b_value ) ) + vlc_player_Lock(player); + enum vlc_player_state state = vlc_player_GetState(player); + vlc_player_Unlock(player); + + if (p_sys->last_state != state) { - b_update = false; + switch (state) + { + case VLC_PLAYER_STATE_STOPPING: + case VLC_PLAYER_STATE_STOPPED: + msg_rc(STATUS_CHANGE "( stop state: 5 )"); + break; + case VLC_PLAYER_STATE_PLAYING: + msg_rc(STATUS_CHANGE "( play state: 3 )"); + break; + case VLC_PLAYER_STATE_PAUSED: + msg_rc(STATUS_CHANGE "( pause state: 4 )"); + break; + default: + break; + } + p_sys->last_state = state; } } - if ( b_update ) + if( item && b_showpos ) { - b_value = !b_value; - order_mode = b_value - ? VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM - : VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL; - vlc_playlist_SetPlaybackOrder(playlist, order_mode); + vlc_player_Lock(player); + i_newpos = 100 * vlc_player_GetPosition( player ); + vlc_player_Unlock(player); + if( i_oldpos != i_newpos ) + { + i_oldpos = i_newpos; + msg_rc( "pos: %d%%", i_newpos ); + } } - msg_rc( "Setting random to %s", b_value ? "true" : "false" ); - } - else if (!strcmp( psz_cmd, "goto" ) ) - { - long long llindex = atoll(newval.psz_string); - size_t index = (size_t)llindex; - size_t count = vlc_playlist_Count(playlist); - if (llindex < 0) - msg_rc("%s", _("Error: `goto' needs an argument greater or equal to zero.")); - else if (index < count) - vlc_playlist_PlayAt(playlist, index); - else - msg_rc(vlc_ngettext("Playlist has only %zu element", - "Playlist has only %zu elements", count), - count); - } - else if( !strcmp( psz_cmd, "stop" ) ) - vlc_playlist_Stop(playlist); - else if( !strcmp( psz_cmd, "clear" ) ) - { - vlc_playlist_Stop(playlist); - vlc_playlist_Clear(playlist); - } - else if ((!strcmp(psz_cmd, "add") || !strcmp(psz_cmd, "enqueue")) && - newval.psz_string && *newval.psz_string) - { - input_item_t *p_item = parse_MRL( newval.psz_string ); - - if( p_item ) - { - msg_rc("Trying to %s %s to playlist.", psz_cmd, newval.psz_string); - size_t count = vlc_playlist_Count(playlist); - int ret = vlc_playlist_InsertOne(playlist, count, p_item); - input_item_Release(p_item); - if (ret != VLC_SUCCESS) - goto end; + /* Is there something to do? */ + if( !b_complete ) continue; - if (!strcmp(psz_cmd, "add")) - vlc_playlist_PlayAt(playlist, count); + /* Skip heading spaces */ + psz_cmd = p_buffer; + while( *psz_cmd == ' ' ) + { + psz_cmd++; } - } - else if( !strcmp( psz_cmd, "playlist" ) ) - { - msg_rc( "+----[ Playlist ]" ); - print_playlist(p_intf, playlist); - msg_rc( "+----[ End of playlist ]" ); - } - else if( !strcmp( psz_cmd, "sort" )) - { - struct vlc_playlist_sort_criterion criteria = - { - .key = VLC_PLAYLIST_SORT_KEY_ARTIST, - .order = VLC_PLAYLIST_SORT_ORDER_ASCENDING - }; - vlc_playlist_Sort(playlist, &criteria, 1); - } - else if( !strcmp( psz_cmd, "status" ) ) - { - input_item_t *item = vlc_player_GetCurrentMedia(player); - if (item) + /* Split psz_cmd at the first space and make sure that + * psz_arg is valid */ + psz_arg = strchr( psz_cmd, ' ' ); + if( psz_arg ) { - char *psz_uri = input_item_GetURI(item); - if( likely(psz_uri != NULL) ) + *psz_arg++ = 0; + while( *psz_arg == ' ' ) { - msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri ); - free( psz_uri ); + psz_arg++; } } - - float volume = vlc_player_aout_GetVolume(player); - if( volume >= 0.f ) - msg_rc( STATUS_CHANGE "( audio volume: %ld )", - lroundf(volume * 100.f ) ); - - enum vlc_player_state state = vlc_player_GetState(player); - switch (state) + else { - case VLC_PLAYER_STATE_STOPPING: - case VLC_PLAYER_STATE_STOPPED: - msg_rc( STATUS_CHANGE "( stop state: 5 )" ); - break; - case VLC_PLAYER_STATE_PLAYING: - msg_rc( STATUS_CHANGE "( play state: 3 )" ); - break; - case VLC_PLAYER_STATE_PAUSED: - msg_rc( STATUS_CHANGE "( pause state: 4 )" ); - break; - default: - msg_rc( STATUS_CHANGE "( unknown state: -1 )" ); - break; + psz_arg = (char*)""; } - } - /* - * sanity check - */ - else - msg_rc( "unknown command!" ); + if( !strcmp( psz_cmd, "quit" ) ) + libvlc_Quit( vlc_object_instance(p_intf) ); -end: - vlc_playlist_Unlock(playlist); -} +#define VOID(name, func) \ + if (strcmp(psz_cmd, name) == 0) { \ + vlc_value_t n; \ + func(VLC_OBJECT(p_intf), psz_cmd, n); \ + } else -static void Intf( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) -{ - VLC_UNUSED(psz_cmd); - intf_Create(vlc_object_instance(p_this), newval.psz_string); -} +#define STRING(name, func) \ + if (strcmp(psz_cmd, name) == 0) { \ + vlc_value_t n = { .psz_string = psz_arg }; \ + func(VLC_OBJECT(p_intf), psz_cmd, n); \ + } else -static void Volume( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) -{ - VLC_UNUSED(psz_cmd); - intf_thread_t *p_intf = (intf_thread_t*)p_this; - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); - vlc_player_Lock(player); - if ( *newval.psz_string ) - { - /* Set. */ - float volume = atol(newval.psz_string) / 100.f; - vlc_player_aout_SetVolume(player, volume); - } - else - { - /* Get. */ - long int volume = lroundf(vlc_player_aout_GetVolume(player) * 100.f); - msg_rc(STATUS_CHANGE "( audio volume: %ld )", volume); - } - vlc_player_Unlock(player); -} -static void VolumeMove( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) -{ - intf_thread_t *p_intf = (intf_thread_t*)p_this; - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); + STRING("intf", Intf) - float volume; - int i_nb_steps = atoi(newval.psz_string); + STRING("add", Playlist) + STRING("repeat", Playlist) + STRING("loop", Playlist) + STRING("random", Playlist) + STRING("enqueue", Playlist) + VOID("playlist", Playlist) + VOID("sort", Playlist) + VOID("play", Playlist) + VOID("stop", Playlist) + VOID("clear", Playlist) + VOID("prev", Playlist) + VOID("next", Playlist) + STRING("goto", Playlist) + STRING("status", Playlist) - if( !strcmp(psz_cmd, "voldown") ) - i_nb_steps *= -1; + /* DVD commands */ + VOID("pause", Input) + STRING("seek", Input) + STRING("title", Input) + VOID("title_n", Input) + VOID("title_p", Input) + STRING("chapter", Input) + VOID("chapter_n", Input) + VOID("chapter_p", Input) - vlc_player_Lock(player); - vlc_player_aout_IncrementVolume(player, i_nb_steps, &volume); - vlc_player_Unlock(player); -} + VOID("fastforward", Input) + VOID("rewind", Input) + VOID("faster", Input) + VOID("slower", Input) + VOID("normal", Input) + VOID("frame", Input) -static void VideoConfig( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t newval ) -{ - intf_thread_t *p_intf = (intf_thread_t*)p_this; - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); - vout_thread_t *p_vout = vlc_player_vout_Hold(player); - const char * psz_variable = NULL; + STRING("atrack", Input) + STRING("vtrack", Input) + STRING("strack", Input) - if( !strcmp( psz_cmd, "vcrop" ) ) - psz_variable = "crop"; - else if( !strcmp( psz_cmd, "vratio" ) ) - psz_variable = "aspect-ratio"; - else if( !strcmp( psz_cmd, "vzoom" ) ) - psz_variable = "zoom"; - else if( !strcmp( psz_cmd, "snapshot" ) ) - psz_variable = "video-snapshot"; - else - /* This case can't happen */ - vlc_assert_unreachable(); + /* video commands */ + STRING("vratio", VideoConfig) + STRING("vcrop", VideoConfig) + STRING("vzoom", VideoConfig) + VOID("snapshot", VideoConfig) - if( newval.psz_string && *newval.psz_string ) - { - /* set */ - if( !strcmp( psz_variable, "zoom" ) ) - { - float f_float = atof( newval.psz_string ); - var_SetFloat( p_vout, psz_variable, f_float ); - } - else - var_SetString( p_vout, psz_variable, newval.psz_string ); - } - else if( !strcmp( psz_cmd, "snapshot" ) ) - vlc_player_vout_Snapshot(player); - else - { - /* get */ - char *name; - vlc_value_t *val; - char **text; - float f_value = 0.; - char *psz_value = NULL; - size_t count; + /* audio commands */ + STRING("volume", Volume) + STRING("volup", VolumeMove) + STRING("voldown", VolumeMove) + STRING("adev", AudioDevice) + STRING("achan", AudioChannel) - if( !strcmp( psz_variable, "zoom" ) ) - f_value = var_GetFloat( p_vout, "zoom" ); - else +#undef STRING +#undef VOID + + /* misc menu commands */ + if( !strcmp( psz_cmd, "stats" ) ) + Statistics( p_intf ); + else if( !strcmp( psz_cmd, "logout" ) ) { - psz_value = var_GetString( p_vout, psz_variable ); - if( psz_value == NULL ) + /* Close connection */ + if( p_sys->i_socket != -1 ) { - vout_Release(p_vout); - return; + net_Close( p_sys->i_socket ); + p_sys->i_socket = -1; } } - - if ( var_Change( p_vout, psz_variable, VLC_VAR_GETCHOICES, - &count, &val, &text ) < 0 ) + else if( !strcmp( psz_cmd, "info" ) ) { - vout_Release(p_vout); - free( psz_value ); - return; - } - - /* Get the descriptive name of the variable */ - var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT, &name ); - if( !name ) name = strdup(psz_variable); + if( item ) + { + int i; + vlc_mutex_lock( &item->lock ); + for ( i = 0; i < item->i_categories; i++ ) + { + info_category_t *p_category = item->pp_categories[i]; + info_t *p_info; - msg_rc( "+----[ %s ]", name ); - if( !strcmp( psz_variable, "zoom" ) ) - { - for ( size_t i = 0; i < count; i++ ) + msg_rc( "+----[ %s ]", p_category->psz_name ); + msg_rc( "| " ); + info_foreach(p_info, &p_category->infos) + msg_rc( "| %s: %s", p_info->psz_name, + p_info->psz_value ); + msg_rc( "| " ); + } + msg_rc( "+----[ end of stream info ]" ); + vlc_mutex_unlock( &item->lock ); + } + else { - msg_rc( "| %f - %s%s", val[i].f_float, text[i], - f_value == val[i].f_float ? " *" : "" ); - free(text[i]); + msg_rc( "no input" ); } } - else + else if( !strcmp( psz_cmd, "is_playing" ) ) { - for ( size_t i = 0; i < count; i++ ) + if (p_sys->last_state != VLC_PLAYER_STATE_PLAYING && + p_sys->last_state != VLC_PLAYER_STATE_PAUSED) { - msg_rc( "| %s - %s%s", val[i].psz_string, text[i], - strcmp(psz_value, val[i].psz_string) - ? "" : " *" ); - free(text[i]); - free(val[i].psz_string); + msg_rc( "0" ); + } + else + { + msg_rc( "1" ); } - free( psz_value ); } - free(text); - free(val); - msg_rc( "+----[ end of %s ]", name ); - - free( name ); - } - vout_Release(p_vout); -} - -static void AudioDevice( vlc_object_t *obj, char const *cmd, vlc_value_t cur ) -{ - intf_thread_t *p_intf = (intf_thread_t *)obj; - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); - audio_output_t *aout = vlc_player_aout_Hold(player); - if (aout == NULL) - return; + else if( !strcmp( psz_cmd, "get_time" ) ) + { + vlc_player_Lock(player); + vlc_tick_t t = vlc_player_GetTime(player); + vlc_player_Unlock(player); + if (t != VLC_TICK_INVALID) + msg_rc("%"PRIu64, SEC_FROM_VLC_TICK(t)); + } + else if( !strcmp( psz_cmd, "get_length" ) ) + { + vlc_player_Lock(player); + vlc_tick_t l = vlc_player_GetLength(player); + vlc_player_Unlock(player); + if (l != VLC_TICK_INVALID) + msg_rc("%"PRIu64, SEC_FROM_VLC_TICK(l)); + } + else if( !strcmp( psz_cmd, "get_title" ) ) + { + vlc_player_Lock(player); + struct vlc_player_title const *title = + vlc_player_GetSelectedTitle(player); + vlc_player_Unlock(player); + msg_rc("%s", title ? title->name : ""); + } + else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 ) + || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) ) + { + Help( p_intf ); + } + else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) ) + { + var_SetInteger( vlc, "key-action", vlc_actions_get_id( psz_arg ) ); + } + else switch( psz_cmd[0] ) + { + case 'f': + case 'F': + { + bool fs; + if( !strncasecmp( psz_arg, "on", 2 ) ) + fs = true; + else if( !strncasecmp( psz_arg, "off", 3 ) ) + fs = false; + else + fs = !vlc_player_vout_IsFullscreen(player); + vlc_player_vout_SetFullscreen(player, fs); + break; + } + case 's': + case 'S': + ; + break; - char **ids, **names; - int n = aout_DevicesList(aout, &ids, &names); - if (n < 0) - goto out; + case '\0': + /* Ignore empty lines */ + break; - bool setdev = cur.psz_string && *cur.psz_string; - if (setdev) - aout_DeviceSet(aout, cur.psz_string); + default: + msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd); + break; + } - if (setdev) - { - int i; - for (i = 0; i < n; ++i) - if (!strcmp(cur.psz_string, ids[i])) - break; - if (i < n) - vlc_player_vout_OSDMessage(player, - _("Audio device: %s"), names[i]); + /* Command processed */ + i_size = 0; p_buffer[0] = 0; } - else - { - char *dev = aout_DeviceGet(aout); - const char *devstr = (dev != NULL) ? dev : ""; - - msg_rc( "+----[ %s ]", cmd ); - for ( int i = 0; i < n; i++ ) - { - const char *fmt = "| %s - %s"; - if( !strcmp(devstr, ids[i]) ) - fmt = "| %s - %s *"; - msg_rc( fmt, ids[i], names[i] ); - free( names[i] ); - free( ids[i] ); - } - msg_rc( "+----[ end of %s ]", cmd ); + msg_rc( STATUS_CHANGE "( stop state: 0 )" ); + msg_rc( STATUS_CHANGE "( quit )" ); - free( dev ); - } + vlc_restorecancel( canc ); - free(ids); - free(names); -out: - aout_Release(aout); + return NULL; } -static void AudioChannel( vlc_object_t *obj, char const *cmd, vlc_value_t cur ) +/***************************************************************************** + * Activate: initialize and create stuff + *****************************************************************************/ +static int Activate( vlc_object_t *p_this ) { - intf_thread_t *p_intf = (intf_thread_t*)obj; - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); - audio_output_t *p_aout = vlc_player_aout_Hold(player); - if ( p_aout == NULL ) - return; + /* FIXME: This function is full of memory leaks and bugs in error paths. */ + intf_thread_t *p_intf = (intf_thread_t*)p_this; + char *psz_host, *psz_unix_path = NULL; + int *pi_socket = NULL; - if ( !*cur.psz_string ) +#ifndef _WIN32 +#if defined(HAVE_ISATTY) + /* Check that stdin is a TTY */ + if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) ) { - /* Retrieve all registered ***. */ - vlc_value_t *val; - char **text; - size_t count; + msg_Warn( p_intf, "fd 0 is not a TTY" ); + return VLC_EGENERIC; + } +#endif - if ( var_Change( p_aout, "stereo-mode", VLC_VAR_GETCHOICES, - &count, &val, &text ) < 0 ) - goto out; + psz_unix_path = var_InheritString( p_intf, "rc-unix" ); + if( psz_unix_path ) + { + int i_socket; - int i_value = var_GetInteger( p_aout, "stereo-mode" ); +#ifndef AF_LOCAL + msg_Warn( p_intf, "your OS doesn't support filesystem sockets" ); + free( psz_unix_path ); + return VLC_EGENERIC; +#else + struct sockaddr_un addr; - msg_rc( "+----[ %s ]", cmd ); - for ( size_t i = 0; i < count; i++ ) + memset( &addr, 0, sizeof(struct sockaddr_un) ); + + msg_Dbg( p_intf, "trying UNIX socket" ); + + if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 ) { - msg_rc( "| %"PRId64" - %s%s", val[i].i_int, text[i], - i_value == val[i].i_int ? " *" : "" ); - free(text[i]); + msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) ); + free( psz_unix_path ); + return VLC_EGENERIC; } - free(text); - free(val); - msg_rc( "+----[ end of %s ]", cmd ); - } - else - var_SetInteger( p_aout, "stereo-mode", atoi( cur.psz_string ) ); -out: - aout_Release(p_aout); -} - -static void Statistics( intf_thread_t *p_intf ) -{ - vlc_player_t *player = vlc_playlist_GetPlayer(p_intf->p_sys->playlist); - vlc_player_Lock(player); - input_item_t *p_item = vlc_player_GetCurrentMedia(player); - vlc_player_Unlock(player); - if (p_item == NULL) - return; + addr.sun_family = AF_LOCAL; + strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) ); + addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0'; - vlc_mutex_lock( &p_item->lock ); - msg_rc( "+----[ begin of statistical info ]" ); + if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)) + && (errno == EADDRINUSE) + && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr)) + && (errno == ECONNREFUSED)) + { + msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path); + unlink (psz_unix_path); - /* Input */ - msg_rc("%s", _("+-[Incoming]")); - msg_rc(_("| input bytes read : %8.0f KiB"), - (float)(p_item->p_stats->i_read_bytes)/1024 ); - msg_rc(_("| input bitrate : %6.0f kb/s"), - (float)(p_item->p_stats->f_input_bitrate)*8000 ); - msg_rc(_("| demux bytes read : %8.0f KiB"), - (float)(p_item->p_stats->i_demux_read_bytes)/1024 ); - msg_rc(_("| demux bitrate : %6.0f kb/s"), - (float)(p_item->p_stats->f_demux_bitrate)*8000 ); - msg_rc(_("| demux corrupted : %5"PRIi64), - p_item->p_stats->i_demux_corrupted ); - msg_rc(_("| discontinuities : %5"PRIi64), - p_item->p_stats->i_demux_discontinuity ); - msg_rc("|"); - /* Video */ - msg_rc("%s", _("+-[Video Decoding]")); - msg_rc(_("| video decoded : %5"PRIi64), - p_item->p_stats->i_decoded_video ); - msg_rc(_("| frames displayed : %5"PRIi64), - p_item->p_stats->i_displayed_pictures ); - msg_rc(_("| frames lost : %5"PRIi64), - p_item->p_stats->i_lost_pictures ); - msg_rc("|"); - /* Audio*/ - msg_rc("%s", _("+-[Audio Decoding]")); - msg_rc(_("| audio decoded : %5"PRIi64), - p_item->p_stats->i_decoded_audio ); - msg_rc(_("| buffers played : %5"PRIi64), - p_item->p_stats->i_played_abuffers ); - msg_rc(_("| buffers lost : %5"PRIi64), - p_item->p_stats->i_lost_abuffers ); - msg_rc("|"); - msg_rc( "+----[ end of statistical info ]" ); - vlc_mutex_unlock( &p_item->lock ); -} + if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))) + { + msg_Err (p_intf, "cannot bind UNIX socket at %s: %s", + psz_unix_path, vlc_strerror_c(errno)); + free (psz_unix_path); + net_Close (i_socket); + return VLC_EGENERIC; + } + } -#if defined(_WIN32) && !VLC_WINSTORE_APP -static bool ReadWin32( intf_thread_t *p_intf, unsigned char *p_buffer, int *pi_size ) -{ - INPUT_RECORD input_record; - DWORD i_dw; + if( listen( i_socket, 1 ) ) + { + msg_Warn (p_intf, "can't listen on socket: %s", + vlc_strerror_c(errno)); + free( psz_unix_path ); + net_Close( i_socket ); + return VLC_EGENERIC; + } - /* On Win32, select() only works on socket descriptors */ - while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn, - MS_FROM_VLC_TICK(INTF_IDLE_SLEEP), TRUE ) == WAIT_OBJECT_0 ) - { - // Prefer to fail early when there's not enough space to store a 4 bytes - // UTF8 character. The function will be immediatly called again and we won't - // lose an input - while( *pi_size < MAX_LINE_LENGTH - 4 && - ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record, 1, &i_dw ) ) + /* FIXME: we need a core function to merge listening sockets sets */ + pi_socket = calloc( 2, sizeof( int ) ); + if( pi_socket == NULL ) { - if( input_record.EventType != KEY_EVENT || - !input_record.Event.KeyEvent.bKeyDown || - input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || - input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL|| - input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU || - input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL ) - { - /* nothing interesting */ - continue; - } - if( input_record.Event.KeyEvent.uChar.AsciiChar == '\n' || - input_record.Event.KeyEvent.uChar.AsciiChar == '\r' ) - { - putc( '\n', stdout ); - break; - } - switch( input_record.Event.KeyEvent.uChar.AsciiChar ) - { - case '\b': - if ( *pi_size == 0 ) - break; - if ( *pi_size > 1 && (p_buffer[*pi_size - 1] & 0xC0) == 0x80 ) - { - // pi_size currently points to the character to be written, so - // we need to roll back from 2 bytes to start erasing the previous - // character - (*pi_size) -= 2; - unsigned int nbBytes = 1; - while( *pi_size > 0 && (p_buffer[*pi_size] & 0xC0) == 0x80 ) - { - (*pi_size)--; - nbBytes++; - } - assert( clz( (unsigned char)~(p_buffer[*pi_size]) ) == nbBytes + 1 ); - // The first utf8 byte will be overriden by a \0 - } - else - (*pi_size)--; - p_buffer[*pi_size] = 0; + free( psz_unix_path ); + net_Close( i_socket ); + return VLC_ENOMEM; + } + pi_socket[0] = i_socket; + pi_socket[1] = -1; +#endif /* AF_LOCAL */ + } +#endif /* !_WIN32 */ - fputs( "\b \b", stdout ); - break; - default: + if( ( pi_socket == NULL ) && + ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL ) + { + vlc_url_t url; + + vlc_UrlParse( &url, psz_host ); + if( url.psz_host == NULL ) + { + vlc_UrlClean( &url ); + char *psz_backward_compat_host; + if( asprintf( &psz_backward_compat_host, "//%s", psz_host ) < 0 ) { - WCHAR psz_winput[] = { input_record.Event.KeyEvent.uChar.UnicodeChar, L'\0' }; - char* psz_input = FromWide( psz_winput ); - int input_size = strlen(psz_input); - if ( *pi_size + input_size > MAX_LINE_LENGTH ) - { - p_buffer[ *pi_size ] = 0; - return false; - } - strcpy( (char*)&p_buffer[*pi_size], psz_input ); - utf8_fprintf( stdout, "%s", psz_input ); - free(psz_input); - *pi_size += input_size; - } + free( psz_host ); + return VLC_EGENERIC; } + free( psz_host ); + psz_host = psz_backward_compat_host; + vlc_UrlParse( &url, psz_host ); } - p_buffer[ *pi_size ] = 0; - return true; - } + msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port ); - vlc_testcancel (); + pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port); + if( pi_socket == NULL ) + { + msg_Warn( p_intf, "can't listen to %s port %i", + url.psz_host, url.i_port ); + vlc_UrlClean( &url ); + free( psz_host ); + return VLC_EGENERIC; + } - return false; -} -#endif + vlc_UrlClean( &url ); + free( psz_host ); + } -bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size ) -{ -#if defined(_WIN32) && !VLC_WINSTORE_APP - if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet ) - return ReadWin32( p_intf, (unsigned char*)p_buffer, pi_size ); - else if( p_intf->p_sys->i_socket == -1 ) + intf_sys_t *p_sys = malloc( sizeof( *p_sys ) ); + if( unlikely(p_sys == NULL) ) { - vlc_tick_sleep( INTF_IDLE_SLEEP ); - return false; + net_ListenClose( pi_socket ); + free( psz_unix_path ); + return VLC_ENOMEM; } + + p_intf->p_sys = p_sys; + p_sys->pi_socket_listen = pi_socket; + p_sys->i_socket = -1; + p_sys->psz_unix_path = psz_unix_path; + vlc_mutex_init( &p_sys->status_lock ); + p_sys->last_state = VLC_PLAYER_STATE_STOPPED; + p_sys->b_input_buffering = false; + p_sys->playlist = vlc_intf_GetMainPlaylist(p_intf);; + vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); + + /* Non-buffered stdout */ + setvbuf( stdout, (char *)NULL, _IOLBF, 0 ); + +#if VLC_WINSTORE_APP + p_sys->b_quiet = true; +#elif defined(_WIN32) + p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" ); + if( !p_sys->b_quiet ) + intf_consoleIntroMsg( p_intf ); #endif - while( *pi_size < MAX_LINE_LENGTH ) - { - if( p_intf->p_sys->i_socket == -1 ) - { - if( read( 0/*STDIN_FILENO*/, p_buffer + *pi_size, 1 ) <= 0 ) - { /* Standard input closed: exit */ - libvlc_Quit( vlc_object_instance(p_intf) ); - p_buffer[*pi_size] = 0; - return true; - } - } - else - { /* Connection closed */ - if( net_Read( p_intf, p_intf->p_sys->i_socket, p_buffer + *pi_size, - 1 ) <= 0 ) - { - net_Close( p_intf->p_sys->i_socket ); - p_intf->p_sys->i_socket = -1; - p_buffer[*pi_size] = 0; - return true; - } - } + if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) ) + goto error; - if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' ) - break; + msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") ); - (*pi_size)++; + static struct vlc_player_cbs const player_cbs = + { + .on_state_changed = player_on_state_changed, + .on_buffering_changed = player_on_buffering_changed, + .on_rate_changed = player_on_rate_changed, + .on_position_changed = player_on_position_changed, + }; + vlc_player_Lock(player); + p_sys->player_listener = + vlc_player_AddListener(player, &player_cbs, p_intf); + if (!p_sys->player_listener) + { + vlc_player_Unlock(player); + goto error; } - if( *pi_size == MAX_LINE_LENGTH || - p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' ) + static struct vlc_player_aout_cbs const player_aout_cbs = { - p_buffer[ *pi_size ] = 0; - return true; - } + .on_volume_changed = player_aout_on_volume_changed, + }; + p_sys->player_aout_listener = + vlc_player_aout_AddListener(player, &player_aout_cbs, p_intf); + vlc_player_Unlock(player); + if (!p_sys->player_aout_listener) + goto error; - return false; + return VLC_SUCCESS; + +error: + if (p_sys->player_listener) + { + vlc_player_Lock(player); + vlc_player_RemoveListener(player, p_sys->player_listener); + vlc_player_Unlock(player); + } + net_ListenClose( pi_socket ); + free( psz_unix_path ); + vlc_mutex_destroy( &p_sys->status_lock ); + free( p_sys ); + return VLC_EGENERIC; } /***************************************************************************** - * parse_MRL: build a input item from a full mrl - ***************************************************************************** - * MRL format: "simplified-mrl [:option-name[=option-value]]" - * We don't check for '"' or '\'', we just assume that a ':' that follows a - * space is a new option. Should be good enough for our purpose. + * Deactivate: uninitialize and cleanup *****************************************************************************/ -static input_item_t *parse_MRL( const char *mrl ) +static void Deactivate( vlc_object_t *p_this ) { -#define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; } -#define SKIPTRAILINGSPACE( p, d ) \ - { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} } + intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_sys_t *p_sys = p_intf->p_sys; - input_item_t *p_item = NULL; - char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl; - char **ppsz_options = NULL; - int i_options = 0; + vlc_player_t *player = vlc_playlist_GetPlayer(p_sys->playlist); + vlc_player_Lock(player); + vlc_player_aout_RemoveListener(player, p_sys->player_aout_listener); + vlc_player_RemoveListener(player, p_sys->player_listener); + vlc_player_Unlock(player); - if( !mrl ) return 0; + vlc_cancel( p_sys->thread ); + vlc_join( p_sys->thread, NULL ); - psz_mrl = psz_orig = strdup( mrl ); - if( !psz_mrl ) - return NULL; - while( *psz_mrl ) + net_ListenClose( p_sys->pi_socket_listen ); + if( p_sys->i_socket != -1 ) + net_Close( p_sys->i_socket ); + if( p_sys->psz_unix_path != NULL ) { - SKIPSPACE( psz_mrl ); - psz_item = psz_mrl; +#if defined(AF_LOCAL) && !defined(_WIN32) + unlink( p_sys->psz_unix_path ); +#endif + free( p_sys->psz_unix_path ); + } + vlc_mutex_destroy( &p_sys->status_lock ); + free( p_sys ); +} - for( ; *psz_mrl; psz_mrl++ ) - { - if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' ) - { - /* We have a complete item */ - break; - } - if( (*psz_mrl == ' ' || *psz_mrl == '\t') && - (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':') - { - /* We have a complete item */ - break; - } - } +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +#define POS_TEXT N_("Show stream position") +#define POS_LONGTEXT N_("Show the current position in seconds within the " \ + "stream from time to time." ) - if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; } - SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) ); +#define TTY_TEXT N_("Fake TTY") +#define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.") - /* Remove '"' and '\'' if necessary */ - if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' ) - { psz_item++; psz_item[strlen(psz_item)-1] = 0; } - if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' ) - { psz_item++; psz_item[strlen(psz_item)-1] = 0; } +#define UNIX_TEXT N_("UNIX socket command input") +#define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \ + "stdin." ) - if( !psz_item_mrl ) - { - if( strstr( psz_item, "://" ) != NULL ) - psz_item_mrl = strdup( psz_item ); - else - psz_item_mrl = vlc_path2uri( psz_item, NULL ); - if( psz_item_mrl == NULL ) - { - free( psz_orig ); - return NULL; - } - } - else if( *psz_item ) - { - i_options++; - ppsz_options = xrealloc( ppsz_options, i_options * sizeof(char *) ); - ppsz_options[i_options - 1] = &psz_item[1]; - } +#define HOST_TEXT N_("TCP command input") +#define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \ + "You can set the address and port the interface will bind to." ) - if( *psz_mrl ) SKIPSPACE( psz_mrl ); - } +#ifdef _WIN32 +#define QUIET_TEXT N_("Do not open a DOS command box interface") +#define QUIET_LONGTEXT N_( \ + "By default the rc interface plugin will start a DOS command box. " \ + "Enabling the quiet mode will not bring this command box but can also " \ + "be pretty annoying when you want to stop VLC and no video window is " \ + "open." ) +#endif - /* Now create a playlist item */ - if( psz_item_mrl ) - { - p_item = input_item_New( psz_item_mrl, NULL ); - for( int i = 0; i < i_options; i++ ) - { - input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED ); - } - free( psz_item_mrl ); - } +vlc_module_begin() + set_shortname(N_("RC")) + set_category(CAT_INTERFACE) + set_subcategory(SUBCAT_INTERFACE_MAIN) + set_description(N_("Remote control interface")) + add_bool("rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true) + +#ifdef _WIN32 + add_bool("rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false) +#else +#if defined (HAVE_ISATTY) + add_bool("rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true) +#endif + add_string("rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true) +#endif + add_string("rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true) - if( i_options ) free( ppsz_options ); - free( psz_orig ); + set_capability("interface", 20) - return p_item; -} + set_callbacks(Activate, Deactivate) +#ifdef _WIN32 + add_shortcut("rc") +#endif +vlc_module_end() -- GitLab