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.
This commit is contained in:
Romain Vimont 2021-07-04 12:42:22 +02:00
parent 4d8bcfc68a
commit 408a301201
4 changed files with 68 additions and 32 deletions

View file

@ -274,14 +274,16 @@ screen_frame_sink_close(struct sc_frame_sink *sink) {
static bool static bool
screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
struct screen *screen = DOWNCAST(sink); 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;
} }
if (previous_frame_skipped) { 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_skipped) {
fps_counter_add_skipped_frame(&screen->fps_counter); fps_counter_add_skipped_frame(&screen->fps_counter);
// The EVENT_NEW_FRAME triggered for the previous frame will consume // The EVENT_NEW_FRAME triggered for the previous frame will consume
// this new frame instead // 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 // Post the event on the UI thread
SDL_PushEvent(&new_frame_event); SDL_PushEvent(&new_frame_event);
} }
return true;
} }
bool bool
@ -304,7 +304,11 @@ screen_init(struct screen *screen, const struct screen_params *params) {
screen->fullscreen = false; screen->fullscreen = false;
screen->maximized = 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) { if (!ok) {
LOGE("Could not initialize video buffer"); LOGE("Could not initialize video buffer");
return false; return false;

View file

@ -139,9 +139,27 @@ run_v4l2_sink(void *data) {
return 0; 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 static bool
sc_v4l2_sink_open(struct sc_v4l2_sink *vs) { 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) { if (!ok) {
LOGE("Could not initialize video buffer"); LOGE("Could not initialize video buffer");
return false; return false;
@ -303,20 +321,7 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) {
static bool static bool
sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) { sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) {
bool previous_skipped; return sc_video_buffer_push(&vs->vb, frame);
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;
} }
static bool static bool

View file

@ -7,8 +7,20 @@
#include "util/log.h" #include "util/log.h"
bool bool
sc_video_buffer_init(struct sc_video_buffer *vb) { sc_video_buffer_init(struct sc_video_buffer *vb,
return sc_frame_buffer_init(&vb->fb); 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 void
@ -17,9 +29,15 @@ sc_video_buffer_destroy(struct sc_video_buffer *vb) {
} }
bool bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame, sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) {
bool *previous_frame_skipped) { bool previous_skipped;
return sc_frame_buffer_push(&vb->fb, frame, previous_frame_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 void

View file

@ -12,17 +12,26 @@ typedef struct AVFrame AVFrame;
struct sc_video_buffer { struct sc_video_buffer {
struct sc_frame_buffer fb; 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 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 void
sc_video_buffer_destroy(struct sc_video_buffer *vb); sc_video_buffer_destroy(struct sc_video_buffer *vb);
bool bool
sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame, sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame);
bool *skipped);
void void
sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst); sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst);