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;