mirror of
https://github.com/mpv-player/mpv.git
synced 2025-12-28 05:33:14 +00:00
command: allow frame-step to go through multiple frames and/or seek
Previously, the default behavior of frame-step and frame-back-step is to play forward 1 frame or seek back 1 frame. We keep this behavior but introduce additional flags to control the exact behavior of the frame stepping. The first argument simply specifies how many frames to go through. The second argument specifies whether to play video to step through frames or to seek to step through frames. Playing through the video to step through frames only works going forwards (otherwise it will always seek). In theory we could use backwards playback for this, but that can be decided later. As a minor note, MPSEEK_BACKSTEP is renamed to MPSEEK_FRAMESTEP since forward seeks can use this as well. Fixes #10128.
This commit is contained in:
@@ -5679,13 +5679,16 @@ static void cmd_frame_step(void *p)
|
||||
{
|
||||
struct mp_cmd_ctx *cmd = p;
|
||||
struct MPContext *mpctx = cmd->mpctx;
|
||||
bool backstep = *(bool *)cmd->priv;
|
||||
int frames = backstep ? -1 : cmd->args[0].v.i;
|
||||
int flags = backstep ? 1 : cmd->args[1].v.i;
|
||||
|
||||
if (!mpctx->playback_initialized) {
|
||||
if (!mpctx->playback_initialized || frames == 0) {
|
||||
cmd->success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->cmd->is_up_down) {
|
||||
if (frames > 0 && !flags && cmd->cmd->is_up_down) {
|
||||
if (cmd->cmd->is_up) {
|
||||
if (mpctx->step_frames < 1)
|
||||
set_pause_state(mpctx, true);
|
||||
@@ -5693,27 +5696,14 @@ static void cmd_frame_step(void *p)
|
||||
if (cmd->cmd->repeated) {
|
||||
set_pause_state(mpctx, false);
|
||||
} else {
|
||||
add_step_frame(mpctx, 1);
|
||||
add_step_frame(mpctx, frames, flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
add_step_frame(mpctx, 1);
|
||||
add_step_frame(mpctx, frames, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_frame_back_step(void *p)
|
||||
{
|
||||
struct mp_cmd_ctx *cmd = p;
|
||||
struct MPContext *mpctx = cmd->mpctx;
|
||||
|
||||
if (!mpctx->playback_initialized) {
|
||||
cmd->success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
add_step_frame(mpctx, -1);
|
||||
}
|
||||
|
||||
static void cmd_quit(void *p)
|
||||
{
|
||||
struct mp_cmd_ctx *cmd = p;
|
||||
@@ -6990,9 +6980,22 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
{ "stop", cmd_stop,
|
||||
{ {"flags", OPT_FLAGS(v.i, {"keep-playlist", 1}), .flags = MP_CMD_OPT_ARG} }
|
||||
},
|
||||
{ "frame-step", cmd_frame_step, .allow_auto_repeat = true,
|
||||
.on_updown = true },
|
||||
{ "frame-back-step", cmd_frame_back_step, .allow_auto_repeat = true },
|
||||
{ "frame-step", cmd_frame_step,
|
||||
{
|
||||
{"frames", OPT_INT(v.i), OPTDEF_INT(1)},
|
||||
{"flags", OPT_CHOICE(v.i,
|
||||
{"play", 0},
|
||||
{"seek", 1}),
|
||||
.flags = MP_CMD_OPT_ARG},
|
||||
},
|
||||
.allow_auto_repeat = true,
|
||||
.on_updown = true,
|
||||
.priv = &(const bool){false},
|
||||
},
|
||||
{ "frame-back-step", cmd_frame_step,
|
||||
.priv = &(const int){true},
|
||||
.allow_auto_repeat = true,
|
||||
},
|
||||
{ "playlist-next", cmd_playlist_next_prev,
|
||||
{
|
||||
{"flags", OPT_CHOICE(v.i,
|
||||
|
||||
@@ -69,7 +69,7 @@ enum seek_type {
|
||||
MPSEEK_RELATIVE,
|
||||
MPSEEK_ABSOLUTE,
|
||||
MPSEEK_FACTOR,
|
||||
MPSEEK_BACKSTEP,
|
||||
MPSEEK_FRAMESTEP,
|
||||
MPSEEK_CHAPTER,
|
||||
};
|
||||
|
||||
@@ -601,7 +601,7 @@ void reset_playback_state(struct MPContext *mpctx);
|
||||
void set_pause_state(struct MPContext *mpctx, bool user_pause);
|
||||
void update_internal_pause_state(struct MPContext *mpctx);
|
||||
void update_core_idle_state(struct MPContext *mpctx);
|
||||
void add_step_frame(struct MPContext *mpctx, int dir);
|
||||
void add_step_frame(struct MPContext *mpctx, int dir, bool use_seek);
|
||||
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
|
||||
enum seek_precision exact, int flags);
|
||||
double get_time_length(struct MPContext *mpctx);
|
||||
|
||||
@@ -203,16 +203,16 @@ void update_screensaver_state(struct MPContext *mpctx)
|
||||
: VOCTRL_KILL_SCREENSAVER, NULL);
|
||||
}
|
||||
|
||||
void add_step_frame(struct MPContext *mpctx, int dir)
|
||||
void add_step_frame(struct MPContext *mpctx, int dir, bool use_seek)
|
||||
{
|
||||
if (!mpctx->vo_chain)
|
||||
return;
|
||||
if (dir > 0) {
|
||||
mpctx->step_frames += 1;
|
||||
if (dir > 0 && !use_seek) {
|
||||
mpctx->step_frames += dir;
|
||||
set_pause_state(mpctx, false);
|
||||
} else if (dir < 0) {
|
||||
} else {
|
||||
if (!mpctx->hrseek_active) {
|
||||
queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, 0);
|
||||
queue_seek(mpctx, MPSEEK_FRAMESTEP, dir, MPSEEK_VERY_EXACT, 0);
|
||||
set_pause_state(mpctx, true);
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,16 @@ void reset_playback_state(struct MPContext *mpctx)
|
||||
update_core_idle_state(mpctx);
|
||||
}
|
||||
|
||||
static double calculate_framestep_pts(MPContext *mpctx, double current_time,
|
||||
int step_frames)
|
||||
{
|
||||
// Crude guess at the pts. Use current_time if step_frames is -1.
|
||||
int previous_frame = mpctx->num_past_frames - 1;
|
||||
int offset = step_frames == -1 ? 0 : step_frames;
|
||||
double pts = mpctx->past_frames[previous_frame].approx_duration * offset;
|
||||
return current_time + pts;
|
||||
}
|
||||
|
||||
static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
@@ -285,8 +295,9 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
||||
case MPSEEK_ABSOLUTE:
|
||||
seek_pts = seek.amount;
|
||||
break;
|
||||
case MPSEEK_BACKSTEP:
|
||||
seek_pts = current_time;
|
||||
case MPSEEK_FRAMESTEP:
|
||||
seek_pts = calculate_framestep_pts(mpctx, current_time,
|
||||
(int)seek.amount);
|
||||
hr_seek_very_exact = true;
|
||||
break;
|
||||
case MPSEEK_RELATIVE:
|
||||
@@ -389,7 +400,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
||||
|
||||
if (hr_seek) {
|
||||
mpctx->hrseek_active = true;
|
||||
mpctx->hrseek_backstep = seek.type == MPSEEK_BACKSTEP;
|
||||
mpctx->hrseek_backstep = seek.type == MPSEEK_FRAMESTEP && seek.amount == -1;
|
||||
mpctx->hrseek_pts = seek_pts * mpctx->play_dir;
|
||||
|
||||
// allow decoder to drop frames before hrseek_pts
|
||||
@@ -445,7 +456,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
|
||||
return;
|
||||
case MPSEEK_ABSOLUTE:
|
||||
case MPSEEK_FACTOR:
|
||||
case MPSEEK_BACKSTEP:
|
||||
case MPSEEK_FRAMESTEP:
|
||||
case MPSEEK_CHAPTER:
|
||||
*seek = (struct seek_params) {
|
||||
.type = type,
|
||||
|
||||
Reference in New Issue
Block a user