2023-03-01 23:31:43 +00:00
|
|
|
#include "delay_buffer.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <libavutil/avutil.h>
|
|
|
|
#include <libavformat/avformat.h>
|
|
|
|
|
|
|
|
#include "util/log.h"
|
|
|
|
|
|
|
|
#define SC_BUFFERING_NDEBUG // comment to debug
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
/** Downcast frame_sink to sc_delay_buffer */
|
|
|
|
#define DOWNCAST(SINK) container_of(SINK, struct sc_delay_buffer, frame_sink)
|
|
|
|
|
2023-03-01 23:31:43 +00:00
|
|
|
static bool
|
|
|
|
sc_delayed_frame_init(struct sc_delayed_frame *dframe, const AVFrame *frame) {
|
|
|
|
dframe->frame = av_frame_alloc();
|
|
|
|
if (!dframe->frame) {
|
|
|
|
LOG_OOM();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (av_frame_ref(dframe->frame, frame)) {
|
|
|
|
LOG_OOM();
|
|
|
|
av_frame_free(&dframe->frame);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_delayed_frame_destroy(struct sc_delayed_frame *dframe) {
|
|
|
|
av_frame_unref(dframe->frame);
|
|
|
|
av_frame_free(&dframe->frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
run_buffering(void *data) {
|
|
|
|
struct sc_delay_buffer *db = data;
|
|
|
|
|
|
|
|
assert(db->delay > 0);
|
|
|
|
|
|
|
|
for (;;) {
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_lock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
while (!db->stopped && sc_vecdeque_is_empty(&db->queue)) {
|
|
|
|
sc_cond_wait(&db->queue_cond, &db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
if (db->stopped) {
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
goto stopped;
|
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
struct sc_delayed_frame dframe = sc_vecdeque_pop(&db->queue);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
|
|
|
sc_tick max_deadline = sc_tick_now() + db->delay;
|
|
|
|
// PTS (written by the server) are expressed in microseconds
|
|
|
|
sc_tick pts = SC_TICK_FROM_US(dframe.frame->pts);
|
|
|
|
|
|
|
|
bool timed_out = false;
|
2023-03-02 20:30:24 +00:00
|
|
|
while (!db->stopped && !timed_out) {
|
|
|
|
sc_tick deadline = sc_clock_to_system_time(&db->clock, pts)
|
2023-03-01 23:31:43 +00:00
|
|
|
+ db->delay;
|
|
|
|
if (deadline > max_deadline) {
|
|
|
|
deadline = max_deadline;
|
|
|
|
}
|
|
|
|
|
|
|
|
timed_out =
|
2023-03-02 20:30:24 +00:00
|
|
|
!sc_cond_timedwait(&db->wait_cond, &db->mutex, deadline);
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
bool stopped = db->stopped;
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
|
|
|
if (stopped) {
|
|
|
|
sc_delayed_frame_destroy(&dframe);
|
|
|
|
goto stopped;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SC_BUFFERING_NDEBUG
|
|
|
|
LOGD("Buffering: %" PRItick ";%" PRItick ";%" PRItick,
|
|
|
|
pts, dframe.push_date, sc_tick_now());
|
|
|
|
#endif
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
bool ok = sc_frame_source_sinks_push(&db->frame_source, dframe.frame);
|
2023-03-01 23:31:43 +00:00
|
|
|
sc_delayed_frame_destroy(&dframe);
|
|
|
|
if (!ok) {
|
|
|
|
LOGE("Delayed frame could not be pushed, stopping");
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_lock(&db->mutex);
|
2023-03-02 20:30:24 +00:00
|
|
|
// Prevent to push any new frame
|
2023-03-02 20:30:24 +00:00
|
|
|
db->stopped = true;
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
goto stopped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stopped:
|
2023-03-02 20:30:24 +00:00
|
|
|
assert(db->stopped);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
|
|
|
// Flush queue
|
2023-03-02 20:30:24 +00:00
|
|
|
while (!sc_vecdeque_is_empty(&db->queue)) {
|
|
|
|
struct sc_delayed_frame *dframe = sc_vecdeque_popref(&db->queue);
|
2023-03-01 23:31:43 +00:00
|
|
|
sc_delayed_frame_destroy(dframe);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGD("Buffering thread ended");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
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;
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
bool ok = sc_mutex_init(&db->mutex);
|
2023-03-02 20:30:24 +00:00
|
|
|
if (!ok) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
ok = sc_cond_init(&db->queue_cond);
|
2023-03-02 20:30:24 +00:00
|
|
|
if (!ok) {
|
|
|
|
goto error_destroy_mutex;
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
ok = sc_cond_init(&db->wait_cond);
|
2023-03-02 20:30:24 +00:00
|
|
|
if (!ok) {
|
|
|
|
goto error_destroy_queue_cond;
|
|
|
|
}
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_clock_init(&db->clock);
|
|
|
|
sc_vecdeque_init(&db->queue);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
if (!sc_frame_source_sinks_open(&db->frame_source, ctx)) {
|
|
|
|
goto error_destroy_wait_cond;
|
|
|
|
}
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
ok = sc_thread_create(&db->thread, run_buffering, "scrcpy-dbuf", db);
|
2023-03-02 20:30:24 +00:00
|
|
|
if (!ok) {
|
|
|
|
LOGE("Could not start buffering thread");
|
|
|
|
goto error_close_sinks;
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
error_close_sinks:
|
|
|
|
sc_frame_source_sinks_close(&db->frame_source);
|
|
|
|
error_destroy_wait_cond:
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_cond_destroy(&db->wait_cond);
|
2023-03-02 20:30:24 +00:00
|
|
|
error_destroy_queue_cond:
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_cond_destroy(&db->queue_cond);
|
2023-03-02 20:30:24 +00:00
|
|
|
error_destroy_mutex:
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_destroy(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
return false;
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
static void
|
|
|
|
sc_delay_buffer_frame_sink_close(struct sc_frame_sink *sink) {
|
|
|
|
struct sc_delay_buffer *db = DOWNCAST(sink);
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_lock(&db->mutex);
|
|
|
|
db->stopped = true;
|
|
|
|
sc_cond_signal(&db->queue_cond);
|
|
|
|
sc_cond_signal(&db->wait_cond);
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-02 20:30:24 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_thread_join(&db->thread, NULL);
|
2023-03-02 20:30:24 +00:00
|
|
|
|
|
|
|
sc_frame_source_sinks_close(&db->frame_source);
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_cond_destroy(&db->wait_cond);
|
|
|
|
sc_cond_destroy(&db->queue_cond);
|
|
|
|
sc_mutex_destroy(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
static bool
|
|
|
|
sc_delay_buffer_frame_sink_push(struct sc_frame_sink *sink,
|
|
|
|
const AVFrame *frame) {
|
|
|
|
struct sc_delay_buffer *db = DOWNCAST(sink);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_lock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
if (db->stopped) {
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc_tick pts = SC_TICK_FROM_US(frame->pts);
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_clock_update(&db->clock, sc_tick_now(), pts);
|
|
|
|
sc_cond_signal(&db->wait_cond);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
if (db->clock.count == 1) {
|
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-02 21:13:48 +00:00
|
|
|
// First frame, push it immediately, not to delay the opening of the
|
|
|
|
// scrcpy window
|
2023-03-02 20:30:24 +00:00
|
|
|
return sc_frame_source_sinks_push(&db->frame_source, frame);
|
2023-03-01 23:31:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct sc_delayed_frame dframe;
|
|
|
|
bool ok = sc_delayed_frame_init(&dframe, frame);
|
|
|
|
if (!ok) {
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SC_BUFFERING_NDEBUG
|
|
|
|
dframe.push_date = sc_tick_now();
|
|
|
|
#endif
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
ok = sc_vecdeque_push(&db->queue, dframe);
|
2023-03-01 23:31:43 +00:00
|
|
|
if (!ok) {
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
LOG_OOM();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_cond_signal(&db->queue_cond);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
2023-03-02 20:30:24 +00:00
|
|
|
sc_mutex_unlock(&db->mutex);
|
2023-03-01 23:31:43 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2023-03-02 20:30:24 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|