From 056901b2be67072f41315fce26e0f58e5569be9a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 16 Jan 2016 21:19:52 +0100 Subject: video: refactor: disentangle decoding/filtering some more This moves some code related to decoding from video.c to dec_video.c, and also removes some accesses to dec_video.c from the filtering code. dec_video.ch is starting to make sense, and simply returns video frames from a demuxer stream. The API exposed is also somewhat intended to be easily changeable to move decoding to a separate thread, if we ever want this (due to libavcodec already being threaded, I don't see much of a reason, but it might still be helpful). --- player/command.c | 2 +- player/core.h | 13 +++-- player/osd.c | 8 +-- player/video.c | 137 ++++++++++++++++++----------------------------- video/decode/dec_video.c | 97 ++++++++++++++++++++++++++++++--- video/decode/dec_video.h | 32 +++++++---- 6 files changed, 180 insertions(+), 109 deletions(-) diff --git a/player/command.c b/player/command.c index 46c608389a..0ff38264e0 100644 --- a/player/command.c +++ b/player/command.c @@ -563,7 +563,7 @@ static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop, if (!mpctx->d_video) return M_PROPERTY_UNAVAILABLE; - return m_property_int_ro(action, arg, mpctx->dropped_frames_total); + return m_property_int_ro(action, arg, mpctx->d_video->dropped_frames); } static int mp_property_mistimed_frame_count(void *ctx, struct m_property *prop, diff --git a/player/core.h b/player/core.h index f3ce5b4178..456d463d5a 100644 --- a/player/core.h +++ b/player/core.h @@ -27,6 +27,7 @@ #include "options/options.h" #include "sub/osd.h" #include "demux/timeline.h" +#include "video/mp_image.h" #include "video/out/vo.h" // definitions used internally by the core player code @@ -154,6 +155,12 @@ struct vo_chain { struct vf_chain *vf; struct vo *vo; + + // 1-element input frame queue. + struct mp_image *input_mpi; + + // Last known input_mpi format (so vf can be reinitialized any time). + struct mp_image_params input_format; }; /* Note that playback can be paused, stopped, etc. at any time. While paused, @@ -308,10 +315,8 @@ typedef struct MPContext { // How much video timing has been changed to make it match the audio // timeline. Used for status line information only. double total_avsync_change; - // Total number of dropped frames that were dropped by decoder. - int dropped_frames_total; - // Number of frames dropped in a row. - int dropped_frames; + // Used to compute the number of frames dropped in a row. + int dropped_frames_start; // A-V sync difference when last frame was displayed. Kept to display // the same value if the status line is updated at a time where no new // video frame is shown. diff --git a/player/osd.c b/player/osd.c index 51327f9c10..3b5ecf98fd 100644 --- a/player/osd.c +++ b/player/osd.c @@ -39,6 +39,7 @@ #include "stream/stream.h" #include "sub/osd.h" +#include "video/decode/dec_video.h" #include "video/out/vo.h" #include "core.h" @@ -242,10 +243,11 @@ static void print_status(struct MPContext *mpctx) talloc_free(r); } int64_t c = vo_get_drop_count(mpctx->video_out); - if (c > 0 || mpctx->dropped_frames_total > 0) { + int dropped_frames = mpctx->d_video->dropped_frames; + if (c > 0 || dropped_frames > 0) { saddf(&line, " Dropped: %"PRId64, c); - if (mpctx->dropped_frames_total) - saddf(&line, "/%d", mpctx->dropped_frames_total); + if (dropped_frames) + saddf(&line, "/%d", dropped_frames); } } } diff --git a/player/video.c b/player/video.c index 6a5c02edd4..dc46140859 100644 --- a/player/video.c +++ b/player/video.c @@ -66,8 +66,6 @@ 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; @@ -156,10 +154,13 @@ static int try_filter(struct vo_chain *vo_c, struct mp_image_params params, return 0; } -// Reconfigure the filter chain according to decoder output. -static void filter_reconfig(struct vo_chain *vo_c, - struct mp_image_params params) +// Reconfigure the filter chain according to the new input format. +static void filter_reconfig(struct vo_chain *vo_c) { + struct mp_image_params params = vo_c->input_format; + if (!params.imgfmt) + return; + set_allowed_vo_formats(vo_c); if (vf_reconfig(vo_c->vf, ¶ms) < 0) @@ -225,7 +226,7 @@ int reinit_video_filters(struct MPContext *mpctx) recreate_video_filters(mpctx); if (need_reconfig) - filter_reconfig(vo_c, d_video->decoder_output); + filter_reconfig(vo_c); mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL); @@ -234,6 +235,7 @@ int reinit_video_filters(struct MPContext *mpctx) 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); @@ -242,7 +244,7 @@ static void vo_chain_reset_state(struct vo_chain *vo_c) void reset_video_state(struct MPContext *mpctx) { if (mpctx->d_video) - video_reset_decoding(mpctx->d_video); + video_reset(mpctx->d_video); if (mpctx->vo_chain) vo_chain_reset_state(mpctx->vo_chain); @@ -258,8 +260,7 @@ 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; @@ -280,9 +281,11 @@ 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); talloc_free(vo_c); + // this does not free the VO } void uninit_video_chain(struct MPContext *mpctx) @@ -362,9 +365,6 @@ int reinit_video_chain(struct MPContext *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); @@ -396,9 +396,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. @@ -410,7 +410,7 @@ void mp_force_video_refresh(struct MPContext *mpctx) } } -static int check_framedrop(struct MPContext *mpctx) +static bool check_framedrop(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; // check for frame-drop: @@ -421,21 +421,12 @@ static int check_framedrop(struct MPContext *mpctx) 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 = + mpctx->d_video->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 @@ -443,38 +434,26 @@ static bool decode_coverart(struct dec_video *d_video) static int decode_image(struct MPContext *mpctx) { struct dec_video *d_video = mpctx->d_video; + struct vo_chain *vo_c = mpctx->vo_chain; - 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; - bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING; - int framedrop_type = check_framedrop(mpctx); - if (hrseek && pkt && pkt->pts < mpctx->hrseek_pts - .005 && - !d_video->has_broken_packet_pts && mpctx->hrseek_framedrop) - { - framedrop_type = 2; - } - 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)); - 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. @@ -496,8 +475,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 = mpctx->vo_chain->vf; + struct vo_chain *vo_c = mpctx->vo_chain; + struct vf_chain *vf = vo_c->vf; if (vf->initialized < 0) return VD_ERROR; @@ -509,29 +488,30 @@ 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->vo_chain, d_video->decoder_output); + 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(d_video, VDCTRL_FORCE_HWDEC_FALLBACK, NULL) == CONTROL_OK) + video_vd_control(mpctx->d_video, 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(&d_video->waiting_decoded_mpi); - d_video->decoder_output = (struct mp_image_params){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 } @@ -544,9 +524,9 @@ static int video_filter(struct MPContext *mpctx, bool eof) } // 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; } @@ -559,22 +539,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); @@ -652,7 +630,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->d_video->dropped_frames; MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time); } @@ -714,16 +692,7 @@ static int video_output_image(struct MPContext *mpctx, double endpts) if (mpctx->d_video->header->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->vo_chain->vf); - 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)) @@ -749,7 +718,7 @@ static int video_output_image(struct MPContext *mpctx, double endpts) if (mpctx->hrseek_backstep) mp_image_setrefp(&mpctx->saved_frame, img); } else { - if (mpctx->hrseek_backstep) { + if (hrseek && mpctx->hrseek_backstep) { if (mpctx->saved_frame) { add_new_frame(mpctx, mpctx->saved_frame); mpctx->saved_frame = NULL; diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c index 00af2e4c7b..f28871c6bb 100644 --- a/video/decode/dec_video.c +++ b/video/decode/dec_video.c @@ -29,6 +29,7 @@ #include "osdep/timer.h" #include "stream/stream.h" +#include "demux/demux.h" #include "demux/packet.h" #include "common/codecs.h" @@ -55,17 +56,19 @@ const vd_functions_t * const mpcodecs_vd_drivers[] = { NULL }; -void video_reset_decoding(struct dec_video *d_video) +void video_reset(struct dec_video *d_video) { video_vd_control(d_video, VDCTRL_RESET, NULL); - mp_image_unrefp(&d_video->waiting_decoded_mpi); d_video->num_buffered_pts = 0; - d_video->last_pts = MP_NOPTS_VALUE; d_video->first_packet_pdts = MP_NOPTS_VALUE; + d_video->start_pts = MP_NOPTS_VALUE; d_video->decoded_pts = MP_NOPTS_VALUE; d_video->codec_pts = MP_NOPTS_VALUE; d_video->codec_dts = MP_NOPTS_VALUE; d_video->last_format = d_video->fixed_format = (struct mp_image_params){0}; + d_video->dropped_frames = 0; + d_video->current_state = VIDEO_SKIP; + mp_image_unrefp(&d_video->current_mpi); } int video_vd_control(struct dec_video *d_video, int cmd, void *arg) @@ -78,7 +81,7 @@ int video_vd_control(struct dec_video *d_video, int cmd, void *arg) void video_uninit(struct dec_video *d_video) { - mp_image_unrefp(&d_video->waiting_decoded_mpi); + mp_image_unrefp(&d_video->current_mpi); mp_image_unrefp(&d_video->cover_art_mpi); if (d_video->vd_driver) { MP_VERBOSE(d_video, "Uninit video.\n"); @@ -125,7 +128,7 @@ static const struct vd_functions *find_driver(const char *name) bool video_init_best_codec(struct dec_video *d_video, char* video_decoders) { assert(!d_video->vd_driver); - video_reset_decoding(d_video); + video_reset(d_video); d_video->has_broken_packet_pts = -10; // needs 10 packets to reach decision struct mp_decoder_entry *decoder = NULL; @@ -258,9 +261,9 @@ static double retrieve_avi_pts(struct dec_video *d_video, double codec_pts) return MP_NOPTS_VALUE; } -struct mp_image *video_decode(struct dec_video *d_video, - struct demux_packet *packet, - int drop_frame) +static struct mp_image *decode_packet(struct dec_video *d_video, + struct demux_packet *packet, + int drop_frame) { struct MPOpts *opts = d_video->opts; bool avi_pts = d_video->header->codec->avi_dts && opts->correct_pts; @@ -364,3 +367,81 @@ void video_reset_aspect(struct dec_video *d_video) { d_video->last_format = (struct mp_image_params){0}; } + +void video_set_framedrop(struct dec_video *d_video, bool enabled) +{ + d_video->framedrop_enabled = enabled; +} + +// Frames before the start timestamp can be dropped. (Used for hr-seek.) +void video_set_start(struct dec_video *d_video, double start_pts) +{ + d_video->start_pts = start_pts; +} + +void video_work(struct dec_video *d_video) +{ + if (d_video->current_mpi) + return; + + if (d_video->header->attached_picture) { + if (d_video->current_state == VIDEO_SKIP && !d_video->cover_art_mpi) { + d_video->cover_art_mpi = + decode_packet(d_video, d_video->header->attached_picture, 0); + // Might need flush. + if (!d_video->cover_art_mpi) + d_video->cover_art_mpi = decode_packet(d_video, NULL, 0); + d_video->current_state = VIDEO_OK; + } + if (d_video->current_state == VIDEO_OK) + d_video->current_mpi = mp_image_new_ref(d_video->cover_art_mpi); + // (VIDEO_OK is returned the first time, when current_mpi is sill set) + d_video->current_state = VIDEO_EOF; + return; + } + + struct demux_packet *pkt; + if (demux_read_packet_async(d_video->header, &pkt) == 0) { + d_video->current_state = VIDEO_WAIT; + return; + } + + int framedrop_type = d_video->framedrop_enabled ? 1 : 0; + if (d_video->start_pts != MP_NOPTS_VALUE && pkt && + pkt->pts < d_video->start_pts - .005 && + !d_video->has_broken_packet_pts) + { + framedrop_type = 2; + } + d_video->current_mpi = decode_packet(d_video, pkt, framedrop_type); + bool had_packet = !!pkt; + talloc_free(pkt); + + d_video->current_state = VIDEO_OK; + if (!d_video->current_mpi) { + d_video->current_state = VIDEO_EOF; + if (had_packet) { + if (framedrop_type == 1) + d_video->dropped_frames += 1; + d_video->current_state = VIDEO_SKIP; + } + } +} + +// Fetch an image decoded with video_work(). Returns one of: +// VIDEO_OK: *out_mpi is set to a new image +// VIDEO_WAIT: waiting for demuxer; will receive a wakeup signal +// VIDEO_EOF: end of file, no more frames to be expected +// VIDEO_SKIP: dropped frame or something similar +int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi) +{ + *out_mpi = NULL; + if (d_video->current_mpi) { + *out_mpi = d_video->current_mpi; + d_video->current_mpi = NULL; + return VIDEO_OK; + } + if (d_video->current_state == VIDEO_OK) + return VIDEO_SKIP; + return d_video->current_state; +} diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h index dc2f62f4a4..3aba448219 100644 --- a/video/decode/dec_video.h +++ b/video/decode/dec_video.h @@ -37,11 +37,11 @@ struct dec_video { char *decoder_desc; - // Used temporarily during decoding (important for format changes) - struct mp_image *waiting_decoded_mpi; - struct mp_image_params decoder_output; // last output of the decoder + float fps; // FPS from demuxer or from user override - struct mp_image *cover_art_mpi; + int dropped_frames; + + // Internal (shared with vd_lavc.c). void *priv; // for free use by vd_driver @@ -57,6 +57,8 @@ struct dec_video { double buffered_pts[128]; int num_buffered_pts; + // Strictly internal (dec_video.c). + // PTS or DTS of packet first read double first_packet_pdts; @@ -66,13 +68,14 @@ struct dec_video { // Final PTS of previously decoded image double decoded_pts; - float fps; // FPS from demuxer or from user override - struct mp_image_params last_format, fixed_format; float initial_decoder_aspect; - // State used only by player/video.c - double last_pts; + double start_pts; + bool framedrop_enabled; + struct mp_image *cover_art_mpi; + struct mp_image *current_mpi; + int current_state; }; struct mp_decoder_list *video_decoder_list(void); @@ -80,13 +83,24 @@ struct mp_decoder_list *video_decoder_list(void); bool video_init_best_codec(struct dec_video *d_video, char* video_decoders); void video_uninit(struct dec_video *d_video); +void video_work(struct dec_video *d_video); + +void video_set_framedrop(struct dec_video *d_video, bool enabled); +void video_set_start(struct dec_video *d_video, double start_pts); + +#define VIDEO_OK 1 +#define VIDEO_WAIT 0 +#define VIDEO_EOF -1 +#define VIDEO_SKIP -2 +int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi); + struct demux_packet; struct mp_image *video_decode(struct dec_video *d_video, struct demux_packet *packet, int drop_frame); int video_vd_control(struct dec_video *d_video, int cmd, void *arg); -void video_reset_decoding(struct dec_video *d_video); +void video_reset(struct dec_video *d_video); void video_reset_aspect(struct dec_video *d_video); #endif /* MPLAYER_DEC_VIDEO_H */ -- cgit v1.2.3