summaryrefslogtreecommitdiffstats
path: root/player/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/video.c')
-rw-r--r--player/video.c220
1 files changed, 146 insertions, 74 deletions
diff --git a/player/video.c b/player/video.c
index faa8a652bb..24e01d3c06 100644
--- a/player/video.c
+++ b/player/video.c
@@ -226,6 +226,8 @@ int reinit_video_filters(struct MPContext *mpctx)
if (need_reconfig)
filter_reconfig(vo_c);
+ mp_force_video_refresh(mpctx);
+
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
return vo_c->vf->initialized;
@@ -237,14 +239,15 @@ static void vo_chain_reset_state(struct vo_chain *vo_c)
if (vo_c->vf->initialized == 1)
vf_seek_reset(vo_c->vf);
vo_seek_reset(vo_c->vo);
+
+ if (vo_c->video_src)
+ video_reset(vo_c->video_src);
}
void reset_video_state(struct MPContext *mpctx)
{
- if (mpctx->vo_chain) {
- video_reset(mpctx->vo_chain->video_src);
+ if (mpctx->vo_chain)
vo_chain_reset_state(mpctx->vo_chain);
- }
for (int n = 0; n < mpctx->num_next_frames; n++)
mp_image_unrefp(&mpctx->next_frames[n]);
@@ -254,7 +257,6 @@ void reset_video_state(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->time_frame = 0;
mpctx->video_pts = MP_NOPTS_VALUE;
- mpctx->video_next_pts = MP_NOPTS_VALUE;
mpctx->num_past_frames = 0;
mpctx->total_avsync_change = 0;
mpctx->last_av_difference = 0;
@@ -279,12 +281,20 @@ void uninit_video_out(struct MPContext *mpctx)
static void vo_chain_uninit(struct vo_chain *vo_c)
{
- mp_image_unrefp(&vo_c->input_mpi);
- if (vo_c) {
- vf_destroy(vo_c->vf);
- if (vo_c->video_src)
- video_uninit(vo_c->video_src);
+ struct track *track = vo_c->track;
+ if (track) {
+ assert(track->vo_c == vo_c);
+ track->vo_c = NULL;
+ assert(track->d_video == vo_c->video_src);
+ track->d_video = NULL;
+ video_uninit(vo_c->video_src);
}
+
+ if (vo_c->filter_src)
+ lavfi_set_connected(vo_c->filter_src, false);
+
+ mp_image_unrefp(&vo_c->input_mpi);
+ vf_destroy(vo_c->vf);
talloc_free(vo_c);
// this does not free the VO
}
@@ -295,22 +305,73 @@ void uninit_video_chain(struct MPContext *mpctx)
reset_video_state(mpctx);
vo_chain_uninit(mpctx->vo_chain);
mpctx->vo_chain = NULL;
+
mpctx->video_status = STATUS_EOF;
- mpctx->sync_audio_to_video = false;
- reselect_demux_streams(mpctx);
+
remove_deint_filter(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
}
+int init_video_decoder(struct MPContext *mpctx, struct track *track)
+{
+ assert(!track->d_video);
+ if (!track->stream)
+ goto err_out;
+
+ track->d_video = talloc_zero(NULL, struct dec_video);
+ struct dec_video *d_video = track->d_video;
+ d_video->global = mpctx->global;
+ d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
+ d_video->opts = mpctx->opts;
+ d_video->header = track->stream;
+ d_video->codec = track->stream->codec;
+ d_video->fps = d_video->header->codec->fps;
+ if (mpctx->vo_chain)
+ d_video->hwdec_info = mpctx->vo_chain->hwdec_info;
+
+ MP_VERBOSE(d_video, "Container reported FPS: %f\n", d_video->fps);
+
+ if (d_video->opts->force_fps) {
+ d_video->fps = d_video->opts->force_fps;
+ MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
+ MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
+ }
+
+ if (!video_init_best_codec(d_video))
+ goto err_out;
+
+ return 1;
+
+err_out:
+ if (track->sink)
+ lavfi_set_connected(track->sink, false);
+ track->sink = NULL;
+ video_uninit(track->d_video);
+ track->d_video = NULL;
+ error_on_track(mpctx, track);
+ return 0;
+}
+
int reinit_video_chain(struct MPContext *mpctx)
{
+ return reinit_video_chain_src(mpctx, NULL);
+}
+
+int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
+{
struct MPOpts *opts = mpctx->opts;
+ struct track *track = NULL;
+ struct sh_stream *sh = NULL;
+ if (!src) {
+ track = mpctx->current_track[0][STREAM_VIDEO];
+ if (!track)
+ return 0;
+ sh = track->stream;
+ if (!sh)
+ goto no_video;
+ }
assert(!mpctx->vo_chain);
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct sh_stream *sh = track ? track->stream : NULL;
- if (!sh)
- goto no_video;
if (!mpctx->video_out) {
struct vo_extra ex = {
@@ -335,49 +396,40 @@ int reinit_video_chain(struct MPContext *mpctx)
mpctx->vo_chain = vo_c;
vo_c->log = mpctx->log;
vo_c->vo = mpctx->video_out;
+ vo_c->vf = vf_new(mpctx->global);
vo_control(vo_c->vo, VOCTRL_GET_HWDEC_INFO, &vo_c->hwdec_info);
- track->d_video = talloc_zero(NULL, struct dec_video);
- struct dec_video *d_video = track->d_video;
- d_video->global = mpctx->global;
- d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
- d_video->opts = mpctx->opts;
- d_video->header = sh;
- d_video->fps = sh->codec->fps;
- d_video->hwdec_info = vo_c->hwdec_info;
+ vo_c->filter_src = src;
+ if (!vo_c->filter_src) {
+ vo_c->track = track;
+ track->vo_c = vo_c;
+ if (!init_video_decoder(mpctx, track))
+ goto err_out;
- MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->codec->fps);
+ vo_c->video_src = track->d_video;
+ vo_c->container_fps = vo_c->video_src->fps;
+ vo_c->is_coverart = !!sh->attached_picture;
- if (opts->force_fps) {
- d_video->fps = opts->force_fps;
- MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
- MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
+ track->vo_c = vo_c;
+ vo_c->track = track;
}
- vo_c->container_fps = d_video->fps;
- vo_c->video_src = d_video;
-
#if HAVE_ENCODING
if (mpctx->encode_lavc_ctx)
- encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
+ encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, vo_c->container_fps);
#endif
recreate_video_filters(mpctx);
- if (!video_init_best_codec(d_video, opts->video_decoders))
- goto err_out;
-
bool saver_state = opts->pause || !opts->stop_screensaver;
vo_control(vo_c->vo, saver_state ? VOCTRL_RESTORE_SCREENSAVER
: VOCTRL_KILL_SCREENSAVER, NULL);
vo_set_paused(vo_c->vo, mpctx->paused);
- mpctx->sync_audio_to_video = !sh->attached_picture;
-
// If we switch on video again, ensure audio position matches up.
- if (mpctx->d_audio)
+ if (mpctx->ao_chain)
mpctx->audio_status = STATUS_SYNCING;
reset_video_state(mpctx);
@@ -388,8 +440,7 @@ int reinit_video_chain(struct MPContext *mpctx)
err_out:
no_video:
uninit_video_chain(mpctx);
- if (track)
- error_on_track(mpctx, track);
+ error_on_track(mpctx, track);
handle_force_window(mpctx, true);
return 0;
}
@@ -418,7 +469,8 @@ static bool check_framedrop(struct MPContext *mpctx, struct vo_chain *vo_c)
struct MPOpts *opts = mpctx->opts;
// check for frame-drop:
if (mpctx->video_status == STATUS_PLAYING && !mpctx->paused &&
- mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao))
+ mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao) &&
+ vo_c->video_src)
{
float fps = vo_c->container_fps;
double frame_time = fps > 0 ? 1.0 / fps : 0;
@@ -437,24 +489,30 @@ static bool check_framedrop(struct MPContext *mpctx, struct vo_chain *vo_c)
static int decode_image(struct MPContext *mpctx)
{
struct vo_chain *vo_c = mpctx->vo_chain;
- struct dec_video *d_video = vo_c->video_src;
+ if (vo_c->input_mpi)
+ return VD_PROGRESS;
- bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING &&
- mpctx->hrseek_framedrop;
- video_set_start(d_video, hrseek ? mpctx->hrseek_pts : MP_NOPTS_VALUE);
+ int res = DATA_EOF;
+ if (vo_c->filter_src) {
+ res = lavfi_request_frame_v(vo_c->filter_src, &vo_c->input_mpi);
+ } else if (vo_c->video_src) {
+ struct dec_video *d_video = vo_c->video_src;
+ bool hrseek = mpctx->hrseek_active && mpctx->hrseek_framedrop &&
+ mpctx->video_status == STATUS_SYNCING;
+ video_set_start(d_video, hrseek ? mpctx->hrseek_pts : MP_NOPTS_VALUE);
- video_set_framedrop(d_video, check_framedrop(mpctx, vo_c));
+ video_set_framedrop(d_video, check_framedrop(mpctx, vo_c));
- video_work(d_video);
+ video_work(d_video);
+ res = video_get_frame(d_video, &vo_c->input_mpi);
+ }
- assert(!vo_c->input_mpi);
- int st = video_get_frame(d_video, &vo_c->input_mpi);
- if (vo_c->input_mpi)
- vo_c->input_format = vo_c->input_mpi->params;
- switch (st) {
- case VIDEO_WAIT: return VD_WAIT;
- case VIDEO_EOF: return VD_EOF;
- default: return VD_PROGRESS;
+ switch (res) {
+ case DATA_WAIT: return VD_WAIT;
+ case DATA_OK:
+ case DATA_AGAIN: return VD_PROGRESS;
+ case DATA_EOF: return VD_EOF;
+ default: abort();
}
}
@@ -506,7 +564,7 @@ static int video_filter(struct MPContext *mpctx, bool eof)
// Most video filters don't work with hardware decoding, so this
// might be the reason why filter reconfig failed.
- if (vf->initialized < 0 &&
+ if (vf->initialized < 0 && vo_c->video_src &&
video_vd_control(vo_c->video_src, VDCTRL_FORCE_HWDEC_FALLBACK, NULL)
== CONTROL_OK)
{
@@ -554,6 +612,8 @@ static int video_decode_and_filter(struct MPContext *mpctx)
if (r == VD_WAIT)
return r;
}
+ if (vo_c->input_mpi)
+ vo_c->input_format = vo_c->input_mpi->params;
bool eof = !vo_c->input_mpi && (r == VD_EOF || r < 0);
r = video_filter(mpctx, eof);
@@ -627,13 +687,14 @@ static void handle_new_frame(struct MPContext *mpctx)
frame_time = 0;
}
}
- mpctx->video_next_pts = pts;
mpctx->delay -= frame_time;
if (mpctx->video_status >= STATUS_PLAYING) {
mpctx->time_frame += frame_time / mpctx->video_speed;
adjust_sync(mpctx, pts, frame_time);
}
- mpctx->dropped_frames_start = mpctx->vo_chain->video_src->dropped_frames;
+ struct dec_video *d_video = mpctx->vo_chain->video_src;
+ if (d_video)
+ mpctx->dropped_frames_start = d_video->dropped_frames;
MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
}
@@ -688,12 +749,12 @@ static bool have_new_frame(struct MPContext *mpctx, bool eof)
// Fill mpctx->next_frames[] with a newly filtered or decoded image.
// returns VD_* code
-static int video_output_image(struct MPContext *mpctx, double endpts)
+static int video_output_image(struct MPContext *mpctx)
{
+ struct vo_chain *vo_c = mpctx->vo_chain;
bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- if (track && track->stream && track->stream->attached_picture) {
+ if (vo_c->is_coverart) {
if (vo_has_frame(mpctx->video_out))
return VD_EOF;
hrseek = false;
@@ -709,8 +770,9 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
r = video_decode_and_filter(mpctx);
if (r < 0)
return r; // error
- struct mp_image *img = vf_read_output_frame(mpctx->vo_chain->vf);
+ struct mp_image *img = vf_read_output_frame(vo_c->vf);
if (img) {
+ double endpts = get_play_end_pts(mpctx);
if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) {
r = VD_EOF;
} else if (mpctx->max_frames == 0) {
@@ -721,6 +783,11 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
/* just skip - but save if backstep active */
if (mpctx->hrseek_backstep)
mp_image_setrefp(&mpctx->saved_frame, img);
+ } else if (mpctx->video_status == STATUS_SYNCING &&
+ mpctx->playback_pts != MP_NOPTS_VALUE &&
+ img->pts < mpctx->playback_pts && !vo_c->is_coverart)
+ {
+ /* skip after stream-switching */
} else {
if (hrseek && mpctx->hrseek_backstep) {
if (mpctx->saved_frame) {
@@ -756,7 +823,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx)
struct MPOpts *opts = mpctx->opts;
struct vo *vo = mpctx->video_out;
- if (!mpctx->sync_audio_to_video || mpctx->video_status < STATUS_READY) {
+ if (mpctx->vo_chain->is_coverart || mpctx->video_status < STATUS_READY) {
mpctx->time_frame = 0;
} else if (mpctx->display_sync_active || opts->video_sync == VS_NONE) {
// don't touch the timing
@@ -889,8 +956,8 @@ static double find_best_speed(struct MPContext *mpctx, double vsync)
static bool using_spdif_passthrough(struct MPContext *mpctx)
{
- if (mpctx->d_audio && mpctx->d_audio->afilter)
- return !af_fmt_is_pcm(mpctx->d_audio->afilter->input.format);
+ if (mpctx->ao_chain)
+ return !af_fmt_is_pcm(mpctx->ao_chain->input_format.format);
return false;
}
@@ -1137,8 +1204,9 @@ static void calculate_frame_duration(struct MPContext *mpctx)
if (pts0 != MP_NOPTS_VALUE && pts1 != MP_NOPTS_VALUE && pts1 >= pts0)
duration = pts1 - pts0;
} else {
- // E.g. last frame on EOF.
- duration = demux_duration;
+ // E.g. last frame on EOF. Only use it if it's significant.
+ if (demux_duration >= 0.1)
+ duration = demux_duration;
}
// The following code tries to compensate for rounded Matroska timestamps
@@ -1176,12 +1244,13 @@ static void calculate_frame_duration(struct MPContext *mpctx)
mpctx->past_frames[0].approx_duration = approx_duration;
}
-void write_video(struct MPContext *mpctx, double endpts)
+void write_video(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
if (!mpctx->vo_chain)
return;
+ struct track *track = mpctx->vo_chain->track;
struct vo *vo = mpctx->vo_chain->vo;
// Actual playback starts when both audio and video are ready.
@@ -1191,7 +1260,7 @@ void write_video(struct MPContext *mpctx, double endpts)
if (mpctx->paused && mpctx->video_status >= STATUS_READY)
return;
- int r = video_output_image(mpctx, endpts);
+ int r = video_output_image(mpctx);
MP_TRACE(mpctx, "video_output_image: %d\n", r);
if (r < 0)
@@ -1202,8 +1271,11 @@ void write_video(struct MPContext *mpctx, double endpts)
if (r == VD_EOF) {
int prev_state = mpctx->video_status;
- mpctx->video_status =
- vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
+ mpctx->video_status = STATUS_EOF;
+ if (mpctx->num_past_frames > 0 && mpctx->past_frames[0].duration > 0) {
+ if (vo_still_displaying(vo))
+ mpctx->video_status = STATUS_DRAINING;
+ }
mpctx->delay = 0;
mpctx->last_av_difference = 0;
MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
@@ -1282,7 +1354,7 @@ void write_video(struct MPContext *mpctx, double endpts)
.pts = pts,
.duration = -1,
.still = mpctx->step_frames > 0,
- .num_frames = mpctx->num_next_frames,
+ .num_frames = MPMIN(mpctx->num_next_frames, VO_MAX_REQ_FRAMES),
.num_vsyncs = 1,
};
for (int n = 0; n < dummy.num_frames; n++)
@@ -1328,7 +1400,7 @@ void write_video(struct MPContext *mpctx, double endpts)
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
- if (!mpctx->sync_audio_to_video)
+ if (mpctx->vo_chain->is_coverart)
mpctx->video_status = STATUS_EOF;
if (mpctx->video_status != STATUS_EOF) {
@@ -1349,7 +1421,7 @@ void write_video(struct MPContext *mpctx, double endpts)
error:
MP_FATAL(mpctx, "Could not initialize video chain.\n");
uninit_video_chain(mpctx);
- error_on_track(mpctx, mpctx->current_track[STREAM_VIDEO][0]);
+ error_on_track(mpctx, track);
handle_force_window(mpctx, true);
mpctx->sleeptime = 0;
}