diff --git a/app/meson.build b/app/meson.build index 9f73b434..392fa6d0 100644 --- a/app/meson.build +++ b/app/meson.build @@ -29,7 +29,6 @@ src = [ 'src/screen.c', 'src/server.c', 'src/version.c', - 'src/video_buffer.c', 'src/trait/frame_source.c', 'src/trait/packet_source.c', 'src/util/acksync.c', diff --git a/app/src/decoder.c b/app/src/decoder.c index 2931c1ec..a8168f66 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -4,7 +4,6 @@ #include #include "events.h" -#include "video_buffer.h" #include "trait/frame_sink.h" #include "util/log.h" diff --git a/app/src/delay_buffer.c b/app/src/delay_buffer.c index 95d47c9c..72af3672 100644 --- a/app/src/delay_buffer.c +++ b/app/src/delay_buffer.c @@ -10,6 +10,9 @@ #define SC_BUFFERING_NDEBUG // comment to debug +/** Downcast frame_sink to sc_delay_buffer */ +#define DOWNCAST(SINK) container_of(SINK, struct sc_delay_buffer, frame_sink) + static bool sc_delayed_frame_init(struct sc_delayed_frame *dframe, const AVFrame *frame) { dframe->frame = av_frame_alloc(); @@ -33,11 +36,6 @@ sc_delayed_frame_destroy(struct sc_delayed_frame *dframe) { av_frame_free(&dframe->frame); } -static bool -sc_delay_buffer_offer(struct sc_delay_buffer *db, const AVFrame *frame) { - return db->cbs->on_new_frame(db, frame, db->cbs_userdata); -} - static int run_buffering(void *data) { struct sc_delay_buffer *db = data; @@ -87,12 +85,12 @@ run_buffering(void *data) { pts, dframe.push_date, sc_tick_now()); #endif - bool ok = sc_delay_buffer_offer(db, dframe.frame); + bool ok = sc_frame_source_sinks_push(&db->frame_source, dframe.frame); sc_delayed_frame_destroy(&dframe); if (!ok) { LOGE("Delayed frame could not be pushed, stopping"); sc_mutex_lock(&db->b.mutex); - // Prevent to push any new packet + // Prevent to push any new frame db->b.stopped = true; sc_mutex_unlock(&db->b.mutex); goto stopped; @@ -113,92 +111,77 @@ stopped: return 0; } -bool -sc_delay_buffer_init(struct sc_delay_buffer *db, sc_tick delay, - const struct sc_delay_buffer_callbacks *cbs, - void *cbs_userdata) { - assert(delay >= 0); +static bool +sc_delay_buffer_frame_sink_open(struct sc_frame_sink *sink, + const AVCodecContext *ctx) { + struct sc_delay_buffer *db = DOWNCAST(sink); + (void) ctx; - if (delay) { - bool ok = sc_mutex_init(&db->b.mutex); - if (!ok) { - return false; - } - - ok = sc_cond_init(&db->b.queue_cond); - if (!ok) { - sc_mutex_destroy(&db->b.mutex); - return false; - } - - ok = sc_cond_init(&db->b.wait_cond); - if (!ok) { - sc_cond_destroy(&db->b.queue_cond); - sc_mutex_destroy(&db->b.mutex); - return false; - } - - sc_clock_init(&db->b.clock); - sc_vecdeque_init(&db->b.queue); + bool ok = sc_mutex_init(&db->b.mutex); + if (!ok) { + return false; } - assert(cbs); - assert(cbs->on_new_frame); + ok = sc_cond_init(&db->b.queue_cond); + if (!ok) { + goto error_destroy_mutex; + } - db->delay = delay; - db->cbs = cbs; - db->cbs_userdata = cbs_userdata; + ok = sc_cond_init(&db->b.wait_cond); + if (!ok) { + goto error_destroy_queue_cond; + } - return true; -} + sc_clock_init(&db->b.clock); + sc_vecdeque_init(&db->b.queue); -bool -sc_delay_buffer_start(struct sc_delay_buffer *db) { - if (db->delay) { - bool ok = - sc_thread_create(&db->b.thread, run_buffering, "scrcpy-dbuf", db); - if (!ok) { - LOGE("Could not start buffering thread"); - return false; - } + if (!sc_frame_source_sinks_open(&db->frame_source, ctx)) { + goto error_destroy_wait_cond; + } + + ok = sc_thread_create(&db->b.thread, run_buffering, "scrcpy-dbuf", db); + if (!ok) { + LOGE("Could not start buffering thread"); + goto error_close_sinks; } return true; + +error_close_sinks: + sc_frame_source_sinks_close(&db->frame_source); +error_destroy_wait_cond: + sc_cond_destroy(&db->b.wait_cond); +error_destroy_queue_cond: + sc_cond_destroy(&db->b.queue_cond); +error_destroy_mutex: + sc_mutex_destroy(&db->b.mutex); + + return false; } -void -sc_delay_buffer_stop(struct sc_delay_buffer *db) { - if (db->delay) { - sc_mutex_lock(&db->b.mutex); - db->b.stopped = true; - sc_cond_signal(&db->b.queue_cond); - sc_cond_signal(&db->b.wait_cond); - sc_mutex_unlock(&db->b.mutex); - } +static void +sc_delay_buffer_frame_sink_close(struct sc_frame_sink *sink) { + struct sc_delay_buffer *db = DOWNCAST(sink); + + sc_mutex_lock(&db->b.mutex); + db->b.stopped = true; + sc_cond_signal(&db->b.queue_cond); + sc_cond_signal(&db->b.wait_cond); + sc_mutex_unlock(&db->b.mutex); + + sc_thread_join(&db->b.thread, NULL); + + sc_frame_source_sinks_close(&db->frame_source); + + sc_cond_destroy(&db->b.wait_cond); + sc_cond_destroy(&db->b.queue_cond); + sc_mutex_destroy(&db->b.mutex); } -void -sc_delay_buffer_join(struct sc_delay_buffer *db) { - if (db->delay) { - sc_thread_join(&db->b.thread, NULL); - } -} - -void -sc_delay_buffer_destroy(struct sc_delay_buffer *db) { - if (db->delay) { - sc_cond_destroy(&db->b.wait_cond); - sc_cond_destroy(&db->b.queue_cond); - sc_mutex_destroy(&db->b.mutex); - } -} - -bool -sc_delay_buffer_push(struct sc_delay_buffer *db, const AVFrame *frame) { - if (!db->delay) { - // No buffering - return sc_delay_buffer_offer(db, frame); - } +static bool +sc_delay_buffer_frame_sink_push(struct sc_frame_sink *sink, + const AVFrame *frame) { + struct sc_delay_buffer *db = DOWNCAST(sink); sc_mutex_lock(&db->b.mutex); @@ -213,11 +196,11 @@ sc_delay_buffer_push(struct sc_delay_buffer *db, const AVFrame *frame) { if (db->b.clock.count == 1) { sc_mutex_unlock(&db->b.mutex); - // First frame, offer it immediately, for two reasons: + // First frame, push it immediately, for two reasons: // - not to delay the opening of the scrcpy window // - the buffering estimation needs at least two clock points, so it // could not handle the first frame - return sc_delay_buffer_offer(db, frame); + return sc_frame_source_sinks_push(&db->frame_source, frame); } struct sc_delayed_frame dframe; @@ -244,3 +227,20 @@ sc_delay_buffer_push(struct sc_delay_buffer *db, const AVFrame *frame) { return true; } + +void +sc_delay_buffer_init(struct sc_delay_buffer *db, sc_tick delay) { + assert(delay > 0); + + db->delay = delay; + + sc_frame_source_init(&db->frame_source); + + static const struct sc_frame_sink_ops ops = { + .open = sc_delay_buffer_frame_sink_open, + .close = sc_delay_buffer_frame_sink_close, + .push = sc_delay_buffer_frame_sink_push, + }; + + db->frame_sink.ops = &ops; +} diff --git a/app/src/delay_buffer.h b/app/src/delay_buffer.h index 9e5347c7..4cb981c8 100644 --- a/app/src/delay_buffer.h +++ b/app/src/delay_buffer.h @@ -6,6 +6,8 @@ #include #include "clock.h" +#include "trait/frame_source.h" +#include "trait/frame_sink.h" #include "util/thread.h" #include "util/tick.h" #include "util/vecdeque.h" @@ -23,9 +25,11 @@ struct sc_delayed_frame { struct sc_delayed_frame_queue SC_VECDEQUE(struct sc_delayed_frame); struct sc_delay_buffer { + struct sc_frame_source frame_source; // frame source trait + struct sc_frame_sink frame_sink; // frame sink trait + sc_tick delay; - // only if delay > 0 struct { sc_thread thread; sc_mutex mutex; @@ -36,9 +40,6 @@ struct sc_delay_buffer { struct sc_delayed_frame_queue queue; bool stopped; } b; // buffering - - const struct sc_delay_buffer_callbacks *cbs; - void *cbs_userdata; }; struct sc_delay_buffer_callbacks { @@ -46,24 +47,12 @@ struct sc_delay_buffer_callbacks { void *userdata); }; -bool -sc_delay_buffer_init(struct sc_delay_buffer *db, sc_tick delay, - const struct sc_delay_buffer_callbacks *cbs, - void *cbs_userdata); - -bool -sc_delay_buffer_start(struct sc_delay_buffer *db); - +/** + * Initialize a delay buffer. + * + * \param delay a (strictly) positive delay + */ void -sc_delay_buffer_stop(struct sc_delay_buffer *db); - -void -sc_delay_buffer_join(struct sc_delay_buffer *db); - -void -sc_delay_buffer_destroy(struct sc_delay_buffer *db); - -bool -sc_delay_buffer_push(struct sc_delay_buffer *db, const AVFrame *frame); +sc_delay_buffer_init(struct sc_delay_buffer *db, sc_tick delay); #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 54858c01..2688cab6 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -15,6 +15,7 @@ #include "controller.h" #include "decoder.h" +#include "delay_buffer.h" #include "demuxer.h" #include "events.h" #include "file_pusher.h" @@ -45,8 +46,10 @@ struct scrcpy { struct sc_decoder video_decoder; struct sc_decoder audio_decoder; struct sc_recorder recorder; + struct sc_delay_buffer display_buffer; #ifdef HAVE_V4L2 struct sc_v4l2_sink v4l2_sink; + struct sc_delay_buffer v4l2_buffer; #endif struct sc_controller controller; struct sc_file_pusher file_pusher; @@ -657,7 +660,6 @@ aoa_hid_end: .mipmaps = options->mipmaps, .fullscreen = options->fullscreen, .start_fps_counter = options->start_fps_counter, - .buffering_time = options->display_buffer, }; if (!sc_screen_init(&s->screen, &screen_params)) { @@ -665,19 +667,31 @@ aoa_hid_end: } screen_initialized = true; - sc_frame_source_add_sink(&s->video_decoder.frame_source, - &s->screen.frame_sink); + struct sc_frame_source *src = &s->video_decoder.frame_source; + if (options->display_buffer) { + sc_delay_buffer_init(&s->display_buffer, options->display_buffer); + sc_frame_source_add_sink(src, &s->display_buffer.frame_sink); + src = &s->display_buffer.frame_source; + } + + sc_frame_source_add_sink(src, &s->screen.frame_sink); } #ifdef HAVE_V4L2 if (options->v4l2_device) { if (!sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, - info->frame_size, options->v4l2_buffer)) { + info->frame_size)) { goto end; } - sc_frame_source_add_sink(&s->video_decoder.frame_source, - &s->v4l2_sink.frame_sink); + struct sc_frame_source *src = &s->video_decoder.frame_source; + if (options->v4l2_buffer) { + sc_delay_buffer_init(&s->v4l2_buffer, options->v4l2_buffer); + sc_frame_source_add_sink(src, &s->v4l2_buffer.frame_sink); + src = &s->v4l2_buffer.frame_source; + } + + sc_frame_source_add_sink(src, &s->v4l2_sink.frame_sink); v4l2_sink_initialized = true; } diff --git a/app/src/screen.c b/app/src/screen.c index ce2e74bb..b814ada1 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -7,7 +7,6 @@ #include "events.h" #include "icon.h" #include "options.h" -#include "video_buffer.h" #include "util/log.h" #define DISPLAY_MARGINS 96 @@ -359,14 +358,12 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) { static bool sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { struct sc_screen *screen = DOWNCAST(sink); - return sc_video_buffer_push(&screen->vb, frame); -} -static bool -sc_video_buffer_on_new_frame(struct sc_video_buffer *vb, bool previous_skipped, - void *userdata) { - (void) vb; - struct sc_screen *screen = userdata; + bool previous_skipped; + bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped); + if (!ok) { + return false; + } if (previous_skipped) { sc_fps_counter_add_skipped_frame(&screen->fps_counter); @@ -404,23 +401,13 @@ sc_screen_init(struct sc_screen *screen, screen->req.fullscreen = params->fullscreen; screen->req.start_fps_counter = params->start_fps_counter; - 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, params->buffering_time, &cbs, - screen); + bool ok = sc_frame_buffer_init(&screen->fb); if (!ok) { return false; } - ok = sc_video_buffer_start(&screen->vb); - if (!ok) { - goto error_destroy_video_buffer; - } - if (!sc_fps_counter_init(&screen->fps_counter)) { - goto error_stop_and_join_video_buffer; + goto error_destroy_frame_buffer; } screen->frame_size = params->frame_size; @@ -552,11 +539,8 @@ error_destroy_window: SDL_DestroyWindow(screen->window); error_destroy_fps_counter: sc_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); +error_destroy_frame_buffer: + sc_frame_buffer_destroy(&screen->fb); return false; } @@ -593,13 +577,11 @@ sc_screen_hide_window(struct sc_screen *screen) { void sc_screen_interrupt(struct sc_screen *screen) { - sc_video_buffer_stop(&screen->vb); sc_fps_counter_interrupt(&screen->fps_counter); } void sc_screen_join(struct sc_screen *screen) { - sc_video_buffer_join(&screen->vb); sc_fps_counter_join(&screen->fps_counter); } @@ -613,7 +595,7 @@ sc_screen_destroy(struct sc_screen *screen) { SDL_DestroyRenderer(screen->renderer); SDL_DestroyWindow(screen->window); sc_fps_counter_destroy(&screen->fps_counter); - sc_video_buffer_destroy(&screen->vb); + sc_frame_buffer_destroy(&screen->fb); } static void @@ -719,7 +701,7 @@ update_texture(struct sc_screen *screen, const AVFrame *frame) { static bool sc_screen_update_frame(struct sc_screen *screen) { av_frame_unref(screen->frame); - sc_video_buffer_consume(&screen->vb, screen->frame); + sc_frame_buffer_consume(&screen->fb, screen->frame); AVFrame *frame = screen->frame; sc_fps_counter_add_rendered_frame(&screen->fps_counter); diff --git a/app/src/screen.h b/app/src/screen.h index 0952c79c..28afea40 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -10,12 +10,12 @@ #include "controller.h" #include "coords.h" #include "fps_counter.h" +#include "frame_buffer.h" #include "input_manager.h" #include "opengl.h" #include "trait/key_processor.h" #include "trait/frame_sink.h" #include "trait/mouse_processor.h" -#include "video_buffer.h" struct sc_screen { struct sc_frame_sink frame_sink; // frame sink trait @@ -25,7 +25,7 @@ struct sc_screen { #endif struct sc_input_manager im; - struct sc_video_buffer vb; + struct sc_frame_buffer fb; struct sc_fps_counter fps_counter; // The initial requested window properties @@ -93,8 +93,6 @@ struct sc_screen_params { bool fullscreen; bool start_fps_counter; - - sc_tick buffering_time; }; // initialize screen, create window, renderer and texture (window is hidden) diff --git a/app/src/v4l2_sink.c b/app/src/v4l2_sink.c index 5dfe37bc..fe11614a 100644 --- a/app/src/v4l2_sink.c +++ b/app/src/v4l2_sink.c @@ -126,7 +126,7 @@ run_v4l2_sink(void *data) { vs->has_frame = false; sc_mutex_unlock(&vs->mutex); - sc_video_buffer_consume(&vs->vb, vs->frame); + sc_frame_buffer_consume(&vs->fb, vs->frame); bool ok = encode_and_write_frame(vs, vs->frame); av_frame_unref(vs->frame); @@ -141,44 +141,19 @@ run_v4l2_sink(void *data) { return 0; } -static bool -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); - } - - return true; -} - static bool sc_v4l2_sink_open(struct sc_v4l2_sink *vs, const AVCodecContext *ctx) { assert(ctx->pix_fmt == AV_PIX_FMT_YUV420P); (void) ctx; - 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, vs->buffering_time, &cbs, vs); + bool ok = sc_frame_buffer_init(&vs->fb); if (!ok) { return false; } - ok = sc_video_buffer_start(&vs->vb); - if (!ok) { - goto error_video_buffer_destroy; - } - ok = sc_mutex_init(&vs->mutex); if (!ok) { - goto error_video_buffer_stop_and_join; + goto error_frame_buffer_destroy; } ok = sc_cond_init(&vs->cond); @@ -303,11 +278,8 @@ 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); +error_frame_buffer_destroy: + sc_frame_buffer_destroy(&vs->fb); return false; } @@ -319,10 +291,7 @@ 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); @@ -332,12 +301,25 @@ sc_v4l2_sink_close(struct sc_v4l2_sink *vs) { avformat_free_context(vs->format_ctx); sc_cond_destroy(&vs->cond); sc_mutex_destroy(&vs->mutex); - sc_video_buffer_destroy(&vs->vb); + sc_frame_buffer_destroy(&vs->fb); } static bool sc_v4l2_sink_push(struct sc_v4l2_sink *vs, const AVFrame *frame) { - return sc_video_buffer_push(&vs->vb, frame); + bool previous_skipped; + bool ok = sc_frame_buffer_push(&vs->fb, 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 @@ -360,7 +342,7 @@ sc_v4l2_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) { bool sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name, - struct sc_size frame_size, sc_tick buffering_time) { + struct sc_size frame_size) { vs->device_name = strdup(device_name); if (!vs->device_name) { LOGE("Could not strdup v4l2 device name"); @@ -368,7 +350,6 @@ sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name, } vs->frame_size = frame_size; - vs->buffering_time = buffering_time; static const struct sc_frame_sink_ops ops = { .open = sc_v4l2_frame_sink_open, diff --git a/app/src/v4l2_sink.h b/app/src/v4l2_sink.h index 339a61f2..789e31c3 100644 --- a/app/src/v4l2_sink.h +++ b/app/src/v4l2_sink.h @@ -8,19 +8,18 @@ #include "coords.h" #include "trait/frame_sink.h" -#include "video_buffer.h" +#include "frame_buffer.h" #include "util/tick.h" struct sc_v4l2_sink { struct sc_frame_sink frame_sink; // frame sink trait - struct sc_video_buffer vb; + struct sc_frame_buffer fb; AVFormatContext *format_ctx; AVCodecContext *encoder_ctx; char *device_name; struct sc_size frame_size; - sc_tick buffering_time; sc_thread thread; sc_mutex mutex; @@ -35,7 +34,7 @@ struct sc_v4l2_sink { bool sc_v4l2_sink_init(struct sc_v4l2_sink *vs, const char *device_name, - struct sc_size frame_size, sc_tick buffering_time); + struct sc_size frame_size); void sc_v4l2_sink_destroy(struct sc_v4l2_sink *vs); diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c deleted file mode 100644 index da47a0b5..00000000 --- a/app/src/video_buffer.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "video_buffer.h" - -#include -#include - -#include -#include - -#include "util/log.h" - -static bool -sc_delay_buffer_on_new_frame(struct sc_delay_buffer *db, const AVFrame *frame, - void *userdata) { - (void) db; - - struct sc_video_buffer *vb = userdata; - - bool previous_skipped; - bool ok = sc_frame_buffer_push(&vb->fb, frame, &previous_skipped); - if (!ok) { - return false; - } - - return vb->cbs->on_new_frame(vb, previous_skipped, vb->cbs_userdata); -} - -bool -sc_video_buffer_init(struct sc_video_buffer *vb, sc_tick delay, - const struct sc_video_buffer_callbacks *cbs, - void *cbs_userdata) { - bool ok = sc_frame_buffer_init(&vb->fb); - if (!ok) { - return false; - } - - static const struct sc_delay_buffer_callbacks db_cbs = { - .on_new_frame = sc_delay_buffer_on_new_frame, - }; - - ok = sc_delay_buffer_init(&vb->db, delay, &db_cbs, vb); - if (!ok) { - sc_frame_buffer_destroy(&vb->fb); - return false; - } - - assert(cbs); - assert(cbs->on_new_frame); - - vb->cbs = cbs; - vb->cbs_userdata = cbs_userdata; - - return true; -} - -bool -sc_video_buffer_start(struct sc_video_buffer *vb) { - return sc_delay_buffer_start(&vb->db); -} - -void -sc_video_buffer_stop(struct sc_video_buffer *vb) { - return sc_delay_buffer_stop(&vb->db); -} - -void -sc_video_buffer_join(struct sc_video_buffer *vb) { - return sc_delay_buffer_join(&vb->db); -} - -void -sc_video_buffer_destroy(struct sc_video_buffer *vb) { - sc_frame_buffer_destroy(&vb->fb); - sc_delay_buffer_destroy(&vb->db); -} - -bool -sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame) { - return sc_delay_buffer_push(&vb->db, frame); -} - -void -sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst) { - sc_frame_buffer_consume(&vb->fb, dst); -} diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h deleted file mode 100644 index ebca3915..00000000 --- a/app/src/video_buffer.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef SC_VIDEO_BUFFER_H -#define SC_VIDEO_BUFFER_H - -#include "common.h" - -#include - -#include "delay_buffer.h" -#include "frame_buffer.h" - -struct sc_video_buffer { - struct sc_delay_buffer db; - struct sc_frame_buffer fb; - - const struct sc_video_buffer_callbacks *cbs; - void *cbs_userdata; -}; - -struct sc_video_buffer_callbacks { - bool (*on_new_frame)(struct sc_video_buffer *vb, bool previous_skipped, - void *userdata); -}; - -bool -sc_video_buffer_init(struct sc_video_buffer *vb, sc_tick delay, - 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); - -bool -sc_video_buffer_push(struct sc_video_buffer *vb, const AVFrame *frame); - -void -sc_video_buffer_consume(struct sc_video_buffer *vb, AVFrame *dst); - -#endif