From 4654cb7126e07cb5c2a21b6b6fba7cab71999e16 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 6 Jun 2019 16:54:35 +0200 Subject: [PATCH] realfps --- app/src/fps_counter.c | 135 ++++++++++++++++++++++++++++++++-------- app/src/fps_counter.h | 26 ++++++-- app/src/input_manager.c | 21 ++++--- app/src/lock_util.c | 10 +++ app/src/lock_util.h | 6 ++ app/src/scrcpy.c | 18 +++++- app/src/video_buffer.c | 14 +++-- app/src/video_buffer.h | 5 +- 8 files changed, 187 insertions(+), 48 deletions(-) diff --git a/app/src/fps_counter.c b/app/src/fps_counter.c index 133c7a25..635585a7 100644 --- a/app/src/fps_counter.c +++ b/app/src/fps_counter.c @@ -1,60 +1,141 @@ #include "fps_counter.h" #include +#include +#include "lock_util.h" #include "log.h" -void -fps_counter_init(struct fps_counter *counter) { - counter->started = false; - // no need to initialize the other fields, they are meaningful only when - // started is true -} +#define FPS_COUNTER_INTERVAL 1000 -void -fps_counter_start(struct fps_counter *counter) { - counter->started = true; - counter->slice_start = SDL_GetTicks(); +bool +fps_counter_init(struct fps_counter *counter) { + counter->mutex = SDL_CreateMutex(); + if (!counter->mutex) { + return false; + } + + counter->state_cond = SDL_CreateCond(); + if (!counter->state_cond) { + SDL_DestroyMutex(counter->mutex); + return false; + } + + counter->thread = NULL; + counter->started = false; + counter->interrupted = false; counter->nr_rendered = 0; counter->nr_skipped = 0; + // no need to initialize slice_start, it is meaningful only when started + + return true; } void -fps_counter_stop(struct fps_counter *counter) { - counter->started = false; +fps_counter_destroy(struct fps_counter *counter) { + SDL_DestroyCond(counter->state_cond); + SDL_DestroyMutex(counter->mutex); } static void display_fps(struct fps_counter *counter) { + unsigned rendered_per_second = + counter->nr_rendered * 1000 / FPS_COUNTER_INTERVAL; if (counter->nr_skipped) { - LOGI("%d fps (+%d frames skipped)", counter->nr_rendered, - counter->nr_skipped); + unsigned skipped_per_second = + counter->nr_skipped * 1000 / FPS_COUNTER_INTERVAL; + LOGI("%u fps (+%u frames skipped)", rendered_per_second, + skipped_per_second); } else { - LOGI("%d fps", counter->nr_rendered); + LOGI("%u fps", rendered_per_second); } } -static void -check_expired(struct fps_counter *counter) { - uint32_t now = SDL_GetTicks(); - if (now - counter->slice_start >= 1000) { - display_fps(counter); - // add a multiple of one second - uint32_t elapsed_slices = (now - counter->slice_start) / 1000; - counter->slice_start += 1000 * elapsed_slices; - counter->nr_rendered = 0; - counter->nr_skipped = 0; +static int +run_fps_counter(void *data) { + struct fps_counter *counter = data; + + mutex_lock(counter->mutex); + while (!counter->interrupted) { + while (!counter->interrupted && !counter->started) { + cond_wait(counter->state_cond, counter->mutex); + } + while (!counter->interrupted && counter->started) { + uint32_t now = SDL_GetTicks(); + if (now >= counter->next_timestamp) { + display_fps(counter); + counter->nr_rendered = 0; + counter->nr_skipped = 0; + // add a multiple of the interval + uint32_t elapsed_slices = + (now - counter->next_timestamp) / FPS_COUNTER_INTERVAL + 1; + counter->next_timestamp += FPS_COUNTER_INTERVAL * elapsed_slices; + } + SDL_assert(counter->next_timestamp > now); + uint32_t remaining = counter->next_timestamp - now; + + // ignore the reason (timeout or signaled), we just loop anyway + cond_wait_timeout(counter->state_cond, counter->mutex, remaining); + } + } + mutex_unlock(counter->mutex); + return 0; +} + +bool +fps_counter_start(struct fps_counter *counter) { + if (!counter->thread) { + counter->thread = + SDL_CreateThread(run_fps_counter, "fps counter", counter); + if (!counter->thread) { + LOGE("Could not start FPS counter thread"); + return false; + } + } + + mutex_lock(counter->mutex); + counter->started = true; + counter->next_timestamp = SDL_GetTicks() + FPS_COUNTER_INTERVAL; + counter->nr_rendered = 0; + counter->nr_skipped = 0; + mutex_unlock(counter->mutex); + + cond_signal(counter->state_cond); + return true; +} + +void +fps_counter_stop(struct fps_counter *counter) { + mutex_lock(counter->mutex); + counter->started = false; + mutex_unlock(counter->mutex); + cond_signal(counter->state_cond); +} + +void +fps_counter_interrupt(struct fps_counter *counter) { + if (counter->thread) { + mutex_lock(counter->mutex); + counter->interrupted = true; + mutex_unlock(counter->mutex); + // wake up blocking wait + cond_signal(counter->state_cond); + } +} + +void +fps_counter_join(struct fps_counter *counter) { + if (counter->thread) { + SDL_WaitThread(counter->thread, NULL); } } void fps_counter_add_rendered_frame(struct fps_counter *counter) { - check_expired(counter); ++counter->nr_rendered; } void fps_counter_add_skipped_frame(struct fps_counter *counter) { - check_expired(counter); ++counter->nr_skipped; } diff --git a/app/src/fps_counter.h b/app/src/fps_counter.h index fecef806..97af52f4 100644 --- a/app/src/fps_counter.h +++ b/app/src/fps_counter.h @@ -3,23 +3,41 @@ #include #include +#include +#include +#include struct fps_counter { + SDL_Thread *thread; + SDL_mutex *mutex; + SDL_cond *state_cond; bool started; - uint32_t slice_start; // initialized by SDL_GetTicks() - int nr_rendered; - int nr_skipped; + bool interrupted; + unsigned nr_rendered; + unsigned nr_skipped; + uint32_t next_timestamp; }; -void +bool fps_counter_init(struct fps_counter *counter); void +fps_counter_destroy(struct fps_counter *counter); + +bool fps_counter_start(struct fps_counter *counter); void fps_counter_stop(struct fps_counter *counter); +// request to stop the thread (on quit) +// must be called before fps_counter_join() +void +fps_counter_interrupt(struct fps_counter *counter); + +void +fps_counter_join(struct fps_counter *counter); + void fps_counter_add_rendered_frame(struct fps_counter *counter); diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 03f299db..422b0f0a 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -172,16 +172,19 @@ set_screen_power_mode(struct controller *controller, } static void -switch_fps_counter_state(struct video_buffer *vb) { - mutex_lock(vb->mutex); - if (vb->fps_counter.started) { +switch_fps_counter_state(struct fps_counter *fps_counter) { + mutex_lock(fps_counter->mutex); + if (fps_counter->started) { + fps_counter_stop(fps_counter); LOGI("FPS counter stopped"); - fps_counter_stop(&vb->fps_counter); } else { - LOGI("FPS counter started"); - fps_counter_start(&vb->fps_counter); + if (fps_counter_start(fps_counter)) { + LOGI("FPS counter started"); + } else { + LOGE("FPS counter starting failed"); + } } - mutex_unlock(vb->mutex); + mutex_unlock(fps_counter->mutex); } static void @@ -339,7 +342,9 @@ input_manager_process_key(struct input_manager *input_manager, return; case SDLK_i: if (ctrl && !meta && !shift && !repeat && down) { - switch_fps_counter_state(input_manager->video_buffer); + struct fps_counter *fps_counter = + input_manager->video_buffer->fps_counter; + switch_fps_counter_state(fps_counter); } return; case SDLK_n: diff --git a/app/src/lock_util.c b/app/src/lock_util.c index 7b70ba6b..36706063 100644 --- a/app/src/lock_util.c +++ b/app/src/lock_util.c @@ -28,6 +28,16 @@ cond_wait(SDL_cond *cond, SDL_mutex *mutex) { } } +int +cond_wait_timeout(SDL_cond *cond, SDL_mutex *mutex, uint32_t ms) { + int r = SDL_CondWaitTimeout(cond, mutex, ms); + if (r < 0) { + LOGC("Could not wait on condition with timeout"); + abort(); + } + return r; +} + void cond_signal(SDL_cond *cond) { if (SDL_CondSignal(cond)) { diff --git a/app/src/lock_util.h b/app/src/lock_util.h index 99c1f8d6..6c27602d 100644 --- a/app/src/lock_util.h +++ b/app/src/lock_util.h @@ -1,6 +1,8 @@ #ifndef LOCKUTIL_H #define LOCKUTIL_H +#include + // forward declarations typedef struct SDL_mutex SDL_mutex; typedef struct SDL_cond SDL_cond; @@ -14,6 +16,10 @@ mutex_unlock(SDL_mutex *mutex); void cond_wait(SDL_cond *cond, SDL_mutex *mutex); +// return 0 or SDL_MUTEX_TIMEDOUT +int +cond_wait_timeout(SDL_cond *cond, SDL_mutex *mutex, uint32_t ms); + void cond_signal(SDL_cond *cond); diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 38ad1ceb..761edb69 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -29,6 +29,7 @@ static struct server server = SERVER_INITIALIZER; static struct screen screen = SCREEN_INITIALIZER; +static struct fps_counter fps_counter; static struct video_buffer video_buffer; static struct stream stream; static struct decoder decoder; @@ -293,6 +294,7 @@ scrcpy(const struct scrcpy_options *options) { bool ret = false; + bool fps_counter_initialized = false; bool video_buffer_initialized = false; bool file_handler_initialized = false; bool recorder_initialized = false; @@ -320,7 +322,13 @@ scrcpy(const struct scrcpy_options *options) { struct decoder *dec = NULL; if (options->display) { - if (!video_buffer_init(&video_buffer, options->render_expired_frames)) { + if (!fps_counter_init(&fps_counter)) { + goto end; + } + fps_counter_initialized = true; + + if (!video_buffer_init(&video_buffer, &fps_counter, + options->render_expired_frames)) { goto end; } video_buffer_initialized = true; @@ -414,6 +422,9 @@ end: if (file_handler_initialized) { file_handler_stop(&file_handler); } + if (fps_counter_initialized) { + fps_counter_interrupt(&fps_counter); + } // shutdown the sockets and kill the server server_stop(&server); @@ -443,6 +454,11 @@ end: video_buffer_destroy(&video_buffer); } + if (fps_counter_initialized) { + fps_counter_join(&fps_counter); + fps_counter_destroy(&fps_counter); + } + if (options->show_touches) { if (!show_touches_waited) { // wait the process which enabled "show touches" diff --git a/app/src/video_buffer.c b/app/src/video_buffer.c index f26af0b6..d08ee748 100644 --- a/app/src/video_buffer.c +++ b/app/src/video_buffer.c @@ -10,7 +10,10 @@ #include "log.h" bool -video_buffer_init(struct video_buffer *vb, bool render_expired_frames) { +video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter, + bool render_expired_frames) { + vb->fps_counter = fps_counter; + if (!(vb->decoding_frame = av_frame_alloc())) { goto error_0; } @@ -37,7 +40,6 @@ video_buffer_init(struct video_buffer *vb, bool render_expired_frames) { // there is initially no rendering frame, so consider it has already been // consumed vb->rendering_frame_consumed = true; - fps_counter_init(&vb->fps_counter); return true; @@ -76,8 +78,8 @@ video_buffer_offer_decoded_frame(struct video_buffer *vb, cond_wait(vb->rendering_frame_consumed_cond, vb->mutex); } } else { - if (vb->fps_counter.started && !vb->rendering_frame_consumed) { - fps_counter_add_skipped_frame(&vb->fps_counter); + if (vb->fps_counter->started && !vb->rendering_frame_consumed) { + fps_counter_add_skipped_frame(vb->fps_counter); } } @@ -93,8 +95,8 @@ const AVFrame * video_buffer_consume_rendered_frame(struct video_buffer *vb) { SDL_assert(!vb->rendering_frame_consumed); vb->rendering_frame_consumed = true; - if (vb->fps_counter.started) { - fps_counter_add_rendered_frame(&vb->fps_counter); + if (vb->fps_counter->started) { + fps_counter_add_rendered_frame(vb->fps_counter); } if (vb->render_expired_frames) { // unblock video_buffer_offer_decoded_frame() diff --git a/app/src/video_buffer.h b/app/src/video_buffer.h index 9f328172..26a6fa1f 100644 --- a/app/src/video_buffer.h +++ b/app/src/video_buffer.h @@ -17,11 +17,12 @@ struct video_buffer { bool interrupted; SDL_cond *rendering_frame_consumed_cond; bool rendering_frame_consumed; - struct fps_counter fps_counter; + struct fps_counter *fps_counter; }; bool -video_buffer_init(struct video_buffer *vb, bool render_expired_frames); +video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter, + bool render_expired_frames); void video_buffer_destroy(struct video_buffer *vb);