summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUoti Urpala <uau@glyph.nonexistent.invalid>2010-12-15 01:09:47 +0200
committerUoti Urpala <uau@glyph.nonexistent.invalid>2010-12-20 19:02:16 +0200
commitf0649f13d698eb6aeffeaacd6801b666c4366caf (patch)
tree809200e48f70f5a13e8a1d27f688f7b92348a744
parent23f598e0ee2151923a44077f510cd484f239480b (diff)
downloadmpv-f0649f13d698eb6aeffeaacd6801b666c4366caf.tar.bz2
mpv-f0649f13d698eb6aeffeaacd6801b666c4366caf.tar.xz
core: add support for precise non-keyframe-limited seeks
Add support for seeking to an arbitrary non-keyframe position by decoding video starting from the previous keyframe. Whether to use this functionality when seeking is controlled by the new option -hr-seek and a new third argument to the "seek" command. The default is to use it for absolute seeks (like chapter seeks) but not for relative ones. Because there's currently no support for cutting encoded audio some desync is expected if encoded audio passthrough is used. Currently precise seeks always go to the first frame with timestamp equal to or greater than the target position; there's no support for "matching or earlier" backwards seeks at frame level.
-rw-r--r--DOCS/man/en/mplayer.121
-rw-r--r--DOCS/tech/slave.txt14
-rw-r--r--cfg-mplayer.h2
-rw-r--r--command.c16
-rw-r--r--input/input.c2
-rw-r--r--mp_core.h3
-rw-r--r--mplayer.c45
-rw-r--r--options.h1
8 files changed, 82 insertions, 22 deletions
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;