* 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 */
} encoder;
};
+static void *
+worker_thread_function(void *);
+
/* bistream code used for writing the packed headers */
#define BITSTREAM_ALLOCATE_STEPPING 4096
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;
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
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;
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]);
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)
{
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) {
vaTerminate(r->va_dpy);
err_fd:
close(r->output_fd);
+err_thread:
+ destroy_worker_thread(r);
err_free:
free(r);
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);
}
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] "
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;
+}