diff options
author | 2010-12-20 19:17:43 +0200 | |
---|---|---|
committer | 2010-12-20 19:17:43 +0200 | |
commit | 0afb326035e66663a90c4609f21560ce772e9718 (patch) | |
tree | 07f443e2bd39745ac7430cff9534529958be9c53 | |
parent | 4a26b4c024498c9b1be4723121d86e0c2b386ed2 (diff) | |
parent | 5bb2f9787f557bd91d5eb9021238ed7b131d5fa9 (diff) |
Merge branch 'hr-seek'
* hr-seek:
input: add default keybindings Shift+[arrow] for small exact seeks
input: support bindings with modifier keys for X input
core: audio: make ogg missing audio timing workaround more complex
core: add support for precise non-keyframe-limited seeks
core: add struct for queued seek info
commands: add generic option -> property wrapper
options: add "choice" option type, use for -pts-association-mode
core: remove looping in update_video(), modify command handling a bit
core: seek: use accurate seek mode with audio-only files
core: avoid using sh_video->pts as "current pts"
libvo: register X11 connection fd in input event system
core: timing: add special handling of long frame intervals
core: move central play loop to a separate function
Conflicts:
DOCS/tech/slave.txt
-rw-r--r-- | DOCS/man/en/mplayer.1 | 34 | ||||
-rw-r--r-- | DOCS/tech/slave.txt | 15 | ||||
-rw-r--r-- | cfg-mplayer.h | 5 | ||||
-rw-r--r-- | command.c | 91 | ||||
-rw-r--r-- | etc/input.conf | 9 | ||||
-rw-r--r-- | input/input.c | 143 | ||||
-rw-r--r-- | libvo/video_out.c | 24 | ||||
-rw-r--r-- | libvo/video_out.h | 2 | ||||
-rw-r--r-- | libvo/vo_xv.c | 16 | ||||
-rw-r--r-- | libvo/x11_common.c | 22 | ||||
-rw-r--r-- | libvo/x11_common.h | 2 | ||||
-rw-r--r-- | m_config.c | 4 | ||||
-rw-r--r-- | m_option.c | 44 | ||||
-rw-r--r-- | m_option.h | 14 | ||||
-rw-r--r-- | mp_core.h | 22 | ||||
-rw-r--r-- | mplayer.c | 829 | ||||
-rw-r--r-- | options.h | 1 | ||||
-rw-r--r-- | osdep/keycodes.h | 6 |
18 files changed, 801 insertions, 482 deletions
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index be9d8cf3e1..01e48d33d9 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -206,8 +206,12 @@ See the \-input option for ways to customize it. .RS .IPs "<\- and \->" Seek backward/\:forward 10 seconds. +Shift+arrow does a 1 second exact seek (see \-hr\-seek; currently modifier +keys like shift only work if used in an X output window). .IPs "up and down" Seek forward/\:backward 1 minute. +Shift+arrow does a 5 second exact seek (see \-hr\-seek; currently modifier +keys like shift only work if used in an X output window). .IPs "pgup and pgdown" Seek forward/\:backward 10 minutes. .IPs "[ and ]" @@ -909,6 +913,27 @@ mplayer \-heartbeat\-cmd "gnome\-screensaver\-command \-p" file .PD 1 . .TP +.B \-hr\-seek off|absolute|always +Select when to use precise seeks that are not limited to keyframes. +Such seeks require decoding video from the previous keyframe up to the target +position and so can take some time depending on decoding performance. +For some video formats precise seeks are disabled. This option selects the +default choice to use for seeks; it's possible to explicitly override that +default in the definition of key bindings and in slave mode commands. +.PD 0 +.RSs +.IPs off +Never use precise seeks. +.IPs absolute +Use precise seeks if the seek is to an absolute position in the file, +such as a chapter seek, but not for relative seeks like the default +behavior of arrow keys (default). +.IPs always +Use precise seeks whenever possible. +.RE +.PD 1 +. +.TP .B \-identify Shorthand for \-msglevel identify=4. Show file parameters in an easily parseable format. @@ -1076,16 +1101,17 @@ MPlayer will not load or search for video segments from other files, and will also ignore any chapter order specified for the main file. . .TP -.B \-pts\-association\-mode <mode number> +.B \-pts\-association\-mode auto|decode|sort Select the method used to determine which container packet timestamp corresponds to a particular output frame from the video decoder. +Normally you shouldn't need to change this option. .PD 0 .RSs -.IPs 0 +.IPs auto Try to pick a working mode from the ones below automatically (default) -.IPs 1 +.IPs decoder Use decoder reordering functionality. -.IPs 2 +.IPs sort Maintain a buffer of unused pts values and use the lowest value for the frame. .RE .PD 1 diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index 1dc9e23aa4..d95ef41d84 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -269,11 +269,16 @@ radio_step_channel <-1|1> radio_step_freq <value> Tune frequency by the <value> (positive - up, negative - down). -seek <value> [type] +seek <value> [type] [hr-seek] Seek to some place in the movie. - 0 is a relative seek of +/- <value> seconds (default). - 1 is a seek to <value> % in the movie. - 2 is a seek to an absolute position of <value> seconds. + type = 0 is a relative seek of +/- <value> seconds (default). + type = 1 is a seek to <value> % in the movie. + type = 2 is a seek to an absolute position of <value> seconds. + The hr-seek parameter controls whether to use precise seeks (not limited + to keyframe positions in video). + hr-seek = 0 means use default set with option -hr-seek (default). + hr-seek = 1 means force precise seek if possible. + hr-seek = -1 means force non-precise seek. seek_chapter <value> [type] Seek to the start of a chapter. @@ -515,6 +520,8 @@ name type min max get set step comment osdlevel int 0 3 X X X as -osdlevel speed float 0.01 100 X X X as -speed loop int -1 X X X as -loop +hr_seek string X X X as -hr-seek +pts_association_mode string X X X as -pts-association-mode pause flag 0 1 X 1 if paused filename string X file playing wo path path string X file playing diff --git a/cfg-mplayer.h b/cfg-mplayer.h index be52df881a..7f0ee1f729 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -309,8 +309,11 @@ const m_option_t mplayer_opts[]={ // a-v sync stuff: OPT_MAKE_FLAGS("correct-pts", user_correct_pts, 0), - OPT_INTRANGE("pts-association-mode", user_pts_assoc_mode, 0, 0, 2), + OPT_CHOICE("pts-association-mode", user_pts_assoc_mode, 0, + ({"auto", 0}, {"decoder", 1}, {"sort", 2})), OPT_MAKE_FLAGS("initial-audio-sync", initial_audio_sync, 0), + OPT_CHOICE("hr-seek", hr_seek, 0, + ({"off", -1}, {"absolute", 0}, {"always", 1}, {"on", 1})), OPT_FLAG_CONSTANTS("noautosync", autosync, 0, 0, -1), OPT_INTRANGE("autosync", autosync, 0, 0, 10000), @@ -34,6 +34,7 @@ #include "libvo/sub.h" #include "m_option.h" #include "m_property.h" +#include "m_config.h" #include "metadata.h" #include "libmpcodecs/vf.h" #include "libmpcodecs/vd.h" @@ -226,6 +227,39 @@ static void log_sub(struct MPContext *mpctx) /// \ingroup Properties ///@{ +static int mp_property_generic_option(struct m_option *prop, int action, + void *arg, MPContext *mpctx) +{ + char *optname = prop->priv; + const struct m_option *opt = m_config_get_option(mpctx->mconfig, optname); + void *valptr = m_option_get_ptr(opt, &mpctx->opts); + + switch (action) { + case M_PROPERTY_GET_TYPE: + *(const struct m_option **)arg = opt; + return M_PROPERTY_OK; + case M_PROPERTY_GET: + m_option_copy(opt, arg, valptr); + return M_PROPERTY_OK; + case M_PROPERTY_SET: + m_option_copy(opt, valptr, arg); + return M_PROPERTY_OK; + case M_PROPERTY_STEP_UP: + if (opt->type == &m_option_type_choice) { + int v = *(int *) valptr; + int best = v; + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1) + best = alt->value; + *(int *) valptr = best; + return M_PROPERTY_OK; + } + break; + } + return M_PROPERTY_NOT_IMPLEMENTED; +} + /// OSD level (RW) static int mp_property_osdlevel(m_option_t *prop, int action, void *arg, MPContext *mpctx) @@ -418,8 +452,7 @@ static int mp_property_percent_pos(m_option_t *prop, int action, return m_property_int_ro(prop, action, arg, get_percent_pos(mpctx)); } - mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR; - mpctx->rel_seek_secs = pos / 100.0; + queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0); return M_PROPERTY_OK; } @@ -433,13 +466,12 @@ static int mp_property_time_pos(m_option_t *prop, int action, case M_PROPERTY_SET: if(!arg) return M_PROPERTY_ERROR; M_PROPERTY_CLAMP(prop, *(double*)arg); - mpctx->abs_seek_pos = SEEK_ABSOLUTE; - mpctx->rel_seek_secs = *(double*)arg; + queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double*)arg, 0); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - mpctx->rel_seek_secs += (arg ? *(double*)arg : 10.0) * - (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0); + queue_seek(mpctx, MPSEEK_RELATIVE, (arg ? *(double*)arg : 10.0) * + (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0), 0); return M_PROPERTY_OK; } return m_property_time_ro(prop, action, arg, get_current_time(mpctx)); @@ -495,20 +527,16 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg, } double next_pts = 0; + queue_seek(mpctx, MPSEEK_NONE, 0, 0); chapter = seek_chapter(mpctx, chapter, &next_pts, &chapter_name); - mpctx->rel_seek_secs = 0; - mpctx->abs_seek_pos = 0; if (chapter >= 0) { - if (next_pts > -1.0) { - mpctx->abs_seek_pos = SEEK_ABSOLUTE; - mpctx->rel_seek_secs = next_pts; - } + if (next_pts > -1.0) + queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0); if (chapter_name) set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration, "Chapter: (%d) %s", chapter + 1, chapter_name); - } - else if (step_all > 0) - mpctx->rel_seek_secs = 1000000000.; + } else if (step_all > 0) + queue_seek(mpctx, MPSEEK_RELATIVE, 1000000000, 0); else set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration, "Chapter: (%d) %s", 0, mp_gtext("unknown")); @@ -2184,6 +2212,10 @@ static const m_option_t mp_properties[] = { M_OPT_RANGE, 0, 1, NULL }, { "capturing", mp_property_capture, CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL }, + { "pts_association_mode", mp_property_generic_option, &m_option_type_choice, + 0, 0, 0, "pts-association-mode" }, + { "hr_seek", mp_property_generic_option, &m_option_type_choice, + 0, 0, 0, "hr-seek" }, // Audio { "volume", mp_property_volume, CONF_TYPE_FLOAT, @@ -2357,6 +2389,8 @@ static struct property_osd_display { { "loop", 0, -1, _("Loop: %s") }, { "chapter", -1, -1, NULL }, { "capturing", 0, -1, _("Capturing: %s") }, + { "pts_association_mode", 0, -1, "PTS association mode: %s" }, + { "hr_seek", 0, -1, "hr-seek: %s" }, // audio { "volume", OSD_VOLUME, -1, _("Volume") }, { "mute", 0, -1, _("Mute: %s") }, @@ -2679,24 +2713,19 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (!set_property_command(mpctx, cmd)) switch (cmd->id) { case MP_CMD_SEEK:{ - float v; - int abs; mpctx->add_osd_seek_info = true; - v = cmd->args[0].v.f; - abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; + float v = cmd->args[0].v.f; + int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; + int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0; if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */ - mpctx->abs_seek_pos = SEEK_ABSOLUTE; - if (sh_video) - mpctx->osd_function = - (v > sh_video->pts) ? OSD_FFW : OSD_REW; - mpctx->rel_seek_secs = v; + queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact); + mpctx->osd_function = v > get_current_time(mpctx) ? + OSD_FFW : OSD_REW; } else if (abs) { /* Absolute seek by percentage */ - mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR; - if (sh_video) - mpctx->osd_function = OSD_FFW; // Direction isn't set correctly - mpctx->rel_seek_secs = v / 100.0; + queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact); + mpctx->osd_function = OSD_FFW; // Direction isn't set correctly } else { - mpctx->rel_seek_secs += v; + queue_seek(mpctx, MPSEEK_RELATIVE, v, exact); mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW; } } @@ -2902,12 +2931,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) case MP_CMD_SUB_STEP: if (sh_video) { int movement = cmd->args[0].v.i; - step_sub(subdata, sh_video->pts, movement); + step_sub(subdata, mpctx->video_pts, movement); #ifdef CONFIG_ASS if (ass_track) sub_delay += ass_step_sub(ass_track, - (sh_video->pts + + (mpctx->video_pts + sub_delay) * 1000 + .5, movement) / 1000.; #endif set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration, diff --git a/etc/input.conf b/etc/input.conf index 0bcfdec9f7..74373d7ffe 100644 --- a/etc/input.conf +++ b/etc/input.conf @@ -10,11 +10,20 @@ ## If you wish to unbind a key, use key ignore. ## e.g. ENTER ignore ## +## You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with +## modifiers Shift, Ctrl, Alt and Meta, but note that currently reading +## key combinations is only supported through the video windows of X-based +## output drivers (not in output windows of other drivers or in a terminal). RIGHT seek +10 LEFT seek -10 DOWN seek -60 UP seek +60 +# Do smaller, always exact (non-keyframe-limited), seeks with shift. +Shift+Right seek +1 0 1 +Shift+Left seek -1 0 1 +Shift+Down seek -5 0 1 +Shift+Up seek +5 0 1 PGUP seek 600 PGDWN seek -600 m mute diff --git a/input/input.c b/input/input.c index ab534968e3..dd4a27d63c 100644 --- a/input/input.c +++ b/input/input.c @@ -45,6 +45,7 @@ #include "path.h" #include "talloc.h" #include "options.h" +#include "bstr.h" #include "joystick.h" @@ -86,7 +87,7 @@ static const mp_cmd_t mp_cmds[] = { { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } }, { MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } }, #endif - { MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, + { MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, { MP_CMD_EDL_MARK, "edl_mark", 0, { {-1,{0}} } }, { MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, { MP_CMD_SPEED_INCR, "speed_incr", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } }, @@ -361,6 +362,16 @@ static const mp_key_name_t key_names[] = { { 0, NULL } }; +struct mp_key_name modifier_names[] = { + { KEY_MODIFIER_SHIFT, "Shift" }, + { KEY_MODIFIER_CTRL, "Ctrl" }, + { KEY_MODIFIER_ALT, "Alt" }, + { KEY_MODIFIER_META, "Meta" }, + { 0 } +}; + +#define KEY_MODIFIER_MASK (KEY_MODIFIER_SHIFT | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT | KEY_MODIFIER_META) + // This is the default binding. The content of input.conf overrides these. // The first arg is a null terminated array of key codes. // The second is the command @@ -385,8 +396,12 @@ static const mp_cmd_bind_t def_cmd_binds[] = { { { KEY_RIGHT, 0 }, "seek 10" }, { { KEY_LEFT, 0 }, "seek -10" }, + { { KEY_MODIFIER_SHIFT + KEY_RIGHT, 0 }, "seek 1 0 1" }, + { { KEY_MODIFIER_SHIFT + KEY_LEFT, 0 }, "seek -1 0 1" }, { { KEY_UP, 0 }, "seek 60" }, { { KEY_DOWN, 0 }, "seek -60" }, + { { KEY_MODIFIER_SHIFT + KEY_UP, 0 }, "seek 5 0 1" }, + { { KEY_MODIFIER_SHIFT + KEY_DOWN, 0 }, "seek -5 0 1" }, { { KEY_PAGE_UP, 0 }, "seek 600" }, { { KEY_PAGE_DOWN, 0 }, "seek -600" }, { { '+', 0 }, "audio_delay 0.100" }, @@ -621,8 +636,27 @@ static const m_option_t mp_input_opts[] = { static int default_cmd_func(int fd,char* buf, int l); -static char *get_key_name(int key, char buffer[12]); +static char *get_key_name(int key) +{ + char *ret = talloc_strdup(NULL, ""); + for (int i = 0; modifier_names[i].name; i++) { + if (modifier_names[i].key & key) { + ret = talloc_asprintf_append_buffer(ret, "%s+", + modifier_names[i].name); + key -= modifier_names[i].key; + } + } + for (int i = 0; key_names[i].name != NULL; i++) { + if (key_names[i].key == key) + return talloc_asprintf_append_buffer(ret, "%s", key_names[i].name); + } + + if (isascii(key)) + return talloc_asprintf_append_buffer(ret, "%c", key); + // Print the hex key code + return talloc_asprintf_append_buffer(ret, "%#-8x", key); +} int mp_input_add_cmd_fd(struct input_ctx *ictx, int fd, int select, mp_cmd_func_t read_func, mp_close_func_t close_func) @@ -1035,7 +1069,6 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys) { char* cmd = NULL; mp_cmd_t* ret; - char key_buf[12]; if (ictx->cmd_binds) cmd = find_bind_for_key(ictx->cmd_binds, n, keys); @@ -1045,12 +1078,16 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys) cmd = find_bind_for_key(def_cmd_binds,n,keys); if(cmd == NULL) { - mp_tmsg(MSGT_INPUT,MSGL_WARN,"No bind found for key '%s'.", get_key_name(keys[0], - key_buf)); + char *key_buf = get_key_name(keys[0]); + mp_tmsg(MSGT_INPUT,MSGL_WARN,"No bind found for key '%s'.", key_buf); + talloc_free(key_buf); if(n > 1) { int s; - for(s=1; s < n; s++) - mp_msg(MSGT_INPUT,MSGL_WARN,"-%s", get_key_name(keys[s], key_buf)); + for(s=1; s < n; s++) { + key_buf = get_key_name(keys[s]); + mp_msg(MSGT_INPUT,MSGL_WARN,"-%s", key_buf); + talloc_free(key_buf); + } } mp_msg(MSGT_INPUT,MSGL_WARN," \n"); return NULL; @@ -1058,13 +1095,16 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys) if (strcmp(cmd, "ignore") == 0) return NULL; ret = mp_input_parse_cmd(cmd); if(!ret) { - mp_tmsg(MSGT_INPUT,MSGL_ERR,"Invalid command for bound key %s", - get_key_name(ictx->key_down[0], key_buf)); + char *key_buf = get_key_name(ictx->key_down[0]); + mp_tmsg(MSGT_INPUT,MSGL_ERR,"Invalid command for bound key %s", key_buf); + talloc_free(key_buf); if (ictx->num_key_down > 1) { unsigned int s; - for(s=1; s < ictx->num_key_down; s++) - mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", get_key_name(ictx->key_down[s], - key_buf)); + for(s=1; s < ictx->num_key_down; s++) { + char *key_buf = get_key_name(ictx->key_down[s]); + mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", key_buf); + talloc_free(key_buf); + } } mp_msg(MSGT_INPUT,MSGL_ERR," : %s \n",cmd); } @@ -1077,6 +1117,14 @@ static mp_cmd_t* interpret_key(struct input_ctx *ictx, int code) unsigned int j; mp_cmd_t* ret; + /* On normal keyboards shift changes the character code of non-special + * keys, so don't count the modifier separately for those. In other words + * 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. + */ + if ((code & ~KEY_MODIFIER_MASK) < 256) + code &= ~KEY_MODIFIER_SHIFT; + if(mp_input_key_cb) { if (code & MP_KEY_DOWN) return NULL; @@ -1372,41 +1420,34 @@ mp_cmd_clone(mp_cmd_t* cmd) { return ret; } -static char *get_key_name(int key, char buffer[12]) +int mp_input_get_key_from_name(const char *name) { - int i; - - for(i = 0; key_names[i].name != NULL; i++) { - if(key_names[i].key == key) - return key_names[i].name; - } - - if(isascii(key)) { - snprintf(buffer, 12, "%c",(char)key); - return buffer; - } - - // Print the hex key code - snprintf(buffer, 12, "%#-8x",key); - return buffer; - -} - -int -mp_input_get_key_from_name(const char *name) { - int i,ret = 0,len = strlen(name); - if(len == 1) { // Direct key code - ret = (unsigned char)name[0]; - return ret; - } else if(len > 2 && strncasecmp("0x",name,2) == 0) - return strtol(name,NULL,16); - - for(i = 0; key_names[i].name != NULL; i++) { - if(strcasecmp(key_names[i].name,name) == 0) - return key_names[i].key; - } + int modifiers = 0; + const char *p; + while (p = strchr(name, '+')) { + for (struct mp_key_name *m = modifier_names; m->name; m++) + if (!bstrcasecmp(BSTR(m->name), (struct bstr){name, p - name})) { + modifiers |= m->key; + goto found; + } + if (!strcmp(name, "+")) + return '+' + modifiers; + return -1; + found: + name = p + 1; + } + int len = strlen(name); + if (len == 1) // Direct key code + return (unsigned char)name[0] + modifiers; + else if (len > 2 && strncasecmp("0x", name, 2) == 0) + return strtol(name, NULL, 16) + modifiers; + + for (int i = 0; key_names[i].name != NULL; i++) { + if (strcasecmp(key_names[i].name, name) == 0) + return key_names[i].key + modifiers; + } - return -1; + return -1; } static int get_input_from_name(char* name,int* keys) { @@ -1589,10 +1630,14 @@ static int parse_config(struct input_ctx *ictx, char *file) // Found new line if(iter[0] == '\n' || iter[0] == '\r') { int i; - char key_buf[12]; - mp_tmsg(MSGT_INPUT,MSGL_ERR,"No command found for key %s", get_key_name(keys[0], key_buf)); - for(i = 1; keys[i] != 0 ; i++) - mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", get_key_name(keys[i], key_buf)); + char *key_buf = get_key_name(keys[0]); + mp_tmsg(MSGT_INPUT,MSGL_ERR,"No command found for key %s", key_buf); + talloc_free(key_buf); + for(i = 1; keys[i] != 0 ; i++) { + char *key_buf = get_key_name(keys[i]); + mp_msg(MSGT_INPUT,MSGL_ERR,"-%s", key_buf); + talloc_free(key_buf); + } mp_msg(MSGT_INPUT,MSGL_ERR,"\n"); keys[0] = 0; if(iter > buffer) { diff --git a/libvo/video_out.c b/libvo/video_out.c index 763527a4c1..3e2e108d4b 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -35,6 +35,8 @@ #include "geometry.h" #include "old_vo_wrapper.h" #include "input/input.h" +#include "mp_fifo.h" + #include "mp_msg.h" @@ -352,8 +354,12 @@ void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration) void vo_check_events(struct vo *vo) { - if (!vo->config_ok) + if (!vo->config_ok) { + if (vo->registered_fd != -1) + mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd); + vo->registered_fd = -1; return; + } vo->driver->check_events(vo); } @@ -365,6 +371,8 @@ void vo_seek_reset(struct vo *vo) void vo_destroy(struct vo *vo) { + if (vo->registered_fd != -1) + mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd); vo->driver->uninit(vo); talloc_free(vo); } @@ -393,6 +401,8 @@ struct vo *init_best_video_out(struct MPOpts *opts, struct vo_x11_state *x11, .x11 = x11, .key_fifo = key_fifo, .input_ctx = input_ctx, + .event_fd = -1, + .registered_fd = -1, }; // first try the preferred drivers, with their optional subdevice param: if (vo_list && vo_list[0]) @@ -441,6 +451,13 @@ struct vo *init_best_video_out(struct MPOpts *opts, struct vo_x11_state *x11, return NULL; } +static int event_fd_callback(void *ctx, int fd) +{ + struct vo *vo = ctx; + vo_check_events(vo); + return mplayer_get_key(vo->key_fifo, 0); +} + int vo_config(struct vo *vo, uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format) @@ -467,6 +484,11 @@ int vo_config(struct vo *vo, uint32_t width, uint32_t height, title, format); vo->config_ok = (ret == 0); vo->config_count += vo->config_ok; + if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) { + mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback, + NULL, vo); + vo->registered_fd = vo->event_fd; + } return ret; } diff --git a/libvo/video_out.h b/libvo/video_out.h index 39ea26a53a..36a161aaf5 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -251,6 +251,8 @@ struct vo { struct vo_x11_state *x11; struct mp_fifo *key_fifo; struct input_ctx *input_ctx; + int event_fd; // check_events() should be called when this has input + int registered_fd; // set to event_fd when registered in input system // requested position/resolution int dx; diff --git a/libvo/vo_xv.c b/libvo/vo_xv.c index 0dbc600847..63221782e2 100644 --- a/libvo/vo_xv.c +++ b/libvo/vo_xv.c @@ -61,9 +61,6 @@ Buffer allocation: #include "subopt-helper.h" -#include "input/input.h" -#include "mp_fifo.h" - #include "libavutil/common.h" static const vo_info_t info = { @@ -104,7 +101,6 @@ struct xvctx { struct vo_rect src_rect; struct vo_rect dst_rect; uint32_t max_width, max_height; // zero means: not set - int event_fd_registered; // for uninit called from preinit int mode_switched; int osd_objects_drawn; void (*draw_alpha_fnc)(void *ctx, int x0, int y0, int w, int h, @@ -645,19 +641,10 @@ static void uninit(struct vo *vo) if (ctx->mode_switched) vo_vm_close(vo); #endif - if (ctx->event_fd_registered) - mp_input_rm_key_fd(vo->input_ctx, ConnectionNumber(vo->x11->display)); // uninit() shouldn't get called unless initialization went past vo_init() vo_x11_uninit(vo); } -static int x11_fd_callback(void *ctx, int fd) -{ - struct vo *vo = ctx; - check_events(vo); - return mplayer_get_key(vo->key_fifo, 0); -} - static int preinit(struct vo *vo, const char *arg) { XvPortID xv_p; @@ -780,9 +767,6 @@ static int preinit(struct vo *vo, const char *arg) ctx->fo = XvListImageFormats(x11->display, x11->xv_port, (int *) &ctx->formats); - mp_input_add_key_fd(vo->input_ctx, ConnectionNumber(x11->display), 1, - x11_fd_callback, NULL, vo); - ctx->event_fd_registered = 1; return 0; error: diff --git a/libvo/x11_common.c b/libvo/x11_common.c index 655b1c69d0..596645662c 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -540,12 +540,12 @@ static const struct mp_keymap keysym_map[] = { {0, 0} }; -static void vo_x11_putkey_ext(struct vo *vo, int keysym) +static void vo_x11_putkey_ext(struct vo *vo, int keysym, int modifiers) { struct mp_fifo *f = vo->key_fifo; int mpkey = lookup_keymap_table(keysym_map, keysym); if (mpkey) - mplayer_put_key(f, mpkey); + mplayer_put_key(f, mpkey + modifiers); } #endif @@ -584,7 +584,7 @@ static const struct mp_keymap keymap[] = { {0, 0} }; -void vo_x11_putkey(struct vo *vo, int key) +static void vo_x11_putkey(struct vo *vo, int key, int modifiers) { static const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]"; int mpkey = 0; @@ -598,7 +598,7 @@ void vo_x11_putkey(struct vo *vo, int key) mpkey = lookup_keymap_table(keymap, key); if (mpkey) - mplayer_put_key(vo->key_fifo, mpkey); + mplayer_put_key(vo->key_fifo, mpkey + modifiers); } @@ -814,13 +814,22 @@ int vo_x11_check_events(struct vo *vo) XLookupString(&Event.xkey, buf, sizeof(buf), &keySym, &x11->compose_status); + int modifiers = 0; + if (Event.xkey.state & ShiftMask) + modifiers |= KEY_MODIFIER_SHIFT; + if (Event.xkey.state & ControlMask) + modifiers |= KEY_MODIFIER_CTRL; + if (Event.xkey.state & Mod1Mask) + modifiers |= KEY_MODIFIER_ALT; + if (Event.xkey.state & Mod4Mask) + modifiers |= KEY_MODIFIER_META; #ifdef XF86XK_AudioPause - vo_x11_putkey_ext(vo, keySym); + vo_x11_putkey_ext(vo, keySym, modifiers); #endif key = ((keySym & 0xff00) != 0 ? ((keySym & 0x00ff) + 256) : (keySym)); - vo_x11_putkey(vo, key); + vo_x11_putkey(vo, key, modifiers); ret |= VO_EVENT_KEYPRESS; } break; @@ -1123,6 +1132,7 @@ final: x11->vo_gc = XCreateGC(mDisplay, x11->window, GCForeground, &xgcv); XSync(mDisplay, False); x11->vo_mouse_autohide = 1; + vo->event_fd = ConnectionNumber(x11->display); } void vo_x11_clearwindow_part(struct vo *vo, Window vo_window, diff --git a/libvo/x11_common.h b/libvo/x11_common.h index 57b1d79f33..824f55b4c8 100644 --- a/libvo/x11_common.h +++ b/libvo/x11_common.h @@ -170,8 +170,6 @@ void xv_setup_colorkeyhandling(struct vo *vo, const char *ck_method_str, const c int xv_test_ck( void * arg ); int xv_test_ckm( void * arg ); -void vo_x11_putkey(struct vo *vo, int key); - #ifdef CONFIG_XF86VM void vo_vm_switch(struct vo *vo); void vo_vm_close(struct vo *vo); diff --git a/m_config.c b/m_config.c index 266ee10323..2752ced85f 100644 --- a/m_config.c +++ b/m_config.c @@ -150,7 +150,7 @@ static void m_option_save(const m_config_t *config, const m_option_t *opt, void *dst) { if (opt->type->save) { - const void *src = opt->new ? (char*)config->optstruct + opt->offset : opt->p; + const void *src = m_option_get_ptr(opt, config->optstruct); opt->type->save(opt, dst, src); } } @@ -159,7 +159,7 @@ static void m_option_set(const m_config_t *config, const m_option_t *opt, const void *src) { if (opt->type->set) { - void *dst = opt->new ? (char*)config->optstruct + opt->offset : opt->p; + void *dst = m_option_get_ptr(opt, config->optstruct); opt->type->set(opt, dst, src); } } diff --git a/m_option.c b/m_option.c index 8563d21e9a..a0f01e6344 100644 --- a/m_option.c +++ b/m_option.c @@ -267,6 +267,50 @@ const struct m_option_type m_option_type_intpair = { .set = copy_opt, }; +static int parse_choice(const struct m_option *opt, const char *name, + const char *param, void *dst, int src) +{ + if (param == NULL) + return M_OPT_MISSING_PARAM; + + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if (!strcmp(param, alt->name)) + break; + if (!alt->name) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid value for option %s: %s\n", + name, param); + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Valid values are:"); + for (alt = opt->priv; alt->name; alt++) + mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", alt->name); + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n"); + return M_OPT_INVALID; + } + if (dst) + *(int *)dst = alt->value; + + return 1; +} + +static char *print_choice(const m_option_t *opt, const void *val) +{ + int v = *(int *)val; + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if (alt->value == v) + return strdup(alt->name); + abort(); +} + +const struct m_option_type m_option_type_choice = { + .name = "String", // same as arbitrary strings in option list for now + .size = sizeof(int), + .parse = parse_choice, + .print = print_choice, + .save = copy_opt, + .set = copy_opt, +}; + // Float #undef VAL diff --git a/m_option.h b/m_option.h index 26c457e24d..05d5751ceb 100644 --- a/m_option.h +++ b/m_option.h @@ -53,6 +53,7 @@ extern const m_option_type_t m_option_type_string_list; extern const m_option_type_t m_option_type_position; extern const m_option_type_t m_option_type_time; extern const m_option_type_t m_option_type_time_size; +extern const m_option_type_t m_option_type_choice; extern const m_option_type_t m_option_type_print; extern const m_option_type_t m_option_type_print_indirect; @@ -169,6 +170,11 @@ typedef struct { /// Ready made settings to parse a \ref m_span_t with a start-end syntax. extern const m_obj_params_t m_span_params_def; +struct m_opt_choice_alternatives { + char *name; + int value; +}; + // FIXME: backward compatibility #define CONF_TYPE_FLAG (&m_option_type_flag) @@ -487,6 +493,12 @@ struct m_option { */ const m_option_t* m_option_list_find(const m_option_t* list,const char* name); +static inline void *m_option_get_ptr(const struct m_option *opt, + void *optstruct) +{ + return opt->new ? (char *) optstruct + opt->offset : opt->p; +} + /// Helper to parse options, see \ref m_option_type::parse. inline static int m_option_parse(const m_option_t* opt,const char *name, const char *param, void* dst, int src) { @@ -543,5 +555,7 @@ int parse_timestring(const char *str, double *time, char endchar); #define OPT_STRING(optname, varname, flags) {optname, NULL, &m_option_type_string, flags, 0, 0, NULL, 1, offsetof(struct MPOpts, varname)} #define OPT_SETTINGSLIST(optname, varname, flags, objlist) {optname, NULL, &m_option_type_obj_settings_list, flags, 0, 0, objlist, 1, offsetof(struct MPOpts, varname)} #define OPT_AUDIOFORMAT(optname, varname, flags) {optname, NULL, &m_option_type_afmt, flags, 0, 0, NULL, 1, offsetof(struct MPOpts, varname)} +#define OPT_HELPER_REMOVEPAREN(...) __VA_ARGS__ +#define OPT_CHOICE(optname, varname, flags, choices) {optname, NULL, &m_option_type_choice, flags, 0, 0, &(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, 1, offsetof(struct MPOpts, varname)} #endif /* MPLAYER_M_OPTION_H */ @@ -129,6 +129,9 @@ typedef struct MPContext { * stream by cutting samples or adding silence at the beginning to make * audio playback position match video position. */ bool syncing_audio; + bool hrseek_active; + bool hrseek_framedrop; + double hrseek_pts; // AV sync: the next frame should be shown when the audio out has this // much (in seconds) buffered data left. Increased when more data is // written to the ao, decreased when moving to the next frame. @@ -148,6 +151,12 @@ typedef struct MPContext { // the same value if the status line is updated at a time where no new // video frame is shown. double last_av_difference; + /* timestamp of video frame currently visible on screen + * (or at least queued to be flipped by VO) */ + double video_pts; + + // used to prevent hanging in some error cases + unsigned int start_timestamp; // Timestamp from the last time some timing functions read the // current time, in (occasionally wrapping) microseconds. Used @@ -155,8 +164,15 @@ typedef struct MPContext { unsigned int last_time; // Used to communicate the parameters of a seek between parts - double rel_seek_secs; - int abs_seek_pos; + struct seek_params { + enum seek_type { + MPSEEK_NONE, MPSEEK_RELATIVE, MPSEEK_ABSOLUTE, MPSEEK_FACTOR + } type; + double amount; + int exact; // -1 = disable, 0 = default, 1 = enable + // currently not set by commands, only used internally by seek() + int direction; // -1 = backward, 0 = default, 1 = forward + } seek; /* Heuristic for relative chapter seeks: keep track which chapter * the user wanted to go to, even if we aren't exactly within the @@ -218,6 +234,8 @@ int reinit_video_chain(struct MPContext *mpctx); void pause_player(struct MPContext *mpctx); void unpause_player(struct MPContext *mpctx); void add_step_frame(struct MPContext *mpctx); +void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, + int exact); int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts, char **chapter_name); double get_time_length(struct MPContext *mpctx); @@ -1222,7 +1222,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) if (mpctx->sh_audio && a_pos == MP_NOPTS_VALUE) a_pos = playing_audio_pts(mpctx); if (mpctx->sh_audio && sh_video && at_frame) { - mpctx->last_av_difference = a_pos - sh_video->pts - audio_delay; + mpctx->last_av_difference = a_pos - mpctx->video_pts - audio_delay; if (mpctx->time_frame > 0) mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed; if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50 @@ -1265,7 +1265,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) // Video time if (sh_video) - saddf(line, &pos, width, "V:%6.1f ", sh_video->pts); + saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts); // A-V sync if (mpctx->sh_audio && sh_video) @@ -2080,15 +2080,28 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) return res; int bytes; + bool did_retry = false; while (1) { double written_pts = written_audio_pts(mpctx); - double ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay + double ptsdiff = written_pts - mpctx->video_pts - mpctx->delay - audio_delay; bytes = ptsdiff * ao_data.bps / mpctx->opts.playback_speed; bytes -= bytes % (ao_data.channels * af_fmt2bits(ao_data.format) / 8); - if (fabs(ptsdiff) > 300 // pts reset or just broken? - || written_pts <= 0) // ogg demuxers give packets without timing + // ogg demuxers give packets without timing + if (written_pts <= 1 && sh_audio->pts == MP_NOPTS_VALUE) { + if (!did_retry) { + // Try to read more data to see packets that have pts + int res = decode_audio(sh_audio, ao_data.bps); + if (res < 0) + return res; + did_retry = true; + continue; + } + bytes = 0; + } + + if (fabs(ptsdiff) > 300) // pts reset or just broken? bytes = 0; if (bytes > 0) @@ -2230,11 +2243,11 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) return 1; } -static int sleep_until_update(struct MPContext *mpctx, float *time_frame, - float *aq_sleep_time) +static int sleep_until_near_frame(struct MPContext *mpctx, float *time_frame, + float *aq_sleep_time) { struct MPOpts *opts = &mpctx->opts; - int frame_time_remaining = 0; + double audio_limit = 2; current_module="calc_sleep_time"; *time_frame -= get_relative_time(mpctx); // reset timer @@ -2259,17 +2272,11 @@ static int sleep_until_update(struct MPContext *mpctx, float *time_frame, } *time_frame = delay - mpctx->delay / opts->playback_speed; - *time_frame -= mpctx->video_out->flip_queue_offset; // delay = amount of audio buffered in soundcard/driver - if (delay > 0.25) delay=0.25; else - if (delay < 0.10) delay=0.10; - - if (*time_frame > delay*0.6) { - // sleep time too big - may cause audio drops (buffer underrun) - frame_time_remaining = 1; - *time_frame = delay*0.5; - } + delay = FFMIN(delay, 0.5); + delay = FFMAX(delay, 0.1); + audio_limit = delay; } else { // If we're lagging more than 200 ms behind the right playback rate, // don't try to "catch up". @@ -2277,19 +2284,19 @@ static int sleep_until_update(struct MPContext *mpctx, float *time_frame, // without sleeping. if (*time_frame < -0.2 || opts->benchmark) *time_frame = 0; - *time_frame -= mpctx->video_out->flip_queue_offset; } - *aq_sleep_time += *time_frame; + double t = *time_frame - mpctx->video_out->flip_queue_offset; + if (t <= 0.05) + return 0; - //============================== SLEEP: =================================== - - // flag 256 means: libvo driver does its timing (dvb card) - if (*time_frame > 0.001 && !(mpctx->sh_video->output_flags&256)) - *time_frame = timing_sleep(mpctx, *time_frame); - *time_frame += mpctx->video_out->flip_queue_offset; - return frame_time_remaining; + t -= 0.05; + if (t > audio_limit * 0.6) + t = audio_limit * 0.5; + *aq_sleep_time += t; + mp_input_get_cmd(mpctx->input, t * 1000 + 1, 1); + return 1; } int reinit_video_chain(struct MPContext *mpctx) @@ -2413,13 +2420,13 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx) struct sh_video *sh_video = mpctx->sh_video; double frame_time = 0; struct vo *video_out = mpctx->video_out; - while (!video_out->frame_loaded) { + while (1) { current_module = "filter_video"; // In nocorrect-pts mode there is no way to properly time these frames if (vo_get_buffered_frame(video_out, 0) >= 0) break; if (vf_output_queued_frame(sh_video->vfilter)) - continue; + break; unsigned char *packet = NULL; frame_time = sh_video->next_frame_time; if (mpctx->restart_playback) @@ -2463,10 +2470,9 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx) #endif if (decoded_frame) { current_module = "filter video"; - if (filter_video(sh_video, decoded_frame, sh_video->pts)) - if (!video_out->config_ok) - break; + filter_video(sh_video, decoded_frame, sh_video->pts); } + break; } return frame_time; } @@ -2513,21 +2519,20 @@ static double update_video(struct MPContext *mpctx) double pts; - bool hit_eof = false; - while (!video_out->frame_loaded) { + while (1) { current_module = "filter_video"; - if (vo_get_buffered_frame(video_out, hit_eof) >= 0) + if (!mpctx->hrseek_active + && vo_get_buffered_frame(video_out, false) >= 0) break; // XXX Time used in this call is not counted in any performance - // timer now, OSD time is not updated correctly for filter-added frames + // timer now if (vf_output_queued_frame(sh_video->vfilter)) - continue; - if (hit_eof) - return -1; + break; unsigned char *packet = NULL; int in_size = ds_get_packet_pts(mpctx->d_video, &packet, &pts); if (pts != MP_NOPTS_VALUE) pts += mpctx->video_offset; + bool hit_eof = false; if (in_size < 0) { // try to extract last frames in case of decoder lag in_size = 0; @@ -2537,18 +2542,26 @@ static double update_video(struct MPContext *mpctx) if (in_size > max_framesize) max_framesize = in_size; current_module = "decode video"; - int framedrop_type = check_framedrop(mpctx, sh_video->frametime); + if (pts >= mpctx->hrseek_pts - .005) + mpctx->hrseek_framedrop = false; + int framedrop_type = mpctx->hrseek_framedrop ? 1 : + check_framedrop(mpctx, sh_video->frametime); void *decoded_frame = decode_video(sh_video, packet, in_size, framedrop_type, pts); if (decoded_frame) { determine_frame_pts(mpctx); current_module = "filter video"; - if (filter_video(sh_video, decoded_frame, sh_video->pts)) - if (!video_out->config_ok) - break; // We'd likely hang in this loop otherwise + filter_video(sh_video, decoded_frame, sh_video->pts); + } else if (hit_eof) { + if (vo_get_buffered_frame(video_out, true) < 0) + return -1; } + break; } + if (!video_out->frame_loaded) + return 0; + pts = video_out->next_pts; if (pts == MP_NOPTS_VALUE) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Video pts after filters MISSING\n"); @@ -2557,6 +2570,9 @@ static double update_video(struct MPContext *mpctx) if (pts == MP_NOPTS_VALUE) pts = sh_video->last_pts; } + if (mpctx->hrseek_active && pts < mpctx->hrseek_pts - .005) + return 0; + mpctx->hrseek_active = false; sh_video->pts = pts; if (sh_video->last_pts == MP_NOPTS_VALUE) sh_video->last_pts = sh_video->pts; @@ -2668,7 +2684,7 @@ static void edl_seek_reset(MPContext *mpctx) next_edl_record = edl_records; while (next_edl_record) { - if (next_edl_record->start_sec >= mpctx->sh_video->pts) + if (next_edl_record->start_sec >= get_current_time(mpctx)) break; if (next_edl_record->action == EDL_MUTE) @@ -2694,11 +2710,10 @@ static void edl_update(MPContext *mpctx) return; } - if (mpctx->sh_video->pts >= next_edl_record->start_sec) { + if (get_current_time(mpctx) >= next_edl_record->start_sec) { if (next_edl_record->action == EDL_SKIP) { mpctx->osd_function = OSD_FFW; - mpctx->abs_seek_pos = 0; - mpctx->rel_seek_secs = next_edl_record->length_sec; + queue_seek(mpctx, MPSEEK_RELATIVE, next_edl_record->length_sec, 0); mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_SKIP: start [%f], stop " "[%f], length [%f]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->length_sec); @@ -2738,6 +2753,7 @@ static void seek_reset(struct MPContext *mpctx) // (which is used by at least vobsub and edl code below) may // be completely wrong (probably 0). mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset; + mpctx->video_pts = mpctx->sh_video->pts; update_subtitles(mpctx, &mpctx->opts, mpctx->sh_video, mpctx->sh_video->pts, mpctx->video_offset, mpctx->d_sub, 1); @@ -2762,6 +2778,8 @@ static void seek_reset(struct MPContext *mpctx) edl_seek_reset(mpctx); + mpctx->hrseek_active = false; + mpctx->hrseek_framedrop = false; mpctx->total_avsync_change = 0; audio_time_usage = 0; video_time_usage = 0; vout_time_usage = 0; drop_frame_cnt = 0; @@ -2805,39 +2823,43 @@ static double timeline_set_from_time(struct MPContext *mpctx, double pts, } -// style & SEEK_ABSOLUTE == 0 means seek relative to current position, == 1 means absolute -// style & SEEK_FACTOR == 0 means amount in seconds, == 2 means fraction of file length // return -1 if seek failed (non-seekable stream?), 0 otherwise -static int seek(MPContext *mpctx, double amount, int style) +static int seek(MPContext *mpctx, struct seek_params seek) { + struct MPOpts *opts = &mpctx->opts; + current_module = "seek"; if (mpctx->stop_play == AT_END_OF_FILE) mpctx->stop_play = KEEP_PLAYING; - if (style & SEEK_FACTOR - || style & SEEK_ABSOLUTE && amount < mpctx->last_chapter_pts - || amount < 0) + bool hr_seek = mpctx->demuxer->accurate_seek && opts->correct_pts; + hr_seek &= seek.exact >= 0 && seek.type != MPSEEK_FACTOR; + hr_seek &= opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE + || opts->hr_seek > 0 || seek.exact > 0; + if (seek.type == MPSEEK_FACTOR + || seek.type == MPSEEK_ABSOLUTE + && seek.amount < mpctx->last_chapter_pts + || seek.amount < 0) mpctx->last_chapter_seek = -1; - if (mpctx->timeline && style & SEEK_FACTOR) { - amount *= mpctx->timeline[mpctx->num_timeline_parts].start; - style &= ~SEEK_FACTOR; + if (mpctx->timeline && seek.type == MPSEEK_FACTOR) { + seek.amount *= mpctx->timeline[mpctx->num_timeline_parts].start; + seek.type = MPSEEK_ABSOLUTE; } - if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && mpctx->sh_video - && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { - style |= SEEK_ABSOLUTE; - if (amount > 0) - style |= SEEK_FORWARD; - else - style |= SEEK_BACKWARD; - amount += mpctx->sh_video->pts; + if ((mpctx->demuxer->accurate_seek || mpctx->timeline) + && seek.type == MPSEEK_RELATIVE) { + seek.type = MPSEEK_ABSOLUTE; + seek.direction = seek.amount > 0 ? 1 : -1; + seek.amount += get_current_time(mpctx); } /* At least the liba52 decoder wants to read from the input stream * during initialization, so reinit must be done after the demux_seek() * call that clears possible stream EOF. */ bool need_reset = false; + double demuxer_amount = seek.amount; if (mpctx->timeline) { - amount = timeline_set_from_time(mpctx, amount, &need_reset); - if (amount == -1) { + demuxer_amount = timeline_set_from_time(mpctx, seek.amount, + &need_reset); + if (demuxer_amount == -1) { mpctx->stop_play = AT_END_OF_FILE; // Clear audio from current position if (mpctx->sh_audio) { @@ -2848,16 +2870,78 @@ static int seek(MPContext *mpctx, double amount, int style) return -1; } } - int seekresult = demux_seek(mpctx->demuxer, amount, audio_delay, style); + int demuxer_style = 0; + switch (seek.type) { + case MPSEEK_FACTOR: + demuxer_style |= SEEK_FACTOR; // fallthrough + case MPSEEK_ABSOLUTE: + demuxer_style |= SEEK_ABSOLUTE; + } + if (hr_seek || seek.direction < 0) + demuxer_style |= SEEK_BACKWARD; + else if (seek.direction > 0) + demuxer_style |= SEEK_FORWARD; + + int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, audio_delay, + demuxer_style); if (need_reset) reinit_decoders(mpctx); if (seekresult == 0) return -1; seek_reset(mpctx); + + /* Use the target time as "current position" for further relative + * seeks etc until a new video frame has been decoded */ + if (seek.type == MPSEEK_ABSOLUTE) + mpctx->video_pts = seek.amount; + + if (hr_seek) { + mpctx->hrseek_active = true; + mpctx->hrseek_framedrop = true; + mpctx->hrseek_pts = seek.amount; + } + + mpctx->start_timestamp = GetTimerMS(); + return 0; } +void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, + int exact) +{ + struct seek_params *seek = &mpctx->seek; + switch (type) { + case MPSEEK_RELATIVE: + if (seek->type == MPSEEK_FACTOR) + return; // Well... not common enough to bother doing better + seek->amount += amount; + seek->exact = FFMAX(seek->exact, exact); + if (seek->type == MPSEEK_NONE) + seek->exact = exact; + if (seek->type == MPSEEK_ABSOLUTE) + return; + if (seek->amount == 0) { + *seek = (struct seek_params){0}; + return; + } + seek->type = MPSEEK_RELATIVE; + return; + case MPSEEK_ABSOLUTE: + case MPSEEK_FACTOR: + *seek = (struct seek_params) { + .type = type, + .amount = amount, + .exact = exact, + }; + return; + case MPSEEK_NONE: + *seek = (struct seek_params){0}; + return; + } + abort(); +} + double get_time_length(struct MPContext *mpctx) { @@ -2895,7 +2979,7 @@ double get_current_time(struct MPContext *mpctx) return demuxer->stream_pts; struct sh_video *sh_video = demuxer->video->sh; if (sh_video) - return sh_video->pts; + return mpctx->video_pts; return playing_audio_pts(mpctx); } @@ -2977,6 +3061,302 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts, return chapter; } + +static void run_playloop(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; + float aq_sleep_time = 0; + + if (opts->chapterrange[1] > 0) { + int cur_chapter = get_current_chapter(mpctx); + if (cur_chapter!=-1 && cur_chapter + 1 > opts->chapterrange[1]) + mpctx->stop_play = PT_NEXT_ENTRY; + } + + if (!mpctx->sh_audio && mpctx->d_audio->sh) { + mpctx->sh_audio = mpctx->d_audio->sh; + mpctx->sh_audio->ds = mpctx->d_audio; + reinit_audio_chain(mpctx); + } + +/*========================== PLAY AUDIO ============================*/ + + if (mpctx->sh_audio && !mpctx->paused + && (!mpctx->restart_playback || !mpctx->sh_video)) + if (!fill_audio_out_buffers(mpctx)) + // at eof, all audio at least written to ao + if (!mpctx->sh_video) + mpctx->stop_play = AT_END_OF_FILE; + + + if (!mpctx->sh_video) { + mpctx->restart_playback = false; + // handle audio-only case: + double a_pos = 0; + // sh_audio can be NULL due to video stream switching + // TODO: handle this better + if (mpctx->sh_audio) + a_pos = playing_audio_pts(mpctx); + + print_status(mpctx, a_pos, false); + + if (end_at.type == END_AT_TIME && end_at.pos < a_pos) + mpctx->stop_play = PT_NEXT_ENTRY; + update_subtitles(mpctx, &mpctx->opts, NULL, a_pos, mpctx->video_offset, + mpctx->d_sub, 0); + update_osd_msg(mpctx); + } else { + +/*========================== PLAY VIDEO ============================*/ + + vo_pts = mpctx->sh_video->timer * 90000.0; + vo_fps = mpctx->sh_video->fps; + + bool blit_frame = mpctx->video_out->frame_loaded; + if (!blit_frame || mpctx->hrseek_active) { + double frame_time = update_video(mpctx); + blit_frame = mpctx->video_out->frame_loaded; + blit_frame &= !mpctx->hrseek_active; + mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "*** ftime=%5.3f ***\n", frame_time); + if (mpctx->sh_video->vf_initialized < 0) { + mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, + "\nFATAL: Could not initialize video filters (-vf) " + "or video output (-vo).\n"); + mpctx->stop_play = PT_NEXT_ENTRY; + return; + } + if (frame_time < 0) + mpctx->stop_play = AT_END_OF_FILE; + else if (!mpctx->restart_playback) { + mpctx->time_frame += frame_time / opts->playback_speed; + adjust_sync(mpctx, frame_time); + } + } + if (mpctx->timeline) { + struct timeline_part *next = + mpctx->timeline + mpctx->timeline_part + 1; + if (mpctx->sh_video->pts >= next->start + || mpctx->stop_play == AT_END_OF_FILE + && mpctx->timeline_part + 1 < mpctx->num_timeline_parts) { + seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE, + .amount = next->start }); + return; + } + } + +// ========================================================================== + + current_module = "vo_check_events"; + vo_check_events(mpctx->video_out); + +#ifdef CONFIG_X11 + if (stop_xscreensaver) { + current_module = "stop_xscreensaver"; + xscreensaver_heartbeat(mpctx->x11_state); + } +#endif + if (heartbeat_cmd) { + static unsigned last_heartbeat; + unsigned now = GetTimerMS(); + if (now - last_heartbeat > 30000) { + last_heartbeat = now; + system(heartbeat_cmd); + } + } + + bool frame_time_remaining = sleep_until_near_frame(mpctx, + &mpctx->time_frame, + &aq_sleep_time); + +//====================== FLIP PAGE (VIDEO BLT): ========================= + + current_module = "flip_page"; + if (!frame_time_remaining && blit_frame) { + struct sh_video *sh_video = mpctx->sh_video; + mpctx->video_pts = sh_video->pts; + update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, + mpctx->video_offset, mpctx->d_sub, 0); + update_teletext(sh_video, mpctx->demuxer, 0); + update_osd_msg(mpctx); + struct vf_instance *vf = sh_video->vfilter; + vf->control(vf, VFCTRL_DRAW_EOSD, NULL); + vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); + vo_osd_changed(0); + + mpctx->time_frame -= mpctx->video_out->flip_queue_offset; + aq_sleep_time += mpctx->time_frame; + // flag 256 means: libvo driver does its timing (dvb card) + if (mpctx->time_frame > 0.001 + && !(mpctx->sh_video->output_flags&256)) + mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame); + mpctx->time_frame += mpctx->video_out->flip_queue_offset; + + unsigned int t2 = GetTimer(); + unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; + int duration = -1; + double pts2 = mpctx->video_out->next_pts2; + if (pts2 != MP_NOPTS_VALUE && opts->correct_pts + && !mpctx->restart_playback) { + // expected A/V sync correction is ignored + double diff = (pts2 - mpctx->video_pts); + diff /= opts->playback_speed; + if (mpctx->time_frame < 0) + diff += mpctx->time_frame; + if (diff < 0) + diff = 0; + if (diff > 10) + diff = 10; + duration = diff * 1e6; + } + vo_flip_page(mpctx->video_out, pts_us | 1, duration); + + mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001; + vout_time_usage += mpctx->last_vo_flip_duration; + if (mpctx->video_out->driver->flip_page_timed) { + // No need to adjust sync based on flip speed + mpctx->last_vo_flip_duration = 0; + // For print_status - VO call finishing early is OK for sync + mpctx->time_frame -= get_relative_time(mpctx); + } + if (mpctx->restart_playback) { + mpctx->syncing_audio = true; + if (mpctx->sh_audio && !mpctx->paused) + fill_audio_out_buffers(mpctx); + mpctx->restart_playback = false; + mpctx->time_frame = 0; + get_relative_time(mpctx); + } + print_status(mpctx, MP_NOPTS_VALUE, true); + } else + print_status(mpctx, MP_NOPTS_VALUE, false); + +//============================ Auto QUALITY ============================ + +/*Output quality adjustments:*/ + if (opts->auto_quality > 0) { + current_module = "autoq"; + if (output_quality < opts->auto_quality && aq_sleep_time > 0) + ++output_quality; + else + if (output_quality>1 && aq_sleep_time<0) + --output_quality; + else if (output_quality>0 && aq_sleep_time<-0.050f) // 50ms + output_quality = 0; + set_video_quality(mpctx->sh_video, output_quality); + } + + if (!frame_time_remaining && blit_frame) { + if (play_n_frames >= 0) { + --play_n_frames; + if (play_n_frames <= 0) + mpctx->stop_play = PT_NEXT_ENTRY; + } + if (mpctx->step_frames > 0) { + mpctx->step_frames--; + if (mpctx->step_frames == 0) + pause_player(mpctx); + } + } + +// FIXME: add size based support for -endpos + if (end_at.type == END_AT_TIME && + !frame_time_remaining && end_at.pos <= mpctx->sh_video->pts) + mpctx->stop_play = PT_NEXT_ENTRY; + + } // end if(mpctx->sh_video) + +#ifdef CONFIG_DVDNAV + if (mpctx->stream->type == STREAMTYPE_DVDNAV) { + nav_highlight_t hl; + mp_dvdnav_get_highlight(mpctx->stream, &hl); + if (!vo_spudec || !spudec_apply_palette_crop(vo_spudec, hl.palette, hl.sx, hl.sy, hl.ex, hl.ey)) { + osd_set_nav_box(hl.sx, hl.sy, hl.ex, hl.ey); + vo_osd_changed(OSDTYPE_DVDNAV); + } else { + osd_set_nav_box(0, 0, 0, 0); + vo_osd_changed(OSDTYPE_DVDNAV); + vo_osd_changed(OSDTYPE_SPU); + } + + if (mp_dvdnav_stream_has_changed(mpctx->stream)) { + double ar = -1.0; + if (mpctx->sh_video && + stream_control(mpctx->demuxer->stream, + STREAM_CTRL_GET_ASPECT_RATIO, &ar) + != STREAM_UNSUPPORTED) + mpctx->sh_video->stream_aspect = ar; + } + } +#endif + +//================= Keyboard events, SEEKing ==================== + + current_module = "key_events"; + + while (1) { + mp_cmd_t *cmd; + while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) { + /* Allow running consecutive seek commands to combine them, + * but execute the seek before running other commands. + * If the user seeks continuously (keeps arrow key down) + * try to finish showing a frame from one location before doing + * another seek (which could lead to unchanging display). */ + if (mpctx->seek.type && cmd->id != MP_CMD_SEEK + || mpctx->restart_playback && cmd->id == MP_CMD_SEEK + && GetTimerMS() - mpctx->start_timestamp < 300) + break; + cmd = mp_input_get_cmd(mpctx->input, 0, 0); + run_command(mpctx, cmd); + mp_cmd_free(cmd); + if (mpctx->stop_play) + break; + } + if (!mpctx->paused || mpctx->stop_play || mpctx->seek.type + || mpctx->restart_playback) + break; + if (mpctx->sh_video) { + update_osd_msg(mpctx); + int hack = vo_osd_changed(0); + vo_osd_changed(hack); + if (hack) { + if (redraw_osd(mpctx->sh_video, mpctx->osd) < 0) { + add_step_frame(mpctx); + break; + } else + vo_osd_changed(0); + } + } + pause_loop(mpctx); + } + +// handle -sstep + if (step_sec > 0 && !mpctx->paused) { + mpctx->osd_function = OSD_FFW; + queue_seek(mpctx, MPSEEK_RELATIVE, step_sec, 0); + } + + edl_update(mpctx); + + /* Looping. */ + if (mpctx->stop_play==AT_END_OF_FILE && opts->loop_times>=0) { + mp_msg(MSGT_CPLAYER, MSGL_V, "loop_times = %d\n", opts->loop_times); + + if (opts->loop_times>1) + opts->loop_times--; + else if (opts->loop_times==1) + opts->loop_times = -1; + play_n_frames = play_n_frames_mf; + mpctx->stop_play = 0; + queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, 0); + } + + if (mpctx->seek.type) { + seek(mpctx, mpctx->seek); + mpctx->seek = (struct seek_params){0}; + } +} + + static int find_ordered_chapter_sources(struct MPContext *mpctx, struct content_source *sources, int num_sources, @@ -4072,8 +4452,6 @@ if (verbose) opts->term_osd = 0; { -int frame_time_remaining=0; // flag -int blit_frame=0; // Make sure old OSD does not stay around, // e.g. with -fixed-vo and same-resolution files @@ -4149,16 +4527,28 @@ if(play_n_frames==0){ mpctx->stop_play=PT_NEXT_ENTRY; goto goto_next_file; } + mpctx->time_frame = 0; + mpctx->drop_message_shown = 0; + mpctx->restart_playback = true; + mpctx->video_pts = 0; + mpctx->hrseek_active = false; + mpctx->hrseek_framedrop = false; + mpctx->total_avsync_change = 0; + mpctx->last_chapter_seek = -1; + // If there's a timeline force an absolute seek to initialize state if (seek_to_sec || mpctx->timeline) { - seek(mpctx, seek_to_sec, SEEK_ABSOLUTE); + queue_seek(mpctx, MPSEEK_ABSOLUTE, seek_to_sec, 0); + seek(mpctx, mpctx->seek); end_at.pos += seek_to_sec; } if (opts->chapterrange[0] > 0) { double pts; if (seek_chapter(mpctx, opts->chapterrange[0]-1, &pts, NULL) >= 0 - && pts > -1.0) - seek(mpctx, pts, SEEK_ABSOLUTE); + && pts > -1.0) { + queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0); + seek(mpctx, mpctx->seek); + } } if (end_at.type == END_AT_SIZE) { @@ -4174,304 +4564,15 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { } #endif + mpctx->seek = (struct seek_params){0}; get_relative_time(mpctx); // reset current delta - mpctx->time_frame = 0; - mpctx->drop_message_shown = 0; - mpctx->restart_playback = true; - mpctx->total_avsync_change = 0; - mpctx->last_chapter_seek = -1; // Make sure VO knows current pause state if (mpctx->sh_video) vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL); -while(!mpctx->stop_play){ - float aq_sleep_time=0; - -if (opts->chapterrange[1] > 0) { - int cur_chapter = get_current_chapter(mpctx); - if(cur_chapter!=-1 && cur_chapter+1 > opts->chapterrange[1]) - goto goto_next_file; -} - -if(!mpctx->sh_audio && mpctx->d_audio->sh) { - mpctx->sh_audio = mpctx->d_audio->sh; - mpctx->sh_audio->ds = mpctx->d_audio; - reinit_audio_chain(mpctx); -} - -/*========================== PLAY AUDIO ============================*/ - -if (mpctx->sh_audio && !mpctx->paused - && (!mpctx->restart_playback || !mpctx->sh_video)) - if (!fill_audio_out_buffers(mpctx)) - // at eof, all audio at least written to ao - if (!mpctx->sh_video) - mpctx->stop_play = AT_END_OF_FILE; - - -if(!mpctx->sh_video) { - // handle audio-only case: - double a_pos=0; - // sh_audio can be NULL due to video stream switching - // TODO: handle this better - if (mpctx->sh_audio) - a_pos = playing_audio_pts(mpctx); - - print_status(mpctx, a_pos, false); - - if(end_at.type == END_AT_TIME && end_at.pos < a_pos) - mpctx->stop_play = PT_NEXT_ENTRY; - update_subtitles(mpctx, &mpctx->opts, NULL, a_pos, mpctx->video_offset, - mpctx->d_sub, 0); - update_osd_msg(mpctx); - -} else { - -/*========================== PLAY VIDEO ============================*/ - - vo_pts=mpctx->sh_video->timer*90000.0; - vo_fps=mpctx->sh_video->fps; - - blit_frame = mpctx->video_out->frame_loaded; - if (!blit_frame) { - double frame_time = update_video(mpctx); - blit_frame = mpctx->video_out->frame_loaded; - mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"*** ftime=%5.3f ***\n",frame_time); - if (mpctx->sh_video->vf_initialized < 0) { - mp_tmsg(MSGT_CPLAYER,MSGL_FATAL, "\nFATAL: Could not initialize video filters (-vf) or video output (-vo).\n"); - mpctx->stop_play = PT_NEXT_ENTRY; - goto goto_next_file; - } - if (blit_frame) { - struct sh_video *sh_video = mpctx->sh_video; - update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, - mpctx->video_offset, mpctx->d_sub, 0); - update_teletext(sh_video, mpctx->demuxer, 0); - update_osd_msg(mpctx); - struct vf_instance *vf = mpctx->sh_video->vfilter; - vf->control(vf, VFCTRL_DRAW_EOSD, NULL); - vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); - vo_osd_changed(0); - } - if (frame_time < 0) - mpctx->stop_play = AT_END_OF_FILE; - else if (!mpctx->restart_playback) { - mpctx->time_frame += frame_time / opts->playback_speed; - adjust_sync(mpctx, frame_time); - } - } - if (mpctx->timeline) { - struct timeline_part *next = mpctx->timeline + mpctx->timeline_part + 1; - if (mpctx->sh_video->pts >= next->start - || mpctx->stop_play == AT_END_OF_FILE - && mpctx->timeline_part + 1 < mpctx->num_timeline_parts) { - seek(mpctx, next->start, SEEK_ABSOLUTE); - continue; - } - } - -// ========================================================================== - -// current_module="draw_osd"; -// if(vo_config_count) mpctx->video_out->draw_osd(); - - current_module="vo_check_events"; - vo_check_events(mpctx->video_out); - -#ifdef CONFIG_X11 - if (stop_xscreensaver) { - current_module = "stop_xscreensaver"; - xscreensaver_heartbeat(mpctx->x11_state); - } -#endif - if (heartbeat_cmd) { - static unsigned last_heartbeat; - unsigned now = GetTimerMS(); - if (now - last_heartbeat > 30000) { - last_heartbeat = now; - system(heartbeat_cmd); - } - } - - frame_time_remaining = sleep_until_update(mpctx, &mpctx->time_frame, &aq_sleep_time); - -//====================== FLIP PAGE (VIDEO BLT): ========================= - - current_module="flip_page"; - if (!frame_time_remaining && blit_frame) { - unsigned int t2=GetTimer(); - unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; - int duration = -1; - double pts2 = mpctx->video_out->next_pts2; - if (pts2 != MP_NOPTS_VALUE && opts->correct_pts - && !mpctx->restart_playback) { - // expected A/V sync correction is ignored - double diff = (pts2 - mpctx->sh_video->pts); - diff /= opts->playback_speed; - if (mpctx->time_frame < 0) - diff += mpctx->time_frame; - if (diff < 0) - diff = 0; - if (diff > 10) - diff = 10; - duration = diff * 1e6; - } - vo_flip_page(mpctx->video_out, pts_us|1, duration); - - mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001; - vout_time_usage += mpctx->last_vo_flip_duration; - if (mpctx->video_out->driver->flip_page_timed) { - // No need to adjust sync based on flip speed - mpctx->last_vo_flip_duration = 0; - // For print_status - VO call finishing early is OK for sync - mpctx->time_frame -= get_relative_time(mpctx); - } - if (mpctx->restart_playback) { - mpctx->syncing_audio = true; - if (mpctx->sh_audio && !mpctx->paused) - fill_audio_out_buffers(mpctx); - mpctx->restart_playback = false; - mpctx->time_frame = 0; - get_relative_time(mpctx); - } - print_status(mpctx, MP_NOPTS_VALUE, true); - } - else - print_status(mpctx, MP_NOPTS_VALUE, false); - -//============================ Auto QUALITY ============================ - -/*Output quality adjustments:*/ -if (opts->auto_quality > 0) { - current_module="autoq"; -// float total=0.000001f * (GetTimer()-aq_total_time); -// if (output_quality < opts->auto_quality && aq_sleep_time > 0.05f * total) - if (output_quality < opts->auto_quality && aq_sleep_time > 0) - ++output_quality; - else -// if(output_quality>0 && aq_sleep_time<-0.05f*total) - if(output_quality>1 && aq_sleep_time<0) - --output_quality; - else - if(output_quality>0 && aq_sleep_time<-0.050f) // 50ms - output_quality=0; -// printf("total: %8.6f sleep: %8.6f q: %d\n",(0.000001f*aq_total_time),aq_sleep_time,output_quality); - set_video_quality(mpctx->sh_video,output_quality); -} - - if (!frame_time_remaining && blit_frame) { - if (play_n_frames >= 0) { - --play_n_frames; - if (play_n_frames <= 0) - mpctx->stop_play = PT_NEXT_ENTRY; - } - if (mpctx->step_frames > 0) { - mpctx->step_frames--; - if (mpctx->step_frames == 0) - pause_player(mpctx); - } - } - - -// FIXME: add size based support for -endpos - if (end_at.type == END_AT_TIME && - !frame_time_remaining && end_at.pos <= mpctx->sh_video->pts) - mpctx->stop_play = PT_NEXT_ENTRY; - -} // end if(mpctx->sh_video) - -#ifdef CONFIG_DVDNAV - if (mpctx->stream->type == STREAMTYPE_DVDNAV) { - nav_highlight_t hl; - mp_dvdnav_get_highlight (mpctx->stream, &hl); - if (!vo_spudec || !spudec_apply_palette_crop(vo_spudec, hl.palette, hl.sx, hl.sy, hl.ex, hl.ey)) { - osd_set_nav_box (hl.sx, hl.sy, hl.ex, hl.ey); - vo_osd_changed (OSDTYPE_DVDNAV); - } else { - osd_set_nav_box(0, 0, 0, 0); - vo_osd_changed(OSDTYPE_DVDNAV); - vo_osd_changed(OSDTYPE_SPU); - } - - if (mp_dvdnav_stream_has_changed(mpctx->stream)) { - double ar = -1.0; - if (mpctx->sh_video && - stream_control (mpctx->demuxer->stream, - STREAM_CTRL_GET_ASPECT_RATIO, &ar) - != STREAM_UNSUPPORTED) - mpctx->sh_video->stream_aspect = ar; - } - } -#endif - -//================= Keyboard events, SEEKing ==================== - - current_module="key_events"; - -{ - while (1) { - mp_cmd_t* cmd; - while ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { - run_command(mpctx, cmd); - mp_cmd_free(cmd); - if (mpctx->stop_play) - break; - if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { - cmd = mp_input_get_cmd(mpctx->input, 0, 1); - /* Allow seek commands to be combined, but execute the real seek - * before processing other commands */ - if (!cmd || cmd->id != MP_CMD_SEEK) - break; - } - } - if (!mpctx->paused || mpctx->stop_play || mpctx->rel_seek_secs - || mpctx->abs_seek_pos) - break; - if (mpctx->sh_video) { - update_osd_msg(mpctx); - int hack = vo_osd_changed(0); - vo_osd_changed(hack); - if (hack) { - if (redraw_osd(mpctx->sh_video, mpctx->osd) < 0) { - add_step_frame(mpctx); - break; - } - else - vo_osd_changed(0); - } - } - pause_loop(mpctx); - } -} - -// handle -sstep -if (step_sec > 0 && !mpctx->paused) { - mpctx->osd_function=OSD_FFW; - mpctx->rel_seek_secs+=step_sec; -} - - edl_update(mpctx); - - /* Looping. */ - if(mpctx->stop_play==AT_END_OF_FILE && opts->loop_times>=0) { - mp_msg(MSGT_CPLAYER,MSGL_V,"loop_times = %d\n", opts->loop_times); - - if(opts->loop_times>1) opts->loop_times--; else - if(opts->loop_times==1) opts->loop_times=-1; - play_n_frames=play_n_frames_mf; - mpctx->stop_play=0; - mpctx->abs_seek_pos=SEEK_ABSOLUTE; mpctx->rel_seek_secs=seek_to_sec; - } - -if(mpctx->rel_seek_secs || mpctx->abs_seek_pos){ - seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); - - mpctx->rel_seek_secs=0; - mpctx->abs_seek_pos=0; -} - -} // while(!mpctx->stop_play) + while (!mpctx->stop_play) + run_playloop(mpctx); mp_msg(MSGT_GLOBAL,MSGL_V,"EOF code: %d \n",mpctx->stop_play); @@ -47,6 +47,7 @@ typedef struct MPOpts { int user_correct_pts; int user_pts_assoc_mode; int initial_audio_sync; + int hr_seek; int autosync; int softsleep; int rtc; diff --git a/osdep/keycodes.h b/osdep/keycodes.h index 4d153a24aa..d197c8e3e9 100644 --- a/osdep/keycodes.h +++ b/osdep/keycodes.h @@ -94,4 +94,10 @@ #define KEY_INTERN (0x1000) #define KEY_CLOSE_WIN (KEY_INTERN+0) +/* Modifiers added to individual keys */ +#define KEY_MODIFIER_SHIFT 0x2000 +#define KEY_MODIFIER_CTRL 0x4000 +#define KEY_MODIFIER_ALT 0x8000 +#define KEY_MODIFIER_META 0x10000 + #endif /* MPLAYER_KEYCODES_H */ |