Fix T91405: Block artefacts in WEBM video

Issue was caused by incorrect FFmpeg asynchronous decoding API. In most
cases, decoder returns 1 frame each time it is fed by 1 packet. Here
decoder wanted to return more frames, but our code always expected only
one.

Before sending new packets to decoder, check if there are frames to
receive. If there are, process them, otherwise continue decoding as
usual.

Reviewed By: zeddb, sergey

Differential Revision: https://developer.blender.org/D13079
This commit is contained in:
Richard Antalik 2021-11-15 20:20:33 +01:00
parent ef8240e64c
commit d3c45e1c39
Notes: blender-bot 2023-05-31 04:43:10 +02:00
Referenced by issue #91405, VSE: Block artefacts in WEBM video on preview or export
1 changed files with 27 additions and 27 deletions

View File

@ -939,14 +939,36 @@ static void ffmpeg_postprocess(struct anim *anim)
}
}
/* decode one video frame also considering the packet read into cur_packet */
static void ffmpeg_decode_store_frame_pts(struct anim *anim)
{
anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
if (anim->pFrame->key_frame) {
anim->cur_key_frame_pts = anim->cur_pts;
}
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
" FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
(int64_t)anim->cur_pts);
}
/* decode one video frame also considering the packet read into cur_packet */
static int ffmpeg_decode_video_frame(struct anim *anim)
{
int rval = 0;
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
/* Sometimes, decoder returns more than one frame per sent packet. Check if frames are available.
* This frames must be read, otherwise decoding will fail. See T91405. */
anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE FROM CODEC BUFFER\n");
ffmpeg_decode_store_frame_pts(anim);
return 1;
}
int rval = 0;
if (anim->cur_packet->stream_index == anim->videoStream) {
av_packet_unref(anim->cur_packet);
anim->cur_packet->stream_index = -1;
@ -963,22 +985,11 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
(anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts,
(anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
if (anim->cur_packet->stream_index == anim->videoStream) {
anim->pFrameComplete = 0;
avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
if (anim->pFrame->key_frame) {
anim->cur_key_frame_pts = anim->cur_pts;
}
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
" FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
(int64_t)anim->cur_pts);
ffmpeg_decode_store_frame_pts(anim);
break;
}
}
@ -988,22 +999,11 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
if (rval == AVERROR_EOF) {
/* Flush any remaining frames out of the decoder. */
anim->pFrameComplete = 0;
avcodec_send_packet(anim->pCodecCtx, NULL);
anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
if (anim->pFrame->key_frame) {
anim->cur_key_frame_pts = anim->cur_pts;
}
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
" FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
(int64_t)anim->cur_pts);
ffmpeg_decode_store_frame_pts(anim);
rval = 0;
}
}