Wrap SDL thread functions into scrcpy-specific API

The goal is to expose a consistent API for system tools, and paves the
way to make the "core" independant of SDL in the future.
This commit is contained in:
Romain Vimont 2021-01-31 18:24:35 +01:00
parent 30e619d37f
commit f6320c7e31
24 changed files with 395 additions and 274 deletions

View file

@ -23,7 +23,8 @@ src = [
'src/video_buffer.c', 'src/video_buffer.c',
'src/util/net.c', 'src/util/net.c',
'src/util/process.c', 'src/util/process.c',
'src/util/str_util.c' 'src/util/str_util.c',
'src/util/thread.c',
] ]
if host_machine.system() == 'windows' if host_machine.system() == 'windows'

View file

@ -2,25 +2,27 @@
#include <assert.h> #include <assert.h>
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
bool bool
controller_init(struct controller *controller, socket_t control_socket) { controller_init(struct controller *controller, socket_t control_socket) {
cbuf_init(&controller->queue); cbuf_init(&controller->queue);
if (!receiver_init(&controller->receiver, control_socket)) { bool ok = receiver_init(&controller->receiver, control_socket);
if (!ok) {
return false; return false;
} }
if (!(controller->mutex = SDL_CreateMutex())) { ok = sc_mutex_init(&controller->mutex);
if (!ok) {
receiver_destroy(&controller->receiver); receiver_destroy(&controller->receiver);
return false; return false;
} }
if (!(controller->msg_cond = SDL_CreateCond())) { ok = sc_cond_init(&controller->msg_cond);
if (!ok) {
receiver_destroy(&controller->receiver); receiver_destroy(&controller->receiver);
SDL_DestroyMutex(controller->mutex); sc_mutex_destroy(&controller->mutex);
return false; return false;
} }
@ -32,8 +34,8 @@ controller_init(struct controller *controller, socket_t control_socket) {
void void
controller_destroy(struct controller *controller) { controller_destroy(struct controller *controller) {
SDL_DestroyCond(controller->msg_cond); sc_cond_destroy(&controller->msg_cond);
SDL_DestroyMutex(controller->mutex); sc_mutex_destroy(&controller->mutex);
struct control_msg msg; struct control_msg msg;
while (cbuf_take(&controller->queue, &msg)) { while (cbuf_take(&controller->queue, &msg)) {
@ -46,13 +48,13 @@ controller_destroy(struct controller *controller) {
bool bool
controller_push_msg(struct controller *controller, controller_push_msg(struct controller *controller,
const struct control_msg *msg) { const struct control_msg *msg) {
mutex_lock(controller->mutex); sc_mutex_lock(&controller->mutex);
bool was_empty = cbuf_is_empty(&controller->queue); bool was_empty = cbuf_is_empty(&controller->queue);
bool res = cbuf_push(&controller->queue, *msg); bool res = cbuf_push(&controller->queue, *msg);
if (was_empty) { if (was_empty) {
cond_signal(controller->msg_cond); sc_cond_signal(&controller->msg_cond);
} }
mutex_unlock(controller->mutex); sc_mutex_unlock(&controller->mutex);
return res; return res;
} }
@ -73,20 +75,20 @@ run_controller(void *data) {
struct controller *controller = data; struct controller *controller = data;
for (;;) { for (;;) {
mutex_lock(controller->mutex); sc_mutex_lock(&controller->mutex);
while (!controller->stopped && cbuf_is_empty(&controller->queue)) { while (!controller->stopped && cbuf_is_empty(&controller->queue)) {
cond_wait(controller->msg_cond, controller->mutex); sc_cond_wait(&controller->msg_cond, &controller->mutex);
} }
if (controller->stopped) { if (controller->stopped) {
// stop immediately, do not process further msgs // stop immediately, do not process further msgs
mutex_unlock(controller->mutex); sc_mutex_unlock(&controller->mutex);
break; break;
} }
struct control_msg msg; struct control_msg msg;
bool non_empty = cbuf_take(&controller->queue, &msg); bool non_empty = cbuf_take(&controller->queue, &msg);
assert(non_empty); assert(non_empty);
(void) non_empty; (void) non_empty;
mutex_unlock(controller->mutex); sc_mutex_unlock(&controller->mutex);
bool ok = process_msg(controller, &msg); bool ok = process_msg(controller, &msg);
control_msg_destroy(&msg); control_msg_destroy(&msg);
@ -102,16 +104,16 @@ bool
controller_start(struct controller *controller) { controller_start(struct controller *controller) {
LOGD("Starting controller thread"); LOGD("Starting controller thread");
controller->thread = SDL_CreateThread(run_controller, "controller", bool ok = sc_thread_create(&controller->thread, run_controller,
controller); "controller", controller);
if (!controller->thread) { if (!ok) {
LOGC("Could not start controller thread"); LOGC("Could not start controller thread");
return false; return false;
} }
if (!receiver_start(&controller->receiver)) { if (!receiver_start(&controller->receiver)) {
controller_stop(controller); controller_stop(controller);
SDL_WaitThread(controller->thread, NULL); sc_thread_join(&controller->thread, NULL);
return false; return false;
} }
@ -120,14 +122,14 @@ controller_start(struct controller *controller) {
void void
controller_stop(struct controller *controller) { controller_stop(struct controller *controller) {
mutex_lock(controller->mutex); sc_mutex_lock(&controller->mutex);
controller->stopped = true; controller->stopped = true;
cond_signal(controller->msg_cond); sc_cond_signal(&controller->msg_cond);
mutex_unlock(controller->mutex); sc_mutex_unlock(&controller->mutex);
} }
void void
controller_join(struct controller *controller) { controller_join(struct controller *controller) {
SDL_WaitThread(controller->thread, NULL); sc_thread_join(&controller->thread, NULL);
receiver_join(&controller->receiver); receiver_join(&controller->receiver);
} }

View file

@ -4,21 +4,20 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "control_msg.h" #include "control_msg.h"
#include "receiver.h" #include "receiver.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/net.h" #include "util/net.h"
#include "util/thread.h"
struct control_msg_queue CBUF(struct control_msg, 64); struct control_msg_queue CBUF(struct control_msg, 64);
struct controller { struct controller {
socket_t control_socket; socket_t control_socket;
SDL_Thread *thread; sc_thread thread;
SDL_mutex *mutex; sc_mutex mutex;
SDL_cond *msg_cond; sc_cond msg_cond;
bool stopped; bool stopped;
struct control_msg_queue queue; struct control_msg_queue queue;
struct receiver receiver; struct receiver receiver;

View file

@ -3,8 +3,6 @@
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/time.h> #include <libavutil/time.h>
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include <unistd.h> #include <unistd.h>
#include "events.h" #include "events.h"

View file

@ -4,7 +4,6 @@
#include <string.h> #include <string.h>
#include "adb.h" #include "adb.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
#define DEFAULT_PUSH_TARGET "/sdcard/" #define DEFAULT_PUSH_TARGET "/sdcard/"
@ -20,12 +19,14 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
cbuf_init(&file_handler->queue); cbuf_init(&file_handler->queue);
if (!(file_handler->mutex = SDL_CreateMutex())) { bool ok = sc_mutex_init(&file_handler->mutex);
if (!ok) {
return false; return false;
} }
if (!(file_handler->event_cond = SDL_CreateCond())) { ok = sc_cond_init(&file_handler->event_cond);
SDL_DestroyMutex(file_handler->mutex); if (!ok) {
sc_mutex_destroy(&file_handler->mutex);
return false; return false;
} }
@ -33,8 +34,8 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
file_handler->serial = strdup(serial); file_handler->serial = strdup(serial);
if (!file_handler->serial) { if (!file_handler->serial) {
LOGW("Could not strdup serial"); LOGW("Could not strdup serial");
SDL_DestroyCond(file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
SDL_DestroyMutex(file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
return false; return false;
} }
} else { } else {
@ -54,8 +55,8 @@ file_handler_init(struct file_handler *file_handler, const char *serial,
void void
file_handler_destroy(struct file_handler *file_handler) { file_handler_destroy(struct file_handler *file_handler) {
SDL_DestroyCond(file_handler->event_cond); sc_cond_destroy(&file_handler->event_cond);
SDL_DestroyMutex(file_handler->mutex); sc_mutex_destroy(&file_handler->mutex);
free(file_handler->serial); free(file_handler->serial);
struct file_handler_request req; struct file_handler_request req;
@ -92,13 +93,13 @@ file_handler_request(struct file_handler *file_handler,
.file = file, .file = file,
}; };
mutex_lock(file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
bool was_empty = cbuf_is_empty(&file_handler->queue); bool was_empty = cbuf_is_empty(&file_handler->queue);
bool res = cbuf_push(&file_handler->queue, req); bool res = cbuf_push(&file_handler->queue, req);
if (was_empty) { if (was_empty) {
cond_signal(file_handler->event_cond); sc_cond_signal(&file_handler->event_cond);
} }
mutex_unlock(file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
return res; return res;
} }
@ -107,14 +108,14 @@ run_file_handler(void *data) {
struct file_handler *file_handler = data; struct file_handler *file_handler = data;
for (;;) { for (;;) {
mutex_lock(file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->current_process = PROCESS_NONE; file_handler->current_process = PROCESS_NONE;
while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) { while (!file_handler->stopped && cbuf_is_empty(&file_handler->queue)) {
cond_wait(file_handler->event_cond, file_handler->mutex); sc_cond_wait(&file_handler->event_cond, &file_handler->mutex);
} }
if (file_handler->stopped) { if (file_handler->stopped) {
// stop immediately, do not process further events // stop immediately, do not process further events
mutex_unlock(file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
break; break;
} }
struct file_handler_request req; struct file_handler_request req;
@ -132,7 +133,7 @@ run_file_handler(void *data) {
file_handler->push_target); file_handler->push_target);
} }
file_handler->current_process = process; file_handler->current_process = process;
mutex_unlock(file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
if (req.action == ACTION_INSTALL_APK) { if (req.action == ACTION_INSTALL_APK) {
if (process_check_success(process, "adb install", false)) { if (process_check_success(process, "adb install", false)) {
@ -150,13 +151,13 @@ run_file_handler(void *data) {
} }
} }
mutex_lock(file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
// Close the process (it is necessary already terminated) // Close the process (it is necessary already terminated)
// Execute this call with mutex locked to avoid race conditions with // Execute this call with mutex locked to avoid race conditions with
// file_handler_stop() // file_handler_stop()
process_close(file_handler->current_process); process_close(file_handler->current_process);
file_handler->current_process = PROCESS_NONE; file_handler->current_process = PROCESS_NONE;
mutex_unlock(file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
file_handler_request_destroy(&req); file_handler_request_destroy(&req);
} }
@ -167,9 +168,9 @@ bool
file_handler_start(struct file_handler *file_handler) { file_handler_start(struct file_handler *file_handler) {
LOGD("Starting file_handler thread"); LOGD("Starting file_handler thread");
file_handler->thread = SDL_CreateThread(run_file_handler, "file_handler", bool ok = sc_thread_create(&file_handler->thread, run_file_handler,
file_handler); "file_handler", file_handler);
if (!file_handler->thread) { if (!ok) {
LOGC("Could not start file_handler thread"); LOGC("Could not start file_handler thread");
return false; return false;
} }
@ -179,18 +180,18 @@ file_handler_start(struct file_handler *file_handler) {
void void
file_handler_stop(struct file_handler *file_handler) { file_handler_stop(struct file_handler *file_handler) {
mutex_lock(file_handler->mutex); sc_mutex_lock(&file_handler->mutex);
file_handler->stopped = true; file_handler->stopped = true;
cond_signal(file_handler->event_cond); sc_cond_signal(&file_handler->event_cond);
if (file_handler->current_process != PROCESS_NONE) { if (file_handler->current_process != PROCESS_NONE) {
if (!process_terminate(file_handler->current_process)) { if (!process_terminate(file_handler->current_process)) {
LOGW("Could not terminate push/install process"); LOGW("Could not terminate push/install process");
} }
} }
mutex_unlock(file_handler->mutex); sc_mutex_unlock(&file_handler->mutex);
} }
void void
file_handler_join(struct file_handler *file_handler) { file_handler_join(struct file_handler *file_handler) {
SDL_WaitThread(file_handler->thread, NULL); sc_thread_join(&file_handler->thread, NULL);
} }

View file

@ -4,11 +4,10 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "adb.h" #include "adb.h"
#include "util/cbuf.h" #include "util/cbuf.h"
#include "util/thread.h"
typedef enum { typedef enum {
ACTION_INSTALL_APK, ACTION_INSTALL_APK,
@ -25,9 +24,9 @@ struct file_handler_request_queue CBUF(struct file_handler_request, 16);
struct file_handler { struct file_handler {
char *serial; char *serial;
const char *push_target; const char *push_target;
SDL_Thread *thread; sc_thread thread;
SDL_mutex *mutex; sc_mutex mutex;
SDL_cond *event_cond; sc_cond event_cond;
bool stopped; bool stopped;
bool initialized; bool initialized;
process_t current_process; process_t current_process;

View file

@ -3,25 +3,24 @@
#include <assert.h> #include <assert.h>
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
#define FPS_COUNTER_INTERVAL_MS 1000 #define FPS_COUNTER_INTERVAL_MS 1000
bool bool
fps_counter_init(struct fps_counter *counter) { fps_counter_init(struct fps_counter *counter) {
counter->mutex = SDL_CreateMutex(); bool ok = sc_mutex_init(&counter->mutex);
if (!counter->mutex) { if (!ok) {
return false; return false;
} }
counter->state_cond = SDL_CreateCond(); ok = sc_cond_init(&counter->state_cond);
if (!counter->state_cond) { if (!ok) {
SDL_DestroyMutex(counter->mutex); sc_mutex_destroy(&counter->mutex);
return false; return false;
} }
counter->thread = NULL; counter->thread_started = false;
atomic_init(&counter->started, 0); atomic_init(&counter->started, 0);
// no need to initialize the other fields, they are unused until started // no need to initialize the other fields, they are unused until started
@ -30,8 +29,8 @@ fps_counter_init(struct fps_counter *counter) {
void void
fps_counter_destroy(struct fps_counter *counter) { fps_counter_destroy(struct fps_counter *counter) {
SDL_DestroyCond(counter->state_cond); sc_cond_destroy(&counter->state_cond);
SDL_DestroyMutex(counter->mutex); sc_mutex_destroy(&counter->mutex);
} }
static inline bool static inline bool
@ -77,10 +76,10 @@ static int
run_fps_counter(void *data) { run_fps_counter(void *data) {
struct fps_counter *counter = data; struct fps_counter *counter = data;
mutex_lock(counter->mutex); sc_mutex_lock(&counter->mutex);
while (!counter->interrupted) { while (!counter->interrupted) {
while (!counter->interrupted && !is_started(counter)) { while (!counter->interrupted && !is_started(counter)) {
cond_wait(counter->state_cond, counter->mutex); sc_cond_wait(&counter->state_cond, &counter->mutex);
} }
while (!counter->interrupted && is_started(counter)) { while (!counter->interrupted && is_started(counter)) {
uint32_t now = SDL_GetTicks(); uint32_t now = SDL_GetTicks();
@ -90,32 +89,35 @@ run_fps_counter(void *data) {
uint32_t remaining = counter->next_timestamp - now; uint32_t remaining = counter->next_timestamp - now;
// ignore the reason (timeout or signaled), we just loop anyway // ignore the reason (timeout or signaled), we just loop anyway
cond_wait_timeout(counter->state_cond, counter->mutex, remaining); sc_cond_timedwait(&counter->state_cond, &counter->mutex, remaining);
} }
} }
mutex_unlock(counter->mutex); sc_mutex_unlock(&counter->mutex);
return 0; return 0;
} }
bool bool
fps_counter_start(struct fps_counter *counter) { fps_counter_start(struct fps_counter *counter) {
mutex_lock(counter->mutex); sc_mutex_lock(&counter->mutex);
counter->next_timestamp = SDL_GetTicks() + FPS_COUNTER_INTERVAL_MS; counter->next_timestamp = SDL_GetTicks() + FPS_COUNTER_INTERVAL_MS;
counter->nr_rendered = 0; counter->nr_rendered = 0;
counter->nr_skipped = 0; counter->nr_skipped = 0;
mutex_unlock(counter->mutex); sc_mutex_unlock(&counter->mutex);
set_started(counter, true); set_started(counter, true);
cond_signal(counter->state_cond); sc_cond_signal(&counter->state_cond);
// counter->thread is always accessed from the same thread, no need to lock // counter->thread_started and counter->thread are always accessed from the
if (!counter->thread) { // same thread, no need to lock
counter->thread = if (!counter->thread_started) {
SDL_CreateThread(run_fps_counter, "fps counter", counter); bool ok = sc_thread_create(&counter->thread, run_fps_counter,
if (!counter->thread) { "fps counter", counter);
if (!ok) {
LOGE("Could not start FPS counter thread"); LOGE("Could not start FPS counter thread");
return false; return false;
} }
counter->thread_started = true;
} }
return true; return true;
@ -124,7 +126,7 @@ fps_counter_start(struct fps_counter *counter) {
void void
fps_counter_stop(struct fps_counter *counter) { fps_counter_stop(struct fps_counter *counter) {
set_started(counter, false); set_started(counter, false);
cond_signal(counter->state_cond); sc_cond_signal(&counter->state_cond);
} }
bool bool
@ -134,21 +136,21 @@ fps_counter_is_started(struct fps_counter *counter) {
void void
fps_counter_interrupt(struct fps_counter *counter) { fps_counter_interrupt(struct fps_counter *counter) {
if (!counter->thread) { if (!counter->thread_started) {
return; return;
} }
mutex_lock(counter->mutex); sc_mutex_lock(&counter->mutex);
counter->interrupted = true; counter->interrupted = true;
mutex_unlock(counter->mutex); sc_mutex_unlock(&counter->mutex);
// wake up blocking wait // wake up blocking wait
cond_signal(counter->state_cond); sc_cond_signal(&counter->state_cond);
} }
void void
fps_counter_join(struct fps_counter *counter) { fps_counter_join(struct fps_counter *counter) {
if (counter->thread) { if (counter->thread_started) {
SDL_WaitThread(counter->thread, NULL); sc_thread_join(&counter->thread, NULL);
} }
} }
@ -158,11 +160,11 @@ fps_counter_add_rendered_frame(struct fps_counter *counter) {
return; return;
} }
mutex_lock(counter->mutex); sc_mutex_lock(&counter->mutex);
uint32_t now = SDL_GetTicks(); uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now); check_interval_expired(counter, now);
++counter->nr_rendered; ++counter->nr_rendered;
mutex_unlock(counter->mutex); sc_mutex_unlock(&counter->mutex);
} }
void void
@ -171,9 +173,9 @@ fps_counter_add_skipped_frame(struct fps_counter *counter) {
return; return;
} }
mutex_lock(counter->mutex); sc_mutex_lock(&counter->mutex);
uint32_t now = SDL_GetTicks(); uint32_t now = SDL_GetTicks();
check_interval_expired(counter, now); check_interval_expired(counter, now);
++counter->nr_skipped; ++counter->nr_skipped;
mutex_unlock(counter->mutex); sc_mutex_unlock(&counter->mutex);
} }

View file

@ -6,13 +6,15 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h> #include "util/thread.h"
struct fps_counter { struct fps_counter {
SDL_Thread *thread; sc_thread thread;
SDL_mutex *mutex; sc_mutex mutex;
SDL_cond *state_cond; sc_cond state_cond;
bool thread_started;
// atomic so that we can check without locking the mutex // atomic so that we can check without locking the mutex
// if the FPS counter is disabled, we don't want to lock unnecessarily // if the FPS counter is disabled, we don't want to lock unnecessarily

View file

@ -4,7 +4,6 @@
#include <SDL2/SDL_keycode.h> #include <SDL2/SDL_keycode.h>
#include "event_converter.h" #include "event_converter.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
static const int ACTION_DOWN = 1; static const int ACTION_DOWN = 1;

View file

@ -4,12 +4,12 @@
#include <SDL2/SDL_clipboard.h> #include <SDL2/SDL_clipboard.h>
#include "device_msg.h" #include "device_msg.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
bool bool
receiver_init(struct receiver *receiver, socket_t control_socket) { receiver_init(struct receiver *receiver, socket_t control_socket) {
if (!(receiver->mutex = SDL_CreateMutex())) { bool ok = sc_mutex_init(&receiver->mutex);
if (!ok) {
return false; return false;
} }
receiver->control_socket = control_socket; receiver->control_socket = control_socket;
@ -18,7 +18,7 @@ receiver_init(struct receiver *receiver, socket_t control_socket) {
void void
receiver_destroy(struct receiver *receiver) { receiver_destroy(struct receiver *receiver) {
SDL_DestroyMutex(receiver->mutex); sc_mutex_destroy(&receiver->mutex);
} }
static void static void
@ -101,8 +101,9 @@ bool
receiver_start(struct receiver *receiver) { receiver_start(struct receiver *receiver) {
LOGD("Starting receiver thread"); LOGD("Starting receiver thread");
receiver->thread = SDL_CreateThread(run_receiver, "receiver", receiver); bool ok = sc_thread_create(&receiver->thread, run_receiver, "receiver",
if (!receiver->thread) { receiver);
if (!ok) {
LOGC("Could not start receiver thread"); LOGC("Could not start receiver thread");
return false; return false;
} }
@ -112,5 +113,5 @@ receiver_start(struct receiver *receiver) {
void void
receiver_join(struct receiver *receiver) { receiver_join(struct receiver *receiver) {
SDL_WaitThread(receiver->thread, NULL); sc_thread_join(&receiver->thread, NULL);
} }

View file

@ -4,17 +4,16 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "util/net.h" #include "util/net.h"
#include "util/thread.h"
// receive events from the device // receive events from the device
// managed by the controller // managed by the controller
struct receiver { struct receiver {
socket_t control_socket; socket_t control_socket;
SDL_Thread *thread; sc_thread thread;
SDL_mutex *mutex; sc_mutex mutex;
}; };
bool bool

View file

@ -3,7 +3,6 @@
#include <assert.h> #include <assert.h>
#include <libavutil/time.h> #include <libavutil/time.h>
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
@ -69,17 +68,17 @@ recorder_init(struct recorder *recorder,
return false; return false;
} }
recorder->mutex = SDL_CreateMutex(); bool ok = sc_mutex_init(&recorder->mutex);
if (!recorder->mutex) { if (!ok) {
LOGC("Could not create mutex"); LOGC("Could not create mutex");
free(recorder->filename); free(recorder->filename);
return false; return false;
} }
recorder->queue_cond = SDL_CreateCond(); ok = sc_cond_init(&recorder->queue_cond);
if (!recorder->queue_cond) { if (!ok) {
LOGC("Could not create cond"); LOGC("Could not create cond");
SDL_DestroyMutex(recorder->mutex); sc_mutex_destroy(&recorder->mutex);
free(recorder->filename); free(recorder->filename);
return false; return false;
} }
@ -97,8 +96,8 @@ recorder_init(struct recorder *recorder,
void void
recorder_destroy(struct recorder *recorder) { recorder_destroy(struct recorder *recorder) {
SDL_DestroyCond(recorder->queue_cond); sc_cond_destroy(&recorder->queue_cond);
SDL_DestroyMutex(recorder->mutex); sc_mutex_destroy(&recorder->mutex);
free(recorder->filename); free(recorder->filename);
} }
@ -258,17 +257,17 @@ run_recorder(void *data) {
struct recorder *recorder = data; struct recorder *recorder = data;
for (;;) { for (;;) {
mutex_lock(recorder->mutex); sc_mutex_lock(&recorder->mutex);
while (!recorder->stopped && queue_is_empty(&recorder->queue)) { while (!recorder->stopped && queue_is_empty(&recorder->queue)) {
cond_wait(recorder->queue_cond, recorder->mutex); sc_cond_wait(&recorder->queue_cond, &recorder->mutex);
} }
// if stopped is set, continue to process the remaining events (to // if stopped is set, continue to process the remaining events (to
// finish the recording) before actually stopping // finish the recording) before actually stopping
if (recorder->stopped && queue_is_empty(&recorder->queue)) { if (recorder->stopped && queue_is_empty(&recorder->queue)) {
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
struct record_packet *last = recorder->previous; struct record_packet *last = recorder->previous;
if (last) { if (last) {
// assign an arbitrary duration to the last packet // assign an arbitrary duration to the last packet
@ -288,7 +287,7 @@ run_recorder(void *data) {
struct record_packet *rec; struct record_packet *rec;
queue_take(&recorder->queue, next, &rec); queue_take(&recorder->queue, next, &rec);
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
// recorder->previous is only written from this thread, no need to lock // recorder->previous is only written from this thread, no need to lock
struct record_packet *previous = recorder->previous; struct record_packet *previous = recorder->previous;
@ -311,11 +310,11 @@ run_recorder(void *data) {
if (!ok) { if (!ok) {
LOGE("Could not record packet"); LOGE("Could not record packet");
mutex_lock(recorder->mutex); sc_mutex_lock(&recorder->mutex);
recorder->failed = true; recorder->failed = true;
// discard pending packets // discard pending packets
recorder_queue_clear(&recorder->queue); recorder_queue_clear(&recorder->queue);
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
break; break;
} }
@ -330,8 +329,9 @@ bool
recorder_start(struct recorder *recorder) { recorder_start(struct recorder *recorder) {
LOGD("Starting recorder thread"); LOGD("Starting recorder thread");
recorder->thread = SDL_CreateThread(run_recorder, "recorder", recorder); bool ok = sc_thread_create(&recorder->thread, run_recorder, "recorder",
if (!recorder->thread) { recorder);
if (!ok) {
LOGC("Could not start recorder thread"); LOGC("Could not start recorder thread");
return false; return false;
} }
@ -341,38 +341,38 @@ recorder_start(struct recorder *recorder) {
void void
recorder_stop(struct recorder *recorder) { recorder_stop(struct recorder *recorder) {
mutex_lock(recorder->mutex); sc_mutex_lock(&recorder->mutex);
recorder->stopped = true; recorder->stopped = true;
cond_signal(recorder->queue_cond); sc_cond_signal(&recorder->queue_cond);
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
} }
void void
recorder_join(struct recorder *recorder) { recorder_join(struct recorder *recorder) {
SDL_WaitThread(recorder->thread, NULL); sc_thread_join(&recorder->thread, NULL);
} }
bool bool
recorder_push(struct recorder *recorder, const AVPacket *packet) { recorder_push(struct recorder *recorder, const AVPacket *packet) {
mutex_lock(recorder->mutex); sc_mutex_lock(&recorder->mutex);
assert(!recorder->stopped); assert(!recorder->stopped);
if (recorder->failed) { if (recorder->failed) {
// reject any new packet (this will stop the stream) // reject any new packet (this will stop the stream)
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
return false; return false;
} }
struct record_packet *rec = record_packet_new(packet); struct record_packet *rec = record_packet_new(packet);
if (!rec) { if (!rec) {
LOGC("Could not allocate record packet"); LOGC("Could not allocate record packet");
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
return false; return false;
} }
queue_push(&recorder->queue, next, rec); queue_push(&recorder->queue, next, rec);
cond_signal(recorder->queue_cond); sc_cond_signal(&recorder->queue_cond);
mutex_unlock(recorder->mutex); sc_mutex_unlock(&recorder->mutex);
return true; return true;
} }

View file

@ -5,12 +5,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include "coords.h" #include "coords.h"
#include "scrcpy.h" #include "scrcpy.h"
#include "util/queue.h" #include "util/queue.h"
#include "util/thread.h"
struct record_packet { struct record_packet {
AVPacket packet; AVPacket packet;
@ -26,9 +25,9 @@ struct recorder {
struct size declared_frame_size; struct size declared_frame_size;
bool header_written; bool header_written;
SDL_Thread *thread; sc_thread thread;
SDL_mutex *mutex; sc_mutex mutex;
SDL_cond *queue_cond; sc_cond queue_cond;
bool stopped; // set on recorder_stop() by the stream reader bool stopped; // set on recorder_stop() by the stream reader
bool failed; // set on packet write failure bool failed; // set on packet write failure
struct recorder_queue queue; struct recorder_queue queue;

View file

@ -26,7 +26,6 @@
#include "stream.h" #include "stream.h"
#include "tiny_xpm.h" #include "tiny_xpm.h"
#include "video_buffer.h" #include "video_buffer.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h" #include "util/net.h"

View file

@ -8,7 +8,6 @@
#include "scrcpy.h" #include "scrcpy.h"
#include "tiny_xpm.h" #include "tiny_xpm.h"
#include "video_buffer.h" #include "video_buffer.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
#define DISPLAY_MARGINS 96 #define DISPLAY_MARGINS 96
@ -454,15 +453,15 @@ update_texture(struct screen *screen, const AVFrame *frame) {
bool bool
screen_update_frame(struct screen *screen, struct video_buffer *vb) { screen_update_frame(struct screen *screen, struct video_buffer *vb) {
mutex_lock(vb->mutex); sc_mutex_lock(&vb->mutex);
const AVFrame *frame = video_buffer_consume_rendered_frame(vb); const AVFrame *frame = video_buffer_consume_rendered_frame(vb);
struct size new_frame_size = {frame->width, frame->height}; struct size new_frame_size = {frame->width, frame->height};
if (!prepare_for_frame(screen, new_frame_size)) { if (!prepare_for_frame(screen, new_frame_size)) {
mutex_unlock(vb->mutex); sc_mutex_unlock(&vb->mutex);
return false; return false;
} }
update_texture(screen, frame); update_texture(screen, frame);
mutex_unlock(vb->mutex); sc_mutex_unlock(&vb->mutex);
screen_render(screen, false); screen_render(screen, false);
return true; return true;

View file

@ -5,12 +5,10 @@
#include <inttypes.h> #include <inttypes.h>
#include <libgen.h> #include <libgen.h>
#include <stdio.h> #include <stdio.h>
#include <SDL2/SDL_thread.h>
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
#include <SDL2/SDL_platform.h> #include <SDL2/SDL_platform.h>
#include "adb.h" #include "adb.h"
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h" #include "util/net.h"
#include "util/str_util.h" #include "util/str_util.h"
@ -357,18 +355,17 @@ bool
server_init(struct server *server) { server_init(struct server *server) {
server->serial = NULL; server->serial = NULL;
server->process = PROCESS_NONE; server->process = PROCESS_NONE;
server->wait_server_thread = NULL;
atomic_flag_clear_explicit(&server->server_socket_closed, atomic_flag_clear_explicit(&server->server_socket_closed,
memory_order_relaxed); memory_order_relaxed);
server->mutex = SDL_CreateMutex(); bool ok = sc_mutex_init(&server->mutex);
if (!server->mutex) { if (!ok) {
return false; return false;
} }
server->process_terminated_cond = SDL_CreateCond(); ok = sc_cond_init(&server->process_terminated_cond);
if (!server->process_terminated_cond) { if (!ok) {
SDL_DestroyMutex(server->mutex); sc_mutex_destroy(&server->mutex);
return false; return false;
} }
@ -391,10 +388,10 @@ run_wait_server(void *data) {
struct server *server = data; struct server *server = data;
process_wait(server->process, false); // ignore exit code process_wait(server->process, false); // ignore exit code
mutex_lock(server->mutex); sc_mutex_lock(&server->mutex);
server->process_terminated = true; server->process_terminated = true;
cond_signal(server->process_terminated_cond); sc_cond_signal(&server->process_terminated_cond);
mutex_unlock(server->mutex); sc_mutex_unlock(&server->mutex);
// no need for synchronization, server_socket is initialized before this // no need for synchronization, server_socket is initialized before this
// thread was created // thread was created
@ -439,9 +436,9 @@ server_start(struct server *server, const char *serial,
// things simple and multiplatform, just spawn a new thread waiting for the // things simple and multiplatform, just spawn a new thread waiting for the
// server process and calling shutdown()/close() on the server socket if // server process and calling shutdown()/close() on the server socket if
// necessary to wake up any accept() blocking call. // necessary to wake up any accept() blocking call.
server->wait_server_thread = bool ok = sc_thread_create(&server->wait_server_thread, run_wait_server,
SDL_CreateThread(run_wait_server, "wait-server", server); "wait-server", server);
if (!server->wait_server_thread) { if (!ok) {
process_terminate(server->process); process_terminate(server->process);
process_wait(server->process, true); // ignore exit code process_wait(server->process, true); // ignore exit code
goto error2; goto error2;
@ -531,33 +528,33 @@ server_stop(struct server *server) {
} }
// Give some delay for the server to terminate properly // Give some delay for the server to terminate properly
mutex_lock(server->mutex); sc_mutex_lock(&server->mutex);
int r = 0; bool signaled = false;
if (!server->process_terminated) { if (!server->process_terminated) {
#define WATCHDOG_DELAY_MS 1000 #define WATCHDOG_DELAY_MS 1000
r = cond_wait_timeout(server->process_terminated_cond, signaled = sc_cond_timedwait(&server->process_terminated_cond,
server->mutex, &server->mutex,
WATCHDOG_DELAY_MS); WATCHDOG_DELAY_MS);
} }
mutex_unlock(server->mutex); sc_mutex_unlock(&server->mutex);
// After this delay, kill the server if it's not dead already. // After this delay, kill the server if it's not dead already.
// On some devices, closing the sockets is not sufficient to wake up the // On some devices, closing the sockets is not sufficient to wake up the
// blocking calls while the device is asleep. // blocking calls while the device is asleep.
if (r == SDL_MUTEX_TIMEDOUT) { if (!signaled) {
// The process is terminated, but not reaped (closed) yet, so its PID // The process is terminated, but not reaped (closed) yet, so its PID
// is still valid. // is still valid.
LOGW("Killing the server..."); LOGW("Killing the server...");
process_terminate(server->process); process_terminate(server->process);
} }
SDL_WaitThread(server->wait_server_thread, NULL); sc_thread_join(&server->wait_server_thread, NULL);
process_close(server->process); process_close(server->process);
} }
void void
server_destroy(struct server *server) { server_destroy(struct server *server) {
free(server->serial); free(server->serial);
SDL_DestroyCond(server->process_terminated_cond); sc_cond_destroy(&server->process_terminated_cond);
SDL_DestroyMutex(server->mutex); sc_mutex_destroy(&server->mutex);
} }

View file

@ -6,21 +6,21 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL2/SDL_thread.h>
#include "adb.h" #include "adb.h"
#include "scrcpy.h" #include "scrcpy.h"
#include "util/log.h" #include "util/log.h"
#include "util/net.h" #include "util/net.h"
#include "util/thread.h"
struct server { struct server {
char *serial; char *serial;
process_t process; process_t process;
SDL_Thread *wait_server_thread; sc_thread wait_server_thread;
atomic_flag server_socket_closed; atomic_flag server_socket_closed;
SDL_mutex *mutex; sc_mutex mutex;
SDL_cond *process_terminated_cond; sc_cond process_terminated_cond;
bool process_terminated; bool process_terminated;
socket_t server_socket; // only used if !tunnel_forward socket_t server_socket; // only used if !tunnel_forward

View file

@ -4,8 +4,6 @@
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/time.h> #include <libavutil/time.h>
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_thread.h>
#include <unistd.h> #include <unistd.h>
#include "decoder.h" #include "decoder.h"
@ -279,8 +277,8 @@ bool
stream_start(struct stream *stream) { stream_start(struct stream *stream) {
LOGD("Starting stream thread"); LOGD("Starting stream thread");
stream->thread = SDL_CreateThread(run_stream, "stream", stream); bool ok = sc_thread_create(&stream->thread, run_stream, "stream", stream);
if (!stream->thread) { if (!ok) {
LOGC("Could not start stream thread"); LOGC("Could not start stream thread");
return false; return false;
} }
@ -296,5 +294,5 @@ stream_stop(struct stream *stream) {
void void
stream_join(struct stream *stream) { stream_join(struct stream *stream) {
SDL_WaitThread(stream->thread, NULL); sc_thread_join(&stream->thread, NULL);
} }

View file

@ -7,15 +7,15 @@
#include <stdint.h> #include <stdint.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <SDL2/SDL_atomic.h> #include <SDL2/SDL_atomic.h>
#include <SDL2/SDL_thread.h>
#include "util/net.h" #include "util/net.h"
#include "util/thread.h"
struct video_buffer; struct video_buffer;
struct stream { struct stream {
socket_t socket; socket_t socket;
SDL_Thread *thread; sc_thread thread;
struct decoder *decoder; struct decoder *decoder;
struct recorder *recorder; struct recorder *recorder;
AVCodecContext *codec_ctx; AVCodecContext *codec_ctx;

View file

@ -1,75 +0,0 @@
#ifndef LOCK_H
#define LOCK_H
#include "common.h"
#include <stdint.h>
#include <SDL2/SDL_mutex.h>
#include "log.h"
static inline void
mutex_lock(SDL_mutex *mutex) {
int r = SDL_LockMutex(mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not lock mutex: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
static inline void
mutex_unlock(SDL_mutex *mutex) {
int r = SDL_UnlockMutex(mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not unlock mutex: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
static inline void
cond_wait(SDL_cond *cond, SDL_mutex *mutex) {
int r = SDL_CondWait(cond, mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not wait on condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
static inline int
cond_wait_timeout(SDL_cond *cond, SDL_mutex *mutex, uint32_t ms) {
int r = SDL_CondWaitTimeout(cond, mutex, ms);
#ifndef NDEBUG
if (r < 0) {
LOGC("Could not wait on condition with timeout: %s", SDL_GetError());
abort();
}
#endif
return r;
}
static inline void
cond_signal(SDL_cond *cond) {
int r = SDL_CondSignal(cond);
#ifndef NDEBUG
if (r) {
LOGC("Could not signal a condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
#endif

133
app/src/util/thread.c Normal file
View file

@ -0,0 +1,133 @@
#include "thread.h"
#include <assert.h>
#include <SDL2/SDL_thread.h>
#include "log.h"
bool
sc_thread_create(sc_thread *thread, sc_thread_fn fn, const char *name,
void *userdata) {
SDL_Thread *sdl_thread = SDL_CreateThread(fn, name, userdata);
if (!sdl_thread) {
return false;
}
thread->thread = sdl_thread;
return true;
}
void
sc_thread_join(sc_thread *thread, int *status) {
SDL_WaitThread(thread->thread, status);
}
bool
sc_mutex_init(sc_mutex *mutex) {
SDL_mutex *sdl_mutex = SDL_CreateMutex();
if (!sdl_mutex) {
return false;
}
mutex->mutex = sdl_mutex;
return true;
}
void
sc_mutex_destroy(sc_mutex *mutex) {
SDL_DestroyMutex(mutex->mutex);
}
void
sc_mutex_lock(sc_mutex *mutex) {
int r = SDL_LockMutex(mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not lock mutex: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
void
sc_mutex_unlock(sc_mutex *mutex) {
int r = SDL_UnlockMutex(mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not lock mutex: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
bool
sc_cond_init(sc_cond *cond) {
SDL_cond *sdl_cond = SDL_CreateCond();
if (!sdl_cond) {
return false;
}
cond->cond = sdl_cond;
return true;
}
void
sc_cond_destroy(sc_cond *cond) {
SDL_DestroyCond(cond->cond);
}
void
sc_cond_wait(sc_cond *cond, sc_mutex *mutex) {
int r = SDL_CondWait(cond->cond, mutex->mutex);
#ifndef NDEBUG
if (r) {
LOGC("Could not wait on condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
bool
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, uint32_t ms) {
int r = SDL_CondWaitTimeout(cond->cond, mutex->mutex, ms);
#ifndef NDEBUG
if (r < 0) {
LOGC("Could not wait on condition with timeout: %s", SDL_GetError());
abort();
}
#endif
assert(r == 0 || r == SDL_MUTEX_TIMEDOUT);
return r == 0;
}
void
sc_cond_signal(sc_cond *cond) {
int r = SDL_CondSignal(cond->cond);
#ifndef NDEBUG
if (r) {
LOGC("Could not signal a condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}
void
sc_cond_broadcast(sc_cond *cond) {
int r = SDL_CondBroadcast(cond->cond);
#ifndef NDEBUG
if (r) {
LOGC("Could not broadcast a condition: %s", SDL_GetError());
abort();
}
#else
(void) r;
#endif
}

66
app/src/util/thread.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef SC_THREAD_H
#define SC_THREAD_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
/* Forward declarations */
typedef struct SDL_Thread SDL_Thread;
typedef struct SDL_mutex SDL_mutex;
typedef struct SDL_cond SDL_cond;
typedef int sc_thread_fn(void *);
typedef struct sc_thread {
SDL_Thread *thread;
} sc_thread;
typedef struct sc_mutex {
SDL_mutex *mutex;
} sc_mutex;
typedef struct sc_cond {
SDL_cond *cond;
} sc_cond;
bool
sc_thread_create(sc_thread *thread, sc_thread_fn fn, const char *name,
void *userdata);
void
sc_thread_join(sc_thread *thread, int *status);
bool
sc_mutex_init(sc_mutex *mutex);
void
sc_mutex_destroy(sc_mutex *mutex);
void
sc_mutex_lock(sc_mutex *mutex);
void
sc_mutex_unlock(sc_mutex *mutex);
bool
sc_cond_init(sc_cond *cond);
void
sc_cond_destroy(sc_cond *cond);
void
sc_cond_wait(sc_cond *cond, sc_mutex *mutex);
// return true on signaled, false on timeout
bool
sc_cond_timedwait(sc_cond *cond, sc_mutex *mutex, uint32_t ms);
void
sc_cond_signal(sc_cond *cond);
void
sc_cond_broadcast(sc_cond *cond);
#endif

View file

@ -1,11 +1,9 @@
#include "video_buffer.h" #include "video_buffer.h"
#include <assert.h> #include <assert.h>
#include <SDL2/SDL_mutex.h>
#include <libavutil/avutil.h> #include <libavutil/avutil.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "util/lock.h"
#include "util/log.h" #include "util/log.h"
bool bool
@ -13,22 +11,26 @@ video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter,
bool render_expired_frames) { bool render_expired_frames) {
vb->fps_counter = fps_counter; vb->fps_counter = fps_counter;
if (!(vb->decoding_frame = av_frame_alloc())) { vb->decoding_frame = av_frame_alloc();
if (!vb->decoding_frame) {
goto error_0; goto error_0;
} }
if (!(vb->rendering_frame = av_frame_alloc())) { vb->rendering_frame = av_frame_alloc();
if (!vb->rendering_frame) {
goto error_1; goto error_1;
} }
if (!(vb->mutex = SDL_CreateMutex())) { bool ok = sc_mutex_init(&vb->mutex);
if (!ok) {
goto error_2; goto error_2;
} }
vb->render_expired_frames = render_expired_frames; vb->render_expired_frames = render_expired_frames;
if (render_expired_frames) { if (render_expired_frames) {
if (!(vb->rendering_frame_consumed_cond = SDL_CreateCond())) { ok = sc_cond_init(&vb->rendering_frame_consumed_cond);
SDL_DestroyMutex(vb->mutex); if (!ok) {
sc_mutex_destroy(&vb->mutex);
goto error_2; goto error_2;
} }
// interrupted is not used if expired frames are not rendered // interrupted is not used if expired frames are not rendered
@ -53,9 +55,9 @@ error_0:
void void
video_buffer_destroy(struct video_buffer *vb) { video_buffer_destroy(struct video_buffer *vb) {
if (vb->render_expired_frames) { if (vb->render_expired_frames) {
SDL_DestroyCond(vb->rendering_frame_consumed_cond); sc_cond_destroy(&vb->rendering_frame_consumed_cond);
} }
SDL_DestroyMutex(vb->mutex); sc_mutex_destroy(&vb->mutex);
av_frame_free(&vb->rendering_frame); av_frame_free(&vb->rendering_frame);
av_frame_free(&vb->decoding_frame); av_frame_free(&vb->decoding_frame);
} }
@ -70,11 +72,11 @@ video_buffer_swap_frames(struct video_buffer *vb) {
void void
video_buffer_offer_decoded_frame(struct video_buffer *vb, video_buffer_offer_decoded_frame(struct video_buffer *vb,
bool *previous_frame_skipped) { bool *previous_frame_skipped) {
mutex_lock(vb->mutex); sc_mutex_lock(&vb->mutex);
if (vb->render_expired_frames) { if (vb->render_expired_frames) {
// wait for the current (expired) frame to be consumed // wait for the current (expired) frame to be consumed
while (!vb->rendering_frame_consumed && !vb->interrupted) { while (!vb->rendering_frame_consumed && !vb->interrupted) {
cond_wait(vb->rendering_frame_consumed_cond, vb->mutex); sc_cond_wait(&vb->rendering_frame_consumed_cond, &vb->mutex);
} }
} else if (!vb->rendering_frame_consumed) { } else if (!vb->rendering_frame_consumed) {
fps_counter_add_skipped_frame(vb->fps_counter); fps_counter_add_skipped_frame(vb->fps_counter);
@ -85,7 +87,7 @@ video_buffer_offer_decoded_frame(struct video_buffer *vb,
*previous_frame_skipped = !vb->rendering_frame_consumed; *previous_frame_skipped = !vb->rendering_frame_consumed;
vb->rendering_frame_consumed = false; vb->rendering_frame_consumed = false;
mutex_unlock(vb->mutex); sc_mutex_unlock(&vb->mutex);
} }
const AVFrame * const AVFrame *
@ -95,7 +97,7 @@ video_buffer_consume_rendered_frame(struct video_buffer *vb) {
fps_counter_add_rendered_frame(vb->fps_counter); fps_counter_add_rendered_frame(vb->fps_counter);
if (vb->render_expired_frames) { if (vb->render_expired_frames) {
// unblock video_buffer_offer_decoded_frame() // unblock video_buffer_offer_decoded_frame()
cond_signal(vb->rendering_frame_consumed_cond); sc_cond_signal(&vb->rendering_frame_consumed_cond);
} }
return vb->rendering_frame; return vb->rendering_frame;
} }
@ -103,10 +105,10 @@ video_buffer_consume_rendered_frame(struct video_buffer *vb) {
void void
video_buffer_interrupt(struct video_buffer *vb) { video_buffer_interrupt(struct video_buffer *vb) {
if (vb->render_expired_frames) { if (vb->render_expired_frames) {
mutex_lock(vb->mutex); sc_mutex_lock(&vb->mutex);
vb->interrupted = true; vb->interrupted = true;
mutex_unlock(vb->mutex); sc_mutex_unlock(&vb->mutex);
// wake up blocking wait // wake up blocking wait
cond_signal(vb->rendering_frame_consumed_cond); sc_cond_signal(&vb->rendering_frame_consumed_cond);
} }
} }

View file

@ -4,9 +4,9 @@
#include "common.h" #include "common.h"
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_mutex.h>
#include "fps_counter.h" #include "fps_counter.h"
#include "util/thread.h"
// forward declarations // forward declarations
typedef struct AVFrame AVFrame; typedef struct AVFrame AVFrame;
@ -14,10 +14,10 @@ typedef struct AVFrame AVFrame;
struct video_buffer { struct video_buffer {
AVFrame *decoding_frame; AVFrame *decoding_frame;
AVFrame *rendering_frame; AVFrame *rendering_frame;
SDL_mutex *mutex; sc_mutex mutex;
bool render_expired_frames; bool render_expired_frames;
bool interrupted; bool interrupted;
SDL_cond *rendering_frame_consumed_cond; sc_cond rendering_frame_consumed_cond;
bool rendering_frame_consumed; bool rendering_frame_consumed;
struct fps_counter *fps_counter; struct fps_counter *fps_counter;
}; };
@ -30,16 +30,16 @@ void
video_buffer_destroy(struct video_buffer *vb); video_buffer_destroy(struct video_buffer *vb);
// set the decoded frame as ready for rendering // set the decoded frame as ready for rendering
// this function locks frames->mutex during its execution // this function locks vb->mutex during its execution
// the output flag is set to report whether the previous frame has been skipped // the output flag is set to report whether the previous frame has been skipped
void void
video_buffer_offer_decoded_frame(struct video_buffer *vb, video_buffer_offer_decoded_frame(struct video_buffer *vb,
bool *previous_frame_skipped); bool *previous_frame_skipped);
// mark the rendering frame as consumed and return it // mark the rendering frame as consumed and return it
// MUST be called with frames->mutex locked!!! // MUST be called with vb->mutex locked!!!
// the caller is expected to render the returned frame to some texture before // the caller is expected to render the returned frame to some texture before
// unlocking frames->mutex // unlocking vb->mutex
const AVFrame * const AVFrame *
video_buffer_consume_rendered_frame(struct video_buffer *vb); video_buffer_consume_rendered_frame(struct video_buffer *vb);