[RFC] compositor-drm: Add hardware accelerated capture of screen using libva

Ander Conselvan de Oliveira conselvan2 at gmail.com
Fri Aug 30 08:25:30 PDT 2013


On 08/30/2013 07:37 AM, Kristian Høgsberg wrote:
> On Fri, Aug 23, 2013 at 05:15:48PM +0300, Ander Conselvan de Oliveira wrote:
>> From: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira at intel.com>
>>
>> This patch adds a feature to the DRM backend that uses libva for
>> encoding the screen contents in H.264. Screen recording can be
>> activated by pressing mod-shift-space q. A file named capture.h264
>> will be created in the current directory, which can be muxed into
>> an MP4 file with gstreamer using
>
> I tried it out and it worked like a charm.  I have IvyBridge here and
> I didn't get the hang you mention.  It's very cool how it encodes h264
> direectly and doesn't seem to load the system very when doing so.
> Newer Intel GPUs have a dedicated hw video encoder so it shouldn't
> take away CPU or GPU resources from whatever weston and clients are
> doing.  They're still competing for memory bandwidth of course.  I
> wonder what the impact of capturing a GPU intensive app (like a modern
> game, potentially under xwayland) might be.
>
> Anyway, I feel like we're better off merging this sooner rather than
> later.  The feature is basically working and for all we know the
> crasher could be SandyBridge specific.  Do you see a problem in
> committing this now and fixing the rest of the issues incrementally?

Yeah, let's do that. Though, I'm not entirely sure if this plugs with 
the drm backend in an optimal way. The frame signal is sent just before 
a flip, and it is in that moment that the front buffer is read and sent 
to be encoded. I guess it would make more sense to do that just after a 
flip. But then we wouldn't use the frame signal at all, and instead hook 
things up with the page flip handler.

Ander

>
> Kristian
>
>> gst-launch filesrc location=capture.h264 ! h264parse ! mp4mux ! \
>>             filesink location=file.mp4
>>
>> This is limitted to the DRM compositor in order to avoid a copy when
>> submitting the front buffer to libva. The code in vaapi-recorder.c
>> takes a dma_buf fd referencing it, does a colorspace conversion using
>> the video post processing pipeline and then uses that as input to the
>> encoder.
>>
>> I'm sending this now so I get comments, but this is not ready for
>> prime time yet. I have a somewhat consistent GPU hang when using
>> i915 with SandyBridge. Sometimes a page flip never completes. If you
>> want to try this anyway and your system get stuck, you might need to
>> run the following:
>>
>>    # echo 1 > /sys/kernel/debug/dri/0/i915_wedged
>>
>> After that, alt-sysrq [rv] should work.
>>
>> Once that's fixed it would also be good to make the parameters used by
>> the encoder more flexible. For now the QP parameter is hardcoded to 0
>> and we have only I and P frames (no B frames), which causes the
>> resulting files to be very large.
>> ---
>>   configure.ac         |    6 +
>>   src/Makefile.am      |    6 +
>>   src/compositor-drm.c |  109 ++++++
>>   src/vaapi-recorder.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   src/vaapi-recorder.h |   35 ++
>>   5 files changed, 1218 insertions(+)
>>   create mode 100644 src/vaapi-recorder.c
>>   create mode 100644 src/vaapi-recorder.h
>>
>> diff --git a/configure.ac b/configure.ac
>> index fab0b48..e5f6afd 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -239,6 +239,11 @@ PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])
>>   AS_IF([test "x$have_webp" = "xyes"],
>>         [AC_DEFINE([HAVE_WEBP], [1], [Have webp])])
>>
>> +PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0], [have_libva=yes], [have_libva=no])
>> +AS_IF([test "x$have_libva" = "xyes"],
>> +      [AC_DEFINE([HAVE_LIBVA], [1], [Have libva])])
>> +AM_CONDITIONAL(ENABLE_LIBVA, test "x$have_libva" = "xyes")
>> +
>>   AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes)
>>   if test x$have_jpeglib = xyes; then
>>     JPEG_LIBS="-ljpeg"
>> @@ -478,4 +483,5 @@ AC_MSG_RESULT([
>>   	LCMS2 Support			${have_lcms}
>>   	libwebp Support			${have_webp}
>>   	libunwind Support		${have_libunwind}
>> +	VA H.264 encoding Support	${have_libva}
>>   ])
>> diff --git a/src/Makefile.am b/src/Makefile.am
>> index 929de31..ab69df2 100644
>> --- a/src/Makefile.am
>> +++ b/src/Makefile.am
>> @@ -152,6 +152,12 @@ drm_backend_la_SOURCES =			\
>>   	launcher-util.h				\
>>   	libbacklight.c				\
>>   	libbacklight.h
>> +
>> +if ENABLE_LIBVA
>> +drm_backend_la_SOURCES += vaapi-recorder.c
>> +drm_backend_la_LIBADD += $(LIBVA_LIBS)
>> +drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
>> +endif
>>   endif
>>
>>   if ENABLE_WAYLAND_COMPOSITOR
>> diff --git a/src/compositor-drm.c b/src/compositor-drm.c
>> index b9e3fc9..dca1e6c 100644
>> --- a/src/compositor-drm.c
>> +++ b/src/compositor-drm.c
>> @@ -47,6 +47,7 @@
>>   #include "pixman-renderer.h"
>>   #include "udev-seat.h"
>>   #include "launcher-util.h"
>> +#include "vaapi-recorder.h"
>>
>>   #ifndef DRM_CAP_TIMESTAMP_MONOTONIC
>>   #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
>> @@ -75,6 +76,7 @@ struct drm_compositor {
>>   	struct {
>>   		int id;
>>   		int fd;
>> +		char *filename;
>>   	} drm;
>>   	struct gbm_device *gbm;
>>   	uint32_t *crtcs;
>> @@ -159,6 +161,9 @@ struct drm_output {
>>   	pixman_image_t *image[2];
>>   	int current_image;
>>   	pixman_region32_t previous_damage;
>> +
>> +	struct vaapi_recorder *recorder;
>> +	struct wl_listener recorder_frame_listener;
>>   };
>>
>>   /*
>> @@ -716,6 +721,11 @@ page_flip_handler(int fd, unsigned int frame,
>>   	if (!output->vblank_pending) {
>>   		msecs = sec * 1000 + usec / 1000;
>>   		weston_output_finish_frame(&output->base, msecs);
>> +
>> +		/* We can't call this from frame_notify, because the output's
>> +		 * repaint needed flag is cleared just after that */
>> +		if (output->recorder)
>> +			weston_output_schedule_repaint(&output->base);
>>   	}
>>   }
>>
>> @@ -1214,6 +1224,7 @@ init_drm(struct drm_compositor *ec, struct udev_device *device)
>>   	weston_log("using %s\n", filename);
>>
>>   	ec->drm.fd = fd;
>> +	ec->drm.filename = strdup(filename);
>>
>>   	ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
>>   	if (ret == 0 && cap == 1)
>> @@ -2434,6 +2445,102 @@ planes_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data
>>   	}
>>   }
>>
>> +#ifdef HAVE_LIBVA
>> +static void
>> +recorder_frame_notify(struct wl_listener *listener, void *data)
>> +{
>> +	struct drm_output *output;
>> +	struct drm_compositor *c;
>> +	int fd, ret;
>> +
>> +	output = container_of(listener, struct drm_output,
>> +			      recorder_frame_listener);
>> +	c = (struct drm_compositor *) output->base.compositor;
>> +
>> +	if (!output->recorder)
>> +		return;
>> +
>> +	ret = drmPrimeHandleToFD(c->drm.fd, output->current->handle,
>> +				 DRM_CLOEXEC, &fd);
>> +	if (ret) {
>> +		weston_log("[libva recorder] "
>> +			   "failed to create prime fd for front buffer\n");
>> +		return;
>> +	}
>> +
>> +	vaapi_recorder_frame(output->recorder, fd, output->current->stride / 4);
>> +
>> +	close(fd);
>> +}
>> +
>> +static void *
>> +create_recorder(struct drm_compositor *c, int width, int height,
>> +		const char *filename)
>> +{
>> +	int fd;
>> +	drm_magic_t magic;
>> +
>> +	fd = open(c->drm.filename, O_RDWR | O_CLOEXEC);
>> +	if (fd < 0)
>> +		return NULL;
>> +
>> +	drmGetMagic(fd, &magic);
>> +	drmAuthMagic(c->drm.fd, magic);
>> +
>> +	return vaapi_recorder_create(fd, width, height, filename);
>> +}
>> +
>> +static void
>> +recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
>> +		 void *data)
>> +{
>> +	struct drm_compositor *c = data;
>> +	struct drm_output *output;
>> +	int width, height;
>> +
>> +	output = container_of(c->base.output_list.next,
>> +			      struct drm_output, base.link);
>> +
>> +	if (!output->recorder) {
>> +		width = output->base.current->width;
>> +		height = output->base.current->height;
>> +
>> +		output->recorder =
>> +			create_recorder(c, width, height, "capture.h264");
>> +		if (!output->recorder) {
>> +			weston_log("failed to create vaapi recorder\n");
>> +			return;
>> +		}
>> +
>> +		output->base.disable_planes++;
>> +
>> +		output->recorder_frame_listener.notify = recorder_frame_notify;
>> +		wl_signal_add(&output->base.frame_signal,
>> +			      &output->recorder_frame_listener);
>> +
>> +		weston_output_schedule_repaint(&output->base);
>> +
>> +		weston_log("[libva recorder] initialized\n");
>> +	} else {
>> +		vaapi_recorder_destroy(output->recorder);
>> +		/* FIXME: close drm fd passed to recorder */
>> +		output->recorder = NULL;
>> +
>> +		output->base.disable_planes--;
>> +
>> +		wl_list_remove(&output->recorder_frame_listener.link);
>> +		weston_log("[libva recorder] done\n");
>> +	}
>> +}
>> +#else
>> +static void
>> +recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
>> +		 void *data)
>> +{
>> +	weston_log("Compiled without libva support\n");
>> +}
>> +#endif
>> +
>>   static struct weston_compositor *
>>   drm_compositor_create(struct wl_display *display,
>>   		      int connector, const char *seat_id, int tty, int pixman,
>> @@ -2567,6 +2674,8 @@ drm_compositor_create(struct wl_display *display,
>>   					    planes_binding, ec);
>>   	weston_compositor_add_debug_binding(&ec->base, KEY_V,
>>   					    planes_binding, ec);
>> +	weston_compositor_add_debug_binding(&ec->base, KEY_Q,
>> +					    recorder_binding, ec);
>>
>>   	return &ec->base;
>>
>> diff --git a/src/vaapi-recorder.c b/src/vaapi-recorder.c
>> new file mode 100644
>> index 0000000..c0210f0
>> --- /dev/null
>> +++ b/src/vaapi-recorder.c
>> @@ -0,0 +1,1062 @@
>> +/*
>> + * Copyright © 2013 Intel Corporation
>> + *
>> + * Permission to use, copy, modify, distribute, and sell this software and
>> + * its documentation for any purpose is hereby granted without fee, provided
>> + * that the above copyright notice appear in all copies and that both that
>> + * copyright notice and this permission notice appear in supporting
>> + * documentation, and that the name of the copyright holders not be used in
>> + * advertising or publicity pertaining to distribution of the software
>> + * without specific, written prior permission.  The copyright holders make
>> + * no representations about the suitability of this software for any
>> + * purpose.  It is provided "as is" without express or implied warranty.
>> + *
>> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
>> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
>> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
>> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
>> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + */
>> +
>> +/* Copyright (c) 2012 Intel Corporation. All Rights Reserved.
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a
>> + * copy of this software and associated documentation files (the
>> + * "Software"), to deal in the Software without restriction, including
>> + * without limitation the rights to use, copy, modify, merge, publish,
>> + * distribute, sub license, and/or sell copies of the Software, and to
>> + * permit persons to whom the Software is furnished to do so, subject to
>> + * the following conditions:
>> + *
>> + * The above copyright notice and this permission notice (including the
>> + * next paragraph) shall be included in all copies or substantial portions
>> + * of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
>> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
>> + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
>> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <stdint.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +
>> +#include <sys/types.h>
>> +#include <sys/stat.h>
>> +#include <fcntl.h>
>> +
>> +
>> +#include <va/va.h>
>> +#include <va/va_drm.h>
>> +#include <va/va_drmcommon.h>
>> +#include <va/va_enc_h264.h>
>> +#include <va/va_vpp.h>
>> +
>> +#include "compositor.h"
>> +#include "vaapi-recorder.h"
>> +
>> +#define NAL_REF_IDC_NONE        0
>> +#define NAL_REF_IDC_LOW         1
>> +#define NAL_REF_IDC_MEDIUM      2
>> +#define NAL_REF_IDC_HIGH        3
>> +
>> +#define NAL_NON_IDR             1
>> +#define NAL_IDR                 5
>> +#define NAL_SPS                 7
>> +#define NAL_PPS                 8
>> +#define NAL_SEI                 6
>> +
>> +#define SLICE_TYPE_P            0
>> +#define SLICE_TYPE_B            1
>> +#define SLICE_TYPE_I            2
>> +
>> +#define ENTROPY_MODE_CAVLC      0
>> +#define ENTROPY_MODE_CABAC      1
>> +
>> +#define PROFILE_IDC_BASELINE    66
>> +#define PROFILE_IDC_MAIN        77
>> +#define PROFILE_IDC_HIGH        100
>> +
>> +struct vaapi_recorder {
>> +	int output_fd;
>> +	int width, height;
>> +	int frame_count;
>> +
>> +	VADisplay va_dpy;
>> +
>> +	/* video post processing is used for colorspace conversion */
>> +	struct {
>> +		VAConfigID cfg;
>> +		VAContextID ctx;
>> +		VABufferID pipeline_buf;
>> +		VASurfaceID output;
>> +	} vpp;
>> +
>> +	struct {
>> +		VAConfigID cfg;
>> +		VAContextID ctx;
>> +		VASurfaceID reference_picture[3];
>> +
>> +		int intra_period;
>> +		int output_size;
>> +		int constraint_set_flag;
>> +
>> +		struct {
>> +			VAEncSequenceParameterBufferH264 seq;
>> +			VAEncPictureParameterBufferH264 pic;
>> +			VAEncSliceParameterBufferH264 slice;
>> +		} param;
>> +	} encoder;
>> +};
>> +
>> +/* bistream code used for writing the packed headers */
>> +
>> +#define BITSTREAM_ALLOCATE_STEPPING	 4096
>> +
>> +struct bitstream {
>> +	unsigned int *buffer;
>> +	int bit_offset;
>> +	int max_size_in_dword;
>> +};
>> +
>> +static unsigned int
>> +va_swap32(unsigned int val)
>> +{
>> +	unsigned char *pval = (unsigned char *)&val;
>> +
>> +	return ((pval[0] << 24) |
>> +		(pval[1] << 16) |
>> +		(pval[2] << 8)  |
>> +		(pval[3] << 0));
>> +}
>> +
>> +static void
>> +bitstream_start(struct bitstream *bs)
>> +{
>> +	bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
>> +	bs->buffer = calloc(bs->max_size_in_dword * sizeof(int), 1);
>> +	bs->bit_offset = 0;
>> +}
>> +
>> +static void
>> +bitstream_end(struct bitstream *bs)
>> +{
>> +	int pos = (bs->bit_offset >> 5);
>> +	int bit_offset = (bs->bit_offset & 0x1f);
>> +	int bit_left = 32 - bit_offset;
>> +
>> +	if (bit_offset) {
>> +		bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
>> +	}
>> +}
>> +
>> +static void
>> +bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits)
>> +{
>> +	int pos = (bs->bit_offset >> 5);
>> +	int bit_offset = (bs->bit_offset & 0x1f);
>> +	int bit_left = 32 - bit_offset;
>> +
>> +	if (!size_in_bits)
>> +		return;
>> +
>> +	bs->bit_offset += size_in_bits;
>> +
>> +	if (bit_left > size_in_bits) {
>> +		bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
>> +		return;
>> +	}
>> +
>> +	size_in_bits -= bit_left;
>> +	bs->buffer[pos] =
>> +		(bs->buffer[pos] << bit_left) | (val >> size_in_bits);
>> +	bs->buffer[pos] = va_swap32(bs->buffer[pos]);
>> +
>> +	if (pos + 1 == bs->max_size_in_dword) {
>> +		bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
>> +		bs->buffer =
>> +			realloc(bs->buffer,
>> +				bs->max_size_in_dword * sizeof(unsigned int));
>> +	}
>> +
>> +	bs->buffer[pos + 1] = val;
>> +}
>> +
>> +static void
>> +bitstream_put_ue(struct bitstream *bs, unsigned int val)
>> +{
>> +	int size_in_bits = 0;
>> +	int tmp_val = ++val;
>> +
>> +	while (tmp_val) {
>> +		tmp_val >>= 1;
>> +		size_in_bits++;
>> +	}
>> +
>> +	bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero
>> +	bitstream_put_ui(bs, val, size_in_bits);
>> +}
>> +
>> +static void
>> +bitstream_put_se(struct bitstream *bs, int val)
>> +{
>> +	unsigned int new_val;
>> +
>> +	if (val <= 0)
>> +		new_val = -2 * val;
>> +	else
>> +		new_val = 2 * val - 1;
>> +
>> +	bitstream_put_ue(bs, new_val);
>> +}
>> +
>> +static void
>> +bitstream_byte_aligning(struct bitstream *bs, int bit)
>> +{
>> +	int bit_offset = (bs->bit_offset & 0x7);
>> +	int bit_left = 8 - bit_offset;
>> +	int new_val;
>> +
>> +	if (!bit_offset)
>> +		return;
>> +
>> +	if (bit)
>> +		new_val = (1 << bit_left) - 1;
>> +	else
>> +		new_val = 0;
>> +
>> +	bitstream_put_ui(bs, new_val, bit_left);
>> +}
>> +
>> +static VAStatus
>> +encoder_create_config(struct vaapi_recorder *r)
>> +{
>> +	VAConfigAttrib attrib[2];
>> +	VAStatus status;
>> +
>> +	/* FIXME: should check if VAEntrypointEncSlice is supported */
>> +
>> +	/* FIXME: should check if specified attributes are supported */
>> +
>> +	attrib[0].type = VAConfigAttribRTFormat;
>> +	attrib[0].value = VA_RT_FORMAT_YUV420;
>> +
>> +	attrib[1].type = VAConfigAttribRateControl;
>> +	attrib[1].value = VA_RC_CQP;
>> +
>> +	status = vaCreateConfig(r->va_dpy, VAProfileH264Main,
>> +				VAEntrypointEncSlice, attrib, 2,
>> +				&r->encoder.cfg);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaCreateContext(r->va_dpy, r->encoder.cfg,
>> +				 r->width, r->height, VA_PROGRESSIVE, 0, 0,
>> +				 &r->encoder.ctx);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		vaDestroyConfig(r->va_dpy, r->encoder.cfg);
>> +		return status;
>> +	}
>> +
>> +	return VA_STATUS_SUCCESS;
>> +}
>> +
>> +static void
>> +encoder_destroy_config(struct vaapi_recorder *r)
>> +{
>> +	vaDestroyContext(r->va_dpy, r->encoder.ctx);
>> +	vaDestroyConfig(r->va_dpy, r->encoder.cfg);
>> +}
>> +
>> +static void
>> +encoder_init_seq_parameters(struct vaapi_recorder *r)
>> +{
>> +	int width_in_mbs, height_in_mbs;
>> +	int frame_cropping_flag = 0;
>> +	int frame_crop_bottom_offset = 0;
>> +
>> +	width_in_mbs = (r->width + 15) / 16;
>> +	height_in_mbs = (r->height + 15) / 16;
>> +
>> +	r->encoder.param.seq.level_idc = 41;
>> +	r->encoder.param.seq.intra_period = r->encoder.intra_period;
>> +	r->encoder.param.seq.max_num_ref_frames = 4;
>> +	r->encoder.param.seq.picture_width_in_mbs = width_in_mbs;
>> +	r->encoder.param.seq.picture_height_in_mbs = height_in_mbs;
>> +	r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1;
>> +
>> +	/* Tc = num_units_in_tick / time_scale */
>> +	r->encoder.param.seq.time_scale = 1800;
>> +	r->encoder.param.seq.num_units_in_tick = 15;
>> +
>> +	if (height_in_mbs * 16 - r->height > 0) {
>> +		frame_cropping_flag = 1;
>> +		frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2;
>> +	}
>> +
>> +	r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag;
>> +	r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset;
>> +
>> +	r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
>> +}
>> +
>> +static VABufferID
>> +encoder_update_seq_parameters(struct vaapi_recorder *r)
>> +{
>> +	VABufferID seq_buf;
>> +	VAStatus status;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncSequenceParameterBufferType,
>> +				sizeof(r->encoder.param.seq),
>> +				1, &r->encoder.param.seq,
>> +				&seq_buf);
>> +
>> +	if (status == VA_STATUS_SUCCESS)
>> +		return seq_buf;
>> +	else
>> +		return VA_INVALID_ID;
>> +}
>> +
>> +static void
>> +encoder_init_pic_parameters(struct vaapi_recorder *r)
>> +{
>> +	VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
>> +
>> +	pic->pic_init_qp = 0;
>> +
>> +	/* ENTROPY_MODE_CABAC */
>> +	pic->pic_fields.bits.entropy_coding_mode_flag = 1;
>> +
>> +	pic->pic_fields.bits.deblocking_filter_control_present_flag = 1;
>> +}
>> +
>> +static VABufferID
>> +encoder_update_pic_parameters(struct vaapi_recorder *r,
>> +			      VABufferID output_buf)
>> +{
>> +	VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
>> +	VAStatus status;
>> +	VABufferID pic_param_buf;
>> +	VASurfaceID curr_pic, pic0;
>> +
>> +	curr_pic = r->encoder.reference_picture[r->frame_count % 2];
>> +	pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2];
>> +
>> +	pic->CurrPic.picture_id = curr_pic;
>> +	pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2;
>> +	pic->ReferenceFrames[0].picture_id = pic0;
>> +	pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2];
>> +	pic->ReferenceFrames[2].picture_id = VA_INVALID_ID;
>> +
>> +	pic->coded_buf = output_buf;
>> +	pic->frame_num = r->frame_count;
>> +
>> +	pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0);
>> +	pic->pic_fields.bits.reference_pic_flag = 1;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncPictureParameterBufferType,
>> +				sizeof(VAEncPictureParameterBufferH264), 1,
>> +				pic, &pic_param_buf);
>> +
>> +	if (status == VA_STATUS_SUCCESS)
>> +		return pic_param_buf;
>> +	else
>> +		return VA_INVALID_ID;
>> +}
>> +
>> +static VABufferID
>> +encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type)
>> +{
>> +	VABufferID slice_param_buf;
>> +	VAStatus status;
>> +
>> +	int width_in_mbs = (r->width + 15) / 16;
>> +	int height_in_mbs = (r->height + 15) / 16;
>> +
>> +	memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice);
>> +
>> +	r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs;
>> +	r->encoder.param.slice.slice_type = slice_type;
>> +
>> +	r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2;
>> +	r->encoder.param.slice.slice_beta_offset_div2 = 2;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncSliceParameterBufferType,
>> +				sizeof(r->encoder.param.slice), 1,
>> +				&r->encoder.param.slice,
>> +				&slice_param_buf);
>> +
>> +	if (status == VA_STATUS_SUCCESS)
>> +		return slice_param_buf;
>> +	else
>> +		return VA_INVALID_ID;
>> +}
>> +
>> +static VABufferID
>> +encoder_update_misc_hdr_parameter(struct vaapi_recorder *r)
>> +{
>> +	VAEncMiscParameterBuffer *misc_param;
>> +	VAEncMiscParameterHRD *hrd;
>> +	VABufferID buffer;
>> +	VAStatus status;
>> +
>> +	int total_size =
>> +		sizeof(VAEncMiscParameterBuffer) +
>> +		sizeof(VAEncMiscParameterRateControl);
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncMiscParameterBufferType, total_size,
>> +				1, NULL, &buffer);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return VA_INVALID_ID;
>> +
>> +	status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		vaDestroyBuffer(r->va_dpy, buffer);
>> +		return VA_INVALID_ID;
>> +	}
>> +
>> +	misc_param->type = VAEncMiscParameterTypeHRD;
>> +	hrd = (VAEncMiscParameterHRD *) misc_param->data;
>> +
>> +	hrd->initial_buffer_fullness = 0;
>> +	hrd->buffer_size = 0;
>> +
>> +	vaUnmapBuffer(r->va_dpy, buffer);
>> +
>> +	return buffer;
>> +}
>> +
>> +static int
>> +setup_encoder(struct vaapi_recorder *r)
>> +{
>> +	VAStatus status;
>> +
>> +	status = encoder_create_config(r);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		return -1;
>> +	}
>> +
>> +	status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
>> +				  r->width, r->height,
>> +				  r->encoder.reference_picture, 3,
>> +				  NULL, 0);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		encoder_destroy_config(r);
>> +		return -1;
>> +	}
>> +
>> +	/* VAProfileH264Main */
>> +	r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
>> +
>> +	r->encoder.output_size = r->width * r->height;
>> +
>> +	r->encoder.intra_period = 30;
>> +
>> +	encoder_init_seq_parameters(r);
>> +	encoder_init_pic_parameters(r);
>> +
>> +	return 0;
>> +}
>> +
>> +static void
>> +encoder_destroy(struct vaapi_recorder *r)
>> +{
>> +	vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3);
>> +
>> +	encoder_destroy_config(r);
>> +}
>> +
>> +static void
>> +nal_start_code_prefix(struct bitstream *bs)
>> +{
>> +	bitstream_put_ui(bs, 0x00000001, 32);
>> +}
>> +
>> +static void
>> +nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type)
>> +{
>> +	/* forbidden_zero_bit: 0 */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	bitstream_put_ui(bs, nal_ref_idc, 2);
>> +	bitstream_put_ui(bs, nal_unit_type, 5);
>> +}
>> +
>> +static void
>> +rbsp_trailing_bits(struct bitstream *bs)
>> +{
>> +	bitstream_put_ui(bs, 1, 1);
>> +	bitstream_byte_aligning(bs, 0);
>> +}
>> +
>> +static void sps_rbsp(struct bitstream *bs,
>> +		     VAEncSequenceParameterBufferH264 *seq,
>> +		     int constraint_set_flag)
>> +{
>> +	int i;
>> +
>> +	bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8);
>> +
>> +	/* constraint_set[0-3] flag */
>> +	for (i = 0; i < 4; i++) {
>> +		int set = (constraint_set_flag & (1 << i)) ? 1 : 0;
>> +		bitstream_put_ui(bs, set, 1);
>> +	}
>> +
>> +	/* reserved_zero_4bits */
>> +	bitstream_put_ui(bs, 0, 4);
>> +	bitstream_put_ui(bs, seq->level_idc, 8);
>> +	bitstream_put_ue(bs, seq->seq_parameter_set_id);
>> +
>> +	bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4);
>> +	bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type);
>> +	bitstream_put_ue(bs,
>> +			 seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
>> +
>> +	bitstream_put_ue(bs, seq->max_num_ref_frames);
>> +
>> +	/* gaps_in_frame_num_value_allowed_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */
>> +	bitstream_put_ue(bs, seq->picture_width_in_mbs - 1);
>> +	bitstream_put_ue(bs, seq->picture_height_in_mbs - 1);
>> +
>> +	bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1);
>> +	bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1);
>> +
>> +	bitstream_put_ui(bs, seq->frame_cropping_flag, 1);
>> +
>> +	if (seq->frame_cropping_flag) {
>> +		bitstream_put_ue(bs, seq->frame_crop_left_offset);
>> +		bitstream_put_ue(bs, seq->frame_crop_right_offset);
>> +		bitstream_put_ue(bs, seq->frame_crop_top_offset);
>> +		bitstream_put_ue(bs, seq->frame_crop_bottom_offset);
>> +	}
>> +
>> +	/* vui_parameters_present_flag */
>> +	bitstream_put_ui(bs, 1, 1);
>> +
>> +	/* aspect_ratio_info_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +	/* overscan_info_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* video_signal_type_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +	/* chroma_loc_info_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* timing_info_present_flag */
>> +	bitstream_put_ui(bs, 1, 1);
>> +	bitstream_put_ui(bs, seq->num_units_in_tick, 32);
>> +	bitstream_put_ui(bs, seq->time_scale, 32);
>> +	/* fixed_frame_rate_flag */
>> +	bitstream_put_ui(bs, 1, 1);
>> +
>> +	/* nal_hrd_parameters_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* vcl_hrd_parameters_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* low_delay_hrd_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* pic_struct_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +	/* bitstream_restriction_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	rbsp_trailing_bits(bs);
>> +}
>> +
>> +static void pps_rbsp(struct bitstream *bs,
>> +		     VAEncPictureParameterBufferH264 *pic)
>> +{
>> +	/* pic_parameter_set_id, seq_parameter_set_id */
>> +	bitstream_put_ue(bs, pic->pic_parameter_set_id);
>> +	bitstream_put_ue(bs, pic->seq_parameter_set_id);
>> +
>> +	bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1);
>> +
>> +	/* pic_order_present_flag: 0 */
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	/* num_slice_groups_minus1 */
>> +	bitstream_put_ue(bs, 0);
>> +
>> +	bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1);
>> +	bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1);
>> +
>> +	bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1);
>> +	bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2);
>> +
>> +	/* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */
>> +	bitstream_put_se(bs, pic->pic_init_qp - 26);
>> +	bitstream_put_se(bs, 0);
>> +	bitstream_put_se(bs, 0);
>> +
>> +	bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1);
>> +
>> +	/* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +	bitstream_put_ui(bs, 0, 1);
>> +
>> +	bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1);
>> +
>> +	/* pic_scaling_matrix_present_flag */
>> +	bitstream_put_ui(bs, 0, 1);
>> +	bitstream_put_se(bs, pic->second_chroma_qp_index_offset );
>> +
>> +	rbsp_trailing_bits(bs);
>> +}
>> +
>> +static int
>> +build_packed_pic_buffer(struct vaapi_recorder *r,
>> +			void **header_buffer)
>> +{
>> +	struct bitstream bs;
>> +
>> +	bitstream_start(&bs);
>> +	nal_start_code_prefix(&bs);
>> +	nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
>> +	pps_rbsp(&bs, &r->encoder.param.pic);
>> +	bitstream_end(&bs);
>> +
>> +	*header_buffer = bs.buffer;
>> +	return bs.bit_offset;
>> +}
>> +
>> +static int
>> +build_packed_seq_buffer(struct vaapi_recorder *r,
>> +			void **header_buffer)
>> +{
>> +	struct bitstream bs;
>> +
>> +	bitstream_start(&bs);
>> +	nal_start_code_prefix(&bs);
>> +	nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
>> +	sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag);
>> +	bitstream_end(&bs);
>> +
>> +	*header_buffer = bs.buffer;
>> +	return bs.bit_offset;
>> +}
>> +
>> +static int
>> +create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers,
>> +			     VAEncPackedHeaderType type,
>> +			     void *data, int bit_length)
>> +{
>> +	VAEncPackedHeaderParameterBuffer packed_header;
>> +	VAStatus status;
>> +
>> +	packed_header.type = type;
>> +	packed_header.bit_length = bit_length;
>> +	packed_header.has_emulation_bytes = 0;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncPackedHeaderParameterBufferType,
>> +				sizeof packed_header, 1, &packed_header,
>> +				&buffers[0]);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return 0;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncPackedHeaderDataBufferType,
>> +				(bit_length + 7) / 8, 1, data, &buffers[1]);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		vaDestroyBuffer(r->va_dpy, buffers[0]);
>> +		return 0;
>> +	}
>> +
>> +	return 2;
>> +}
>> +
>> +static int
>> +encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers)
>> +{
>> +	VABufferID *p;
>> +
>> +	int bit_length;
>> +	void *data;
>> +
>> +	p = buffers;
>> +
>> +	bit_length = build_packed_seq_buffer(r, &data);
>> +	p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence,
>> +					  data, bit_length);
>> +	free(data);
>> +
>> +	bit_length = build_packed_pic_buffer(r, &data);
>> +	p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture,
>> +					  data, bit_length);
>> +	free(data);
>> +
>> +	return p - buffers;
>> +}
>> +
>> +static VAStatus
>> +encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input,
>> +		       VABufferID *buffers, int count)
>> +{
>> +	VAStatus status;
>> +
>> +	status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaEndPicture(r->va_dpy, r->encoder.ctx);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	return vaSyncSurface(r->va_dpy, input);
>> +}
>> +
>> +static VABufferID
>> +encoder_create_output_buffer(struct vaapi_recorder *r)
>> +{
>> +	VABufferID output_buf;
>> +	VAStatus status;
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
>> +				VAEncCodedBufferType, r->encoder.output_size,
>> +				1, NULL, &output_buf);
>> +	if (status == VA_STATUS_SUCCESS)
>> +		return output_buf;
>> +	else
>> +		return VA_INVALID_ID;
>> +}
>> +
>> +static int
>> +encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
>> +{
>> +	VACodedBufferSegment *segment;
>> +	VAStatus status;
>> +	int count;
>> +
>> +	status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return -1;
>> +
>> +	if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
>> +		r->encoder.output_size *= 2;
>> +		vaUnmapBuffer(r->va_dpy, output_buf);
>> +		return -1;
>> +	}
>> +
>> +	count = write(r->output_fd, segment->buf, segment->size);
>> +
>> +	vaUnmapBuffer(r->va_dpy, output_buf);
>> +
>> +	return count;
>> +}
>> +
>> +static void
>> +encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
>> +{
>> +	VABufferID output_buf = VA_INVALID_ID;
>> +
>> +	VABufferID buffers[8];
>> +	int count = 0;
>> +
>> +	int slice_type;
>> +	int ret, i;
>> +
>> +	if ((r->frame_count % r->encoder.intra_period) == 0)
>> +		slice_type = SLICE_TYPE_I;
>> +	else
>> +		slice_type = SLICE_TYPE_P;
>> +
>> +	buffers[count++] = encoder_update_seq_parameters(r);
>> +	buffers[count++] = encoder_update_misc_hdr_parameter(r);
>> +	buffers[count++] = encoder_update_slice_parameter(r, slice_type);
>> +
>> +	for (i = 0; i < count; i++)
>> +		if (buffers[i] == VA_INVALID_ID)
>> +			goto bail;
>> +
>> +	if (r->frame_count == 0)
>> +		count += encoder_prepare_headers(r, buffers + count);
>> +
>> +	do {
>> +		output_buf = encoder_create_output_buffer(r);
>> +		if (output_buf == VA_INVALID_ID)
>> +			goto bail;
>> +
>> +		buffers[count++] =
>> +			encoder_update_pic_parameters(r, output_buf);
>> +		if (buffers[count - 1] == VA_INVALID_ID)
>> +			goto bail;
>> +
>> +		encoder_render_picture(r, input, buffers, count);
>> +		ret = encoder_write_output(r, output_buf);
>> +
>> +		vaDestroyBuffer(r->va_dpy, output_buf);
>> +		output_buf = VA_INVALID_ID;
>> +
>> +		vaDestroyBuffer(r->va_dpy, buffers[--count]);
>> +	} while (ret < 0);
>> +
>> +	for (i = 0; i < count; i++)
>> +		vaDestroyBuffer(r->va_dpy, buffers[i]);
>> +
>> +	r->frame_count++;
>> +	return;
>> +
>> +bail:
>> +	for (i = 0; i < count; i++)
>> +		vaDestroyBuffer(r->va_dpy, buffers[i]);
>> +	if (output_buf != VA_INVALID_ID)
>> +		vaDestroyBuffer(r->va_dpy, output_buf);
>> +}
>> +
>> +
>> +static int
>> +setup_vpp(struct vaapi_recorder *r)
>> +{
>> +	VAStatus status;
>> +
>> +	status = vaCreateConfig(r->va_dpy, VAProfileNone,
>> +				VAEntrypointVideoProc, NULL, 0,
>> +				&r->vpp.cfg);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("vaapi: failed to create VPP config\n");
>> +		return -1;
>> +	}
>> +
>> +	status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height,
>> +				 0, NULL, 0, &r->vpp.ctx);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("vaapi: failed to create VPP context\n");
>> +		goto err_cfg;
>> +	}
>> +
>> +	status = vaCreateBuffer(r->va_dpy, r->vpp.ctx,
>> +				VAProcPipelineParameterBufferType,
>> +				sizeof(VAProcPipelineParameterBuffer),
>> +				1, NULL, &r->vpp.pipeline_buf);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("vaapi: failed to create VPP pipeline buffer\n");
>> +		goto err_ctx;
>> +	}
>> +
>> +	status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
>> +				  r->width, r->height, &r->vpp.output, 1,
>> +				  NULL, 0);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("vaapi: failed to create YUV surface\n");
>> +		goto err_buf;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_buf:
>> +	vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
>> +err_ctx:
>> +	vaDestroyConfig(r->va_dpy, r->vpp.ctx);
>> +err_cfg:
>> +	vaDestroyConfig(r->va_dpy, r->vpp.cfg);
>> +
>> +	return -1;
>> +}
>> +
>> +static void
>> +vpp_destroy(struct vaapi_recorder *r)
>> +{
>> +	vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1);
>> +	vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
>> +	vaDestroyConfig(r->va_dpy, r->vpp.ctx);
>> +	vaDestroyConfig(r->va_dpy, r->vpp.cfg);
>> +}
>> +
>> +struct vaapi_recorder *
>> +vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
>> +{
>> +	struct vaapi_recorder *r;
>> +	VAStatus status;
>> +	int major, minor;
>> +	int flags;
>> +
>> +	r = calloc(1, sizeof *r);
>> +	if (!r)
>> +		return NULL;
>> +
>> +	r->width = width;
>> +	r->height = height;
>> +
>> +	flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
>> +	r->output_fd = open(filename, flags, 0644);
>> +
>> +	if (r->output_fd < 0)
>> +		goto err_free;
>> +
>> +	r->va_dpy = vaGetDisplayDRM(drm_fd);
>> +	if (!r->va_dpy) {
>> +		weston_log("failed to create VA display\n");
>> +		goto err_fd;
>> +	}
>> +
>> +	status = vaInitialize(r->va_dpy, &major, &minor);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("vaapi: failed to initialize display\n");
>> +		goto err_fd;
>> +	}
>> +
>> +	if (setup_vpp(r) < 0) {
>> +		weston_log("vaapi: failed to initialize VPP pipeline\n");
>> +		goto err_va_dpy;
>> +	}
>> +
>> +	if (setup_encoder(r) < 0) {
>> +		goto err_vpp;
>> +	}
>> +
>> +	return r;
>> +
>> +err_vpp:
>> +	vpp_destroy(r);
>> +err_va_dpy:
>> +	vaTerminate(r->va_dpy);
>> +err_fd:
>> +	close(r->output_fd);
>> +err_free:
>> +	free(r);
>> +
>> +	return NULL;
>> +}
>> +
>> +void
>> +vaapi_recorder_destroy(struct vaapi_recorder *r)
>> +{
>> +	encoder_destroy(r);
>> +	vpp_destroy(r);
>> +
>> +	vaTerminate(r->va_dpy);
>> +
>> +	close(r->output_fd);
>> +
>> +	free(r);
>> +}
>> +
>> +static VAStatus
>> +create_surface_from_fd(struct vaapi_recorder *r, int prime_fd,
>> +		       int stride, VASurfaceID *surface)
>> +{
>> +	VASurfaceAttrib va_attribs[2];
>> +	VASurfaceAttribExternalBuffers va_attrib_extbuf;
>> +	VAStatus status;
>> +
>> +	unsigned long buffer_fd = prime_fd;
>> +
>> +	va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
>> +	va_attrib_extbuf.width = r->width;
>> +	va_attrib_extbuf.height = r->height;
>> +	va_attrib_extbuf.data_size = r->height * stride;
>> +	va_attrib_extbuf.num_planes = 1;
>> +	va_attrib_extbuf.pitches[0] = stride;
>> +	va_attrib_extbuf.offsets[0] = 0;
>> +	va_attrib_extbuf.buffers = &buffer_fd;
>> +	va_attrib_extbuf.num_buffers = 1;
>> +	va_attrib_extbuf.flags = 0;
>> +	va_attrib_extbuf.private_data = NULL;
>> +
>> +	va_attribs[0].type = VASurfaceAttribMemoryType;
>> +	va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
>> +	va_attribs[0].value.type = VAGenericValueTypeInteger;
>> +	va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
>> +
>> +	va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
>> +	va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
>> +	va_attribs[1].value.type = VAGenericValueTypePointer;
>> +	va_attribs[1].value.value.p = &va_attrib_extbuf;
>> +
>> +	status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32,
>> +				  r->width, r->height, surface, 1,
>> +				  va_attribs, 2);
>> +
>> +	return status;
>> +}
>> +
>> +static VAStatus
>> +convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface)
>> +{
>> +	VAProcPipelineParameterBuffer *pipeline_param;
>> +	VAStatus status;
>> +
>> +	status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf,
>> +			     (void **) &pipeline_param);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	memset(pipeline_param, 0, sizeof *pipeline_param);
>> +
>> +	pipeline_param->surface = rgb_surface;
>> +	pipeline_param->surface_color_standard  = VAProcColorStandardNone;
>> +
>> +	pipeline_param->output_background_color = 0xff000000;
>> +	pipeline_param->output_color_standard   = VAProcColorStandardNone;
>> +
>> +	status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaRenderPicture(r->va_dpy, r->vpp.ctx,
>> +				 &r->vpp.pipeline_buf, 1);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	status = vaEndPicture(r->va_dpy, r->vpp.ctx);
>> +	if (status != VA_STATUS_SUCCESS)
>> +		return status;
>> +
>> +	return status;
>> +}
>> +
>> +void
>> +vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd,
>> +		     int stride)
>> +{
>> +	VASurfaceID rgb_surface;
>> +	VAStatus status;
>> +
>> +	status = create_surface_from_fd(r, prime_fd, stride, &rgb_surface);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("[libva recorder] "
>> +			   "failed to create surface from bo\n");
>> +		return;
>> +	}
>> +
>> +	status = convert_rgb_to_yuv(r, rgb_surface);
>> +	if (status != VA_STATUS_SUCCESS) {
>> +		weston_log("[libva recorder] "
>> +			   "color space conversion failed\n");
>> +		return;
>> +	}
>> +
>> +	encoder_encode(r, r->vpp.output);
>> +
>> +	vaDestroySurfaces(r->va_dpy, &rgb_surface, 1);
>> +}
>> +
>> +
>> diff --git a/src/vaapi-recorder.h b/src/vaapi-recorder.h
>> new file mode 100644
>> index 0000000..664b1f9
>> --- /dev/null
>> +++ b/src/vaapi-recorder.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * Copyright © 2013 Intel Corporation
>> + *
>> + * Permission to use, copy, modify, distribute, and sell this software and
>> + * its documentation for any purpose is hereby granted without fee, provided
>> + * that the above copyright notice appear in all copies and that both that
>> + * copyright notice and this permission notice appear in supporting
>> + * documentation, and that the name of the copyright holders not be used in
>> + * advertising or publicity pertaining to distribution of the software
>> + * without specific, written prior permission.  The copyright holders make
>> + * no representations about the suitability of this software for any
>> + * purpose.  It is provided "as is" without express or implied warranty.
>> + *
>> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
>> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
>> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
>> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
>> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
>> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> + */
>> +
>> +#ifndef _VAAPI_RECORDER_H_
>> +#define _VAAPI_RECORDER_H_
>> +
>> +struct vaapi_recorder;
>> +
>> +struct vaapi_recorder *
>> +vaapi_recorder_create(int drm_fd, int width, int height, const char *filename);
>> +void
>> +vaapi_recorder_destroy(struct vaapi_recorder *r);
>> +void
>> +vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride);
>> +
>> +#endif /* _VAAPI_RECORDER_H_ */
>> --
>> 1.7.9.5
>>
>> _______________________________________________
>> wayland-devel mailing list
>> wayland-devel at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/wayland-devel



More information about the wayland-devel mailing list