vaapi: prototype 23/25323/8
authorSeokYeon Hwang <syeon.hwang@samsung.com>
Sun, 3 Aug 2014 10:24:10 +0000 (19:24 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Fri, 22 Aug 2014 06:13:25 +0000 (15:13 +0900)
Prototype implementation of vaapi plugin.

Change-Id: I0815734f5232413ea650fec17c04c6d1179a3c0e
Signed-off-by: SeokYeon Hwang <syeon.hwang@samsung.com>
tizen/src/Makefile
tizen/src/hw/pci/Makefile.objs
tizen/src/hw/pci/maru_brillcodec.c
tizen/src/hw/pci/maru_brillcodec.h
tizen/src/hw/pci/maru_brillcodec_device.c
tizen/src/hw/pci/maru_brillcodec_plugin.h [new file with mode: 0644]
tizen/src/hw/pci/maru_brillcodec_vaapi.c [new file with mode: 0644]

index f7e01f1..d77fd4d 100644 (file)
@@ -19,14 +19,25 @@ ifndef CONFIG_LINUX
 TARGET_EXE += util/check-hax$(EXESUF)
 endif
 
-all: qemu skin_client $(TARGET_EXE)
-qemu: $(TARGET_EXE) build_info
+ifdef CONFIG_LINUX
+TARGET_DSO += hw/pci/brillcodec-vaapi$(DSOSUF)
+endif
+
+all: qemu skin_client $(TARGET_EXE) $(TARGET_DSO)
+qemu: $(TARGET_EXE) $(TARGET_DSO) build_info
        cd ../../ && $(MAKE)
 qemu_clean:
        cd ../../ && $(MAKE) clean
 qemu_distclean:
        cd ../../ && $(MAKE) distclean
 
+# Building brillcodec-vaapi
+BRILLCODEC_VAAPI_CFLAGS = -c $(DSO_CFLAGS) $(LIBAV_CFLAGS) $(GLIB_CFLAGS) -I$(SRC_PATH) -I$(SRC_PATH)/include -I. -fPIC
+hw/pci/brillcodec-vaapi$(DSOSUF): hw/pci/maru_brillcodec_vaapi.o
+       gcc $< $(LDFLAGS_SHARED) -o $@ -lva -lva-x11
+hw/pci/maru_brillcodec_vaapi.o: %.o: %.c
+       gcc $< $(BRILLCODEC_VAAPI_CFLAGS) -o $@
+
 # Building check-gl
 CHECK_GL_OBJS = util/check_gl.o util/check_gl_core.o
 CHECK_GL_CFLAGS = -c -I$(SRC_PATH)/hw/yagl/yagl_inc
index 6a86b9a..c33630e 100644 (file)
@@ -1,4 +1,5 @@
 obj-y += maru_brillcodec_device.o
+LIBS += -ldl
 obj-y += maru_brillcodec.o
 
 obj-y += maru_brightness.o
index 2fb6efe..9f2035a 100644 (file)
@@ -700,7 +700,7 @@ static enum PixelFormat get_format(AVCodecContext *avctx,
     CodecContext *context = (CodecContext *)avctx->opaque;
     MaruBrillCodecState *s = context->state;
 
-    if (s->hwaccel_plugin) {
+    if (!s->hwaccel_plugin) {
         goto end;
     }
 
@@ -720,8 +720,7 @@ static enum PixelFormat get_format(AVCodecContext *avctx,
         goto end;
     }
 
-
-    if (s->hwaccel_plugin->setup(avctx, avctx->width, avctx->height)) {
+    if (!s->hwaccel_plugin->setup(avctx, avctx->width, avctx->height)) {
         goto end;
     }
 
@@ -735,10 +734,12 @@ static enum PixelFormat get_format(AVCodecContext *avctx,
         goto end;
     }
 
+    INFO("HW_ACCEL is enabled with pix_fmt [%s]\n", av_get_pix_fmt_name(pi_fmt[i]));
     context->is_hwaccel = true;
     return pi_fmt[i];
 
 end:
+    INFO("HW_ACCEL is disabled\n");
     context->is_hwaccel = false;
     return avcodec_default_get_format(avctx, pi_fmt);
 }
@@ -770,8 +771,9 @@ static AVCodecContext *maru_brill_codec_alloc_context(MaruBrillCodecState *s, in
 
     TRACE("allocate %d of context and frame.\n", ctx_id);
 
+    CONTEXT(s, ctx_id)->avctx = avcodec_alloc_context3(NULL);
+
     AVCodecContext *avctx = CONTEXT(s, ctx_id)->avctx;
-    avctx = avcodec_alloc_context3(NULL);
     avctx->get_format = get_format;
     avctx->get_buffer = get_buffer;
     avctx->reget_buffer = avcodec_default_reget_buffer;
@@ -784,7 +786,7 @@ static AVCodecContext *maru_brill_codec_alloc_context(MaruBrillCodecState *s, in
 
     TRACE("leave: %s\n", __func__);
 
-    return CONTEXT(s, ctx_id)->avctx;
+    return avctx;
 }
 
 static AVCodec *maru_brill_codec_find_avcodec(uint8_t *mem_buf)
index 3e0ab9d..df035a2 100644 (file)
@@ -35,6 +35,8 @@
 
 #include "libavcodec/avcodec.h"
 
+#include "maru_brillcodec_plugin.h"
+
 #define CODEC_CONTEXT_MAX           1024
 #define CODEC_MEM_SIZE          (32 * 1024 * 1024)
 
@@ -74,19 +76,6 @@ typedef struct CodecThreadPool {
     QemuCond            cond;
 } CodecThreadPool;
 
-typedef struct DataHandler {
-    void (*get_data)(void *dst, void *src, size_t size);
-    void (*release)(void *opaque);
-} DataHandler;
-
-typedef struct CodecPlugin {
-    enum PixelFormat    pix_fmt;
-    bool                (*setup)(AVCodecContext *, int , int);
-    int                 (*get_buffer)(struct AVCodecContext *, AVFrame *);
-    void                (*release_buffer)(struct AVCodecContext *, AVFrame *);
-    DataHandler         *video_decode_data_handler;
-} CodecPlugin;
-
 struct MaruBrillCodecState {
     PCIDevice           dev;
 
@@ -135,9 +124,4 @@ void maru_brill_codec_pop_writequeue(MaruBrillCodecState *s, uint32_t ctx_idx);
 
 void *maru_brill_codec_threads(void *opaque);
 
-// plugins
-#ifdef CONFIG_VAAPI
-CodecPlugin *vaapi_probe(void);
-#endif
-
 #endif // __MARU_BRILLCODEC_H__
index 2a05e5f..7ea5596 100644 (file)
  *
  */
 
+#include "dlfcn.h"
+
 #include "qemu/main-loop.h"
+#include "hw/pci/pci.h"
 
 #include "hw/maru_device_ids.h"
 #include "util/osutil.h"
@@ -241,6 +244,26 @@ static const MemoryRegionOps maru_brill_codec_mmio_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+//FIXME
+#define CONFIG_VAAPI
+
+#ifdef CONFIG_VAAPI
+static CodecPlugin *vaapi_probe(void) {
+    // FIXME: probing...
+    void *handle;
+
+    handle = dlopen("brillcodec-vaapi.so", RTLD_NOW);
+    if (!handle) {
+        INFO("plugin load failed : %s\n", dlerror());
+        return NULL;
+    }
+
+    CodecPlugin *plugin = dlsym(handle, "vaapi_plugin");
+
+    return plugin;
+}
+#endif
+
 static int maru_brill_codec_initfn(PCIDevice *dev)
 {
     MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
@@ -270,8 +293,8 @@ static int maru_brill_codec_initfn(PCIDevice *dev)
 
     // register plugins
 #ifdef CONFIG_VAAPI
-    if (!(s->hwaccel_plugin = vaapi_probe())) {
-        INFO("VA-API extension is enabled.");
+    if ((s->hwaccel_plugin = vaapi_probe())) {
+        INFO("VA-API extension is enabled.\n");
     }
 #endif
 
diff --git a/tizen/src/hw/pci/maru_brillcodec_plugin.h b/tizen/src/hw/pci/maru_brillcodec_plugin.h
new file mode 100644 (file)
index 0000000..273efea
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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
+ *
+ */
+
+#ifndef __MARU_BRILLCODEC_PLUGIN_H__
+#define __MARU_BRILLCODEC_PLUGIN_H__
+
+#include "libavcodec/avcodec.h"
+
+#include "stdbool.h"
+
+typedef struct DataHandler {
+    void (*get_data)(void *dst, void *src, size_t size);
+    void (*release)(void *opaque);
+} DataHandler;
+
+typedef struct CodecPlugin {
+    enum PixelFormat    pix_fmt;
+    bool                (*setup)(AVCodecContext *, int , int);
+    int                 (*get_buffer)(struct AVCodecContext *, AVFrame *);
+    void                (*release_buffer)(struct AVCodecContext *, AVFrame *);
+    DataHandler         *video_decode_data_handler;
+} CodecPlugin;
+
+#endif //__MARU_BRILLCODEC_PLUGIN_H__
diff --git a/tizen/src/hw/pci/maru_brillcodec_vaapi.c b/tizen/src/hw/pci/maru_brillcodec_vaapi.c
new file mode 100644 (file)
index 0000000..10a761a
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * 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,
+};