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.
This commit is contained in:
Romain Vimont
2021-02-19 21:16:57 +01:00
parent cb197ee3a2
commit 441d3fb119
4 changed files with 54 additions and 55 deletions

View File

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