summaryrefslogtreecommitdiffstats
path: root/player/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/video.c')
-rw-r--r--player/video.c446
1 files changed, 248 insertions, 198 deletions
diff --git a/player/video.c b/player/video.c
index 9be4f6a9a0..faa8a652bb 100644
--- a/player/video.c
+++ b/player/video.c
@@ -22,7 +22,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "options/options.h"
@@ -66,74 +66,114 @@ static const char av_desync_help_text[] =
"position will not match to the video (see A-V status field).\n"
"\n";
-static bool decode_coverart(struct dec_video *d_video);
+int video_set_colors(struct vo_chain *vo_c, const char *item, int value)
+{
+ vf_equalizer_t data;
+
+ data.item = item;
+ data.value = value;
+
+ MP_VERBOSE(vo_c, "set video colors %s=%d \n", item, value);
+ if (video_vf_vo_control(vo_c, VFCTRL_SET_EQUALIZER, &data) == CONTROL_TRUE)
+ return 1;
+ MP_VERBOSE(vo_c, "Video attribute '%s' is not supported by selected vo.\n",
+ item);
+ return 0;
+}
-static void set_allowed_vo_formats(struct vf_chain *c, struct vo *vo)
+int video_get_colors(struct vo_chain *vo_c, const char *item, int *value)
{
- vo_query_formats(vo, c->allowed_output_formats);
+ vf_equalizer_t data;
+
+ data.item = item;
+
+ MP_VERBOSE(vo_c, "get video colors %s \n", item);
+ if (video_vf_vo_control(vo_c, VFCTRL_GET_EQUALIZER, &data) == CONTROL_TRUE) {
+ *value = data.value;
+ return 1;
+ }
+ return 0;
}
-static int try_filter(struct MPContext *mpctx, struct mp_image_params params,
- char *name, char *label, char **args)
+// Send a VCTRL, or if it doesn't work, translate it to a VOCTRL and try the VO.
+int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data)
+{
+ if (vo_c->vf->initialized > 0) {
+ int r = vf_control_any(vo_c->vf, vf_cmd, data);
+ if (r != CONTROL_UNKNOWN)
+ return r;
+ }
+
+ switch (vf_cmd) {
+ case VFCTRL_GET_DEINTERLACE:
+ return vo_control(vo_c->vo, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_SET_DEINTERLACE:
+ return vo_control(vo_c->vo, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_SET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!vo_c->vo->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_set_equalizer_args param = {
+ eq->item, eq->value
+ };
+ return vo_control(vo_c->vo, VOCTRL_SET_EQUALIZER, &param) == VO_TRUE;
+ }
+ case VFCTRL_GET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!vo_c->vo->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_get_equalizer_args param = {
+ eq->item, &eq->value
+ };
+ return vo_control(vo_c->vo, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
+ }
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static void set_allowed_vo_formats(struct vo_chain *vo_c)
{
- struct dec_video *d_video = mpctx->d_video;
+ vo_query_formats(vo_c->vo, vo_c->vf->allowed_output_formats);
+}
- struct vf_instance *vf = vf_append_filter(d_video->vfilter, name, args);
+static int try_filter(struct vo_chain *vo_c, struct mp_image_params params,
+ char *name, char *label, char **args)
+{
+ struct vf_instance *vf = vf_append_filter(vo_c->vf, name, args);
if (!vf)
return -1;
vf->label = talloc_strdup(vf, label);
- if (video_reconfig_filters(d_video, &params) < 0) {
- vf_remove_filter(d_video->vfilter, vf);
+ if (vf_reconfig(vo_c->vf, &params) < 0) {
+ vf_remove_filter(vo_c->vf, vf);
// restore
- video_reconfig_filters(d_video, &params);
+ vf_reconfig(vo_c->vf, &params);
return -1;
}
return 0;
}
-// Reconfigure the filter chain according to decoder output.
-// probe_only: don't force fallback to software when doing hw decoding, and
-// the filter chain couldn't be configured
-static void filter_reconfig(struct MPContext *mpctx,
- bool probe_only)
+// Reconfigure the filter chain according to the new input format.
+static void filter_reconfig(struct vo_chain *vo_c)
{
- struct dec_video *d_video = mpctx->d_video;
-
- struct mp_image_params params = d_video->decoder_output;
-
- mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
-
- set_allowed_vo_formats(d_video->vfilter, mpctx->video_out);
-
- if (video_reconfig_filters(d_video, &params) < 0) {
- // Most video filters don't work with hardware decoding, so this
- // might be the reason why filter reconfig failed.
- if (!probe_only &&
- video_vd_control(d_video, VDCTRL_FORCE_HWDEC_FALLBACK, NULL) == CONTROL_OK)
- {
- // Fallback active; decoder will return software format next
- // time. Don't abort video decoding.
- d_video->vfilter->initialized = 0;
- mp_image_unrefp(&d_video->waiting_decoded_mpi);
- d_video->decoder_output = (struct mp_image_params){0};
- MP_VERBOSE(mpctx, "hwdec falback due to filters.\n");
- }
+ struct mp_image_params params = vo_c->input_format;
+ if (!params.imgfmt)
return;
- }
- if (d_video->vfilter->initialized < 1)
+ set_allowed_vo_formats(vo_c);
+
+ if (vf_reconfig(vo_c->vf, &params) < 0)
return;
if (params.rotate && (params.rotate % 90 == 0)) {
- if (!(mpctx->video_out->driver->caps & VO_CAP_ROTATE90)) {
+ if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90)) {
// Try to insert a rotation filter.
char *args[] = {"angle", "auto", NULL};
- if (try_filter(mpctx, params, "rotate", "autorotate", args) >= 0) {
+ if (try_filter(vo_c, params, "rotate", "autorotate", args) >= 0) {
params.rotate = 0;
} else {
- MP_ERR(mpctx, "Can't insert rotation filter.\n");
+ MP_ERR(vo_c, "Can't insert rotation filter.\n");
}
}
}
@@ -144,8 +184,8 @@ static void filter_reconfig(struct MPContext *mpctx,
char *to = (char *)MP_STEREO3D_NAME(params.stereo_out);
if (to) {
char *args[] = {"in", "auto", "out", to, NULL, NULL};
- if (try_filter(mpctx, params, "stereo3d", "stereo3d", args) < 0)
- MP_ERR(mpctx, "Can't insert 3D conversion filter.\n");
+ if (try_filter(vo_c, params, "stereo3d", "stereo3d", args) < 0)
+ MP_ERR(vo_c, "Can't insert 3D conversion filter.\n");
}
}
}
@@ -153,49 +193,58 @@ static void filter_reconfig(struct MPContext *mpctx,
static void recreate_video_filters(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
- assert(d_video);
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ assert(vo_c);
- vf_destroy(d_video->vfilter);
- d_video->vfilter = vf_new(mpctx->global);
- d_video->vfilter->hwdec = d_video->hwdec_info;
- d_video->vfilter->wakeup_callback = wakeup_playloop;
- d_video->vfilter->wakeup_callback_ctx = mpctx;
- d_video->vfilter->container_fps = d_video->fps;
- vo_control(mpctx->video_out, VOCTRL_GET_DISPLAY_FPS,
- &d_video->vfilter->display_fps);
+ vf_destroy(vo_c->vf);
+ vo_c->vf = vf_new(mpctx->global);
+ vo_c->vf->hwdec = vo_c->hwdec_info;
+ vo_c->vf->wakeup_callback = wakeup_playloop;
+ vo_c->vf->wakeup_callback_ctx = mpctx;
+ vo_c->vf->container_fps = vo_c->container_fps;
+ vo_control(vo_c->vo, VOCTRL_GET_DISPLAY_FPS, &vo_c->vf->display_fps);
- vf_append_filter_list(d_video->vfilter, opts->vf_settings);
+ vf_append_filter_list(vo_c->vf, opts->vf_settings);
// for vf_sub
osd_set_render_subs_in_filter(mpctx->osd,
- vf_control_any(d_video->vfilter, VFCTRL_INIT_OSD, mpctx->osd) > 0);
+ vf_control_any(vo_c->vf, VFCTRL_INIT_OSD, mpctx->osd) > 0);
- set_allowed_vo_formats(d_video->vfilter, mpctx->video_out);
+ set_allowed_vo_formats(vo_c);
}
int reinit_video_filters(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
- if (!d_video)
+ if (!vo_c)
return 0;
- bool need_reconfig = d_video->vfilter->initialized != 0;
+ bool need_reconfig = vo_c->vf->initialized != 0;
recreate_video_filters(mpctx);
if (need_reconfig)
- filter_reconfig(mpctx, true);
+ filter_reconfig(vo_c);
- return d_video->vfilter->initialized;
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
+
+ return vo_c->vf->initialized;
+}
+
+static void vo_chain_reset_state(struct vo_chain *vo_c)
+{
+ mp_image_unrefp(&vo_c->input_mpi);
+ if (vo_c->vf->initialized == 1)
+ vf_seek_reset(vo_c->vf);
+ vo_seek_reset(vo_c->vo);
}
void reset_video_state(struct MPContext *mpctx)
{
- if (mpctx->d_video)
- video_reset_decoding(mpctx->d_video);
- if (mpctx->video_out)
- vo_seek_reset(mpctx->video_out);
+ if (mpctx->vo_chain) {
+ video_reset(mpctx->vo_chain->video_src);
+ vo_chain_reset_state(mpctx->vo_chain);
+ }
for (int n = 0; n < mpctx->num_next_frames; n++)
mp_image_unrefp(&mpctx->next_frames[n]);
@@ -209,14 +258,13 @@ void reset_video_state(struct MPContext *mpctx)
mpctx->num_past_frames = 0;
mpctx->total_avsync_change = 0;
mpctx->last_av_difference = 0;
- mpctx->dropped_frames_total = 0;
- mpctx->dropped_frames = 0;
+ mpctx->dropped_frames_start = 0;
mpctx->mistimed_frames_total = 0;
mpctx->drop_message_shown = 0;
mpctx->display_sync_drift_dir = 0;
mpctx->display_sync_broken = false;
- mpctx->video_status = mpctx->d_video ? STATUS_SYNCING : STATUS_EOF;
+ mpctx->video_status = mpctx->vo_chain ? STATUS_SYNCING : STATUS_EOF;
}
void uninit_video_out(struct MPContext *mpctx)
@@ -229,12 +277,24 @@ void uninit_video_out(struct MPContext *mpctx)
mpctx->video_out = NULL;
}
+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);
+ }
+ talloc_free(vo_c);
+ // this does not free the VO
+}
+
void uninit_video_chain(struct MPContext *mpctx)
{
- if (mpctx->d_video) {
+ if (mpctx->vo_chain) {
reset_video_state(mpctx);
- video_uninit(mpctx->d_video);
- mpctx->d_video = NULL;
+ 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);
@@ -246,7 +306,7 @@ void uninit_video_chain(struct MPContext *mpctx)
int reinit_video_chain(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- assert(!mpctx->d_video);
+ assert(!mpctx->vo_chain);
struct track *track = mpctx->current_track[0][STREAM_VIDEO];
struct sh_stream *sh = track ? track->stream : NULL;
if (!sh)
@@ -271,16 +331,23 @@ int reinit_video_chain(struct MPContext *mpctx)
update_window_title(mpctx, true);
- struct dec_video *d_video = talloc_zero(NULL, struct dec_video);
- mpctx->d_video = d_video;
+ struct vo_chain *vo_c = talloc_zero(NULL, struct vo_chain);
+ mpctx->vo_chain = vo_c;
+ vo_c->log = mpctx->log;
+ vo_c->vo = mpctx->video_out;
+
+ 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->video->fps;
- d_video->vo = mpctx->video_out;
+ d_video->fps = sh->codec->fps;
+ d_video->hwdec_info = vo_c->hwdec_info;
- MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->video->fps);
+ MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->codec->fps);
if (opts->force_fps) {
d_video->fps = opts->force_fps;
@@ -288,29 +355,26 @@ int reinit_video_chain(struct MPContext *mpctx)
MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
}
+ 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);
#endif
- vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);
-
recreate_video_filters(mpctx);
if (!video_init_best_codec(d_video, opts->video_decoders))
goto err_out;
- if (d_video->header->attached_picture && !decode_coverart(d_video))
- goto err_out;
-
bool saver_state = opts->pause || !opts->stop_screensaver;
- vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
- : VOCTRL_KILL_SCREENSAVER, NULL);
+ vo_control(vo_c->vo, saver_state ? VOCTRL_RESTORE_SCREENSAVER
+ : VOCTRL_KILL_SCREENSAVER, NULL);
- vo_set_paused(mpctx->video_out, mpctx->paused);
+ vo_set_paused(vo_c->vo, mpctx->paused);
mpctx->sync_audio_to_video = !sh->attached_picture;
- mpctx->vo_pts_history_seek_ts++;
// If we switch on video again, ensure audio position matches up.
if (mpctx->d_audio)
@@ -335,9 +399,9 @@ no_video:
void mp_force_video_refresh(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
- if (!d_video || !d_video->decoder_output.imgfmt)
+ if (!vo_c || !vo_c->input_format.imgfmt)
return;
// If not paused, the next frame should come soon enough.
@@ -349,73 +413,50 @@ void mp_force_video_refresh(struct MPContext *mpctx)
}
}
-static int check_framedrop(struct MPContext *mpctx)
+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))
{
- float fps = mpctx->d_video->fps;
+ float fps = vo_c->container_fps;
double frame_time = fps > 0 ? 1.0 / fps : 0;
// we should avoid dropping too many frames in sequence unless we
// are too late. and we allow 100ms A-V delay here:
- if (mpctx->last_av_difference - 0.100 > mpctx->dropped_frames * frame_time)
+ int dropped_frames =
+ vo_c->video_src->dropped_frames - mpctx->dropped_frames_start;
+ if (mpctx->last_av_difference - 0.100 > dropped_frames * frame_time)
return !!(opts->frame_dropping & 2);
}
- return 0;
-}
-
-static bool decode_coverart(struct dec_video *d_video)
-{
- d_video->cover_art_mpi =
- video_decode(d_video, d_video->header->attached_picture, 0);
- // Might need flush.
- if (!d_video->cover_art_mpi)
- d_video->cover_art_mpi = video_decode(d_video, NULL, 0);
-
- return !!d_video->cover_art_mpi;
+ return false;
}
// Read a packet, store decoded image into d_video->waiting_decoded_mpi
// returns VD_* code
static int decode_image(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ struct dec_video *d_video = vo_c->video_src;
- if (d_video->header->attached_picture) {
- d_video->waiting_decoded_mpi = mp_image_new_ref(d_video->cover_art_mpi);
- return VD_EOF;
- }
+ 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);
- struct demux_packet *pkt;
- if (demux_read_packet_async(d_video->header, &pkt) == 0)
- return VD_WAIT;
- if ((pkt && pkt->pts >= mpctx->hrseek_pts - .005) ||
- d_video->has_broken_packet_pts ||
- !mpctx->opts->hr_seek_framedrop)
- {
- mpctx->hrseek_framedrop = false;
- }
- bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
- int framedrop_type = hrseek && mpctx->hrseek_framedrop ?
- 2 : check_framedrop(mpctx);
- d_video->waiting_decoded_mpi =
- video_decode(d_video, pkt, framedrop_type);
- bool had_packet = !!pkt;
- talloc_free(pkt);
-
- if (had_packet && !d_video->waiting_decoded_mpi &&
- mpctx->video_status == STATUS_PLAYING &&
- (mpctx->opts->frame_dropping & 2))
- {
- mpctx->dropped_frames_total++;
- mpctx->dropped_frames++;
- }
+ video_set_framedrop(d_video, check_framedrop(mpctx, vo_c));
- return had_packet ? VD_PROGRESS : VD_EOF;
-}
+ video_work(d_video);
+ 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;
+ }
+}
// Called after video reinit. This can be generally used to try to insert more
// filters using the filter chain edit functionality in command.c.
@@ -437,8 +478,8 @@ static void init_filter_params(struct MPContext *mpctx)
// If eof=true, drain the filter chain, and return VD_EOF if empty.
static int video_filter(struct MPContext *mpctx, bool eof)
{
- struct dec_video *d_video = mpctx->d_video;
- struct vf_chain *vf = d_video->vfilter;
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ struct vf_chain *vf = vo_c->vf;
if (vf->initialized < 0)
return VD_ERROR;
@@ -450,28 +491,45 @@ static int video_filter(struct MPContext *mpctx, bool eof)
// Decoder output is different from filter input?
bool need_vf_reconfig = !vf->input_params.imgfmt || vf->initialized < 1 ||
- !mp_image_params_equal(&d_video->decoder_output, &vf->input_params);
+ !mp_image_params_equal(&vo_c->input_format, &vf->input_params);
// (If imgfmt==0, nothing was decoded yet, and the format is unknown.)
- if (need_vf_reconfig && d_video->decoder_output.imgfmt) {
+ if (need_vf_reconfig && vo_c->input_format.imgfmt) {
// Drain the filter chain.
if (vf_output_frame(vf, true) > 0)
return VD_PROGRESS;
// The filter chain is drained; execute the filter format change.
- filter_reconfig(mpctx, false);
- if (vf->initialized == 0)
- return VD_PROGRESS; // hw decoding fallback; try again
- if (vf->initialized < 1)
+ filter_reconfig(mpctx->vo_chain);
+
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
+
+ // Most video filters don't work with hardware decoding, so this
+ // might be the reason why filter reconfig failed.
+ if (vf->initialized < 0 &&
+ video_vd_control(vo_c->video_src, VDCTRL_FORCE_HWDEC_FALLBACK, NULL)
+ == CONTROL_OK)
+ {
+ // Fallback active; decoder will return software format next
+ // time. Don't abort video decoding.
+ vf->initialized = 0;
+ mp_image_unrefp(&vo_c->input_mpi);
+ vo_c->input_format = (struct mp_image_params){0};
+ MP_VERBOSE(mpctx, "hwdec falback due to filters.\n");
+ return VD_PROGRESS; // try again
+ }
+ if (vf->initialized < 1) {
+ MP_FATAL(mpctx, "Cannot initialize video filters.\n");
return VD_ERROR;
+ }
init_filter_params(mpctx);
return VD_RECONFIG;
}
// If something was decoded, and the filter chain is ready, filter it.
- if (!need_vf_reconfig && d_video->waiting_decoded_mpi) {
- vf_filter_frame(vf, d_video->waiting_decoded_mpi);
- d_video->waiting_decoded_mpi = NULL;
+ if (!need_vf_reconfig && vo_c->input_mpi) {
+ vf_filter_frame(vf, vo_c->input_mpi);
+ vo_c->input_mpi = NULL;
return VD_PROGRESS;
}
@@ -484,22 +542,20 @@ static int video_filter(struct MPContext *mpctx, bool eof)
// the promise that calling this function again will eventually do something.
static int video_decode_and_filter(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
int r = video_filter(mpctx, false);
if (r < 0)
return r;
- if (!d_video->waiting_decoded_mpi) {
+ if (!vo_c->input_mpi) {
// Decode a new image, or at least feed the decoder a packet.
r = decode_image(mpctx);
if (r == VD_WAIT)
return r;
- if (d_video->waiting_decoded_mpi)
- d_video->decoder_output = d_video->waiting_decoded_mpi->params;
}
- bool eof = !d_video->waiting_decoded_mpi && (r == VD_EOF || r < 0);
+ bool eof = !vo_c->input_mpi && (r == VD_EOF || r < 0);
r = video_filter(mpctx, eof);
if (r == VD_RECONFIG) // retry feeding decoded image
r = video_filter(mpctx, eof);
@@ -508,8 +564,7 @@ static int video_decode_and_filter(struct MPContext *mpctx)
static int video_feed_async_filter(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
- struct vf_chain *vf = d_video->vfilter;
+ struct vf_chain *vf = mpctx->vo_chain->vf;
if (vf->initialized < 0)
return VD_ERROR;
@@ -565,22 +620,10 @@ static void handle_new_frame(struct MPContext *mpctx)
if (mpctx->video_pts != MP_NOPTS_VALUE) {
frame_time = pts - mpctx->video_pts;
double tolerance = 15;
- if (mpctx->demuxer->ts_resets_possible) {
- // Fortunately no real framerate is likely to go below this. It
- // still could be that the file is VFR, but the demuxer reports a
- // higher rate, so account for the case of e.g. 60hz demuxer fps
- // but 23hz actual fps.
- double fps = 23.976;
- if (mpctx->d_video->fps > 0 && mpctx->d_video->fps < fps)
- fps = mpctx->d_video->fps;
- tolerance = 3 * 1.0 / fps;
- }
if (frame_time <= 0 || frame_time >= tolerance) {
// Assume a discontinuity.
MP_WARN(mpctx, "Invalid video timestamp: %f -> %f\n",
mpctx->video_pts, pts);
- if (mpctx->d_audio && fabs(frame_time) > 1.0)
- mpctx->audio_status = STATUS_SYNCING;
frame_time = 0;
}
}
@@ -590,7 +633,7 @@ static void handle_new_frame(struct MPContext *mpctx)
mpctx->time_frame += frame_time / mpctx->video_speed;
adjust_sync(mpctx, pts, frame_time);
}
- mpctx->dropped_frames = 0;
+ mpctx->dropped_frames_start = mpctx->vo_chain->video_src->dropped_frames;
MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
}
@@ -617,7 +660,7 @@ static int get_req_frames(struct MPContext *mpctx, bool eof)
return mpctx->opts->video_sync == VS_DEFAULT ? 1 : 2;
int req = vo_get_num_req_frames(mpctx->video_out);
- return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames));
+ return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames) - 1);
}
// Whether it's fine to call add_new_frame() now.
@@ -629,7 +672,7 @@ static bool needs_new_frame(struct MPContext *mpctx)
// Queue a frame to mpctx->next_frames[]. Call only if needs_new_frame() signals ok.
static void add_new_frame(struct MPContext *mpctx, struct mp_image *frame)
{
- assert(needs_new_frame(mpctx));
+ assert(mpctx->num_next_frames < MP_ARRAY_SIZE(mpctx->next_frames));
assert(frame);
mpctx->next_frames[mpctx->num_next_frames++] = frame;
if (mpctx->num_next_frames == 1)
@@ -649,19 +692,11 @@ 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) {
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ if (track && track->stream && track->stream->attached_picture) {
if (vo_has_frame(mpctx->video_out))
return VD_EOF;
- if (mpctx->num_next_frames >= 1)
- return VD_NEW_FRAME;
- int r = video_decode_and_filter(mpctx);
- video_filter(mpctx, true); // force EOF filtering (avoid decoding more)
- mpctx->next_frames[0] = vf_read_output_frame(mpctx->d_video->vfilter);
- if (mpctx->next_frames[0]) {
- mpctx->next_frames[0]->pts = MP_NOPTS_VALUE;
- mpctx->num_next_frames = 1;
- }
- return r <= 0 ? VD_EOF : VD_PROGRESS;
+ hrseek = false;
}
if (have_new_frame(mpctx, false))
@@ -674,11 +709,8 @@ 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->d_video->vfilter);
+ struct mp_image *img = vf_read_output_frame(mpctx->vo_chain->vf);
if (img) {
- // Always add these; they make backstepping after seeking faster.
- add_frame_pts(mpctx, img->pts);
-
if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) {
r = VD_EOF;
} else if (mpctx->max_frames == 0) {
@@ -686,8 +718,19 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
} else if (hrseek && mpctx->hrseek_lastframe) {
mp_image_setrefp(&mpctx->saved_frame, img);
} else if (hrseek && img->pts < mpctx->hrseek_pts - .005) {
- /* just skip */
+ /* just skip - but save if backstep active */
+ if (mpctx->hrseek_backstep)
+ mp_image_setrefp(&mpctx->saved_frame, img);
} else {
+ if (hrseek && mpctx->hrseek_backstep) {
+ if (mpctx->saved_frame) {
+ add_new_frame(mpctx, mpctx->saved_frame);
+ mpctx->saved_frame = NULL;
+ } else {
+ MP_WARN(mpctx, "Backstep failed.\n");
+ }
+ mpctx->hrseek_backstep = false;
+ }
add_new_frame(mpctx, img);
img = NULL;
}
@@ -779,19 +822,19 @@ static void update_av_diff(struct MPContext *mpctx, double offset)
static void init_vo(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
if (opts->gamma_gamma != 1000)
- video_set_colors(d_video, "gamma", opts->gamma_gamma);
+ video_set_colors(vo_c, "gamma", opts->gamma_gamma);
if (opts->gamma_brightness != 1000)
- video_set_colors(d_video, "brightness", opts->gamma_brightness);
+ video_set_colors(vo_c, "brightness", opts->gamma_brightness);
if (opts->gamma_contrast != 1000)
- video_set_colors(d_video, "contrast", opts->gamma_contrast);
+ video_set_colors(vo_c, "contrast", opts->gamma_contrast);
if (opts->gamma_saturation != 1000)
- video_set_colors(d_video, "saturation", opts->gamma_saturation);
+ video_set_colors(vo_c, "saturation", opts->gamma_saturation);
if (opts->gamma_hue != 1000)
- video_set_colors(d_video, "hue", opts->gamma_hue);
- video_set_colors(d_video, "output-levels", opts->video_output_levels);
+ video_set_colors(vo_c, "hue", opts->gamma_hue);
+ video_set_colors(vo_c, "output-levels", opts->video_output_levels);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
@@ -1084,8 +1127,8 @@ static void calculate_frame_duration(struct MPContext *mpctx)
{
assert(mpctx->num_past_frames >= 1 && mpctx->num_next_frames >= 1);
- double demux_duration =
- mpctx->d_video->fps > 0 ? 1.0 / mpctx->d_video->fps : -1;
+ double demux_duration = mpctx->vo_chain->container_fps > 0
+ ? 1.0 / mpctx->vo_chain->container_fps : -1;
double duration = -1;
if (mpctx->num_next_frames >= 2) {
@@ -1136,10 +1179,10 @@ static void calculate_frame_duration(struct MPContext *mpctx)
void write_video(struct MPContext *mpctx, double endpts)
{
struct MPOpts *opts = mpctx->opts;
- struct vo *vo = mpctx->video_out;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return;
+ struct vo *vo = mpctx->vo_chain->vo;
// Actual playback starts when both audio and video are ready.
if (mpctx->video_status == STATUS_READY)
@@ -1186,8 +1229,11 @@ void write_video(struct MPContext *mpctx, double endpts)
const struct vo_driver *info = mpctx->video_out->driver;
char extra[20] = {0};
- if (p.w != p.d_w || p.h != p.d_h)
- snprintf(extra, sizeof(extra), " => %dx%d", p.d_w, p.d_h);
+ if (p.p_w != p.p_h) {
+ int d_w, d_h;
+ mp_image_params_get_dsize(&p, &d_w, &d_h);
+ snprintf(extra, sizeof(extra), " => %dx%d", d_w, d_h);
+ }
MP_INFO(mpctx, "VO: [%s] %dx%d%s %s\n",
info->name, p.w, p.h, extra, vo_format_name(p.imgfmt));
MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description);
@@ -1203,6 +1249,11 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->time_frame -= get_relative_time(mpctx);
update_avsync_before_frame(mpctx);
+ if (!update_subtitles(mpctx, mpctx->next_frames[0]->pts)) {
+ MP_VERBOSE(mpctx, "Video frame delayed due waiting on subtitles.\n");
+ return;
+ }
+
double time_frame = MPMAX(mpctx->time_frame, -1);
int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);
@@ -1259,7 +1310,6 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->osd_force_update = true;
update_osd_msg(mpctx);
- update_subtitles(mpctx);
vo_queue_frame(vo, frame);