summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/av_common.c16
-rw-r--r--core/av_log.c7
-rw-r--r--core/bstr.c9
-rw-r--r--core/bstr.h7
-rw-r--r--core/cfg-mplayer.h15
-rw-r--r--core/command.c105
-rw-r--r--core/defaultopts.c7
-rw-r--r--core/encode_lavc.c5
-rw-r--r--core/input/input.c52
-rw-r--r--core/input/input.h2
-rw-r--r--core/input/keycodes.h4
-rw-r--r--core/m_config.c5
-rw-r--r--core/m_option.c426
-rw-r--r--core/m_option.h19
-rw-r--r--core/mp_core.h21
-rw-r--r--core/mp_msg.c5
-rw-r--r--core/mp_msg.h2
-rw-r--r--core/mplayer.c494
-rw-r--r--core/options.h8
-rw-r--r--core/parser-mpcmd.c21
-rw-r--r--core/path.c14
-rw-r--r--core/path.h2
-rw-r--r--core/timeline/tl_edl.c2
-rw-r--r--core/timeline/tl_matroska.c123
24 files changed, 907 insertions, 464 deletions
diff --git a/core/av_common.c b/core/av_common.c
index bf1af853d9..5e6c8a4352 100644
--- a/core/av_common.c
+++ b/core/av_common.c
@@ -75,13 +75,15 @@ void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type)
int mp_codec_to_av_codec_id(const char *codec)
{
int id = AV_CODEC_ID_NONE;
- const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(codec);
- if (desc)
- id = desc->id;
- if (id == AV_CODEC_ID_NONE) {
- AVCodec *avcodec = avcodec_find_decoder_by_name(codec);
- if (avcodec)
- id = avcodec->id;
+ if (codec) {
+ const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(codec);
+ if (desc)
+ id = desc->id;
+ if (id == AV_CODEC_ID_NONE) {
+ AVCodec *avcodec = avcodec_find_decoder_by_name(codec);
+ if (avcodec)
+ id = avcodec->id;
+ }
}
return id;
}
diff --git a/core/av_log.c b/core/av_log.c
index 9f6f84a39b..37c308be7a 100644
--- a/core/av_log.c
+++ b/core/av_log.c
@@ -37,6 +37,10 @@
#include <libavdevice/avdevice.h>
#endif
+#ifdef CONFIG_LIBAVFILTER
+#include <libavfilter/avfilter.h>
+#endif
+
static int av_log_level_to_mp_level(int av_level)
{
if (av_level > AV_LOG_VERBOSE)
@@ -118,6 +122,9 @@ void init_libav(void)
av_register_all();
avformat_network_init();
+#ifdef CONFIG_LIBAVFILTER
+ avfilter_register_all();
+#endif
#ifdef CONFIG_LIBAVDEVICE
avdevice_register_all();
#endif
diff --git a/core/bstr.c b/core/bstr.c
index a472fbfb02..16da0993ea 100644
--- a/core/bstr.c
+++ b/core/bstr.c
@@ -82,6 +82,15 @@ int bstrcspn(struct bstr str, const char *reject)
return i;
}
+int bstrspn(struct bstr str, const char *accept)
+{
+ int i;
+ for (i = 0; i < str.len; i++)
+ if (!strchr(accept, str.start[i]))
+ break;
+ return i;
+}
+
int bstr_find(struct bstr haystack, struct bstr needle)
{
for (int i = 0; i < haystack.len; i++)
diff --git a/core/bstr.h b/core/bstr.h
index bcac6cdba3..ce9e029ea5 100644
--- a/core/bstr.h
+++ b/core/bstr.h
@@ -40,6 +40,12 @@ static inline char *bstrdup0(void *talloc_ctx, struct bstr str)
return talloc_strndup(talloc_ctx, (char *)str.start, str.len);
}
+// Like bstrdup0(), but always return a valid C-string.
+static inline char *bstrto0(void *talloc_ctx, struct bstr str)
+{
+ return str.start ? bstrdup0(talloc_ctx, str) : talloc_strdup(talloc_ctx, "");
+}
+
// Return start = NULL iff that is true for the original.
static inline struct bstr bstrdup(void *talloc_ctx, struct bstr str)
{
@@ -58,6 +64,7 @@ int bstrcmp(struct bstr str1, struct bstr str2);
int bstrcasecmp(struct bstr str1, struct bstr str2);
int bstrchr(struct bstr str, int c);
int bstrrchr(struct bstr str, int c);
+int bstrspn(struct bstr str, const char *accept);
int bstrcspn(struct bstr str, const char *reject);
int bstr_find(struct bstr haystack, struct bstr needle);
diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h
index 5994f24056..baf0820f48 100644
--- a/core/cfg-mplayer.h
+++ b/core/cfg-mplayer.h
@@ -317,7 +317,6 @@ const m_option_t common_opts[] = {
#ifdef CONFIG_LIBBLURAY
{"bluray-device", &bluray_device, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"bluray-angle", &bluray_angle, CONF_TYPE_INT, CONF_RANGE, 0, 999, NULL},
- {"bluray-chapter", &bluray_chapter, CONF_TYPE_INT, CONF_RANGE, 0, 999, NULL},
#endif /* CONFIG_LIBBLURAY */
#ifdef CONFIG_NETWORKING
@@ -348,7 +347,7 @@ const m_option_t common_opts[] = {
OPT_REL_TIME("end", play_end, 0),
OPT_REL_TIME("length", play_length, 0),
- OPT_FLAG("pause", start_paused, 0),
+ OPT_FLAG("pause", pause, 0),
OPT_FLAG("keep-open", keep_open, 0),
// AVI specific: force non-interleaved mode
@@ -443,6 +442,7 @@ const m_option_t common_opts[] = {
{"vdpau", 1},
{"vda", 2},
{"crystalhd", 3})),
+ OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
// postprocessing:
{"pp", &divx_quality, CONF_TYPE_INT, 0, 0, 0, NULL},
@@ -569,6 +569,7 @@ const m_option_t mplayer_opts[]={
OPT_INTRANGE("fsmode-dontuse", vo.fsmode, 0, 31, 4096),
OPT_INT("colorkey", vo.colorkey, 0),
OPT_FLAG_STORE("no-colorkey", vo.colorkey, 0, 0x1000000),
+ OPT_FLAG("native-keyrepeat", vo.native_keyrepeat, 0),
OPT_FLOATRANGE("panscan", vo.panscan, 0, 0.0, 1.0),
OPT_FLOATRANGE("panscanrange", vo.panscanrange, 0, -19.0, 99.0),
OPT_FLAG("force-rgba-osd-rendering", force_rgba_osd, 0),
@@ -576,7 +577,8 @@ const m_option_t mplayer_opts[]={
({"auto", MP_CSP_AUTO},
{"BT.601", MP_CSP_BT_601},
{"BT.709", MP_CSP_BT_709},
- {"SMPTE-240M", MP_CSP_SMPTE_240M})),
+ {"SMPTE-240M", MP_CSP_SMPTE_240M},
+ {"YCgCo", MP_CSP_YCGCO})),
OPT_CHOICE("colormatrix-input-range", requested_input_range, 0,
({"auto", MP_CSP_LEVELS_AUTO},
{"limited", MP_CSP_LEVELS_TV},
@@ -604,6 +606,10 @@ const m_option_t mplayer_opts[]={
OPT_CHOICE_OR_INT("fs-screen", vo.fsscreen_id, 0, 0, 32,
({"all", -2}, {"current", -1})),
+#ifdef CONFIG_COCOA
+ OPT_FLAG("native-fs", vo.native_fs, 0),
+#endif
+
OPT_INTRANGE("brightness", gamma_brightness, 0, -100, 100),
OPT_INTRANGE("saturation", gamma_saturation, 0, -100, 100),
OPT_INTRANGE("contrast", gamma_contrast, 0, -100, 100),
@@ -643,6 +649,9 @@ const m_option_t mplayer_opts[]={
{"{", NULL, CONF_TYPE_STORE, CONF_NOCFG, 0, 0, NULL},
{"}", NULL, CONF_TYPE_STORE, CONF_NOCFG, 0, 0, NULL},
+ OPT_FLAG("resume-playback", position_resume, 0),
+ OPT_FLAG("save-position-on-quit", position_save_on_quit, 0),
+
OPT_FLAG("ordered-chapters", ordered_chapters, 0),
OPT_INTRANGE("chapter-merge-threshold", chapter_merge_threshold, 0, 0, 10000),
diff --git a/core/command.c b/core/command.c
index 115ea9236f..d8a8882c26 100644
--- a/core/command.c
+++ b/core/command.c
@@ -328,6 +328,19 @@ static int mp_property_time_pos(m_option_t *prop, int action,
return M_PROPERTY_NOT_IMPLEMENTED;
}
+static int mp_property_remaining(m_option_t *prop, int action,
+ void *arg, MPContext *mpctx)
+{
+ double len = get_time_length(mpctx);
+ double pos = get_current_time(mpctx);
+ double start = get_start_time(mpctx);
+
+ if (!(int)len)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(prop, action, arg, len - (pos - start));
+}
+
/// Current chapter (RW)
static int mp_property_chapter(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
@@ -350,14 +363,11 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg,
case M_PROPERTY_SET: ;
int step_all = *(int *)arg - chapter;
chapter += step_all;
- double next_pts = 0;
- queue_seek(mpctx, MPSEEK_NONE, 0, 0);
- chapter = seek_chapter(mpctx, chapter, &next_pts);
- if (chapter >= 0) {
- if (next_pts > -1.0)
- queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
- } else if (step_all > 0)
+ if (chapter >= get_chapter_count(mpctx) && step_all > 0) {
mpctx->stop_play = PT_NEXT_ENTRY;
+ } else {
+ mp_seek_chapter(mpctx, chapter);
+ }
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -471,6 +481,17 @@ static int mp_property_angle(m_option_t *prop, int action, void *arg,
resync_audio_stream(sh_audio);
}
return M_PROPERTY_OK;
+ case M_PROPERTY_GET_TYPE: {
+ struct m_option opt = {
+ .name = prop->name,
+ .type = CONF_TYPE_INT,
+ .flags = CONF_RANGE,
+ .min = 1,
+ .max = angles,
+ };
+ *(struct m_option *)arg = opt;
+ return M_PROPERTY_OK;
+ }
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
@@ -518,19 +539,15 @@ static int mp_property_pause(m_option_t *prop, int action, void *arg,
{
MPContext *mpctx = ctx;
- switch (action) {
- case M_PROPERTY_SET:
+ if (action == M_PROPERTY_SET) {
if (*(int *)arg) {
pause_player(mpctx);
} else {
unpause_player(mpctx);
}
return M_PROPERTY_OK;
- case M_PROPERTY_GET:
- *(int *)arg = mpctx->paused;
- return M_PROPERTY_OK;
}
- return M_PROPERTY_NOT_IMPLEMENTED;
+ return mp_property_generic_option(prop, action, arg, ctx);
}
static int mp_property_cache(m_option_t *prop, int action, void *arg,
@@ -741,7 +758,7 @@ static int property_switch_track(m_option_t *prop, int action, void *arg,
switch (action) {
case M_PROPERTY_GET:
- *(int *) arg = track ? track->user_tid : -1;
+ *(int *) arg = track ? track->user_tid : -2;
return M_PROPERTY_OK;
case M_PROPERTY_PRINT:
if (!track)
@@ -769,9 +786,8 @@ static int property_switch_track(m_option_t *prop, int action, void *arg,
case M_PROPERTY_SET:
mp_switch_track(mpctx, type, mp_track_by_tid(mpctx, type, *(int *)arg));
return M_PROPERTY_OK;
- default:
- return M_PROPERTY_NOT_IMPLEMENTED;
}
+ return mp_property_generic_option(prop, action, arg, mpctx);
}
/// Selected audio id (RW)
@@ -825,10 +841,12 @@ static int mp_property_program(m_option_t *prop, int action, void *arg,
"Selected program contains no audio or video streams!\n");
return M_PROPERTY_ERROR;
}
- mp_switch_track(mpctx, STREAM_AUDIO,
- find_track_by_demuxer_id(mpctx, STREAM_AUDIO, prog.aid));
mp_switch_track(mpctx, STREAM_VIDEO,
find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.vid));
+ mp_switch_track(mpctx, STREAM_AUDIO,
+ find_track_by_demuxer_id(mpctx, STREAM_AUDIO, prog.aid));
+ mp_switch_track(mpctx, STREAM_SUB,
+ find_track_by_demuxer_id(mpctx, STREAM_VIDEO, prog.sid));
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -1295,6 +1313,19 @@ static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
#endif
+static int mp_property_alias(m_option_t *prop, int action, void *arg,
+ MPContext *mpctx)
+{
+ const char *real_property = prop->priv;
+ int r = mp_property_do(real_property, action, arg, mpctx);
+ if (action == M_PROPERTY_GET_TYPE && r >= 0) {
+ // Fix the property name
+ struct m_option *type = arg;
+ type->name = prop->name;
+ }
+ return r;
+}
+
// Use option-to-property-bridge. (The property and option have the same names.)
#define M_OPTION_PROPERTY(name) \
{(name), mp_property_generic_option, &m_option_type_dummy, 0, 0, 0, (name)}
@@ -1306,6 +1337,10 @@ static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
#define M_OPTION_PROPERTY_CUSTOM_(name, handler, ...) \
{(name), (handler), &m_option_type_dummy, 0, 0, 0, (name), __VA_ARGS__}
+// Redirect a property name to another
+#define M_PROPERTY_ALIAS(name, real_property) \
+ {(name), mp_property_alias, &m_option_type_dummy, 0, 0, 0, (real_property)}
+
/// All properties available in MPlayer.
/** \ingroup Properties
*/
@@ -1341,6 +1376,7 @@ static const m_option_t mp_properties[] = {
M_OPT_RANGE, 0, 100, NULL },
{ "time-pos", mp_property_time_pos, CONF_TYPE_TIME,
M_OPT_MIN, 0, 0, NULL },
+ { "time-remaining", mp_property_remaining, CONF_TYPE_TIME },
{ "chapter", mp_property_chapter, CONF_TYPE_INT,
M_OPT_MIN, 0, 0, NULL },
M_OPTION_PROPERTY_CUSTOM("edition", mp_property_edition),
@@ -1349,12 +1385,10 @@ static const m_option_t mp_properties[] = {
{ "chapters", mp_property_chapters, CONF_TYPE_INT,
0, 0, 0, NULL },
{ "editions", mp_property_editions, CONF_TYPE_INT },
- { "angle", mp_property_angle, CONF_TYPE_INT,
- CONF_RANGE, -2, 10, NULL },
+ { "angle", mp_property_angle, &m_option_type_dummy },
{ "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
0, 0, 0, NULL },
- { "pause", mp_property_pause, CONF_TYPE_FLAG,
- M_OPT_RANGE, 0, 1, NULL },
+ M_OPTION_PROPERTY_CUSTOM("pause", mp_property_pause),
{ "cache", mp_property_cache, CONF_TYPE_INT },
M_OPTION_PROPERTY("pts-association-mode"),
M_OPTION_PROPERTY("hr-seek"),
@@ -1375,8 +1409,7 @@ static const m_option_t mp_properties[] = {
0, 0, 0, NULL },
{ "channels", mp_property_channels, CONF_TYPE_INT,
0, 0, 0, NULL },
- { "audio", mp_property_audio, CONF_TYPE_INT,
- CONF_RANGE, -2, 65535, NULL },
+ M_OPTION_PROPERTY_CUSTOM("aid", mp_property_audio),
{ "balance", mp_property_balance, CONF_TYPE_FLOAT,
M_OPT_RANGE, -1, 1, NULL },
@@ -1419,14 +1452,12 @@ static const m_option_t mp_properties[] = {
0, 0, 0, NULL },
{ "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
CONF_RANGE, 0, 10, NULL },
- { "video", mp_property_video, CONF_TYPE_INT,
- CONF_RANGE, -2, 65535, NULL },
+ M_OPTION_PROPERTY_CUSTOM("vid", mp_property_video),
{ "program", mp_property_program, CONF_TYPE_INT,
CONF_RANGE, -1, 65535, NULL },
// Subs
- { "sub", mp_property_sub, CONF_TYPE_INT,
- M_OPT_MIN, -1, 0, NULL },
+ M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub),
M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay),
M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos),
{ "sub-visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
@@ -1450,6 +1481,10 @@ static const m_option_t mp_properties[] = {
M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
#endif
+ M_PROPERTY_ALIAS("video", "vid"),
+ M_PROPERTY_ALIAS("audio", "aid"),
+ M_PROPERTY_ALIAS("sub", "sid"),
+
{0},
};
@@ -1820,7 +1855,11 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
}
case MP_CMD_FRAME_STEP:
- add_step_frame(mpctx);
+ add_step_frame(mpctx, 1);
+ break;
+
+ case MP_CMD_FRAME_BACK_STEP:
+ add_step_frame(mpctx, -1);
break;
case MP_CMD_QUIT:
@@ -1828,6 +1867,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
mpctx->quit_player_rc = cmd->args[0].v.i;
break;
+ case MP_CMD_QUIT_WATCH_LATER:
+ mp_write_watch_later_conf(mpctx);
+ mpctx->stop_play = PT_QUIT;
+ mpctx->quit_player_rc = 0;
+ break;
+
case MP_CMD_PLAYLIST_NEXT:
case MP_CMD_PLAYLIST_PREV:
{
@@ -2287,7 +2332,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
pause_player(mpctx);
break;
case 3: // "pausing_toggle"
- if (mpctx->paused)
+ if (opts->pause)
unpause_player(mpctx);
else
pause_player(mpctx);
diff --git a/core/defaultopts.c b/core/defaultopts.c
index f1778f2fff..3ffc5e2555 100644
--- a/core/defaultopts.c
+++ b/core/defaultopts.c
@@ -53,6 +53,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.ordered_chapters = 1,
.chapter_merge_threshold = 100,
.load_config = 1,
+ .position_resume = 1,
.stream_cache_min_percent = 20.0,
.stream_cache_seek_min_percent = 50.0,
.stream_cache_pause = 10.0,
@@ -86,6 +87,8 @@ void set_default_mplayer_options(struct MPOpts *opts)
.ass_style_override = 1,
.use_embedded_fonts = 1,
+ .hwdec_codecs = "all",
+
.lavc_param = {
.workaround_bugs = 1, // autodetect
.error_concealment = 3,
@@ -96,8 +99,8 @@ void set_default_mplayer_options(struct MPOpts *opts)
},
.input = {
.key_fifo_size = 7,
- .ar_delay = 100,
- .ar_rate = 8,
+ .ar_delay = 200,
+ .ar_rate = 40,
.use_joystick = 1,
.use_lirc = 1,
.use_lircc = 1,
diff --git a/core/encode_lavc.c b/core/encode_lavc.c
index 9bb1abcdcd..457f6ab987 100644
--- a/core/encode_lavc.c
+++ b/core/encode_lavc.c
@@ -117,6 +117,11 @@ struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options)
{
struct encode_lavc_context *ctx;
+ if (options->file && (
+ !strcmp(options->file, "pipe:") ||
+ !strcmp(options->file, "pipe:1")))
+ mp_msg_stdout_in_use = 1;
+
ctx = talloc_zero(NULL, struct encode_lavc_context);
encode_lavc_discontinuity(ctx);
ctx->options = options;
diff --git a/core/input/input.c b/core/input/input.c
index 1790834a53..7ba6b64cdc 100644
--- a/core/input/input.c
+++ b/core/input/input.c
@@ -61,7 +61,7 @@
#include <lirc/lircc.h>
#endif
-#define MP_MAX_KEY_DOWN 32
+#define MP_MAX_KEY_DOWN 4
struct cmd_bind {
int input[MP_MAX_KEY_DOWN + 1];
@@ -126,8 +126,10 @@ static const mp_cmd_t mp_cmds[] = {
}},
{ MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } },
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
+ { MP_CMD_QUIT_WATCH_LATER, "quit_watch_later", },
{ MP_CMD_STOP, "stop", },
{ MP_CMD_FRAME_STEP, "frame_step", },
+ { MP_CMD_FRAME_BACK_STEP, "frame_back_step", },
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", {
OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
{"force", 1}, {"1", 1})),
@@ -1142,11 +1144,9 @@ static struct cmd_bind *section_find_bind_for_key(struct input_ctx *ictx,
return bs->cmd_binds ? find_bind_for_key(bs->cmd_binds, n, keys) : NULL;
}
-static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
+static struct cmd_bind *find_any_bind_for_key(struct input_ctx *ictx,
+ int n, int *keys)
{
- if (ictx->test)
- return handle_test(ictx, n, keys);
-
struct cmd_bind *cmd
= section_find_bind_for_key(ictx, false, ictx->section, n, keys);
if (ictx->default_bindings && cmd == NULL)
@@ -1157,6 +1157,20 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
if (ictx->default_bindings && cmd == NULL)
cmd = section_find_bind_for_key(ictx, true, "default", n, keys);
}
+ return cmd;
+}
+
+static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
+{
+ if (ictx->test)
+ return handle_test(ictx, n, keys);
+
+ struct cmd_bind *cmd = find_any_bind_for_key(ictx, n, keys);
+ if (cmd == NULL && n > 1) {
+ // Hitting two keys at once, and if there's no binding for this
+ // combination, the key hit last should be checked.
+ cmd = find_any_bind_for_key(ictx, 1, (int[]){keys[n - 1]});
+ }
if (cmd == NULL) {
char *key_buf = get_key_combo_name(keys, n);
@@ -1186,7 +1200,7 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
* we want to have "a" and "A" instead of "a" and "Shift+A"; but a separate
* shift modifier is still kept for special keys like arrow keys.
*/
- int unmod = code & ~MP_KEY_MODIFIER_MASK;
+ int unmod = code & ~(MP_KEY_MODIFIER_MASK | MP_KEY_STATE_DOWN);
if (unmod >= 32 && unmod < MP_KEY_BASE)
code &= ~MP_KEY_MODIFIER_SHIFT;
@@ -1208,7 +1222,10 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
ictx->num_key_down++;
ictx->last_key_down = GetTimer();
ictx->ar_state = 0;
- return NULL;
+ ret = NULL;
+ if (!(code & MP_NO_REPEAT_KEY))
+ ret = get_cmd_from_keys(ictx, ictx->num_key_down, ictx->key_down);
+ return ret;
}
// button released or press of key with no separate down/up events
for (j = 0; j < ictx->num_key_down; j++) {
@@ -1224,6 +1241,7 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
j = ictx->num_key_down - 1;
ictx->key_down[j] = code;
}
+ bool emit_key = ictx->last_key_down && (code & MP_NO_REPEAT_KEY);
if (j == ictx->num_key_down) { // was not already down; add temporarily
if (ictx->num_key_down > MP_MAX_KEY_DOWN) {
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Too many key down events "
@@ -1232,12 +1250,12 @@ static mp_cmd_t *interpret_key(struct input_ctx *ictx, int code)
}
ictx->key_down[ictx->num_key_down] = code;
ictx->num_key_down++;
- ictx->last_key_down = 1;
+ emit_key = true;
}
// Interpret only maximal point of multibutton event
- ret = ictx->last_key_down ?
- get_cmd_from_keys(ictx, ictx->num_key_down, ictx->key_down)
- : NULL;
+ ret = NULL;
+ if (emit_key)
+ ret = get_cmd_from_keys(ictx, ictx->num_key_down, ictx->key_down);
if (doubleclick) {
ictx->key_down[j] = code - MP_MOUSE_BTN0_DBL + MP_MOUSE_BTN0;
return ret;
@@ -1260,6 +1278,8 @@ static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
if (ictx->ar_rate > 0 && ictx->ar_state >= 0 && ictx->num_key_down > 0
&& !(ictx->key_down[ictx->num_key_down - 1] & MP_NO_REPEAT_KEY)) {
unsigned int t = GetTimer();
+ if (ictx->last_ar + 2000000 < t)
+ ictx->last_ar = t;
// First time : wait delay
if (ictx->ar_state == 0
&& (t - ictx->last_key_down) >= ictx->ar_delay * 1000)
@@ -1272,12 +1292,12 @@ static mp_cmd_t *check_autorepeat(struct input_ctx *ictx)
return NULL;
}
ictx->ar_state = 1;
- ictx->last_ar = t;
+ ictx->last_ar = ictx->last_key_down + ictx->ar_delay * 1000;
return mp_cmd_clone(ictx->ar_cmd);
// Then send rate / sec event
} else if (ictx->ar_state == 1
&& (t - ictx->last_ar) >= 1000000 / ictx->ar_rate) {
- ictx->last_ar = t;
+ ictx->last_ar += 1000000 / ictx->ar_rate;
return mp_cmd_clone(ictx->ar_cmd);
}
}
@@ -1288,11 +1308,13 @@ void mp_input_feed_key(struct input_ctx *ictx, int code)
{
ictx->got_new_events = true;
if (code == MP_INPUT_RELEASE_ALL) {
+ mp_msg(MSGT_INPUT, MSGL_V, "input: release all\n");
memset(ictx->key_down, 0, sizeof(ictx->key_down));
ictx->num_key_down = 0;
ictx->last_key_down = 0;
return;
}
+ mp_msg(MSGT_INPUT, MSGL_V, "input: key code=%#x\n", code);
struct mp_cmd *cmd = interpret_key(ictx, code);
if (!cmd)
return;
@@ -1349,6 +1371,10 @@ static void read_key_fd(struct input_ctx *ictx, struct input_fd *key_fd)
*/
static void read_events(struct input_ctx *ictx, int time)
{
+ if (ictx->num_key_down) {
+ time = FFMIN(time, 1000 / ictx->ar_rate);
+ time = FFMIN(time, ictx->ar_delay);
+ }
ictx->got_new_events = false;
struct input_fd *key_fds = ictx->key_fds;
struct input_fd *cmd_fds = ictx->cmd_fds;
diff --git a/core/input/input.h b/core/input/input.h
index 30cc2ce9ed..367abedfca 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -28,6 +28,7 @@ enum mp_command_type {
MP_CMD_IGNORE,
MP_CMD_SEEK,
MP_CMD_QUIT,
+ MP_CMD_QUIT_WATCH_LATER,
MP_CMD_PLAYLIST_NEXT,
MP_CMD_PLAYLIST_PREV,
MP_CMD_OSD,
@@ -44,6 +45,7 @@ enum mp_command_type {
MP_CMD_TV_SET_FREQ,
MP_CMD_TV_SET_NORM,
MP_CMD_FRAME_STEP,
+ MP_CMD_FRAME_BACK_STEP,
MP_CMD_SPEED_MULT,
MP_CMD_RUN,
MP_CMD_SUB_ADD,
diff --git a/core/input/keycodes.h b/core/input/keycodes.h
index 14f7b9ee85..2e0e5fd33f 100644
--- a/core/input/keycodes.h
+++ b/core/input/keycodes.h
@@ -186,8 +186,12 @@
MP_KEY_MODIFIER_ALT | MP_KEY_MODIFIER_META)
// Use this when the key shouldn't be auto-repeated (like mouse buttons)
+// This is not a modifier, but is part of the keycode itself.
#define MP_NO_REPEAT_KEY (1<<28)
+// Flag for key events. Multiple down events are idempotent. Release keys by
+// sending the key code without this flag, or by sending MP_INPUT_RELEASE_ALL
+// as key code.
#define MP_KEY_STATE_DOWN (1<<29)
#endif /* MPLAYER_KEYCODES_H */
diff --git a/core/m_config.c b/core/m_config.c
index 65d60bdab9..3781a92b90 100644
--- a/core/m_config.c
+++ b/core/m_config.c
@@ -585,10 +585,7 @@ int m_config_option_requires_param(struct m_config *config, bstr name)
if (opt) {
if (bstr_endswith0(name, "-clr"))
return 0;
- if (((opt->flags & M_OPT_OPTIONAL_PARAM) ||
- (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM)))
- return 0;
- return 1;
+ return m_option_required_params(opt);
}
return M_OPT_UNKNOWN;
}
diff --git a/core/m_option.c b/core/m_option.c
index b1e0f50dd9..fcf8cc6a67 100644
--- a/core/m_option.c
+++ b/core/m_option.c
@@ -59,6 +59,14 @@ char *m_option_strerror(int code)
}
}
+int m_option_required_params(const m_option_t *opt)
+{
+ if (((opt->flags & M_OPT_OPTIONAL_PARAM) ||
+ (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM)))
+ return 0;
+ return 1;
+}
+
static const struct m_option *m_option_list_findb(const struct m_option *list,
struct bstr name)
{
@@ -1140,6 +1148,78 @@ const m_option_type_t m_option_type_print_func = {
#undef VAL
#define VAL(x) (*(char ***)(x))
+// Read s sub-option name, or a positional sub-opt value.
+// Return 0 on succes, M_OPT_ error code otherwise.
+// optname is for error reporting.
+static int read_subparam(bstr optname, bstr *str, bstr *out_subparam)
+{
+ bstr p = *str;
+ bstr subparam = {0};
+
+ if (bstr_eatstart0(&p, "\"")) {
+ int optlen = bstrcspn(p, "\"");
+ subparam = bstr_splice(p, 0, optlen);
+ p = bstr_cut(p, optlen);
+ if (!bstr_startswith0(p, "\"")) {
+ mp_msg(MSGT_CFGPARSER, MSGL_ERR,
+ "Terminating '\"' missing for '%.*s'\n",
+ BSTR_P(optname));
+ return M_OPT_INVALID;
+ }
+ p = bstr_cut(p, 1);
+ } else if (bstr_eatstart0(&p, "[")) {
+ if (!bstr_split_tok(p, "]", &subparam, &p)) {
+ mp_msg(MSGT_CFGPARSER, MSGL_ERR,
+ "Terminating ']' missing for '%.*s'\n",
+ BSTR_P(optname));
+ return M_OPT_INVALID;
+ }
+ } else if (bstr_eatstart0(&p, "%")) {
+ int optlen = bstrtoll(p, &p, 0);
+ if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
+ mp_msg(MSGT_CFGPARSER, MSGL_ERR,
+ "Invalid length %d for '%.*s'\n",
+ optlen, BSTR_P(optname));
+ return M_OPT_INVALID;
+ }
+ subparam = bstr_splice(p, 1, optlen + 1);
+ p = bstr_cut(p, optlen + 1);
+ } else {
+ // Skip until the next character that could possibly be a meta
+ // character in option parsing.
+ int optlen = bstrcspn(p, ":=,\\%\"'[]");
+ subparam = bstr_splice(p, 0, optlen);
+ p = bstr_cut(p, optlen);
+ }
+
+ *str = p;
+ *out_subparam = subparam;
+ return 0;
+}
+
+// Return 0 on success, otherwise error code
+// On success, set *out_name and *out_val, and advance *str
+// out_val.start is NULL if there was no parameter.
+// optname is for error reporting.
+static int split_subconf(bstr optname, bstr *str, bstr *out_name, bstr *out_val)
+{
+ bstr p = *str;
+ bstr subparam = {0};
+ bstr subopt;
+ int r = read_subparam(optname, &p, &subopt);
+ if (r < 0)
+ return r;
+ if (bstr_eatstart0(&p, "=")) {
+ r = read_subparam(subopt, &p, &subparam);
+ if (r < 0)
+ return r;
+ }
+ *str = p;
+ *out_name = subopt;
+ *out_val = subparam;
+ return 0;
+}
+
static int parse_subconf(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst)
{
@@ -1152,41 +1232,10 @@ static int parse_subconf(const m_option_t *opt, struct bstr name,
struct bstr p = param;
while (p.len) {
- int optlen = bstrcspn(p, ":=");
- struct bstr subopt = bstr_splice(p, 0, optlen);
- struct bstr subparam = bstr0(NULL);
- p = bstr_cut(p, optlen);
- if (bstr_startswith0(p, "=")) {
- p = bstr_cut(p, 1);
- if (bstr_startswith0(p, "\"")) {