From: gunsoo83.kim Date: Wed, 20 Aug 2014 07:11:01 +0000 (+0900) Subject: DXVA: Prototype X-Git-Tag: Tizen_Studio_1.3_Release_p2.3.2~620^2~31 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6583504fa9b9b40fa8c6841c40677575367a325d;p=sdk%2Femulator%2Fqemu.git DXVA: Prototype Prototype implementation of DXVA plugin. Change-Id: I994740402456f4b30125546ef6e257fae664d0e6 Signed-off-by: gunsoo83.kim --- diff --git a/tizen/src/hw/pci/Makefile.objs b/tizen/src/hw/pci/Makefile.objs index c33630e360..46a584ff1d 100644 --- a/tizen/src/hw/pci/Makefile.objs +++ b/tizen/src/hw/pci/Makefile.objs @@ -10,6 +10,7 @@ obj-y += maru_camera_linux_pci.o LIBS += -lv4l2 -lv4lconvert endif ifdef CONFIG_WIN32 +obj-y += maru_dxva2_plugin.o obj-y += maru_camera_win32_pci.o LIBS += -lole32 -loleaut32 -luuid -lstrmiids endif @@ -23,3 +24,4 @@ endif maru_brillcodec.o-cflags := $(LIBAV_CFLAGS) maru_brillcodec_device.o-cflags := $(LIBAV_CFLAGS) +maru_dxva2_plugin.o-cflags := $(LIBAV_CFLAGS) diff --git a/tizen/src/hw/pci/maru_brillcodec_device.c b/tizen/src/hw/pci/maru_brillcodec_device.c index 3b0040e790..21ee8811c7 100644 --- a/tizen/src/hw/pci/maru_brillcodec_device.c +++ b/tizen/src/hw/pci/maru_brillcodec_device.c @@ -28,7 +28,13 @@ * */ +//FIXME +//#define CONFIG_VAAPI +//#define CONFIG_DXVA2 + +#ifdef CONFIG_VAAPI #include "dlfcn.h" +#endif #include "qemu/main-loop.h" #include "hw/pci/pci.h" @@ -47,6 +53,10 @@ MULTI_DEBUG_CHANNEL(qemu, brillcodec); #define CODEC_REG_SIZE (256) #define DEFAULT_WORKER_THREAD_CNT 8 +#ifdef CONFIG_DXVA2 +extern CodecPlugin dxva_plugin; +#endif + enum codec_io_cmd { CODEC_CMD_API_INDEX = 0x28, CODEC_CMD_CONTEXT_INDEX = 0x2C, @@ -244,9 +254,6 @@ static const MemoryRegionOps brillcodec_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -//FIXME -#define CONFIG_VAAPI - #ifdef CONFIG_VAAPI static CodecPlugin *vaapi_probe(void) { // FIXME: probing... @@ -263,6 +270,16 @@ static CodecPlugin *vaapi_probe(void) { return plugin; } #endif +#ifdef CONFIG_DXVA2 +static CodecPlugin *dxva2_probe(void) { + CodecPlugin *plugin = NULL; + + plugin = &dxva_plugin; + + return plugin; +} +#endif + static int brillcodec_initfn(PCIDevice *dev) { @@ -297,6 +314,11 @@ static int brillcodec_initfn(PCIDevice *dev) INFO("VA-API extension is enabled.\n"); } #endif +#ifdef CONFIG_DXVA2 + if ((s->hwaccel_plugin = dxva2_probe())) { + INFO("DXVA2 extension is enabled.\n"); + } +#endif return 0; } diff --git a/tizen/src/hw/pci/maru_dxva2_plugin.c b/tizen/src/hw/pci/maru_dxva2_plugin.c new file mode 100644 index 0000000000..4cc938adf6 --- /dev/null +++ b/tizen/src/hw/pci/maru_dxva2_plugin.c @@ -0,0 +1,1009 @@ +/* + * DXVA2 Module for Decoder Device + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: + * GunSoo Kim + * SangHo Park + * + * 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 _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#else +# if _WIN32_WINNT < 0x600 +/* dxva2 needs Vista support */ +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x600 +# endif +#endif + +#define DXVA2API_USE_BITFIELDS +#define COBJMACROS + +#ifndef INITGUID +#define INITGUID +#endif + +#include "glib.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/dxva2.h" +#include "libavutil/pixdesc.h" +#include "libavutil/imgutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* must be last included to not redefine existing GUIDs */ + +/* dxva2api.h GUIDs: http://msdn.microsoft.com/en-us/library/windows/desktop/ms697067(v=vs100).aspx + * assume that they are declared in dxva2api.h */ +#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) + +#ifdef __MINGW32__ +# include <_mingw.h> + +# if !defined(__MINGW64_VERSION_MAJOR) +# undef MS_GUID +# define MS_GUID DEFINE_GUID /* dxva2api.h fails to declare those, redefine as static */ +# define DXVA2_E_NEW_VIDEO_DEVICE MAKE_HRESULT(1, 4, 4097) +# else +# include +# endif + +#endif /* __MINGW32__ */ + +#include "maru_brillcodec_plugin.h" +#include "debug_ch.h" + +/* define debug channel */ +MULTI_DEBUG_CHANNEL(qemu, dxva2_plugin); + + +MS_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); +MS_GUID(IID_IDirectXVideoAccelerationService, 0xfc51a550, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); + +MS_GUID (DXVA_NoEncrypt, 0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); + +/* Codec capabilities GUID, sorted by codec */ +MS_GUID (DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b, 0x61b0, 0x4563, 0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66); +MS_GUID (DXVA2_ModeMPEG2_IDCT, 0xbf22ad00, 0x03ea, 0x4690, 0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e); +MS_GUID (DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9); +DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60); +DEFINE_GUID(DXVA2_ModeMPEG1_VLD, 0x6f3ec719, 0x3735, 0x42cc, 0x80, 0x63, 0x65, 0xcc, 0x3c, 0xb3, 0x66, 0x16); + +MS_GUID (DXVA2_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeH264_B, 0x1b81be65, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeH264_C, 0x1b81be66, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeH264_D, 0x1b81be67, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +DEFINE_GUID(DXVA_ModeH264_VLD_Multiview, 0x9901CCD3, 0xca12, 0x4b7e, 0x86, 0x7a, 0xe2, 0x22, 0x3d, 0x92, 0x55, 0xc3); // MVC +DEFINE_GUID(DXVA_ModeH264_VLD_WithFMOASO_NoFGT, 0xd5f04ff9, 0x3418, 0x45d8, 0x95, 0x61, 0x32, 0xa7, 0x6a, 0xae, 0x2d, 0xdd); +DEFINE_GUID(DXVADDI_Intel_ModeH264_A, 0x604F8E64, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); +DEFINE_GUID(DXVADDI_Intel_ModeH264_C, 0x604F8E66, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); +DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6); // DXVA_Intel_H264_NoFGT_ClearVideo +DEFINE_GUID(DXVA_ModeH264_VLD_NoFGT_Flash, 0x4245F676, 0x2BBC, 0x4166, 0xa0, 0xBB, 0x54, 0xE7, 0xB8, 0x49, 0xC3, 0x80); + +MS_GUID (DXVA2_ModeWMV8_A, 0x1b81be80, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeWMV8_B, 0x1b81be81, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); + +MS_GUID (DXVA2_ModeWMV9_A, 0x1b81be90, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeWMV9_B, 0x1b81be91, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeWMV9_C, 0x1b81be94, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); + +MS_GUID (DXVA2_ModeVC1_A, 0x1b81beA0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeVC1_B, 0x1b81beA1, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeVC1_C, 0x1b81beA2, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +MS_GUID (DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); +DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5); // August 2010 update +DEFINE_GUID(DXVA_Intel_VC1_ClearVideo, 0xBCC5DB6D, 0xA2B6, 0x4AF0, 0xAC, 0xE4, 0xAD, 0xB1, 0xF7, 0x87, 0xBC, 0x89); +DEFINE_GUID(DXVA_Intel_VC1_ClearVideo_2, 0xE07EC519, 0xE651, 0x4CD6, 0xAC, 0x84, 0x13, 0x70, 0xCC, 0xEE, 0xC8, 0x51); + +DEFINE_GUID(DXVA_nVidia_MPEG4_ASP, 0x9947EC6F, 0x689B, 0x11DC, 0xA3, 0x20, 0x00, 0x19, 0xDB, 0xBC, 0x41, 0x84); +DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_Simple, 0xefd64d74, 0xc9e8, 0x41d7, 0xa5, 0xe9, 0xe9, 0xb0, 0xe3, 0x9f, 0xa3, 0x19); +DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0xed418a9f, 0x010d, 0x4eda, 0x9a, 0xe3, 0x9a, 0x65, 0x35, 0x8d, 0x8d, 0x2e); +DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0xab998b5b, 0x4258, 0x44a9, 0x9f, 0xeb, 0x94, 0xe5, 0x97, 0xa6, 0xba, 0xae); +DEFINE_GUID(DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0x7C74ADC6, 0xe2ba, 0x4ade, 0x86, 0xde, 0x30, 0xbe, 0xab, 0xb4, 0x0c, 0xc1); + +/* */ +typedef struct { + const char *name; + const GUID *guid; + int codec; +} dxva2_mode_t; +/* XXX Prefered modes must come first */ +static const dxva2_mode_t dxva2_modes[] = { + /* MPEG-1/2 */ + { "MPEG-2 variable-length decoder", &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, + { "MPEG-2 & MPEG-1 variable-length decoder", &DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO }, + { "MPEG-2 motion compensation", &DXVA2_ModeMPEG2_MoComp, 0 }, + { "MPEG-2 inverse discrete cosine transform", &DXVA2_ModeMPEG2_IDCT, 0 }, + + { "MPEG-1 variable-length decoder", &DXVA2_ModeMPEG1_VLD, 0 }, + + /* H.264 */ + { "H.264 variable-length decoder, film grain technology", &DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, + { "H.264 variable-length decoder, no film grain technology (Intel ClearVideo)", &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, + { "H.264 variable-length decoder, no film grain technology", &DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, + { "H.264 variable-length decoder, no film grain technology, FMO/ASO", &DXVA_ModeH264_VLD_WithFMOASO_NoFGT, AV_CODEC_ID_H264 }, + { "H.264 variable-length decoder, no film grain technology, Flash", &DXVA_ModeH264_VLD_NoFGT_Flash, AV_CODEC_ID_H264 }, + + { "H.264 inverse discrete cosine transform, film grain technology", &DXVA2_ModeH264_D, 0 }, + { "H.264 inverse discrete cosine transform, no film grain technology", &DXVA2_ModeH264_C, 0 }, + { "H.264 inverse discrete cosine transform, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_C, 0 }, + + { "H.264 motion compensation, film grain technology", &DXVA2_ModeH264_B, 0 }, + { "H.264 motion compensation, no film grain technology", &DXVA2_ModeH264_A, 0 }, + { "H.264 motion compensation, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_A, 0 }, + + /* WMV */ + { "Windows Media Video 8 motion compensation", &DXVA2_ModeWMV8_B, 0 }, + { "Windows Media Video 8 post processing", &DXVA2_ModeWMV8_A, 0 }, + + { "Windows Media Video 9 IDCT", &DXVA2_ModeWMV9_C, 0 }, + { "Windows Media Video 9 motion compensation", &DXVA2_ModeWMV9_B, 0 }, + { "Windows Media Video 9 post processing", &DXVA2_ModeWMV9_A, 0 }, + + /* VC-1 */ + { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, + { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, + { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 }, + { "VC-1 variable-length decoder", &DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 }, + { "VC-1 variable-length decoder 2 (Intel)", &DXVA_Intel_VC1_ClearVideo_2, 0 }, + { "VC-1 variable-length decoder (Intel)", &DXVA_Intel_VC1_ClearVideo, 0 }, + + { "VC-1 inverse discrete cosine transform", &DXVA2_ModeVC1_C, 0 }, + { "VC-1 motion compensation", &DXVA2_ModeVC1_B, 0 }, + { "VC-1 post processing", &DXVA2_ModeVC1_A, 0 }, + + /* Xvid/Divx: TODO */ + { "MPEG-4 Part 2 nVidia bitstream decoder", &DXVA_nVidia_MPEG4_ASP, 0 }, + { "MPEG-4 Part 2 variable-length decoder, Simple Profile", &DXVA_ModeMPEG4pt2_VLD_Simple, 0 }, + { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, no GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC, 0 }, + { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, GMC", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC, 0 }, + { "MPEG-4 Part 2 variable-length decoder, Simple&Advanced Profile, Avivo", &DXVA_ModeMPEG4pt2_VLD_AdvSimple_Avivo, 0 }, + + { NULL, NULL, 0 } +}; + +/* */ +typedef struct { + const char *name; + D3DFORMAT format; +} d3d_format_t; +/* XXX Prefered format must come first */ +static const d3d_format_t d3d_formats[] = { + { "YV12", MAKEFOURCC('Y','V','1','2')}, + { "NV12", MAKEFOURCC('N','V','1','2')}, + { "IMC3", MAKEFOURCC('I','M','C','3')}, + { NULL, 0 } +}; + +/* */ +typedef struct { + LPDIRECT3DSURFACE9 d3d; + bool is_occupied; +} DXVAPluginSurface; + +#define VA_DXVA2_MAX_SURFACE_COUNT (64) +typedef struct DXVAPluginContext +{ + /* Direct3D */ + D3DPRESENT_PARAMETERS d3dpp; + LPDIRECT3D9 d3dobj; + LPDIRECT3DDEVICE9 d3ddev; + + /* Device manager */ + IDirect3DDeviceManager9 *devmng; + HANDLE hd3ddev; + + /* Video service */ + IDirectXVideoDecoderService *vs; + GUID guid_decdev; + D3DFORMAT render_fmt; + + /* Video decoder */ + DXVA2_ConfigPictureDecode cfg; + IDirectXVideoDecoder *decoder; + + /* Option conversion */ + D3DFORMAT output; + + /* */ + unsigned surface_count; + int surface_width; + int surface_height; + + int thread_count; + + DXVAPluginSurface surface[VA_DXVA2_MAX_SURFACE_COUNT]; + LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT]; + + struct dxva_context *hw_context; +} DXVAPluginContext; + +// FIXME : modify dxva_ctx to multi instance for multi-thread decoding. +static DXVAPluginContext *dxva_ctx = &(DXVAPluginContext) {}; + +/* DLL */ +static HINSTANCE hd3d9_dll; +static HINSTANCE hdxva2_dll; + + +static const dxva2_mode_t *Dxva2FindMode(const GUID *guid) +{ + unsigned i = 0; + + for (i = 0; dxva2_modes[i].name; i++) { + if (IsEqualGUID(dxva2_modes[i].guid, guid)) + return &dxva2_modes[i]; + } + return NULL; +} + +static const d3d_format_t *D3dFindFormat(D3DFORMAT format) +{ + unsigned i = 0; + + for (i = 0; d3d_formats[i].name; i++) { + if (d3d_formats[i].format == format) + return &d3d_formats[i]; + } + return NULL; +} + +/** + * It creates a Direct3D device usable for DXVA 2 + */ +static int D3dCreateDevice(void) +{ + /* */ + LPDIRECT3D9 (WINAPI *Create9)(UINT SDKVersion); + Create9 = (void *)GetProcAddress(hd3d9_dll, "Direct3DCreate9"); + if (!Create9) { + ERR("Cannot locate reference to Direct3DCreate9 ABI in DLL\n"); + return -1; + } + + /* */ + LPDIRECT3D9 d3dobj; + d3dobj = Create9(D3D_SDK_VERSION); + if (!d3dobj) { + ERR("Direct3DCreate9 failed\n"); + return -1; + } + dxva_ctx->d3dobj = d3dobj; + + /* */ + D3DPRESENT_PARAMETERS *d3dpp = &dxva_ctx->d3dpp; + ZeroMemory(d3dpp, sizeof(*d3dpp)); + d3dpp->Flags = D3DPRESENTFLAG_VIDEO; + d3dpp->Windowed = TRUE; + d3dpp->hDeviceWindow = NULL; + d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + d3dpp->BackBufferCount = 0; /* FIXME what to put here */ + d3dpp->BackBufferFormat = D3DFMT_X8R8G8B8; /* FIXME what to put here */ + d3dpp->BackBufferWidth = 0; + d3dpp->BackBufferHeight = 0; + d3dpp->EnableAutoDepthStencil = FALSE; + + /* Direct3D needs a HWND to create a device, even without using ::Present + this HWND is used to alert Direct3D when there's a change of focus window. + For now, use GetDesktopWindow, as it looks harmless */ + LPDIRECT3DDEVICE9 d3ddev; + if (FAILED(IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, GetDesktopWindow(), + D3DCREATE_SOFTWARE_VERTEXPROCESSING | + D3DCREATE_MULTITHREADED, + d3dpp, &d3ddev))) { + ERR("IDirect3D9_CreateDevice failed\n"); + return -1; + } + dxva_ctx->d3ddev = d3ddev; + + return 0; +} + +/** + * It creates a Direct3D device manager + */ +static int D3dCreateDeviceManager(void) +{ + HRESULT (WINAPI *CreateDeviceManager9)(UINT *pResetToken, + IDirect3DDeviceManager9 **); + CreateDeviceManager9 = + (void *)GetProcAddress(hdxva2_dll, + "DXVA2CreateDirect3DDeviceManager9"); + + if (!CreateDeviceManager9) { + ERR("cannot load function\n"); + return -1; + } + + UINT token; + IDirect3DDeviceManager9 *devmng; + if (FAILED(CreateDeviceManager9(&token, &devmng))) { + ERR("OurDirect3DCreateDeviceManager9 failed\n"); + return -1; + } + TRACE("OurDirect3DCreateDeviceManager9 Success!\n"); + + dxva_ctx->devmng = devmng; + TRACE("obtained IDirect3DDeviceManager9\n"); + + HRESULT hr = IDirect3DDeviceManager9_ResetDevice(devmng, dxva_ctx->d3ddev, token); + if (FAILED(hr)) { + ERR("IDirect3DDeviceManager9_ResetDevice failed: %08x\n", (unsigned)hr); + return -1; + } + + return 0; +} + +/** + * It creates a DirectX video service + */ +static int DxCreateVideoService(void) +{ + HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *, + REFIID riid, + void **ppService); + CreateVideoService = + (void *)GetProcAddress(hdxva2_dll, "DXVA2CreateVideoService"); + + if (!CreateVideoService) { + ERR("cannot load function\n"); + return -1; + } + TRACE("DXVA2CreateVideoService Success!\n"); + + HRESULT hr; + + HANDLE hd3ddev; + hr = IDirect3DDeviceManager9_OpenDeviceHandle(dxva_ctx->devmng, &hd3ddev); + if (FAILED(hr)) { + ERR("OpenDeviceHandle failed\n"); + return -1; + } + dxva_ctx->hd3ddev = hd3ddev; + + void *pv; + hr = IDirect3DDeviceManager9_GetVideoService(dxva_ctx->devmng, hd3ddev, + &IID_IDirectXVideoDecoderService, &pv); + if (FAILED(hr)) { + ERR("GetVideoService failed\n"); + return -1; + } + dxva_ctx->vs = pv; + + return 0; +} + +/** + * Find the best suited decoder mode GUID and render format. + */ +static int DxFindVideoServiceConversion(int codec_id) +{ + unsigned i = 0; + GUID *guid_decdev = &dxva_ctx->guid_decdev; + D3DFORMAT *render_fmt = &dxva_ctx->render_fmt; + + /* Retreive supported modes from the decoder service */ + UINT decdev_count = 0; + GUID *guid_decdev_list = NULL; + if (FAILED(IDirectXVideoDecoderService_GetDecoderDeviceGuids(dxva_ctx->vs, + &decdev_count, + &guid_decdev_list))) { + ERR("IDirectXVideoDecoderService_GetDecoderDeviceGuids failed\n"); + return -1; + } + + INFO("IDirectXVideoDecoderService_GetDecoderDeviceGuids success. count=%d\n", decdev_count); + + for (i = 0; i < decdev_count; i++) { + const GUID *g = &guid_decdev_list[i]; + const dxva2_mode_t *mode = Dxva2FindMode(g); + if (mode) { + INFO("- '%s' is supported by hardware\n", mode->name); + } else { + WARN("- Unknown GUID = %08X-%04x-%04x-XXXX\n", + (unsigned)g->Data1, g->Data2, g->Data3); + } + } + + /* Try all supported mode by our priority */ + for (i = 0; dxva2_modes[i].name; i++) { + const dxva2_mode_t *mode = &dxva2_modes[i]; + unsigned j = 0; + if (!mode->codec || mode->codec != codec_id) + continue; + + /* */ + bool is_supported = false; + const GUID *g = &guid_decdev_list[0]; + for (; !is_supported && g < &guid_decdev_list[decdev_count]; g++) { + is_supported = IsEqualGUID(mode->guid, g); + } + if (!is_supported) + continue; + + /* */ + INFO("Trying to use '%s' as input\n", mode->name); + UINT render_fmt_count = 0; + D3DFORMAT *render_fmt_list = NULL; + if (FAILED(IDirectXVideoDecoderService_GetDecoderRenderTargets(dxva_ctx->vs, mode->guid, + &render_fmt_count, + &render_fmt_list))) { + ERR("IDirectXVideoDecoderService_GetDecoderRenderTargets failed\n"); + continue; + } + for (j = 0; j < render_fmt_count; j++) { + const D3DFORMAT f = render_fmt_list[j]; + const d3d_format_t *format = D3dFindFormat(f); + + INFO("HOST supported format %d (%4.4s)\n", f, (const char*)&f); + if (format) { + INFO("%s is supported for output\n", format->name); + } else { + INFO("%d is supported for output (%4.4s)\n", f, (const char*)&f); + } + } + + /* */ + for (j = 0; d3d_formats[j].name; j++) { + const d3d_format_t *format = &d3d_formats[j]; + unsigned k = 0; + + /* */ + bool is_supported = false; + for (k = 0; !is_supported && k < render_fmt_count; k++) { + is_supported = format->format == render_fmt_list[k]; + } + if (!is_supported) + continue; + + /* We have our solution */ + INFO("Using '%s' to decode to '%s'\n", mode->name, format->name); + *guid_decdev = *mode->guid; + *render_fmt = format->format; + CoTaskMemFree(render_fmt_list); + CoTaskMemFree(guid_decdev_list); + return 0; + } + CoTaskMemFree(render_fmt_list); + } + CoTaskMemFree(guid_decdev_list); + return -1; +} + +static void DxDestroyVideoDecoder(void) +{ + unsigned i = 0; + + if (dxva_ctx->decoder) { + IDirectXVideoDecoder_Release(dxva_ctx->decoder); + } + dxva_ctx->decoder = NULL; + + for (i = 0; i < dxva_ctx->surface_count; i++) { + IDirect3DSurface9_Release(dxva_ctx->surface[i].d3d); + } + dxva_ctx->surface_count = 0; +} + +/** + * It creates a DXVA2 decoder using the given video format + */ +static int DxCreateVideoDecoder(AVCodecContext *dec_ctx) +{ + int surface_count = 0; + unsigned i = 0; + int width = 0; + int height = 0; + int codec_id = 0; + + width = dec_ctx->width; + height = dec_ctx->height; + codec_id = dec_ctx->codec_id; + + + TRACE("DxCreateVideoDecoder id %d %dx%d\n", + codec_id, width, height); + + /* Allocates all surfaces needed for the decoder */ + dxva_ctx->surface_width = (width + 15) & ~15; + dxva_ctx->surface_height = (height + 15) & ~15; + switch (codec_id) { + case AV_CODEC_ID_H264: + surface_count = 16 + dxva_ctx->thread_count + 2; + break; + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + surface_count = 2 + 2; + break; + default: + surface_count = 2 + 1; + break; + } + if (surface_count > VA_DXVA2_MAX_SURFACE_COUNT) + return -1; + dxva_ctx->surface_count = surface_count; + if (FAILED(IDirectXVideoDecoderService_CreateSurface(dxva_ctx->vs, + dxva_ctx->surface_width, + dxva_ctx->surface_height, + dxva_ctx->surface_count - 1, + dxva_ctx->render_fmt, + D3DPOOL_DEFAULT, + 0, + DXVA2_VideoDecoderRenderTarget, + dxva_ctx->hw_surface, + NULL))) { + ERR("IDirectXVideoAccelerationService_CreateSurface failed\n"); + dxva_ctx->surface_count = 0; + return -1; + } + for (i = 0; i < dxva_ctx->surface_count; i++) { + DXVAPluginSurface *surface = &dxva_ctx->surface[i]; + surface->d3d = dxva_ctx->hw_surface[i]; + surface->is_occupied = false; + } + TRACE("IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)\n", + dxva_ctx->surface_count, width, height); + + /* */ + DXVA2_VideoDesc dsc; + ZeroMemory(&dsc, sizeof(dsc)); + dsc.SampleWidth = width; + dsc.SampleHeight = height; + dsc.Format = dxva_ctx->render_fmt; + dsc.InputSampleFreq.Numerator = 0; + dsc.InputSampleFreq.Denominator = 0; + dsc.OutputFrameFreq = dsc.InputSampleFreq; + dsc.UABProtectionLevel = FALSE; + dsc.Reserved = 0; + + /* FIXME I am unsure we can let unknown everywhere */ + DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; + ext->SampleFormat = 0;//DXVA2_SampleUnknown; + ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown; + ext->NominalRange = 0;//DXVA2_NominalRange_Unknown; + ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown; + ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown; + ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown; + ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown; + + /* List all configurations available for the decoder */ + UINT cfg_count = 0; + DXVA2_ConfigPictureDecode *cfg_list = NULL; + if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations(dxva_ctx->vs, + &dxva_ctx->guid_decdev, + &dsc, + NULL, + &cfg_count, + &cfg_list))) { + ERR("IDirectXVideoDecoderService_GetDecoderConfigurations failed\n"); + return -1; + } + TRACE("we got %d decoder configurations\n", cfg_count); + + /* Select the best decoder configuration */ + int cfg_score = 0; + for (i = 0; i < cfg_count; i++) { + const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; + + /* */ + TRACE("configuration[%d] ConfigBitstreamRaw %d\n", + i, cfg->ConfigBitstreamRaw); + + /* */ + int score; + if (cfg->ConfigBitstreamRaw == 1) + score = 1; + else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) + score = 2; + else + continue; + if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt)) + score += 16; + + if (cfg_score < score) { + dxva_ctx->cfg = *cfg; + cfg_score = score; + } + } + CoTaskMemFree(cfg_list); + if (cfg_score <= 0) { + ERR("Failed to find a supported decoder configuration\n"); + return -1; + } + + /* Create the decoder */ + IDirectXVideoDecoder *decoder; + if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(dxva_ctx->vs, + &dxva_ctx->guid_decdev, + &dsc, + &dxva_ctx->cfg, + dxva_ctx->hw_surface, + dxva_ctx->surface_count, + &decoder))) { + ERR("IDirectXVideoDecoderService_CreateVideoDecoder failed\n"); + return -1; + } + dxva_ctx->decoder = decoder; + TRACE("IDirectXVideoDecoderService_CreateVideoDecoder succeed\n"); + return 0; +} + +static void DxCreateVideoConversion(void) +{ + unsigned int output = dxva_ctx->render_fmt; + + switch (output) { + case MAKEFOURCC('N','V','1','2'): + case MAKEFOURCC('I','M','C','3'): + dxva_ctx->output = MAKEFOURCC('Y','V','1','2'); + break; + default: + dxva_ctx->output = dxva_ctx->render_fmt; + break; + } +} + +/** + * It destroys a DirectX video service + */ +static void DxDestroyVideoService(void) +{ + if (dxva_ctx->hd3ddev) + IDirect3DDeviceManager9_CloseDeviceHandle(dxva_ctx->devmng, dxva_ctx->hd3ddev); + if (dxva_ctx->vs) + IDirectXVideoDecoderService_Release(dxva_ctx->vs); +} + +/** + * It destroys a Direct3D device manager + */ +static void D3dDestroyDeviceManager(void) +{ + if (dxva_ctx->devmng) + IDirect3DDeviceManager9_Release(dxva_ctx->devmng); +} + +/** + * It releases a Direct3D device and its resources. + */ +static void D3dDestroyDevice(void) +{ + if (dxva_ctx->d3ddev) + IDirect3DDevice9_Release(dxva_ctx->d3ddev); + if (dxva_ctx->d3dobj) + IDirect3D9_Release(dxva_ctx->d3dobj); +} + +#if 0 +static int DxResetVideoDecoder(void) +{ + ERR("DxResetVideoDecoder unimplemented\n"); + return -1; +} +#endif + +static void split_planes(uint8_t *dstu, size_t dstu_pitch, + uint8_t *dstv, size_t dstv_pitch, + const uint8_t *src, size_t src_pitch, + unsigned width, unsigned height) +{ + unsigned int y = 0; + unsigned int x = 0; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + dstu[x] = src[2*x+0]; + dstv[x] = src[2*x+1]; + } + src += src_pitch; + dstu += dstu_pitch; + dstv += dstv_pitch; + } +} + +static void copy_plane(uint8_t *dst, size_t dst_pitch, + 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 += dst_pitch; + } +#else + memcpy(dst, src, width * height); +#endif +} + +static void copy_yv12(uint8_t *dst[3], size_t dst_pitch[3], + uint8_t *src[3], size_t src_pitch[3], + unsigned width, unsigned height) +{ + copy_plane(dst[0], dst_pitch[0], + src[0], src_pitch[0], width, height); + copy_plane(dst[1], dst_pitch[1], + src[2], src_pitch[2], width / 2, height / 2); + copy_plane(dst[2], dst_pitch[2], + src[1], src_pitch[1], width / 2, height / 2); +} + +static void copy_nv12(uint8_t *dst[3], int linesizes[4], + uint8_t *src[2], size_t src_pitch[2], + unsigned width, unsigned height) +{ + copy_plane(dst[0], linesizes[0], + src[0], src_pitch[0], width, height); + split_planes(dst[1], linesizes[1], dst[2], linesizes[2], + src[1], src_pitch[1], width / 2, height / 2); +} + + +static void maru_dxva2_close(void) +{ + DxDestroyVideoDecoder(); + DxDestroyVideoService(); + D3dDestroyDeviceManager(); + D3dDestroyDevice(); + + if (hdxva2_dll) { + FreeLibrary(hdxva2_dll); + hdxva2_dll = NULL; + } + if (hd3d9_dll) { + FreeLibrary(hd3d9_dll); + hd3d9_dll = NULL; + } + + if (dxva_ctx->hw_context) { + free(dxva_ctx->hw_context); + dxva_ctx->hw_context = NULL; + } + + //free(dxva_ctx); +} + +static int maru_dxva2_open(AVCodecContext *dec_ctx) +{ + if (hd3d9_dll == NULL || hdxva2_dll == NULL) { + hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL")); + if (!hd3d9_dll) { + ERR("cannot load d3d9.dll\n"); + goto error; + } + hdxva2_dll = LoadLibrary(TEXT("DXVA2.DLL")); + if (!hdxva2_dll) { + ERR("cannot load dxva2.dll\n"); + goto error; + } + TRACE("DLLs loaded\n"); + + if (D3dCreateDevice() < 0) { + ERR("Failed to create Direct3D device\n"); + goto error; + } + TRACE("D3dCreateDevice succeed\n"); + + if (D3dCreateDeviceManager() < 0) { + ERR("D3dCreateDeviceManager failed\n"); + goto error; + } + + if (DxCreateVideoService() < 0) { + ERR("DxCreateVideoService failed\n"); + goto error; + } + } + + if (DxFindVideoServiceConversion(dec_ctx->codec_id)) { + ERR("DxFindVideoServiceConversion failed\n"); + goto error; + } + + dxva_ctx->thread_count = dec_ctx->thread_count; + + return 0; + +error: + maru_dxva2_close(); + return -1; +} + +static bool dxva_setup(AVCodecContext *dec_ctx, int width, int height) +{ + if(maru_dxva2_open(dec_ctx) < 0) { + ERR("DXVA initialization failed\n"); + return false; + } + + //TODO: destroy surface and decoder at avcontext deinit() + DxDestroyVideoDecoder(); + + if (DxCreateVideoDecoder(dec_ctx) < -1) { + ERR("DxCreateVideoDecoder failed\n"); + return false; + } + + // prepare the libav hardware context + if (dxva_ctx->hw_context == NULL) { + dxva_ctx->hw_context = g_malloc0(sizeof(struct dxva_context)); + } + dec_ctx->hwaccel_context = dxva_ctx->hw_context; + + dxva_ctx->hw_context->decoder = dxva_ctx->decoder; + dxva_ctx->hw_context->cfg = &dxva_ctx->cfg; + dxva_ctx->hw_context->surface_count = dxva_ctx->surface_count; + dxva_ctx->hw_context->surface = dxva_ctx->hw_surface; + + DxCreateVideoConversion(); + + return true; +} + +static int dxva_get_surface(AVCodecContext *dec_ctx, AVFrame *frame) +{ + unsigned i = 0; + DXVAPluginSurface *surface = NULL; + + /* Check the device */ +#if 0 + HRESULT hr = IDirect3DDeviceManager9_TestDevice(dxva_ctx->devmng, dxva_ctx->hd3ddev); + if (hr == DXVA2_E_NEW_VIDEO_DEVICE) { + if (DxResetVideoDecoder()) + return -1; + } else if (FAILED(hr)) { + ERR("IDirect3DDeviceManager9_TestDevice %u\n", (unsigned)hr); + return -1; + } +#endif + + for (i = 0; i < dxva_ctx->surface_count; ++i) { + surface = &dxva_ctx->surface[i]; + if (surface->is_occupied) continue; + + break; + } + + surface->is_occupied = true; + + frame->data[0] = (void *)surface->d3d; + frame->data[3] = frame->data[0]; + frame->type = FF_BUFFER_TYPE_USER; + frame->opaque = surface; + + return 0; +} + +static void dxva_release_surface(AVCodecContext *dec_ctx, AVFrame *frame) +{ + DXVAPluginSurface *surface = NULL; + unsigned int i; + + for (i = 0; i < 4; ++i) { + frame->data[i] = NULL; + } + + surface = frame->opaque; + surface->is_occupied = false; +} + +static int extract(AVFrame *src, void *dst, size_t size) +{ + LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)src->data[3]; + + /* */ + assert(dxva_ctx->output == MAKEFOURCC('Y','V','1','2')); + + /* */ + D3DLOCKED_RECT lock; + if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) { + ERR("Failed to lock surface\n"); + return -1; + } + + if (dxva_ctx->render_fmt == MAKEFOURCC('Y','V','1','2') || + dxva_ctx->render_fmt == MAKEFOURCC('I','M','C','3')) { + bool imc3 = dxva_ctx->render_fmt == MAKEFOURCC('I','M','C','3'); + size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2); + + size_t pitch[3] = { + lock.Pitch, + chroma_pitch, + chroma_pitch, + }; + + uint8_t *plane[3] = { + (uint8_t*)lock.pBits, + (uint8_t*)lock.pBits + pitch[0] * dxva_ctx->surface_height, + (uint8_t*)lock.pBits + pitch[0] * dxva_ctx->surface_height + + pitch[1] * dxva_ctx->surface_height / 2, + }; + + if (imc3) { + uint8_t *V = plane[1]; + plane[1] = plane[2]; + plane[2] = V; + } + + uint8_t *data[4]; + av_image_fill_pointers(data, PIX_FMT_YUV420P, src->height, dst, (const int *)pitch); + copy_yv12(data, pitch, plane, pitch, src->width, src->height); + } else if (dxva_ctx->render_fmt == MAKEFOURCC('N','V','1','2')) { + uint8_t *plane[2] = { + lock.pBits, + (uint8_t*)lock.pBits + lock.Pitch * dxva_ctx->surface_height + }; + size_t pitch[2] = { + lock.Pitch, + lock.Pitch, + }; + + uint8_t *data[4]; + int linesizes[4]; + av_image_fill_linesizes(linesizes, AV_PIX_FMT_YUV420P, src->width); + av_image_fill_pointers(data, PIX_FMT_YUV420P, src->height, dst, linesizes); + copy_nv12(data, linesizes, plane, pitch, src->width, src->height); + } else { + ERR("Not supported format.(%x)\n", dxva_ctx->render_fmt); + IDirect3DSurface9_UnlockRect(d3d); + return -1; + } + + /* */ + IDirect3DSurface9_UnlockRect(d3d); + return 0; +} + +static void dxva_extract(void *dst, void *src, size_t size) { + extract((AVFrame *)src, dst, size); +} + +static void dxva_release(void *buf) {} + +static DataHandler dxva_video_decode_data_handler = { + .get_data = dxva_extract, + .release = dxva_release, +}; + +CodecPlugin dxva_plugin = { + .pix_fmt = PIX_FMT_DXVA2_VLD, + .setup = dxva_setup, + .get_buffer = dxva_get_surface, + .release_buffer = dxva_release_surface, + .video_decode_data_handler = &dxva_video_decode_data_handler, +};