buffering_thread

This commit is contained in:
Romain Vimont 2021-07-04 15:24:51 +02:00
parent 05c8ed1640
commit 40721126e2
4 changed files with 218 additions and 22 deletions

View file

@ -308,15 +308,21 @@ screen_init(struct screen *screen, const struct screen_params *params) {
.on_new_frame = sc_video_buffer_on_new_frame,
};
bool ok = sc_video_buffer_init(&screen->vb, &cbs, screen);
bool ok = sc_video_buffer_init(&screen->vb, 0, &cbs, screen);
if (!ok) {
LOGE("Could not initialize video buffer");
return false;
}
ok = sc_video_buffer_start(&screen->vb);
if (!ok) {
LOGE("Could not start video_buffer");
goto error_destroy_video_buffer;
}
if (!fps_counter_init(&screen->fps_counter)) {
LOGE("Could not initialize FPS counter");
goto error_destroy_video_buffer;
goto error_stop_and_join_video_buffer;
}
screen->frame_size = params->frame_size;
@ -457,6 +463,9 @@ error_destroy_window:
SDL_DestroyWindow(screen->window);
error_destroy_fps_counter:
fps_counter_destroy(&screen->fps_counter);
error_stop_and_join_video_buffer:
sc_video_buffer_stop(&screen->vb);
sc_video_buffer_join(&screen->vb);
error_destroy_video_buffer:
sc_video_buffer_destroy(&screen->vb);
@ -475,11 +484,13 @@ screen_hide_window(struct screen *screen) {
void
screen_interrupt(struct screen *screen) {
sc_video_buffer_stop(&screen->vb);
fps_counter_interrupt(&screen->fps_counter);
}
void
screen_join(struct screen *screen) {
sc_video_buffer_join(&screen->vb);
fps_counter_join(&screen->fps_counter);
}

View file

@ -159,16 +159,22 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
.on_new_frame = sc_video_buffer_on_new_frame,
};
bool ok = sc_video_buffer_init(&vs->vb, &cbs, vs);
bool ok = sc_video_buffer_init(&vs->vb, 0, &cbs, vs);
if (!ok) {
LOGE("Could not initialize video buffer");
return false;
}
ok = sc_video_buffer_start(&vs->vb);
if (!ok) {
LOGE("Could not start video buffer");
goto error_video_buffer_destroy;
}
ok = sc_mutex_init(&vs->mutex);
if (!ok) {
LOGC("Could not create mutex");
goto error_video_buffer_destroy;
goto error_video_buffer_stop_and_join;
}
ok = sc_cond_init(&vs->cond);
@ -293,6 +299,9 @@ error_cond_destroy:
sc_cond_destroy(&vs->cond);
error_mutex_destroy:
sc_mutex_destroy(&vs->mutex);
error_video_buffer_stop_and_join:
sc_video_buffer_stop(&vs->vb);
sc_video_buffer_join(&vs->vb);
error_video_buffer_destroy:
sc_video_buffer_destroy(&vs->vb);
@ -306,7 +315,10 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
sc_cond_signal(&vs->cond);
sc_mutex_unlock(&vs->mutex);
sc_video_buffer_stop(&vs->vb);
sc_thread_join(&vs->thread, NULL);
sc_video_buffer_join(&vs->vb);
av_packet_free(&vs->packet);
av_frame_free(&vs->frame);

View file

@ -6,30 +6,37 @@
#include "util/log.h"
bool
sc_video_buffer_init(struct sc_video_buffer *vb,
const struct sc_video_buffer_callbacks *cbs,
void *cbs_userdata) {
bool ok = sc_frame_buffer_init(&vb->fb);
if (!ok) {
return false;
static struct sc_video_buffer_frame *
sc_video_buffer_frame_new(const AVFrame *frame) {
struct sc_video_buffer_frame *vb_frame = malloc(sizeof(*vb_frame));
if (!vb_frame) {
return NULL;
}
assert(cbs);
assert(cbs->on_new_frame);
vb_frame->frame = av_frame_alloc();
if (!vb_frame->frame) {
free(vb_frame);
return NULL;
}
vb->cbs = cbs;
vb->cbs_userdata = cbs_userdata;
return true;
if (av_frame_ref(vb_frame->frame, frame)) {
av_frame_free(&vb_frame->frame);
free(vb_frame);
return NULL;
}
return vb_frame;
}
void
sc_video_buffer_destroy(struct sc_video_buffer *vb) {
sc_frame_buffer_destroy(&vb->fb);
static void
sc_video_buffer_frame_delete(struct sc_video_buffer_frame *vb_frame) {
av_frame_unref(vb_frame->frame);
av_frame_free(&vb_frame->frame);
free(vb_frame);
}
bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
static bool
sc_video_buffer_offer(struct sc_video_buffer *vb, const AVFrame *frame) {
bool previous_skipped;
bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped);
if (!ok) {
@ -40,6 +47,143 @@ sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
return true;
}
static int
run_buffering(void *data) {
struct sc_video_buffer *vb = data;
assert(vb->buffering_ms);
for (;;) {
sc_mutex_lock(&vb->b.mutex);
while (!vb->b.stopped && sc_queue_is_empty(&vb->b.queue)) {
sc_cond_wait(&vb->b.queue_cond, &vb->b.mutex);
}
if (vb->b.stopped) {
// Flush queue
while (!sc_queue_is_empty(&vb->b.queue)) {
struct sc_video_buffer_frame *vb_frame;
sc_queue_take(&vb->b.queue, next, &vb_frame);
sc_video_buffer_frame_delete(vb_frame);
}
break;
}
struct sc_video_buffer_frame *vb_frame;
sc_queue_take(&vb->b.queue, next, &vb_frame);
sc_mutex_unlock(&vb->b.mutex);
usleep(vb->buffering_ms * 1000);
sc_video_buffer_offer(vb, vb_frame->frame);
sc_video_buffer_frame_delete(vb_frame);
}
LOGD("Buffering thread ended");
return 0;
}
bool
sc_video_buffer_init(struct sc_video_buffer *vb, unsigned buffering_ms,
const struct sc_video_buffer_callbacks *cbs,
void *cbs_userdata) {
bool ok = sc_frame_buffer_init(&vb->fb);
if (!ok) {
return false;
}
if (buffering_ms) {
ok = sc_mutex_init(&vb->b.mutex);
if (!ok) {
LOGC("Could not create mutex");
sc_frame_buffer_destroy(&vb->fb);
return false;
}
ok = sc_cond_init(&vb->b.queue_cond);
if (!ok) {
LOGC("Could not create cond");
sc_mutex_destroy(&vb->b.mutex);
sc_frame_buffer_destroy(&vb->fb);
return false;
}
sc_queue_init(&vb->b.queue);
}
assert(cbs);
assert(cbs->on_new_frame);
vb->buffering_ms = buffering_ms;
vb->cbs = cbs;
vb->cbs_userdata = cbs_userdata;
return true;
}
bool
sc_video_buffer_start(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
bool ok =
sc_thread_create(&vb->b.thread, run_buffering, "buffering", vb);
if (!ok) {
LOGE("Could not start buffering thread");
return false;
}
}
return true;
}
void
sc_video_buffer_stop(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
sc_mutex_lock(&vb->b.mutex);
vb->b.stopped = true;
sc_mutex_unlock(&vb->b.mutex);
}
}
void
sc_video_buffer_join(struct sc_video_buffer *vb) {
if (vb->buffering_ms) {
sc_thread_join(&vb->b.thread, NULL);
}
}
void
sc_video_buffer_destroy(struct sc_video_buffer *vb) {
sc_frame_buffer_destroy(&vb->fb);
if (vb->buffering_ms) {
sc_cond_destroy(&vb->b.queue_cond);
sc_mutex_destroy(&vb->b.mutex);
}
}
bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
if (!vb->buffering_ms) {
// no buffering
return sc_video_buffer_offer(vb, frame);
}
struct sc_video_buffer_frame *vb_frame = sc_video_buffer_frame_new(frame);
if (!vb_frame) {
LOGE("Could not allocate frame");
return false;
}
sc_mutex_lock(&vb->b.mutex);
sc_queue_push(&vb->b.queue, next, vb_frame);
sc_cond_signal(&vb->b.queue_cond);
sc_mutex_unlock(&vb->b.mutex);
return true;
}
void
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) {
sc_frame_buffer_consume(&vb->fb, dst);

View file

@ -6,13 +6,33 @@
#include <stdbool.h>
#include "frame_buffer.h"
#include "util/queue.h"
#include "util/thread.h"
// forward declarations
typedef struct AVFrame AVFrame;
struct sc_video_buffer_frame {
AVFrame *frame;
struct sc_video_buffer_frame *next;
};
struct sc_video_buffer_frame_queue SC_QUEUE(struct sc_video_buffer_frame);
struct sc_video_buffer {
struct sc_frame_buffer fb;
unsigned buffering_ms;
// only if buffering_ms > 0
struct {
sc_thread thread;
sc_mutex mutex;
sc_cond queue_cond;
struct sc_video_buffer_frame_queue queue;
bool stopped;
} b; // buffering
const struct sc_video_buffer_callbacks *cbs;
void *cbs_userdata;
};
@ -23,10 +43,19 @@ struct sc_video_buffer_callbacks {
};
bool
sc_video_buffer_init(struct sc_video_buffer *vb,
sc_video_buffer_init(struct sc_video_buffer *vb, unsigned buffering_ms,
const struct sc_video_buffer_callbacks *cbs,
void *cbs_userdata);
bool
sc_video_buffer_start(struct sc_video_buffer *vb);
void
sc_video_buffer_stop(struct sc_video_buffer *vb);
void
sc_video_buffer_join(struct sc_video_buffer *vb);
void
sc_video_buffer_destroy(struct sc_video_buffer *vb);