compositor: quick fix for sub-surface mapping
[profile/ivi/weston-ivi-shell.git] / src / vaapi-recorder.c
index c0210f0..921494d 100644 (file)
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include "config.h"
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <unistd.h>
+#include <assert.h>
+#include <errno.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#include <pthread.h>
 
 #include <va/va.h>
 #include <va/va_drm.h>
 #define PROFILE_IDC_HIGH        100
 
 struct vaapi_recorder {
-       int output_fd;
+       int drm_fd, output_fd;
        int width, height;
        int frame_count;
 
+       int error;
+       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 +132,9 @@ struct vaapi_recorder {
        } encoder;
 };
 
+static void *
+worker_thread_function(void *);
+
 /* bistream code used for writing the packed headers */
 
 #define BITSTREAM_ALLOCATE_STEPPING     4096
@@ -744,7 +763,13 @@ encoder_create_output_buffer(struct vaapi_recorder *r)
                return VA_INVALID_ID;
 }
 
-static int
+enum output_write_status {
+       OUTPUT_WRITE_SUCCESS,
+       OUTPUT_WRITE_OVERFLOW,
+       OUTPUT_WRITE_FATAL
+};
+
+static enum output_write_status
 encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
 {
        VACodedBufferSegment *segment;
@@ -753,19 +778,22 @@ encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
 
        status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
        if (status != VA_STATUS_SUCCESS)
-               return -1;
+               return OUTPUT_WRITE_FATAL;
 
        if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
                r->encoder.output_size *= 2;
                vaUnmapBuffer(r->va_dpy, output_buf);
-               return -1;
+               return OUTPUT_WRITE_OVERFLOW;
        }
 
        count = write(r->output_fd, segment->buf, segment->size);
 
        vaUnmapBuffer(r->va_dpy, output_buf);
 
-       return count;
+       if (count < 0)
+               return OUTPUT_WRITE_FATAL;
+
+       return OUTPUT_WRITE_SUCCESS;
 }
 
 static void
@@ -775,9 +803,8 @@ encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
 
        VABufferID buffers[8];
        int count = 0;
-
-       int slice_type;
-       int ret, i;
+       int i, slice_type;
+       enum output_write_status ret;
 
        if ((r->frame_count % r->encoder.intra_period) == 0)
                slice_type = SLICE_TYPE_I;
@@ -812,7 +839,10 @@ encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
                output_buf = VA_INVALID_ID;
 
                vaDestroyBuffer(r->va_dpy, buffers[--count]);
-       } while (ret < 0);
+       } while (ret == OUTPUT_WRITE_OVERFLOW);
+
+       if (ret == OUTPUT_WRITE_FATAL)
+               r->error = errno;
 
        for (i = 0; i < count; i++)
                vaDestroyBuffer(r->va_dpy, buffers[i]);
@@ -886,6 +916,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)
 {
@@ -900,12 +957,15 @@ vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
 
        r->width = width;
        r->height = height;
+       r->drm_fd = drm_fd;
+
+       if (setup_worker_thread(r) < 0)
+               goto err_free;
 
        flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
        r->output_fd = open(filename, flags, 0644);
-
        if (r->output_fd < 0)
-               goto err_free;
+               goto err_thread;
 
        r->va_dpy = vaGetDisplayDRM(drm_fd);
        if (!r->va_dpy) {
@@ -936,6 +996,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,12 +1007,15 @@ err_free:
 void
 vaapi_recorder_destroy(struct vaapi_recorder *r)
 {
+       destroy_worker_thread(r);
+
        encoder_destroy(r);
        vpp_destroy(r);
 
        vaTerminate(r->va_dpy);
 
        close(r->output_fd);
+       close(r->drm_fd);
 
        free(r);
 }
@@ -1033,20 +1098,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 +1126,55 @@ 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;
+}
+
+int
+vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride)
+{
+       int ret = 0;
+
+       pthread_mutex_lock(&r->mutex);
 
+       if (r->error) {
+               errno = r->error;
+               ret = -1;
+               goto unlock;
+       }
+
+       /* 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);
+
+unlock:
+       pthread_mutex_unlock(&r->mutex);
+
+       return ret;
+}