--- /dev/null
+/*
+ * VA-API plugin for virtual codec device
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * Refer to VLC.
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "glib.h"
+#include "libavcodec/avcodec.h"
+#include "libavcodec/vaapi.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/imgutils.h"
+#include "va/va.h"
+#include "va/va_x11.h"
+
+#include "maru_brillcodec_plugin.h"
+
+#define SURFACE_COUNT 20
+#define PROFILE VAProfileH264High
+
+typedef struct VAPluginContext {
+ VASurfaceID surface_id[SURFACE_COUNT];
+ bool surface_is_occupied[SURFACE_COUNT];
+
+ VAImage image;
+ bool is_supports_derive;
+} VAPluginContext;
+
+static void *va_display;
+
+// FIXME
+static VAPluginContext *va_ctx = &(VAPluginContext) {};
+
+static int create_surfaces(AVCodecContext *ctx, int width, int height)
+{
+ assert(width > 0 && height > 0);
+
+ VAConfigID config_id;
+ VAProfile profile = PROFILE; // FIXME
+
+ /* Create a VA configuration */
+ VAConfigAttrib attrib;
+ memset(&attrib, 0, sizeof(attrib));
+ attrib.type = VAConfigAttribRTFormat;
+ if (vaGetConfigAttributes(va_display,
+ profile, VAEntrypointVLD, &attrib, 1)) {
+ goto error;
+ }
+
+ /* Not sure what to do if not, I don't have a way to test */
+ if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
+ goto error;
+
+ if (vaCreateConfig(va_display,
+ profile, VAEntrypointVLD, &attrib, 1, &config_id)) {
+ goto error;
+ }
+
+ /* Create surfaces */
+ if (vaCreateSurfaces(va_display, VA_RT_FORMAT_YUV420, width, height,
+ va_ctx->surface_id, SURFACE_COUNT, NULL, 0)) {
+ goto error;
+ }
+
+ VAContextID context_id = VA_INVALID_ID;
+ /* Create a context */
+ if (vaCreateContext(va_display, config_id,
+ width, height, VA_PROGRESSIVE,
+ va_ctx->surface_id, SURFACE_COUNT, &context_id)) {
+ goto error;
+ }
+
+ int fmt_count = vaMaxNumImageFormats(va_display);
+ VAImageFormat *p_fmt = calloc(fmt_count, sizeof(*p_fmt));
+ if (!p_fmt) {
+ goto error;
+ }
+
+ if (vaQueryImageFormats(va_display, p_fmt, &fmt_count)) {
+ free( p_fmt );
+ goto error;
+ }
+
+ VAImage test_image;
+ if (vaDeriveImage(va_display, va_ctx->surface_id[0], &test_image) == VA_STATUS_SUCCESS) {
+ va_ctx->is_supports_derive = true;
+ vaDestroyImage(va_display, test_image.image_id);
+ }
+
+ int i;
+ for (i = 0; i < fmt_count; ++i) {
+ if (p_fmt[i].fourcc == VA_FOURCC_YV12) { // we support only VA_FOURCC_YV12 for now...
+ if (vaCreateImage(va_display, &p_fmt[i], width, height, &va_ctx->image )) {
+ goto error;
+ }
+ }
+ }
+
+ if (va_ctx->is_supports_derive) {
+ vaDestroyImage(va_display, va_ctx->image.image_id);
+ va_ctx->image.image_id = VA_INVALID_ID;
+ }
+
+ // prepare the libav hardware context
+ struct vaapi_context *hw_context = g_malloc0(sizeof(struct vaapi_context));
+ ctx->hwaccel_context = hw_context;
+
+ hw_context->display = va_display;
+ hw_context->config_id = config_id;
+ hw_context->context_id = context_id;
+
+ return 0;
+
+error:
+ // TODO: destroy sufaces
+ return -1;
+}
+
+static bool setup(AVCodecContext *ctx, int width, int height) {
+ if (!va_display) {
+ Display *display = XOpenDisplay(NULL);
+ if(!display)
+ {
+ printf("Could not connect to x server\n");
+ }
+
+ va_display = vaGetDisplay(display);
+ if(!va_display)
+ {
+ printf("Could not get a VAAPI device\n");
+ }
+
+ int major, minor;
+ if(vaInitialize(va_display, &major, &minor))
+ {
+ printf("Failed to initialize the VAAPI device\n");
+ }
+ }
+
+ if(create_surfaces(ctx, width, height)) {
+ printf("Failed to initialize the VAAPI device\n");
+ }
+
+ return true;
+}
+
+static void copy_plane(uint8_t *dst,
+ const uint8_t *src, size_t src_pitch,
+ unsigned width, unsigned height)
+{
+ unsigned int y;
+#ifndef TESTING1
+ for (y = 0; y < height; y++) {
+ memcpy(dst, src, width);
+ src += src_pitch;
+ dst += src_pitch;
+ }
+#else
+ memcpy(dst, src, width * height);
+#endif
+}
+
+static void copy_yv12(uint8_t *dst[3],
+ uint8_t *src[3], size_t src_pitch[3],
+ unsigned width, unsigned height)
+{
+ copy_plane(dst[0],
+ src[0], src_pitch[0], width, height);
+ copy_plane(dst[1],
+ src[2], src_pitch[2], width / 2, height / 2);
+ copy_plane(dst[2],
+ src[1], src_pitch[1], width / 2, height / 2);
+}
+
+static int extract(AVFrame *src, void* dst, size_t size)
+{
+ VASurfaceID surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+
+#if VA_CHECK_VERSION(0,31,0)
+ if (vaSyncSurface(va_display, surface_id))
+#else
+#error
+#endif
+ {
+ return -1;
+ }
+
+ if (va_ctx->is_supports_derive) {
+ if (vaDeriveImage(va_display, surface_id, &va_ctx->image) != VA_STATUS_SUCCESS) {
+ return -1;
+ }
+ } else {
+ if (vaGetImage(va_display, surface_id,
+ 0, 0, src->width, src->height,
+ va_ctx->image.image_id)) {
+ return -1;
+ }
+ }
+
+ void *p_base;
+ if (vaMapBuffer(va_display, va_ctx->image.buf, &p_base)) {
+ return -1;
+ }
+
+ const uint32_t fourcc = va_ctx->image.format.fourcc;
+ if (fourcc == VA_FOURCC_YV12) {
+ uint8_t *plane[3];
+ size_t pitch[3];
+ int i;
+
+#ifndef TESTING2
+ for (i = 0; i < 3; ++i) {
+ plane[i] = (uint8_t*)p_base + va_ctx->image.offsets[i];
+ pitch[i] = va_ctx->image.pitches[i];
+ }
+ uint8_t *data[4];
+ av_image_fill_pointers(data, PIX_FMT_YUV420P, src->height, dst, (const int *)pitch);
+ copy_yv12(data, plane, pitch, src->width, src->height);
+#else
+ // for performance testing... U, V plane order is reversed
+ memcpy(dst, p_base + va_ctx->image.offsets[0], size);
+#endif
+ } else {
+ return -1;
+ }
+
+ if (vaUnmapBuffer(va_display, va_ctx->image.buf)) {
+ return -1;
+ }
+
+ if (va_ctx->is_supports_derive)
+ {
+ vaDestroyImage(va_display, va_ctx->image.image_id);
+ va_ctx->image.image_id = VA_INVALID_ID;
+ }
+
+ return 0;
+}
+
+static int get_surface(AVCodecContext *p_context,
+ AVFrame *frame)
+{
+ int i;
+ for (i = 0; i < SURFACE_COUNT; ++i) {
+ if (va_ctx->surface_is_occupied[i]) continue;
+
+ break;
+ }
+
+ frame->data[0] = (void *)(uintptr_t)va_ctx->surface_id[i];
+ frame->data[3] = frame->data[0];
+ frame->type = FF_BUFFER_TYPE_USER;
+ frame->opaque = (void *)&(va_ctx->surface_is_occupied[i]);
+ // is_occupied = true;
+ *((bool *)frame->opaque) = true;
+
+ return 0;
+}
+
+static void release_surface(AVCodecContext *p_context,
+ AVFrame *frame)
+{
+ int i;
+ for (i = 0; i < 4; ++i) {
+ frame->data[i] = NULL;
+ }
+
+ // is_occupied = false;
+ *((bool *)frame->opaque) = false;
+}
+
+
+static void vaapi_extract(void *dst, void *src, size_t size) {
+ extract((AVFrame *)src, dst, size);
+}
+
+static void release(void *buf) {}
+
+static DataHandler vaapi_video_decode_data_handler = {
+ .get_data = vaapi_extract,
+ .release = release,
+};
+
+CodecPlugin vaapi_plugin = {
+ .pix_fmt = PIX_FMT_VAAPI_VLD,
+ .setup = setup,
+ .get_buffer = get_surface,
+ .release_buffer = release_surface,
+ .video_decode_data_handler = &vaapi_video_decode_data_handler,
+};