diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index a6c103ce25..95ef58de81 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -909,6 +909,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. diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index be0ebf9937..6187affab5 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -272,11 +272,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. @@ -513,6 +518,7 @@ 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, use with pausing_keep_force filename string X file playing wo path diff --git a/cfg-mplayer.h b/cfg-mplayer.h index 3545b77716..7f0ee1f729 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -312,6 +312,8 @@ const m_option_t mplayer_opts[]={ 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), diff --git a/command.c b/command.c index 394bf566c4..c1519d001d 100644 --- a/command.c +++ b/command.c @@ -2219,6 +2219,8 @@ static const m_option_t mp_properties[] = { 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, @@ -2393,6 +2395,7 @@ static struct property_osd_display { { "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") }, @@ -2713,20 +2716,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 */ - queue_seek(mpctx, MPSEEK_ABSOLUTE, v, 0); + 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 */ - queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, 0); + queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact); mpctx->osd_function = OSD_FFW; // Direction isn't set correctly } else { - queue_seek(mpctx, MPSEEK_RELATIVE, v, 0); + queue_seek(mpctx, MPSEEK_RELATIVE, v, exact); mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW; } } diff --git a/input/input.c b/input/input.c index c08f3d49f0..72725a5409 100644 --- a/input/input.c +++ b/input/input.c @@ -86,7 +86,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}} } }, diff --git a/mp_core.h b/mp_core.h index c64de2cdbe..7f6bb2ccf4 100644 --- a/mp_core.h +++ b/mp_core.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. diff --git a/mplayer.c b/mplayer.c index 7eb2d138b5..f209ca0214 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2558,7 +2558,8 @@ static double update_video(struct MPContext *mpctx) while (1) { current_module = "filter_video"; - if (vo_get_buffered_frame(video_out, false) >= 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 @@ -2578,7 +2579,10 @@ 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) { @@ -2603,6 +2607,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; @@ -2808,6 +2815,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; @@ -2854,9 +2863,15 @@ static double timeline_set_from_time(struct MPContext *mpctx, double pts, // return -1 if seek failed (non-seekable stream?), 0 otherwise 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; + 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 @@ -2899,7 +2914,7 @@ static int seek(MPContext *mpctx, struct seek_params seek) case MPSEEK_ABSOLUTE: demuxer_style |= SEEK_ABSOLUTE; } - if (seek.direction < 0) + if (hr_seek || seek.direction < 0) demuxer_style |= SEEK_BACKWARD; else if (seek.direction > 0) demuxer_style |= SEEK_FORWARD; @@ -2918,6 +2933,12 @@ static int seek(MPContext *mpctx, struct seek_params seek) 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; @@ -3129,9 +3150,10 @@ static void run_playloop(struct MPContext *mpctx) vo_fps = mpctx->sh_video->fps; bool blit_frame = mpctx->video_out->frame_loaded; - if (!blit_frame) { + 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, @@ -4547,6 +4569,15 @@ 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) { queue_seek(mpctx, MPSEEK_ABSOLUTE, seek_to_sec, 0); @@ -4577,12 +4608,6 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { 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->video_pts = 0; - 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, diff --git a/options.h b/options.h index 66048e77b2..12ac475f9e 100644 --- a/options.h +++ b/options.h @@ -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;