From 408a301201fb170c1541028235e9d63cf88e53c7 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 4 Jul 2021 12:42:22 +0200 Subject: [PATCH] Notify new frames via callbacks Currently, a frame is available to the consumer as soon as it is pushed by the producer (which can detect if the previous frame is skipped). Notify the new frames (and frame skipped) via callbacks instead. This paves the way to add (optional) buffering, which will introduce a delay between the time when the frame is produced and the time it is available to be consumed. --- app/src/screen.c | 22 +++++++++++++--------- app/src/v4l2_sink.c | 35 ++++++++++++++++++++--------------- app/src/video_buffer.c | 28 +++++++++++++++++++++++----- app/src/video_buffer.h | 15 ++++++++++++--- 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index a11235c9..a9ee1fb0 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -274,14 +274,16 @@ screen_frame_sink_close(struct sc_frame_sink *sink) { static bool screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { struct screen *screen = DOWNCAST(sink); + return sc_video_buffer_push(&screen->vb, frame); +} - bool previous_frame_skipped; - bool ok = sc_video_buffer_push(&screen->vb, frame, &previous_frame_skipped); - if (!ok) { - return false; - } +static void +sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped, + void *userdata) { + (void) vb; + struct screen *screen = userdata; - if (previous_frame_skipped) { + if (previous_skipped) { fps_counter_add_skipped_frame(&screen->fps_counter); // The EVENT_NEW_FRAME triggered for the previous frame will consume // this new frame instead @@ -293,8 +295,6 @@ screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { // Post the event on the UI thread SDL_PushEvent(&new_frame_event); } - - return true; } bool @@ -304,7 +304,11 @@ screen_init(struct screen *screen, const struct screen_params *params) { screen->fullscreen = false; screen->maximized = false; - bool ok = sc_video_buffer_init(&screen->vb); + static const struct sc_video_buffer_callbacks cbs = { + .on_new_frame = sc_video_buffer_on_new_frame, + }; + + bool ok = sc_video_buffer_init(&screen->vb, &cbs, screen); if (!ok) { LOGE("Could not initialize video buffer"); return false; diff --git a/app/src/v4l2_sink.c b/app/src/v4l2_sink.c index 4b8ea043..21bbe404 100644 --- a/app/src/v4l2_sink.c +++ b/app/src/v4l2_sink.c @@ -139,9 +139,27 @@ run_v4l2_sink(void *data) { return 0; } +static void +sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped, + void *userdata) { + (void) vb; + struct sc_v4l2_sink *vs = userdata; + + if (!previous_skipped) { + sc_mutex_lock(&vs->mutex); + vs->has_frame = true; + sc_cond_signal(&vs->cond); + sc_mutex_unlock(&vs->mutex); + } +} + static bool sc_v4l2_sink_open(struct sc_v4l2_sink *vs) { - bool ok = sc_video_buffer_init(&vs->vb); + static const struct sc_video_buffer_callbacks cbs = { + .on_new_frame = sc_video_buffer_on_new_frame, + }; + + bool ok = sc_video_buffer_init(&vs->vb, &cbs, vs); if (!ok) { LOGE("Could not initialize video buffer"); return false; @@ -303,20 +321,7 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) { static bool sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) { - bool previous_skipped; - bool ok = sc_video_buffer_push(&vs->vb, frame, &previous_skipped); - if (!ok) { - return false; - } - - if (!previous_skipped) { - sc_mutex_lock(&vs->mutex); - vs->has_frame = true; - sc_cond_signal(&vs->cond); - sc_mutex_unlock(&vs->mutex); - } - - return true; + return sc_video_buffer_push(&vs->vb, frame); } static bool diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index 9a5fed43..664eb6c1 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -7,8 +7,20 @@ #include "util/log.h" bool -sc_video_buffer_init(struct sc_video_buffer *vb) { - return sc_frame_buffer_init(&vb->fb); +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; + } + + assert(cbs); + assert(cbs->on_new_frame); + + vb->cbs = cbs; + vb->cbs_userdata = cbs_userdata; + return true; } void @@ -17,9 +29,15 @@ sc_video_buffer_destroy(struct sc_video_buffer *vb) { } bool -sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame, - bool *previous_frame_skipped) { - return sc_frame_buffer_push(&vb->fb, frame, previous_frame_skipped); +sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) { + bool previous_skipped; + bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped); + if (!ok) { + return false; + } + + vb->cbs->on_new_frame(vb, previous_skipped, vb->cbs_userdata); + return true; } void diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 9befd26d..6f258980 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -12,17 +12,26 @@ typedef struct AVFrame AVFrame; struct sc_video_buffer { struct sc_frame_buffer fb; + + const struct sc_video_buffer_callbacks *cbs; + void *cbs_userdata; +}; + +struct sc_video_buffer_callbacks { + void (*on_new_frame)(struct sc_video_buffer *vb, bool previous_skipped, + void *userdata); }; bool -sc_video_buffer_init(struct sc_video_buffer *vb); +sc_video_buffer_init(struct sc_video_buffer *vb, + const struct sc_video_buffer_callbacks *cbs, + void *cbs_userdata); void sc_video_buffer_destroy(struct sc_video_buffer *vb); bool -sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame, - bool *skipped); +sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame); void sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst);