From c8a4d05dd3c79c5ab9b8d18b90a18f1f7e9d111c Mon Sep 17 00:00:00 2001 From: SeokYeon Hwang Date: Sun, 3 Aug 2014 19:24:10 +0900 Subject: [PATCH] vaapi: prototype Prototype implementation of vaapi plugin. Change-Id: I0815734f5232413ea650fec17c04c6d1179a3c0e Signed-off-by: SeokYeon Hwang --- tizen/src/Makefile | 15 +- tizen/src/hw/pci/Makefile.objs | 1 + tizen/src/hw/pci/maru_brillcodec.c | 12 +- tizen/src/hw/pci/maru_brillcodec.h | 20 +- tizen/src/hw/pci/maru_brillcodec_device.c | 27 ++- tizen/src/hw/pci/maru_brillcodec_plugin.h | 49 +++++ tizen/src/hw/pci/maru_brillcodec_vaapi.c | 318 ++++++++++++++++++++++++++++++ 7 files changed, 415 insertions(+), 27 deletions(-) create mode 100644 tizen/src/hw/pci/maru_brillcodec_plugin.h create mode 100644 tizen/src/hw/pci/maru_brillcodec_vaapi.c diff --git a/tizen/src/Makefile b/tizen/src/Makefile index f7e01f1..d77fd4d 100644 --- a/tizen/src/Makefile +++ b/tizen/src/Makefile @@ -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 diff --git a/tizen/src/hw/pci/Makefile.objs b/tizen/src/hw/pci/Makefile.objs index 6a86b9a..c33630e 100644 --- a/tizen/src/hw/pci/Makefile.objs +++ b/tizen/src/hw/pci/Makefile.objs @@ -1,4 +1,5 @@ obj-y += maru_brillcodec_device.o +LIBS += -ldl obj-y += maru_brillcodec.o obj-y += maru_brightness.o diff --git a/tizen/src/hw/pci/maru_brillcodec.c b/tizen/src/hw/pci/maru_brillcodec.c index 2fb6efe..9f2035a 100644 --- a/tizen/src/hw/pci/maru_brillcodec.c +++ b/tizen/src/hw/pci/maru_brillcodec.c @@ -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) diff --git a/tizen/src/hw/pci/maru_brillcodec.h b/tizen/src/hw/pci/maru_brillcodec.h index 3e0ab9d..df035a2 100644 --- a/tizen/src/hw/pci/maru_brillcodec.h +++ b/tizen/src/hw/pci/maru_brillcodec.h @@ -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__ diff --git a/tizen/src/hw/pci/maru_brillcodec_device.c b/tizen/src/hw/pci/maru_brillcodec_device.c index 2a05e5f..7ea5596 100644 --- a/tizen/src/hw/pci/maru_brillcodec_device.c +++ b/tizen/src/hw/pci/maru_brillcodec_device.c @@ -28,7 +28,10 @@ * */ +#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 index 0000000..273efea --- /dev/null +++ b/tizen/src/hw/pci/maru_brillcodec_plugin.h @@ -0,0 +1,49 @@ +/* + * Virtual Codec device + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: + * SeokYeon Hwang + * + * 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 index 0000000..10a761a --- /dev/null +++ b/tizen/src/hw/pci/maru_brillcodec_vaapi.c @@ -0,0 +1,318 @@ +/* + * VA-API plugin for virtual codec device + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: + * SeokYeon Hwang + * + * 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 +#include +#include +#include + +#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, +}; -- 2.7.4