From 51b1b0f6038d952aa71790d3c50801df7d9bf50d Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 19 Feb 2021 21:16:57 +0100 Subject: [PATCH] Make video buffer more generic Video buffer is a tool between a frame producer and a frame consumer. For now, it is used between a decoder and a renderer, but in the future another instance might be used to swscale decoded frames. --- app/src/decoder.c | 6 ++-- app/src/screen.c | 2 +- app/src/video_buffer.c | 63 +++++++++++++++++++++--------------------- app/src/video_buffer.h | 38 ++++++++++++------------- 4 files changed, 54 insertions(+), 55 deletions(-) diff --git a/app/src/decoder.c b/app/src/decoder.c index 4b1f0fce..87cd0fe7 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -15,8 +15,8 @@ static void push_frame(struct decoder *decoder) { bool previous_frame_skipped; - video_buffer_offer_decoded_frame(decoder->video_buffer, - &previous_frame_skipped); + video_buffer_producer_offer_frame(decoder->video_buffer, + &previous_frame_skipped); if (previous_frame_skipped) { fps_counter_add_skipped_frame(decoder->fps_counter); // the previous EVENT_NEW_FRAME will consume this frame @@ -69,7 +69,7 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) { return false; } ret = avcodec_receive_frame(decoder->codec_ctx, - decoder->video_buffer->decoding_frame); + decoder->video_buffer->producer_frame); if (!ret) { // a frame was received push_frame(decoder); diff --git a/app/src/screen.c b/app/src/screen.c index 021bc7af..31dab89c 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -453,7 +453,7 @@ update_texture(struct screen *screen, const AVFrame *frame) { static bool screen_update_frame(struct screen *screen) { - const AVFrame *frame = video_buffer_take_rendering_frame(screen->vb); + const AVFrame *frame = video_buffer_consumer_take_frame(screen->vb); fps_counter_add_rendered_frame(screen->fps_counter); diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index 903df070..e7e150a2 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -7,9 +7,9 @@ #include "util/log.h" bool -video_buffer_init(struct video_buffer *vb, bool render_expired_frames) { - vb->decoding_frame = av_frame_alloc(); - if (!vb->decoding_frame) { +video_buffer_init(struct video_buffer *vb, bool wait_consumer) { + vb->producer_frame = av_frame_alloc(); + if (!vb->producer_frame) { goto error_0; } @@ -18,8 +18,8 @@ video_buffer_init(struct video_buffer *vb, bool render_expired_frames) { goto error_1; } - vb->rendering_frame = av_frame_alloc(); - if (!vb->rendering_frame) { + vb->consumer_frame = av_frame_alloc(); + if (!vb->consumer_frame) { goto error_2; } @@ -28,73 +28,72 @@ video_buffer_init(struct video_buffer *vb, bool render_expired_frames) { goto error_3; } - vb->render_expired_frames = render_expired_frames; - if (render_expired_frames) { + vb->wait_consumer = wait_consumer; + if (wait_consumer) { ok = sc_cond_init(&vb->pending_frame_consumed_cond); if (!ok) { sc_mutex_destroy(&vb->mutex); goto error_2; } - // interrupted is not used if expired frames are not rendered - // since offering a frame will never block + // interrupted is not used if wait_consumer is disabled since offering + // a frame will never block vb->interrupted = false; } - // there is initially no rendering frame, so consider it has already been - // consumed + // there is initially no frame, so consider it has already been consumed vb->pending_frame_consumed = true; return true; error_3: - av_frame_free(&vb->rendering_frame); + av_frame_free(&vb->consumer_frame); error_2: av_frame_free(&vb->pending_frame); error_1: - av_frame_free(&vb->decoding_frame); + av_frame_free(&vb->producer_frame); error_0: return false; } void video_buffer_destroy(struct video_buffer *vb) { - if (vb->render_expired_frames) { + if (vb->wait_consumer) { sc_cond_destroy(&vb->pending_frame_consumed_cond); } sc_mutex_destroy(&vb->mutex); - av_frame_free(&vb->rendering_frame); + av_frame_free(&vb->consumer_frame); av_frame_free(&vb->pending_frame); - av_frame_free(&vb->decoding_frame); + av_frame_free(&vb->producer_frame); } static void -video_buffer_swap_decoding_frame(struct video_buffer *vb) { +video_buffer_swap_producer_frame(struct video_buffer *vb) { sc_mutex_assert(&vb->mutex); - AVFrame *tmp = vb->decoding_frame; - vb->decoding_frame = vb->pending_frame; + AVFrame *tmp = vb->producer_frame; + vb->producer_frame = vb->pending_frame; vb->pending_frame = tmp; } static void -video_buffer_swap_rendering_frame(struct video_buffer *vb) { +video_buffer_swap_consumer_frame(struct video_buffer *vb) { sc_mutex_assert(&vb->mutex); - AVFrame *tmp = vb->rendering_frame; - vb->rendering_frame = vb->pending_frame; + AVFrame *tmp = vb->consumer_frame; + vb->consumer_frame = vb->pending_frame; vb->pending_frame = tmp; } void -video_buffer_offer_decoded_frame(struct video_buffer *vb, - bool *previous_frame_skipped) { +video_buffer_producer_offer_frame(struct video_buffer *vb, + bool *previous_frame_skipped) { sc_mutex_lock(&vb->mutex); - if (vb->render_expired_frames) { + if (vb->wait_consumer) { // wait for the current (expired) frame to be consumed while (!vb->pending_frame_consumed && !vb->interrupted) { sc_cond_wait(&vb->pending_frame_consumed_cond, &vb->mutex); } } - video_buffer_swap_decoding_frame(vb); + video_buffer_swap_producer_frame(vb); *previous_frame_skipped = !vb->pending_frame_consumed; vb->pending_frame_consumed = false; @@ -103,26 +102,26 @@ video_buffer_offer_decoded_frame(struct video_buffer *vb, } const AVFrame * -video_buffer_take_rendering_frame(struct video_buffer *vb) { +video_buffer_consumer_take_frame(struct video_buffer *vb) { sc_mutex_lock(&vb->mutex); assert(!vb->pending_frame_consumed); vb->pending_frame_consumed = true; - video_buffer_swap_rendering_frame(vb); + video_buffer_swap_consumer_frame(vb); - if (vb->render_expired_frames) { + if (vb->wait_consumer) { // unblock video_buffer_offer_decoded_frame() sc_cond_signal(&vb->pending_frame_consumed_cond); } sc_mutex_unlock(&vb->mutex); - // rendering_frame is only written from this thread, no need to lock - return vb->rendering_frame; + // consumer_frame is only written from this thread, no need to lock + return vb->consumer_frame; } void video_buffer_interrupt(struct video_buffer *vb) { - if (vb->render_expired_frames) { + if (vb->wait_consumer) { sc_mutex_lock(&vb->mutex); vb->interrupted = true; sc_mutex_unlock(&vb->mutex); diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 4c82fba4..6c5d197c 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -13,28 +13,28 @@ typedef struct AVFrame AVFrame; /** * There are 3 frames in memory: - * - one frame is held by the decoder (decoding_frame) - * - one frame is held by the renderer (rendering_frame) - * - one frame is shared between the decoder and the renderer (pending_frame) + * - one frame is held by the producer (producer_frame) + * - one frame is held by the consumer (consumer_frame) + * - one frame is shared between the producer and the consumer (pending_frame) * - * The decoder decodes a packet into the decoding_frame (it may takes time). + * The producer generates a frame into the producer_frame (it may takes time). * - * Once the frame is decoded, it calls video_buffer_offer_decoded_frame(), - * which swaps the decoding and pending frames. + * Once the frame is produced, it calls video_buffer_producer_offer_frame(), + * which swaps the producer and pending frames. * - * When the renderer is notified that a new frame is available, it calls - * video_buffer_take_rendering_frame() to retrieve it, which swaps the pending - * and rendering frames. The frame is valid until the next call, without - * blocking the decoder. + * When the consumer is notified that a new frame is available, it calls + * video_buffer_consumer_take_frame() to retrieve it, which swaps the pending + * and consumer frames. The frame is valid until the next call, without + * blocking the producer. */ struct video_buffer { - AVFrame *decoding_frame; + AVFrame *producer_frame; AVFrame *pending_frame; - AVFrame *rendering_frame; + AVFrame *consumer_frame; sc_mutex mutex; - bool render_expired_frames; + bool wait_consumer; // never overwrite a pending frame if it is not consumed bool interrupted; sc_cond pending_frame_consumed_cond; @@ -42,21 +42,21 @@ struct video_buffer { }; bool -video_buffer_init(struct video_buffer *vb, bool render_expired_frames); +video_buffer_init(struct video_buffer *vb, bool wait_consumer); void video_buffer_destroy(struct video_buffer *vb); -// set the decoded frame as ready for rendering +// set the producer frame as ready for consuming // the output flag is set to report whether the previous frame has been skipped void -video_buffer_offer_decoded_frame(struct video_buffer *vb, - bool *previous_frame_skipped); +video_buffer_producer_offer_frame(struct video_buffer *vb, + bool *previous_frame_skipped); -// mark the rendering frame as consumed and return it +// mark the consumer frame as consumed and return it // the frame is valid until the next call to this function const AVFrame * -video_buffer_take_rendering_frame(struct video_buffer *vb); +video_buffer_consumer_take_frame(struct video_buffer *vb); // wake up and avoid any blocking call void -- GitLab