From b85ded0cb8e420ca123be5fc6033aef94e8924e4 Mon Sep 17 00:00:00 2001 From: Ander Conselvan de Oliveira Date: Fri, 6 Sep 2013 17:49:37 +0300 Subject: [PATCH] vaapi-recorder: Encode frames in a separate thread Previously, vaapi_recorder_frame() would wait until the encoded contents for a frame is written to the output file descriptor. This delayed the repainting of the next frame, and affected frame rate when capturing with high resolutions. Instead, wait only if there is and attempted to encode two frames at the same time. Increases framerate from 30 to 60 fps when capturing at 1920x1200 on my SandryBridge system, although there are periodic slowdowns due to disk writes. --- src/compositor-drm.c | 2 - src/vaapi-recorder.c | 101 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index eb3ec612..7f6ffbcf 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -2471,8 +2471,6 @@ recorder_frame_notify(struct wl_listener *listener, void *data) } vaapi_recorder_frame(output->recorder, fd, output->current->stride / 4); - - close(fd); } static void * diff --git a/src/vaapi-recorder.c b/src/vaapi-recorder.c index c0210f03..e9127da5 100644 --- a/src/vaapi-recorder.c +++ b/src/vaapi-recorder.c @@ -47,11 +47,13 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -89,6 +91,16 @@ struct vaapi_recorder { int width, height; int frame_count; + int destroying; + pthread_t worker_thread; + pthread_mutex_t mutex; + pthread_cond_t input_cond; + + struct { + int valid; + int prime_fd, stride; + } input; + VADisplay va_dpy; /* video post processing is used for colorspace conversion */ @@ -116,6 +128,9 @@ struct vaapi_recorder { } encoder; }; +static void * +worker_thread_function(void *); + /* bistream code used for writing the packed headers */ #define BITSTREAM_ALLOCATE_STEPPING 4096 @@ -886,6 +901,33 @@ vpp_destroy(struct vaapi_recorder *r) vaDestroyConfig(r->va_dpy, r->vpp.cfg); } +static int +setup_worker_thread(struct vaapi_recorder *r) +{ + pthread_mutex_init(&r->mutex, NULL); + pthread_cond_init(&r->input_cond, NULL); + pthread_create(&r->worker_thread, NULL, worker_thread_function, r); + + return 1; +} + +static void +destroy_worker_thread(struct vaapi_recorder *r) +{ + pthread_mutex_lock(&r->mutex); + + /* Make sure the worker thread finishes */ + r->destroying = 1; + pthread_cond_signal(&r->input_cond); + + pthread_mutex_unlock(&r->mutex); + + pthread_join(r->worker_thread, NULL); + + pthread_mutex_destroy(&r->mutex); + pthread_cond_destroy(&r->input_cond); +} + struct vaapi_recorder * vaapi_recorder_create(int drm_fd, int width, int height, const char *filename) { @@ -904,9 +946,12 @@ vaapi_recorder_create(int drm_fd, int width, int height, const char *filename) flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC; r->output_fd = open(filename, flags, 0644); - if (r->output_fd < 0) + if (setup_worker_thread(r) < 0) goto err_free; + if (r->output_fd < 0) + goto err_thread; + r->va_dpy = vaGetDisplayDRM(drm_fd); if (!r->va_dpy) { weston_log("failed to create VA display\n"); @@ -936,6 +981,8 @@ err_va_dpy: vaTerminate(r->va_dpy); err_fd: close(r->output_fd); +err_thread: + destroy_worker_thread(r); err_free: free(r); @@ -945,6 +992,8 @@ err_free: void vaapi_recorder_destroy(struct vaapi_recorder *r) { + destroy_worker_thread(r); + encoder_destroy(r); vpp_destroy(r); @@ -1033,20 +1082,22 @@ convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface) return status; } -void -vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, - int stride) +static void +recorder_frame(struct vaapi_recorder *r) { VASurfaceID rgb_surface; VAStatus status; - status = create_surface_from_fd(r, prime_fd, stride, &rgb_surface); + status = create_surface_from_fd(r, r->input.prime_fd, + r->input.stride, &rgb_surface); if (status != VA_STATUS_SUCCESS) { weston_log("[libva recorder] " "failed to create surface from bo\n"); return; } + close(r->input.prime_fd); + status = convert_rgb_to_yuv(r, rgb_surface); if (status != VA_STATUS_SUCCESS) { weston_log("[libva recorder] " @@ -1059,4 +1110,44 @@ vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, vaDestroySurfaces(r->va_dpy, &rgb_surface, 1); } +static void * +worker_thread_function(void *data) +{ + struct vaapi_recorder *r = data; + + pthread_mutex_lock(&r->mutex); + + while (!r->destroying) { + if (!r->input.valid) + pthread_cond_wait(&r->input_cond, &r->mutex); + + /* If the thread is awaken by destroy_worker_thread(), + * there might not be valid input */ + if (!r->input.valid) + continue; + + recorder_frame(r); + r->input.valid = 0; + } + + pthread_mutex_unlock(&r->mutex); + + return NULL; +} + +void +vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride) +{ + pthread_mutex_lock(&r->mutex); + + /* The mutex is never released while encoding, so this point should + * never be reached if input.valid is true. */ + assert(!r->input.valid); + r->input.prime_fd = prime_fd; + r->input.stride = stride; + r->input.valid = 1; + pthread_cond_signal(&r->input_cond); + + pthread_mutex_unlock(&r->mutex); +}