From 5cfa159e16e0b3e094e05ee1a52626fc93336fcf 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 | 15 +++++++++++---- app/src/decoder.h | 1 + app/src/video_buffer.c | 22 +++++++--------------- app/src/video_buffer.h | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/src/decoder.c b/app/src/decoder.c index 99119aa6..7d5923b0 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -14,7 +14,7 @@ // set the decoded frame as ready for rendering, and notify static void push_frame(struct decoder *decoder) { - video_buffer_producer_offer_frame(decoder->video_buffer); + video_buffer_producer_offer_frame(decoder->video_buffer, &decoder->frame); } void @@ -36,11 +36,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); } @@ -55,8 +63,7 @@ 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 push_frame(decoder); @@ -67,7 +74,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 27afcd8e..57cfd3af 100644 --- a/app/src/decoder.h +++ b/app/src/decoder.h @@ -11,6 +11,7 @@ struct video_buffer; 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 fc5e7c93..e871a987 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -10,24 +10,19 @@ bool video_buffer_init(struct video_buffer *vb, bool wait_consumer, const struct video_buffer_callbacks *cbs, void *cbs_userdata) { - 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; @@ -54,12 +49,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; } @@ -72,7 +65,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 @@ -83,7 +75,7 @@ swap_frames(AVFrame **lhs, AVFrame **rhs) { } void -video_buffer_producer_offer_frame(struct video_buffer *vb) { +video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) { sc_mutex_lock(&vb->mutex); if (vb->wait_consumer) { // wait for the current (expired) frame to be consumed @@ -93,7 +85,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; if (skipped) { diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 96456f32..4741a47b 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; @@ -60,8 +59,9 @@ void video_buffer_destroy(struct video_buffer *vb); // 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