diff --git a/app/src/decoder.c b/app/src/decoder.c index 87cd0fe7..7b016d21 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -19,13 +19,7 @@ push_frame(struct decoder *decoder) { &previous_frame_skipped); if (previous_frame_skipped) { fps_counter_add_skipped_frame(decoder->fps_counter); - // the previous EVENT_NEW_FRAME will consume this frame - return; } - static SDL_Event new_frame_event = { - .type = EVENT_NEW_FRAME, - }; - SDL_PushEvent(&new_frame_event); } void diff --git a/app/src/screen.c b/app/src/screen.c index 473d2e35..397aa2d1 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -191,12 +191,31 @@ screen_update_content_rect(struct screen *screen) { } } +static void +on_frame_available(struct video_buffer *vb, void *userdata) { + (void) vb; + (void) userdata; + + static SDL_Event new_frame_event = { + .type = EVENT_NEW_FRAME, + }; + + // Post the event on the UI thread + SDL_PushEvent(&new_frame_event); +} + void screen_init(struct screen *screen, struct video_buffer *vb, struct fps_counter *fps_counter) { *screen = (struct screen) SCREEN_INITIALIZER; screen->vb = vb; screen->fps_counter = fps_counter; + + static const struct video_buffer_callbacks cbs = { + .on_frame_available = on_frame_available, + }; + + video_buffer_set_consumer_callbacks(vb, &cbs, NULL); } static inline SDL_Texture * diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index e7e150a2..c145de08 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -43,6 +43,10 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer) { // there is initially no frame, so consider it has already been consumed vb->pending_frame_consumed = true; + // The callbacks must be set by the consumer via + // video_buffer_set_consumer_callbacks() + vb->cbs = NULL; + return true; error_3: @@ -82,9 +86,22 @@ video_buffer_swap_consumer_frame(struct video_buffer *vb) { vb->pending_frame = tmp; } +void +video_buffer_set_consumer_callbacks(struct video_buffer *vb, + const struct video_buffer_callbacks *cbs, + void *cbs_userdata) { + assert(!vb->cbs); // must be set only once + assert(cbs); + assert(cbs->on_frame_available); + vb->cbs = cbs; + vb->cbs_userdata = cbs_userdata; +} + void video_buffer_producer_offer_frame(struct video_buffer *vb, bool *previous_frame_skipped) { + assert(vb->cbs); + sc_mutex_lock(&vb->mutex); if (vb->wait_consumer) { // wait for the current (expired) frame to be consumed @@ -95,10 +112,17 @@ video_buffer_producer_offer_frame(struct video_buffer *vb, video_buffer_swap_producer_frame(vb); - *previous_frame_skipped = !vb->pending_frame_consumed; + bool skipped = !vb->pending_frame_consumed; + *previous_frame_skipped = skipped; vb->pending_frame_consumed = false; sc_mutex_unlock(&vb->mutex); + + if (!skipped) { + // If skipped, then the previous call will consume this frame, the + // callback must not be called + vb->cbs->on_frame_available(vb, vb->cbs_userdata); + } } const AVFrame * diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 6c5d197c..a41521a3 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -39,6 +39,16 @@ struct video_buffer { sc_cond pending_frame_consumed_cond; bool pending_frame_consumed; + + const struct video_buffer_callbacks *cbs; + void *cbs_userdata; +}; + +struct video_buffer_callbacks { + // Called when a new frame can be consumed by + // video_buffer_consumer_take_frame(vb) + // This callback is mandatory (it must not be NULL). + void (*on_frame_available)(struct video_buffer *vb, void *userdata); }; bool @@ -47,6 +57,11 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer); void video_buffer_destroy(struct video_buffer *vb); +void +video_buffer_set_consumer_callbacks(struct video_buffer *vb, + const struct video_buffer_callbacks *cbs, + void *cbs_userdata); + // set the producer frame as ready for consuming // the output flag is set to report whether the previous frame has been skipped void