diff options
Diffstat (limited to 'player/video.c')
-rw-r--r-- | player/video.c | 446 |
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, ¶m) == 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, ¶m) == 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, ¶ms) < 0) { - vf_remove_filter(d_video->vfilter, vf); + if (vf_reconfig(vo_c->vf, ¶ms) < 0) { + vf_remove_filter(vo_c->vf, vf); // restore - video_reconfig_filters(d_video, ¶ms); + vf_reconfig(vo_c->vf, ¶ms); 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, ¶ms) < 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, ¶ms) < 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); |