Use delay buffer as a frame source/sink

The components needing delayed frames (sc_screen and sc_v4l2_sink)
managed a sc_video_buffer instance, which itself embedded a
sc_frame_buffer instance (to keep only the most recent frame).

In theory, these components should not be aware of delaying: they should
just receive AVFrames later, and only handle a sc_frame_buffer.

Therefore, refactor sc_delay_buffer as a frame source (it consumes)
frames) and a frame sink (it produces frames, after some delay), and
plug an instance in the pipeline only when a delay is requested.

This also removes the need for a specific sc_video_buffer.

PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
Romain Vimont
2023-03-02 21:30:24 +01:00
parent 974227a3fc
commit 1230149fdd
11 changed files with 150 additions and 320 deletions

View File

@@ -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,