mirror of
https://github.com/mpv-player/mpv
synced 2024-12-26 00:42:57 +00:00
player: when seeking past EOF with --keep-open, seek to last frame
It feels strange that seeking past EOF with --keep-open actually leaves the player at a random position. You can't even unpause, because the demuxer is in the EOF state, and what you see on screen is just what was around before the seek. Improve this by attempting to seek to the last video frame if EOF happens. We explicitly don't do this if EOF was reached normally to increase robustness (if the VO got a frame since the last seek, it obviously means we had normal playback before EOF). If an error happens when trying to find the last frame (such as not actually finding a last frame because e.g. the demuxer misbehaves), this will probably turn your CPU into a heater. There is no logic to prevent reinitiating the last-frame search if the last-frame search reached EOF. (Pausing usually prevents that EOF is reached again after a successful last-frame search.) Fixes #819.
This commit is contained in:
parent
6adaddbe63
commit
090f6cfc30
@ -224,14 +224,16 @@ typedef struct MPContext {
|
||||
struct vo *video_out;
|
||||
// next_frame[0] is the next frame, next_frame[1] the one after that.
|
||||
struct mp_image *next_frame[2];
|
||||
struct mp_image *saved_frame; // for hrseek_lastframe
|
||||
|
||||
enum playback_status video_status, audio_status;
|
||||
bool restart_complete;
|
||||
/* Set if audio should be timed to start with video frame after seeking,
|
||||
* not set when e.g. playing cover art */
|
||||
bool sync_audio_to_video;
|
||||
bool hrseek_active;
|
||||
bool hrseek_framedrop;
|
||||
bool hrseek_active; // skip all data until hrseek_pts
|
||||
bool hrseek_framedrop; // allow decoder to drop frames before hrseek_pts
|
||||
bool hrseek_lastframe; // drop everything until last frame reached
|
||||
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
|
||||
|
@ -151,6 +151,7 @@ void reset_playback_state(struct MPContext *mpctx)
|
||||
|
||||
mpctx->hrseek_active = false;
|
||||
mpctx->hrseek_framedrop = false;
|
||||
mpctx->hrseek_lastframe = false;
|
||||
mpctx->playback_pts = MP_NOPTS_VALUE;
|
||||
mpctx->last_seek_pts = MP_NOPTS_VALUE;
|
||||
mpctx->cache_wait_time = 0;
|
||||
@ -780,6 +781,28 @@ static void handle_loop_file(struct MPContext *mpctx)
|
||||
}
|
||||
}
|
||||
|
||||
static void seek_to_last_frame(struct MPContext *mpctx)
|
||||
{
|
||||
if (!mpctx->d_video)
|
||||
return;
|
||||
MP_VERBOSE(mpctx, "seeking to last frame...\n");
|
||||
// Approximately seek close to the end of the file.
|
||||
// Usually, it will seek some seconds before end.
|
||||
double end = get_play_end_pts(mpctx);
|
||||
if (end == MP_NOPTS_VALUE)
|
||||
end = get_time_length(mpctx);
|
||||
mp_seek(mpctx, (struct seek_params){
|
||||
.type = MPSEEK_ABSOLUTE,
|
||||
.amount = end,
|
||||
.exact = 2, // "very exact", no framedrop
|
||||
}, false);
|
||||
// Make it exact: stop seek only if last frame was reached.
|
||||
if (mpctx->hrseek_active) {
|
||||
mpctx->hrseek_pts = 1e99; // "infinite"
|
||||
mpctx->hrseek_lastframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_keep_open(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
@ -787,8 +810,11 @@ static void handle_keep_open(struct MPContext *mpctx)
|
||||
!playlist_get_next(mpctx->playlist, 1) && opts->loop_times < 0)
|
||||
{
|
||||
mpctx->stop_play = KEEP_PLAYING;
|
||||
if (mpctx->d_video)
|
||||
if (mpctx->d_video) {
|
||||
if (!vo_has_frame(mpctx->video_out)) // EOF not reached normally
|
||||
seek_to_last_frame(mpctx);
|
||||
mpctx->playback_pts = mpctx->last_vo_pts;
|
||||
}
|
||||
if (!mpctx->opts->pause)
|
||||
pause_player(mpctx);
|
||||
}
|
||||
|
@ -214,6 +214,7 @@ void reset_video_state(struct MPContext *mpctx)
|
||||
|
||||
mp_image_unrefp(&mpctx->next_frame[0]);
|
||||
mp_image_unrefp(&mpctx->next_frame[1]);
|
||||
mp_image_unrefp(&mpctx->saved_frame);
|
||||
|
||||
mpctx->delay = 0;
|
||||
mpctx->time_frame = 0;
|
||||
@ -534,6 +535,8 @@ static bool have_new_frame(struct MPContext *mpctx)
|
||||
// returns VD_* code
|
||||
static int video_output_image(struct MPContext *mpctx, double endpts)
|
||||
{
|
||||
bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
|
||||
|
||||
if (mpctx->d_video->header->attached_picture) {
|
||||
if (vo_has_frame(mpctx->video_out))
|
||||
return VD_EOF;
|
||||
@ -591,16 +594,18 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
|
||||
add_frame_pts(mpctx, img->pts);
|
||||
|
||||
bool drop = false;
|
||||
bool hrseek = mpctx->hrseek_active
|
||||
&& mpctx->video_status == STATUS_SYNCING;
|
||||
if (hrseek && img->pts < mpctx->hrseek_pts - .005)
|
||||
drop = true;
|
||||
if ((endpts != MP_NOPTS_VALUE && img->pts >= endpts) ||
|
||||
mpctx->max_frames == 0)
|
||||
{
|
||||
drop = true;
|
||||
r = VD_EOF;
|
||||
}
|
||||
if (!drop && hrseek && mpctx->hrseek_lastframe) {
|
||||
mp_image_setrefp(&mpctx->saved_frame, img);
|
||||
drop = true;
|
||||
}
|
||||
if (hrseek && img->pts < mpctx->hrseek_pts - .005)
|
||||
drop = true;
|
||||
if (drop) {
|
||||
talloc_free(img);
|
||||
} else {
|
||||
@ -613,6 +618,13 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
|
||||
if (have_new_frame(mpctx) || (r <= 0 && mpctx->next_frame[0]))
|
||||
return VD_NEW_FRAME;
|
||||
|
||||
// Last-frame seek
|
||||
if (r <= 0 && hrseek && mpctx->hrseek_lastframe && mpctx->saved_frame) {
|
||||
mpctx->next_frame[1] = mpctx->saved_frame;
|
||||
mpctx->saved_frame = NULL;
|
||||
return VD_PROGRESS;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user