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
This commit is contained in:
Aras Pranckevicius 2025-06-02 16:24:41 +02:00 committed by Aras Pranckevicius
parent f2ad6b1ef0
commit 71897ebf95

View File

@ -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) {