From 1e33d9b3063347ff386061b9e27ba69ae11831a5 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 21 Feb 2021 17:39:59 +0100 Subject: [PATCH] Move decoder frame to decoder The video buffer held 3 frames: - the producer frame (for decoding) - the pending frame (to exchange between the producer and consumer) - the consumer frame (for rendering) It worked well, but it prevented video buffers to be chained, because the consumer frame of the first video buffer must be the same as the producer frame of the second video buffer. To solve this problem, make the decoder handle its decoding frame, and keep only the pending and consumer frames in the video_buffer. decoder -> pending -> consumer -> pending -> consumer |---------------------||---------------------| video_buffer 1 video_buffer 2 This paves the way to support asynchronous swscale. --- app/src/decoder.c | 16 ++++++++++++---- app/src/decoder.h | 1 + app/src/video_buffer.c | 22 +++++++--------------- app/src/video_buffer.h | 4 ++-- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/src/decoder.c b/app/src/decoder.c index 7da959c6..dd2a8a1e 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -30,11 +30,19 @@ decoder_open(struct decoder *decoder, const AVCodec *codec) { return false; } + decoder->frame = av_frame_alloc(); + if (!decoder->frame) { + avcodec_close(decoder->codec_ctx); + avcodec_free_context(&decoder->codec_ctx); + return false; + } + return true; } void decoder_close(struct decoder *decoder) { + av_frame_free(&decoder->frame); avcodec_close(decoder->codec_ctx); avcodec_free_context(&decoder->codec_ctx); } @@ -49,11 +57,11 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) { LOGE("Could not send video packet: %d", ret); return false; } - ret = avcodec_receive_frame(decoder->codec_ctx, - decoder->video_buffer->producer_frame); + ret = avcodec_receive_frame(decoder->codec_ctx, decoder->frame); if (!ret) { // a frame was received - video_buffer_producer_offer_frame(decoder->video_buffer); + video_buffer_producer_offer_frame(decoder->video_buffer, + &decoder->frame); } else if (ret != AVERROR(EAGAIN)) { LOGE("Could not receive video frame: %d", ret); return false; @@ -61,7 +69,7 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) { #else int got_picture; int len = avcodec_decode_video2(decoder->codec_ctx, - decoder->video_buffer->decoding_frame, + decoder->frame, &got_picture, packet); if (len < 0) { diff --git a/app/src/decoder.h b/app/src/decoder.h index bbd7a9a7..69e06828 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -12,6 +12,7 @@ struct decoder { struct video_buffer *video_buffer; AVCodecContext *codec_ctx; + AVFrame *frame; }; void diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index 94619840..d22e96b9 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -8,24 +8,19 @@ bool video_buffer_init(struct video_buffer *vb, bool wait_consumer) { - vb->producer_frame = av_frame_alloc(); - if (!vb->producer_frame) { - goto error_0; - } - vb->pending_frame = av_frame_alloc(); if (!vb->pending_frame) { - goto error_1; + goto error_0; } vb->consumer_frame = av_frame_alloc(); if (!vb->consumer_frame) { - goto error_2; + goto error_1; } bool ok = sc_mutex_init(&vb->mutex); if (!ok) { - goto error_3; + goto error_2; } vb->wait_consumer = wait_consumer; @@ -49,12 +44,10 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer) { return true; -error_3: - av_frame_free(&vb->consumer_frame); error_2: - av_frame_free(&vb->pending_frame); + av_frame_free(&vb->consumer_frame); error_1: - av_frame_free(&vb->producer_frame); + av_frame_free(&vb->pending_frame); error_0: return false; } @@ -67,7 +60,6 @@ video_buffer_destroy(struct video_buffer *vb) { sc_mutex_destroy(&vb->mutex); av_frame_free(&vb->consumer_frame); av_frame_free(&vb->pending_frame); - av_frame_free(&vb->producer_frame); } static inline void @@ -89,7 +81,7 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb, } void -video_buffer_producer_offer_frame(struct video_buffer *vb) { +video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) { assert(vb->cbs); sc_mutex_lock(&vb->mutex); @@ -101,7 +93,7 @@ video_buffer_producer_offer_frame(struct video_buffer *vb) { } av_frame_unref(vb->pending_frame); - swap_frames(&vb->producer_frame, &vb->pending_frame); + swap_frames(pframe, &vb->pending_frame); bool skipped = !vb->pending_frame_consumed; vb->pending_frame_consumed = false; diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 4d11e3ab..e7efdfbf 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -29,7 +29,6 @@ typedef struct AVFrame AVFrame; */ struct video_buffer { - AVFrame *producer_frame; AVFrame *pending_frame; AVFrame *consumer_frame; @@ -67,8 +66,9 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb, void *cbs_userdata); // set the producer frame as ready for consuming +// the produced frame is exchanged with an unused allocated frame void -video_buffer_producer_offer_frame(struct video_buffer *vb); +video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe); // mark the consumer frame as consumed and return it // the frame is valid until the next call to this function