mirror of
https://github.com/mpv-player/mpv
synced 2025-02-16 20:27:23 +00:00
player: make looping slightly more seamless
This affects A-B loops and --loop-file, and audio. Instead of dropping audio by resetting the AO, try to make it seamless by not sending data after the loop point, and after the seek send new data without a reset.
This commit is contained in:
parent
bbcd0b6a03
commit
a1dec6f54a
@ -75,8 +75,6 @@ struct command_ctx {
|
||||
double last_seek_pts;
|
||||
double marked_pts;
|
||||
|
||||
double prev_pts;
|
||||
|
||||
char **warned_deprecated;
|
||||
int num_warned_deprecated;
|
||||
|
||||
@ -220,7 +218,7 @@ static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
|
||||
}
|
||||
|
||||
// Call before a seek, in order to allow revert-seek to undo the seek.
|
||||
static void mark_seek(struct MPContext *mpctx)
|
||||
void mark_seek(struct MPContext *mpctx)
|
||||
{
|
||||
struct command_ctx *cmd = mpctx->command_ctx;
|
||||
double now = mp_time_sec();
|
||||
@ -3301,9 +3299,11 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
|
||||
}
|
||||
int r = mp_property_generic_option(mpctx, prop, action, arg);
|
||||
if (r > 0 && action == M_PROPERTY_SET) {
|
||||
mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
|
||||
if (strcmp(prop->name, "ab-loop-b") == 0) {
|
||||
struct command_ctx *cctx = mpctx->command_ctx;
|
||||
cctx->prev_pts = opts->ab_loop[0];
|
||||
if (opts->ab_loop[1] != MP_NOPTS_VALUE &&
|
||||
mpctx->playback_pts <= opts->ab_loop[1])
|
||||
mpctx->ab_loop_clip = true;
|
||||
}
|
||||
// Update if visible
|
||||
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
|
||||
@ -5375,7 +5375,6 @@ void command_init(struct MPContext *mpctx)
|
||||
mpctx->command_ctx = talloc(NULL, struct command_ctx);
|
||||
*mpctx->command_ctx = (struct command_ctx){
|
||||
.last_seek_pts = MP_NOPTS_VALUE,
|
||||
.prev_pts = MP_NOPTS_VALUE,
|
||||
};
|
||||
}
|
||||
|
||||
@ -5388,8 +5387,6 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
|
||||
ctx->marked_pts = MP_NOPTS_VALUE;
|
||||
}
|
||||
|
||||
if (event == MPV_EVENT_SEEK)
|
||||
ctx->prev_pts = MP_NOPTS_VALUE;
|
||||
if (event == MPV_EVENT_IDLE)
|
||||
ctx->is_idle = true;
|
||||
if (event == MPV_EVENT_START_FILE)
|
||||
@ -5400,35 +5397,6 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
void handle_ab_loop(struct MPContext *mpctx)
|
||||
{
|
||||
struct command_ctx *ctx = mpctx->command_ctx;
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
|
||||
if (opts->pause)
|
||||
return;
|
||||
|
||||
double now = mpctx->restart_complete ? mpctx->playback_pts : MP_NOPTS_VALUE;
|
||||
if (now != MP_NOPTS_VALUE && (opts->ab_loop[0] != MP_NOPTS_VALUE ||
|
||||
opts->ab_loop[1] != MP_NOPTS_VALUE))
|
||||
{
|
||||
double start = opts->ab_loop[0];
|
||||
if (start == MP_NOPTS_VALUE)
|
||||
start = 0;
|
||||
double end = opts->ab_loop[1];
|
||||
if (end == MP_NOPTS_VALUE)
|
||||
end = INFINITY;
|
||||
if (ctx->prev_pts >= start && ctx->prev_pts < end &&
|
||||
(now >= end || mpctx->stop_play == AT_END_OF_FILE))
|
||||
{
|
||||
mark_seek(mpctx);
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, start,
|
||||
MPSEEK_EXACT, MPSEEK_FLAG_DELAY);
|
||||
}
|
||||
}
|
||||
ctx->prev_pts = now;
|
||||
}
|
||||
|
||||
void handle_command_updates(struct MPContext *mpctx)
|
||||
{
|
||||
struct command_ctx *ctx = mpctx->command_ctx;
|
||||
|
@ -57,6 +57,6 @@ enum {
|
||||
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
|
||||
void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
|
||||
|
||||
void handle_ab_loop(struct MPContext *mpctx);
|
||||
void mark_seek(struct MPContext *mpctx);
|
||||
|
||||
#endif /* MPLAYER_COMMAND_H */
|
||||
|
@ -82,6 +82,7 @@ enum seek_precision {
|
||||
|
||||
enum seek_flags {
|
||||
MPSEEK_FLAG_DELAY = 1 << 0, // give player chance to coalesce multiple seeks
|
||||
MPSEEK_FLAG_NOFLUSH = 1 << 1, // keeping remaining data for seamless loops
|
||||
};
|
||||
|
||||
enum video_sync {
|
||||
@ -326,6 +327,7 @@ typedef struct MPContext {
|
||||
bool hrseek_lastframe; // drop everything until last frame reached
|
||||
bool hrseek_backstep; // go to frame before seek target
|
||||
double hrseek_pts;
|
||||
bool ab_loop_clip; // clip to the "b" part of an A-B loop if available
|
||||
// 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 video frame.
|
||||
|
@ -90,6 +90,12 @@ double get_play_end_pts(struct MPContext *mpctx)
|
||||
if (cend != MP_NOPTS_VALUE && (end == MP_NOPTS_VALUE || cend < end))
|
||||
end = cend;
|
||||
}
|
||||
if (mpctx->ab_loop_clip && opts->ab_loop[1] != MP_NOPTS_VALUE &&
|
||||
opts->ab_loop[1] > opts->ab_loop[0])
|
||||
{
|
||||
if (end == MP_NOPTS_VALUE || end > opts->ab_loop[1])
|
||||
end = opts->ab_loop[1];
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
@ -172,6 +172,7 @@ void reset_playback_state(struct MPContext *mpctx)
|
||||
mpctx->last_seek_pts = MP_NOPTS_VALUE;
|
||||
mpctx->cache_wait_time = 0;
|
||||
mpctx->step_frames = 0;
|
||||
mpctx->ab_loop_clip = true;
|
||||
mpctx->restart_complete = false;
|
||||
|
||||
#if HAVE_ENCODING
|
||||
@ -272,7 +273,9 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
||||
}
|
||||
}
|
||||
|
||||
clear_audio_output_buffers(mpctx);
|
||||
if (!(seek.flags & MPSEEK_FLAG_NOFLUSH))
|
||||
clear_audio_output_buffers(mpctx);
|
||||
|
||||
reset_playback_state(mpctx);
|
||||
|
||||
/* Use the target time as "current position" for further relative
|
||||
@ -301,6 +304,8 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
|
||||
|
||||
mpctx->audio_allow_second_chance_seek =
|
||||
!hr_seek && !(demux_flags & SEEK_FORWARD);
|
||||
|
||||
mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1];
|
||||
}
|
||||
|
||||
// This combines consecutive seek requests.
|
||||
@ -697,10 +702,25 @@ static void handle_sstep(struct MPContext *mpctx)
|
||||
static void handle_loop_file(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
|
||||
if (mpctx->stop_play == AT_END_OF_FILE &&
|
||||
(opts->ab_loop[0] != MP_NOPTS_VALUE || opts->ab_loop[1] != MP_NOPTS_VALUE))
|
||||
{
|
||||
// Assumes execute_queued_seek() happens before next audio/video is
|
||||
// attempted to be decoded or filtered.
|
||||
mpctx->stop_play = KEEP_PLAYING;
|
||||
double start = 0;
|
||||
if (opts->ab_loop[0] != MP_NOPTS_VALUE)
|
||||
start = opts->ab_loop[0];
|
||||
mark_seek(mpctx);
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, start, MPSEEK_EXACT,
|
||||
MPSEEK_FLAG_NOFLUSH);
|
||||
}
|
||||
|
||||
if (opts->loop_file && mpctx->stop_play == AT_END_OF_FILE) {
|
||||
mpctx->stop_play = KEEP_PLAYING;
|
||||
set_osd_function(mpctx, OSD_FFW);
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, MPSEEK_DEFAULT, 0);
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, MPSEEK_DEFAULT, MPSEEK_FLAG_NOFLUSH);
|
||||
if (opts->loop_file > 0)
|
||||
opts->loop_file--;
|
||||
}
|
||||
@ -894,6 +914,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
|
||||
mpctx->hrseek_active = false;
|
||||
mpctx->restart_complete = true;
|
||||
mpctx->audio_allow_second_chance_seek = false;
|
||||
handle_playback_time(mpctx);
|
||||
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
|
||||
if (!mpctx->playing_msg_shown) {
|
||||
if (opts->playing_msg && opts->playing_msg[0]) {
|
||||
@ -913,6 +934,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
|
||||
}
|
||||
mpctx->playing_msg_shown = true;
|
||||
mpctx->sleeptime = 0;
|
||||
mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
|
||||
MP_VERBOSE(mpctx, "playback restart complete\n");
|
||||
}
|
||||
}
|
||||
@ -1013,8 +1035,6 @@ void run_playloop(struct MPContext *mpctx)
|
||||
|
||||
handle_loop_file(mpctx);
|
||||
|
||||
handle_ab_loop(mpctx);
|
||||
|
||||
handle_keep_open(mpctx);
|
||||
|
||||
handle_sstep(mpctx);
|
||||
|
Loading…
Reference in New Issue
Block a user