From 71897ebf959de6cff40f7d6944fa1ba883e82255 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Mon, 2 Jun 2025 16:24:41 +0200 Subject: [PATCH] Fix #139580: Movie proxies can be missing the last frame Commit 611940805ec changed movie proxies to use MP4 container format instead of AVI (to support video rotation metadata), however MP4 container needs more care compared to AVI. Several parts of proxy generation were subtly wrong before: - AVStream.avg_frame_rate was not set, this can make the mp4 muxer incorrectly determine the video duration for the "moov" atom. - AV_CODEC_FLAG_GLOBAL_HEADER (needed by mp4 more than by avi) was not set since the code was checking the flags on the wrong structure. - avcodec_parameters_from_context were called at the wrong place (need to be called after avcodec_open2, not before it) - avcodec_flush_buffers call before av_write_trailer was incorrect; the codec state should not be reset there! - avcodec_free_context should be called after fully finishing with the file (i.e. after avio_close) Pull Request: https://projects.blender.org/blender/blender/pulls/139731 --- .../imbuf/movie/intern/movie_proxy_indexer.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/source/blender/imbuf/movie/intern/movie_proxy_indexer.cc b/source/blender/imbuf/movie/intern/movie_proxy_indexer.cc index 8a5b7e1ebd6..e456d90cd24 100644 --- a/source/blender/imbuf/movie/intern/movie_proxy_indexer.cc +++ b/source/blender/imbuf/movie/intern/movie_proxy_indexer.cc @@ -425,6 +425,7 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(MovieReader *anim, rv->c->time_base.den = 25; rv->c->time_base.num = 1; rv->st->time_base = rv->c->time_base; + rv->st->avg_frame_rate = av_inv_q(rv->c->time_base); /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality, * `crf_range_max` to highest quality. */ @@ -455,7 +456,7 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(MovieReader *anim, rv->c->thread_type = FF_THREAD_SLICE; } - if (rv->of->flags & AVFMT_GLOBALHEADER) { + if (rv->of->oformat->flags & AVFMT_GLOBALHEADER) { rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } @@ -464,8 +465,6 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(MovieReader *anim, rv->c->color_trc = codec_ctx->color_trc; rv->c->colorspace = codec_ctx->colorspace; - avcodec_parameters_from_context(rv->st->codecpar, rv->c); - ffmpeg_copy_display_matrix(st, rv->st); int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE); @@ -499,6 +498,8 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(MovieReader *anim, return nullptr; } + avcodec_parameters_from_context(rv->st->codecpar, rv->c); + rv->orig_height = st->codecpar->height; if (st->codecpar->width != width || st->codecpar->height != height || @@ -627,17 +628,14 @@ static void free_proxy_output_ffmpeg(proxy_output_ctx *ctx, int rollback) add_to_proxy_output_ffmpeg(ctx, nullptr); } - avcodec_flush_buffers(ctx->c); - av_write_trailer(ctx->of); - avcodec_free_context(&ctx->c); - if (ctx->of->oformat) { if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { avio_close(ctx->of->pb); } } + avcodec_free_context(&ctx->c); avformat_free_context(ctx->of); if (ctx->sws_ctx) {