mirror of
https://github.com/mpv-player/mpv
synced 2024-12-21 14:22:17 +00:00
d5b081152a
Anticipated use: simple solution for dealing with audio APIs which request configuration changes via events.
314 lines
12 KiB
C
314 lines
12 KiB
C
/*
|
|
* This file is part of MPlayer.
|
|
*
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <limits.h>
|
|
|
|
#include "common/common.h"
|
|
#include "common/msg.h"
|
|
#include "options/m_option.h"
|
|
|
|
#include "input.h"
|
|
#include "cmd_list.h"
|
|
#include "cmd_parse.h"
|
|
|
|
// This does not specify the real destination of the command parameter values,
|
|
// it just provides a dummy for the OPT_ macros.
|
|
#define OPT_BASE_STRUCT struct mp_cmd_arg
|
|
#define ARG(t) "", v. t
|
|
|
|
/* This array defines all known commands.
|
|
* The first field is an id used to recognize the command.
|
|
* The second is the command name used in slave mode and input.conf.
|
|
* Then comes the definition of each argument, first mandatory arguments
|
|
* (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments
|
|
* (OARG_INT(default), etc) if any. The command will be given the default
|
|
* argument value if the user didn't give enough arguments to specify it.
|
|
* A command can take a maximum of MP_CMD_MAX_ARGS arguments.
|
|
*/
|
|
|
|
#define ARG_INT OPT_INT(ARG(i), 0)
|
|
#define ARG_FLOAT OPT_FLOAT(ARG(f), 0)
|
|
#define ARG_DOUBLE OPT_DOUBLE(ARG(d), 0)
|
|
#define ARG_STRING OPT_STRING(ARG(s), 0)
|
|
#define ARG_CHOICE(c) OPT_CHOICE(ARG(i), 0, c)
|
|
#define ARG_CHOICE_OR_INT(...) OPT_CHOICE_OR_INT(ARG(i), 0, __VA_ARGS__)
|
|
#define ARG_TIME OPT_TIME(ARG(d), 0)
|
|
#define OARG_DOUBLE(def) OPT_DOUBLE(ARG(d), 0, OPTDEF_DOUBLE(def))
|
|
#define OARG_INT(def) OPT_INT(ARG(i), 0, OPTDEF_INT(def))
|
|
#define OARG_CHOICE(def, c) OPT_CHOICE(ARG(i), 0, c, OPTDEF_INT(def))
|
|
#define OARG_STRING(def) OPT_STRING(ARG(s), 0, OPTDEF_STR(def))
|
|
|
|
#define OARG_CYCLEDIR(def) OPT_CYCLEDIR(ARG(d), 0, OPTDEF_DOUBLE(def))
|
|
|
|
const struct mp_cmd_def mp_cmds[] = {
|
|
{ MP_CMD_IGNORE, "ignore", },
|
|
|
|
{ MP_CMD_SEEK, "seek", {
|
|
ARG_TIME,
|
|
OARG_CHOICE(0, ({"relative", 0}, {"0", 0}, {"-", 0},
|
|
{"absolute-percent", 1}, {"1", 1},
|
|
{"absolute", 2}, {"2", 2})),
|
|
OARG_CHOICE(0, ({"default-precise", 0}, {"0", 0},
|
|
{"exact", 1}, {"1", 1},
|
|
{"keyframes", -1}, {"-1", -1})),
|
|
},
|
|
.allow_auto_repeat = true,
|
|
},
|
|
{ MP_CMD_REVERT_SEEK, "revert_seek", },
|
|
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
|
|
{ MP_CMD_QUIT_WATCH_LATER, "quit_watch_later", { OARG_INT(0) } },
|
|
{ MP_CMD_STOP, "stop", },
|
|
{ MP_CMD_FRAME_STEP, "frame_step", .allow_auto_repeat = true },
|
|
{ MP_CMD_FRAME_BACK_STEP, "frame_back_step", .allow_auto_repeat = true },
|
|
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", {
|
|
OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
|
|
{"force", 1}, {"1", 1})),
|
|
}},
|
|
{ MP_CMD_PLAYLIST_PREV, "playlist_prev", {
|
|
OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
|
|
{"force", 1}, {"1", 1})),
|
|
}},
|
|
{ MP_CMD_SUB_STEP, "sub_step", { ARG_INT }, .allow_auto_repeat = true },
|
|
{ MP_CMD_SUB_SEEK, "sub_seek", { ARG_INT }, .allow_auto_repeat = true },
|
|
{ MP_CMD_OSD, "osd", { OARG_INT(-1) } },
|
|
{ MP_CMD_PRINT_TEXT, "print_text", { ARG_STRING }, .allow_auto_repeat = true },
|
|
{ MP_CMD_SHOW_TEXT, "show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) },
|
|
.allow_auto_repeat = true},
|
|
{ MP_CMD_SHOW_PROGRESS, "show_progress", .allow_auto_repeat = true},
|
|
{ MP_CMD_SUB_ADD, "sub_add", { ARG_STRING,
|
|
OARG_CHOICE(0, ({"select", 0}, {"auto", 1}, {"cached", 2})),
|
|
OARG_STRING(""), OARG_STRING("") } },
|
|
{ MP_CMD_SUB_REMOVE, "sub_remove", { OARG_INT(-1) } },
|
|
{ MP_CMD_SUB_RELOAD, "sub_reload", { OARG_INT(-1) } },
|
|
|
|
{ MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", },
|
|
|
|
{ MP_CMD_SCREENSHOT, "screenshot", {
|
|
OARG_CHOICE(2, ({"video", 0},
|
|
{"window", 1},
|
|
{"subtitles", 2}, {"-", 2})),
|
|
OARG_CHOICE(0, ({"single", 0},
|
|
{"each-frame", 1})),
|
|
}},
|
|
{ MP_CMD_SCREENSHOT_TO_FILE, "screenshot_to_file", {
|
|
ARG_STRING,
|
|
OARG_CHOICE(2, ({"video", 0},
|
|
{"window", 1},
|
|
{"subtitles", 2})),
|
|
}},
|
|
{ MP_CMD_LOADFILE, "loadfile", {
|
|
ARG_STRING,
|
|
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
|
{"append", 1}, {"1", 1},
|
|
{"append-play", 2})),
|
|
OPT_KEYVALUELIST(ARG(str_list), MP_CMD_OPT_ARG),
|
|
}},
|
|
{ MP_CMD_LOADLIST, "loadlist", {
|
|
ARG_STRING,
|
|
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
|
{"append", 1}, {"1", 1})),
|
|
}},
|
|
{ MP_CMD_PLAYLIST_CLEAR, "playlist_clear", },
|
|
{ MP_CMD_PLAYLIST_REMOVE, "playlist_remove", {
|
|
ARG_CHOICE_OR_INT(0, INT_MAX, ({"current", -1})),
|
|
}},
|
|
{ MP_CMD_PLAYLIST_MOVE, "playlist_move", { ARG_INT, ARG_INT } },
|
|
{ MP_CMD_RUN, "run", { ARG_STRING, ARG_STRING }, .vararg = true },
|
|
|
|
{ MP_CMD_SET, "set", { ARG_STRING, ARG_STRING } },
|
|
{ MP_CMD_GET_PROPERTY, "get_property", { ARG_STRING } },
|
|
{ MP_CMD_ADD, "add", { ARG_STRING, OARG_DOUBLE(0) },
|
|
.allow_auto_repeat = true},
|
|
{ MP_CMD_CYCLE, "cycle", {
|
|
ARG_STRING,
|
|
OARG_CYCLEDIR(1),
|
|
},
|
|
.allow_auto_repeat = true
|
|
},
|
|
{ MP_CMD_MULTIPLY, "multiply", { ARG_STRING, ARG_DOUBLE },
|
|
.allow_auto_repeat = true},
|
|
|
|
{ MP_CMD_CYCLE_VALUES, "cycle_values", { ARG_STRING, ARG_STRING, ARG_STRING },
|
|
.vararg = true},
|
|
|
|
{ MP_CMD_ENABLE_INPUT_SECTION, "enable_section", {
|
|
ARG_STRING,
|
|
OARG_CHOICE(0, ({"default", 0},
|
|
{"exclusive", 1})),
|
|
}},
|
|
{ MP_CMD_DISABLE_INPUT_SECTION, "disable_section", { ARG_STRING } },
|
|
|
|
{ MP_CMD_DISCNAV, "discnav", { ARG_STRING } },
|
|
|
|
{ MP_CMD_AF, "af", { ARG_STRING, ARG_STRING } },
|
|
{ MP_CMD_AO_RELOAD, "ao_reload", },
|
|
|
|
{ MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } },
|
|
|
|
{ MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } },
|
|
|
|
{ MP_CMD_SCRIPT_DISPATCH, "script_dispatch", { ARG_STRING, ARG_INT } },
|
|
{ MP_CMD_SCRIPT_MESSAGE, "script_message", { ARG_STRING }, .vararg = true },
|
|
{ MP_CMD_SCRIPT_MESSAGE_TO, "script_message_to", { ARG_STRING, ARG_STRING },
|
|
.vararg = true },
|
|
|
|
{ MP_CMD_OVERLAY_ADD, "overlay_add",
|
|
{ ARG_INT, ARG_INT, ARG_INT, ARG_STRING, ARG_INT, ARG_STRING, ARG_INT,
|
|
ARG_INT, ARG_INT }},
|
|
{ MP_CMD_OVERLAY_REMOVE, "overlay_remove", { ARG_INT } },
|
|
|
|
{ MP_CMD_WRITE_WATCH_LATER_CONFIG, "write_watch_later_config", },
|
|
|
|
{ MP_CMD_HOOK_ADD, "hook_add", { ARG_STRING, ARG_INT, ARG_INT } },
|
|
{ MP_CMD_HOOK_ACK, "hook_ack", { ARG_STRING } },
|
|
|
|
{0}
|
|
};
|
|
|
|
#undef OPT_BASE_STRUCT
|
|
#undef ARG
|
|
|
|
// Map legacy commands to proper commands
|
|
struct legacy_cmd {
|
|
const char *old, *new;
|
|
};
|
|
static const struct legacy_cmd legacy_cmds[] = {
|
|
{"loop", "cycle loop"},
|
|
{"seek_chapter", "add chapter"},
|
|
{"switch_angle", "cycle angle"},
|
|
{"pause", "cycle pause"},
|
|
{"volume", "add volume"},
|
|
{"mute", "cycle mute"},
|
|
{"audio_delay", "add audio-delay"},
|
|
{"switch_audio", "cycle audio"},
|
|
{"balance", "add balance"},
|
|
{"vo_fullscreen", "cycle fullscreen"},
|
|
{"panscan", "add panscan"},
|
|
{"vo_ontop", "cycle ontop"},
|
|
{"vo_border", "cycle border"},
|
|
{"frame_drop", "cycle framedrop"},
|
|
{"gamma", "add gamma"},
|
|
{"brightness", "add brightness"},
|
|
{"contrast", "add contrast"},
|
|
{"saturation", "add saturation"},
|
|
{"hue", "add hue"},
|
|
{"switch_vsync", "cycle vsync"},
|
|
{"sub_load", "sub_add"},
|
|
{"sub_select", "cycle sub"},
|
|
{"sub_pos", "add sub-pos"},
|
|
{"sub_delay", "add sub-delay"},
|
|
{"sub_visibility", "cycle sub-visibility"},
|
|
{"forced_subs_only", "cycle sub-forced-only"},
|
|
{"sub_scale", "add sub-scale"},
|
|
{"ass_use_margins", "cycle ass-use-margins"},
|
|
{"tv_set_brightness", "add tv-brightness"},
|
|
{"tv_set_hue", "add tv-hue"},
|
|
{"tv_set_saturation", "add tv-saturation"},
|
|
{"tv_set_contrast", "add tv-contrast"},
|
|
{"step_property_osd", "cycle"},
|
|
{"step_property", "no-osd cycle"},
|
|
{"set_property", "no-osd set"},
|
|
{"set_property_osd", "set"},
|
|
{"speed_set", "set speed"},
|
|
{"osd_show_text", "show_text"},
|
|
{"osd_show_property_text", "show_text"},
|
|
{"osd_show_progression", "show_progress"},
|
|
{"show_chapters_osd", "show_text ${chapter-list}"},
|
|
{"show_chapters", "show_text ${chapter-list}"},
|
|
{"show_tracks_osd", "show_text ${track-list}"},
|
|
{"show_tracks", "show_text ${track-list}"},
|
|
{"show_playlist", "show_text ${playlist}"},
|
|
{"speed_mult", "multiply speed"},
|
|
{"dvdnav", "discnav"},
|
|
|
|
// Approximate (can fail if user added additional whitespace)
|
|
{"pt_step 1", "playlist_next"},
|
|
{"pt_step -1", "playlist_prev"},
|
|
// Switch_ratio without argument resets aspect ratio
|
|
{"switch_ratio ", "set aspect "},
|
|
{"switch_ratio", "set aspect 0"},
|
|
{0}
|
|
};
|
|
|
|
bool mp_replace_legacy_cmd(void *t, bstr *s)
|
|
{
|
|
for (const struct legacy_cmd *entry = legacy_cmds; entry->old; entry++) {
|
|
bstr old = bstr0(entry->old);
|
|
if (bstrcasecmp(bstr_splice(*s, 0, old.len), old) == 0) {
|
|
bstr rest = bstr_cut(*s, old.len);
|
|
*s = bstr0(talloc_asprintf(t, "%s%.*s", entry->new, BSTR_P(rest)));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 0: no, 1: maybe, 2: sure
|
|
static int is_abort_cmd(struct mp_cmd *cmd)
|
|
{
|
|
switch (cmd->id) {
|
|
case MP_CMD_QUIT:
|
|
case MP_CMD_QUIT_WATCH_LATER:
|
|
case MP_CMD_STOP:
|
|
return 2;
|
|
case MP_CMD_PLAYLIST_NEXT:
|
|
case MP_CMD_PLAYLIST_PREV:
|
|
return 1;
|
|
case MP_CMD_COMMAND_LIST:;
|
|
int r = 0;
|
|
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) {
|
|
int x = is_abort_cmd(sub);
|
|
r = MPMAX(r, x);
|
|
}
|
|
return r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool mp_input_is_maybe_abort_cmd(struct mp_cmd *cmd)
|
|
{
|
|
return is_abort_cmd(cmd) >= 1;
|
|
}
|
|
|
|
bool mp_input_is_abort_cmd(struct mp_cmd *cmd)
|
|
{
|
|
return is_abort_cmd(cmd) >= 2;
|
|
}
|
|
|
|
bool mp_input_is_repeatable_cmd(struct mp_cmd *cmd)
|
|
{
|
|
return (cmd->def && cmd->def->allow_auto_repeat) ||
|
|
cmd->id == MP_CMD_COMMAND_LIST;
|
|
}
|
|
|
|
void mp_print_cmd_list(struct mp_log *out)
|
|
{
|
|
for (int i = 0; mp_cmds[i].name; i++) {
|
|
const struct mp_cmd_def *def = &mp_cmds[i];
|
|
mp_info(out, "%-20.20s", def->name);
|
|
for (int j = 0; j < MP_CMD_MAX_ARGS && def->args[j].type; j++) {
|
|
const char *type = def->args[j].type->name;
|
|
if (def->args[j].defval)
|
|
mp_info(out, " [%s]", type);
|
|
else
|
|
mp_info(out, " %s", type);
|
|
}
|
|
mp_info(out, "\n");
|
|
}
|
|
}
|