diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/av_common.c | 16 | ||||
-rw-r--r-- | core/av_log.c | 7 | ||||
-rw-r--r-- | core/bstr.c | 9 | ||||
-rw-r--r-- | core/bstr.h | 7 | ||||
-rw-r--r-- | core/cfg-mplayer.h | 15 | ||||
-rw-r--r-- | core/command.c | 105 | ||||
-rw-r--r-- | core/defaultopts.c | 7 | ||||
-rw-r--r-- | core/encode_lavc.c | 5 | ||||
-rw-r--r-- | core/input/input.c | 52 | ||||
-rw-r--r-- | core/input/input.h | 2 | ||||
-rw-r--r-- | core/input/keycodes.h | 4 | ||||
-rw-r--r-- | core/m_config.c | 5 | ||||
-rw-r--r-- | core/m_option.c | 426 | ||||
-rw-r--r-- | core/m_option.h | 19 | ||||
-rw-r--r-- | core/mp_core.h | 21 | ||||
-rw-r--r-- | core/mp_msg.c | 5 | ||||
-rw-r--r-- | core/mp_msg.h | 2 | ||||
-rw-r--r-- | core/mplayer.c | 494 | ||||
-rw-r--r-- | core/options.h | 8 | ||||
-rw-r--r-- | core/parser-mpcmd.c | 21 | ||||
-rw-r--r-- | core/path.c | 14 | ||||
-rw-r--r-- | core/path.h | 2 | ||||
-rw-r--r-- | core/timeline/tl_edl.c | 2 | ||||
-rw-r--r-- | core/timeline/tl_matroska.c | 123 |
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, "\" |