summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
Diffstat (limited to 'player')
-rw-r--r--player/audio.c3
-rw-r--r--player/core.h2
-rw-r--r--player/misc.c13
-rw-r--r--player/osd.c2
-rw-r--r--player/playloop.c32
-rw-r--r--player/video.c62
6 files changed, 67 insertions, 47 deletions
diff --git a/player/audio.c b/player/audio.c
index aae1198835..8aba3d860b 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -625,7 +625,8 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
!mp_audio_buffer_samples(mpctx->ao_chain->ao_buffer))
return false; // no audio read yet
- bool sync_to_video = mpctx->vo_chain && mpctx->video_status != STATUS_EOF;
+ bool sync_to_video = mpctx->vo_chain && mpctx->video_status != STATUS_EOF &&
+ !mpctx->vo_chain->is_sparse;
double sync_pts = MP_NOPTS_VALUE;
if (sync_to_video) {
diff --git a/player/core.h b/player/core.h
index 865b2e9f53..a6606af91c 100644
--- a/player/core.h
+++ b/player/core.h
@@ -230,6 +230,8 @@ enum playback_status {
STATUS_EOF, // playback has ended, or is disabled
};
+const char *mp_status_str(enum playback_status st);
+
#define NUM_PTRACKS 2
typedef struct MPContext {
diff --git a/player/misc.c b/player/misc.c
index f6a63ec7ef..4246c1c6f9 100644
--- a/player/misc.c
+++ b/player/misc.c
@@ -301,3 +301,16 @@ void merge_playlist_files(struct playlist *pl)
playlist_add_file(pl, edl);
talloc_free(edl);
}
+
+const char *mp_status_str(enum playback_status st)
+{
+ switch (st) {
+ case STATUS_SYNCING: return "syncing";
+ case STATUS_FILLING: return "filling";
+ case STATUS_READY: return "ready";
+ case STATUS_PLAYING: return "playing";
+ case STATUS_DRAINING: return "draining";
+ case STATUS_EOF: return "eof";
+ default: return "bug";
+ }
+}
diff --git a/player/osd.c b/player/osd.c
index 93d6302cc0..ff9bd9b429 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -192,7 +192,7 @@ static char *get_term_status_msg(struct MPContext *mpctx)
saddf(&line, " x%4.2f", opts->playback_speed);
// A-V sync
- if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_coverart) {
+ if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_sparse) {
saddf(&line, " A-V:%7.3f", mpctx->last_av_difference);
if (fabs(mpctx->total_avsync_change) > 0.05)
saddf(&line, " ct:%7.3f", mpctx->total_avsync_change);
diff --git a/player/playloop.c b/player/playloop.c
index 6c390a76e2..597bcd3981 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -290,10 +290,11 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
double demux_pts = seek_pts;
- bool hr_seek = opts->correct_pts && seek.exact != MPSEEK_KEYFRAME &&
- ((opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) ||
- opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT) &&
- seek_pts != MP_NOPTS_VALUE;
+ bool hr_seek = (opts->correct_pts && seek.exact != MPSEEK_KEYFRAME &&
+ seek_pts != MP_NOPTS_VALUE) &&
+ (seek.exact >= MPSEEK_EXACT || opts->hr_seek == 1 ||
+ (opts->hr_seek >= 0 && seek.type == MPSEEK_ABSOLUTE) ||
+ (opts->hr_seek == 2 && (!mpctx->vo_chain || mpctx->vo_chain->is_sparse)));
if (seek.type == MPSEEK_FACTOR || seek.amount < 0 ||
(seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts))
@@ -661,7 +662,7 @@ static void handle_osd_redraw(struct MPContext *mpctx)
return;
}
// Don't redraw immediately during a seek (makes it significantly slower).
- bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_coverart;
+ bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_sparse;
if (use_video && mp_time_sec() - mpctx->start_timestamp < 0.1) {
mp_set_timeout(mpctx, 0.1);
return;
@@ -1110,13 +1111,6 @@ static void handle_playback_restart(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- // Do not wait for video stream if it only has sparse frames.
- if (mpctx->vo_chain && mpctx->vo_chain->is_sparse &&
- mpctx->video_status < STATUS_READY)
- {
- mpctx->video_status = STATUS_READY;
- }
-
if (mpctx->audio_status < STATUS_READY ||
mpctx->video_status < STATUS_READY)
return;
@@ -1127,6 +1121,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->video_status = STATUS_PLAYING;
get_relative_time(mpctx);
mp_wakeup_core(mpctx);
+ MP_DBG(mpctx, "starting video playback\n");
}
if (mpctx->audio_status == STATUS_READY) {
@@ -1139,14 +1134,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
return;
}
- // Video needed, but not started yet -> wait.
- if (mpctx->vo_chain &&
- !mpctx->vo_chain->is_coverart &&
- !mpctx->vo_chain->is_sparse &&
- mpctx->video_status <= STATUS_READY)
- return;
-
- MP_VERBOSE(mpctx, "starting audio playback\n");
+ MP_DBG(mpctx, "starting audio playback\n");
mpctx->audio_status = STATUS_PLAYING;
fill_audio_out_buffers(mpctx); // actually play prepared buffer
mp_wakeup_core(mpctx);
@@ -1178,7 +1166,9 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->playing_msg_shown = true;
mp_wakeup_core(mpctx);
update_ab_loop_clip(mpctx);
- MP_VERBOSE(mpctx, "playback restart complete @ %f\n", mpctx->playback_pts);
+ MP_VERBOSE(mpctx, "playback restart complete @ %f, audio=%s, video=%s\n",
+ mpctx->playback_pts, mp_status_str(mpctx->video_status),
+ mp_status_str(mpctx->audio_status));
// Continuous seeks past EOF => treat as EOF instead of repeating seek.
if (mpctx->seek.type == MPSEEK_RELATIVE && mpctx->seek.amount > 0 &&
diff --git a/player/video.c b/player/video.c
index 5d9879bc51..d1e1554ef0 100644
--- a/player/video.c
+++ b/player/video.c
@@ -261,7 +261,7 @@ void reinit_video_chain_src(struct MPContext *mpctx, struct track *track)
vo_c->dec_src = track->dec->f->pins[0];
vo_c->filter->container_fps = track->dec->fps;
vo_c->is_coverart = !!track->stream->attached_picture;
- vo_c->is_sparse = track->stream->still_image;
+ vo_c->is_sparse = track->stream->still_image || vo_c->is_coverart;
track->vo_c = vo_c;
vo_c->track = track;
@@ -402,23 +402,28 @@ static void shift_frames(struct MPContext *mpctx)
mpctx->num_next_frames -= 1;
}
+static bool use_video_lookahead(struct MPContext *mpctx)
+{
+ return mpctx->video_out &&
+ !(mpctx->video_out->driver->caps & VO_CAP_NORETAIN) &&
+ !(mpctx->opts->untimed || mpctx->video_out->driver->untimed) &&
+ !mpctx->opts->video_latency_hacks;
+}
+
static int get_req_frames(struct MPContext *mpctx, bool eof)
{
// On EOF, drain all frames.
if (eof)
return 1;
- if (mpctx->video_out->driver->caps & VO_CAP_NORETAIN)
+ if (!use_video_lookahead(mpctx))
return 1;
if (mpctx->vo_chain && mpctx->vo_chain->is_sparse)
return 1;
- if (mpctx->opts->untimed || mpctx->video_out->driver->untimed)
- return 1;
-
// Normally require at least 2 frames, so we can compute a frame duration.
- int min = mpctx->opts->video_latency_hacks ? 1 : 2;
+ int min = 2;
// On the first frame, output a new frame as quickly as possible.
if (mpctx->video_pts == MP_NOPTS_VALUE)
@@ -452,6 +457,7 @@ static bool have_new_frame(struct MPContext *mpctx, bool eof)
}
// Fill mpctx->next_frames[] with a newly filtered or decoded image.
+// logical_eof: is set to true if there is EOF after currently queued frames
// returns VD_* code
static int video_output_image(struct MPContext *mpctx, bool *logical_eof)
{
@@ -471,6 +477,7 @@ static int video_output_image(struct MPContext *mpctx, bool *logical_eof)
}
if (vo_c->is_coverart) {
+ *logical_eof = true;
if (vo_has_frame(mpctx->video_out))
return VD_EOF;
hrseek = false;
@@ -533,9 +540,10 @@ static int video_output_image(struct MPContext *mpctx, bool *logical_eof)
if (!hrseek)
mp_image_unrefp(&mpctx->saved_frame);
- // If hr-seek went past EOF, use the last frame.
- if (mpctx->saved_frame && r == VD_EOF) {
- add_new_frame(mpctx, mpctx->saved_frame);
+ if (r == VD_EOF) {
+ // If hr-seek went past EOF, use the last frame.
+ if (mpctx->saved_frame)
+ add_new_frame(mpctx, mpctx->saved_frame);
mpctx->saved_frame = NULL;
*logical_eof = true;
}
@@ -1010,7 +1018,8 @@ void write_video(struct MPContext *mpctx)
bool logical_eof = false;
int r = video_output_image(mpctx, &logical_eof);
- MP_TRACE(mpctx, "video_output_image: %d\n", r);
+ MP_TRACE(mpctx, "video_output_image: r=%d/eof=%d/st=%s\n", r, logical_eof,
+ mp_status_str(mpctx->video_status));
if (r < 0)
goto error;
@@ -1039,9 +1048,7 @@ void write_video(struct MPContext *mpctx)
if (mpctx->video_status <= STATUS_PLAYING) {
mpctx->video_status = STATUS_DRAINING;
get_relative_time(mpctx);
- if (mpctx->num_past_frames == 1 && mpctx->past_frames[0].pts == 0 &&
- !mpctx->ao_chain)
- {
+ if (vo_c->is_sparse && !mpctx->ao_chain) {
MP_VERBOSE(mpctx, "assuming this is an image\n");
mpctx->time_frame += opts->image_display_duration;
} else if (mpctx->last_frame_duration > 0) {
@@ -1082,6 +1089,20 @@ void write_video(struct MPContext *mpctx)
return;
}
+ if (logical_eof && !mpctx->num_past_frames && mpctx->num_next_frames == 1 &&
+ use_video_lookahead(mpctx) && !vo_c->is_sparse)
+ {
+ // Too much danger to accidentally mark video as sparse when e.g.
+ // seeking exactly to the last frame, so as a heuristic, do this only
+ // if it looks like the "first" video frame (unreliable, but often
+ // works out well). Helps with seeking with single-image video tracks,
+ // as well as detecting whether as video track is really an image.
+ if (mpctx->next_frames[0]->pts == 0) {
+ MP_VERBOSE(mpctx, "assuming single-image video stream\n");
+ vo_c->is_sparse = true;
+ }
+ }
+
// Filter output is different from VO input?
struct mp_image_params p = mpctx->next_frames[0]->params;
if (!vo->params || !mp_image_params_equal(&p, vo->params)) {
@@ -1199,17 +1220,10 @@ void write_video(struct MPContext *mpctx)
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
- if (vo_c->filter->got_output_eof && !mpctx->num_next_frames &&
- mpctx->ao_chain)
- {
- MP_VERBOSE(mpctx, "assuming this was the last video frame\n");
- // The main point of doing this is to prevent use of this for the
- // playback_pts if audio is still running (=> seek behavior).
- mpctx->video_status = STATUS_EOF;
- }
-
- // hr-seek past EOF -> returns last frame, but terminates playback.
- if (logical_eof)
+ // hr-seek past EOF -> returns last frame, but terminates playback. The
+ // early EOF is needed to trigger the exit before the next seek is executed.
+ // Always using early EOF breaks other cases, like images.
+ if (logical_eof && !mpctx->num_next_frames && mpctx->ao_chain)
mpctx->video_status = STATUS_EOF;
if (mpctx->video_status != STATUS_EOF) {