Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created May 3, 2015 20:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/b9893194449ee8f0dfcb to your computer and use it in GitHub Desktop.
Save roxlu/b9893194449ee8f0dfcb to your computer and use it in GitHub Desktop.
#include <VideoDecoderQuickSync.h>
VideoDecoderQuickSync::VideoDecoderQuickSync()
:factory(NULL)
,intel_adapter(NULL)
,d3d_device(NULL)
,d3d_context(NULL)
,video_decode(NULL)
,output_surface(NULL)
{
version.Major = 1;
version.Minor = 8;
}
VideoDecoderQuickSync::~VideoDecoderQuickSync() {
shutdown();
}
int VideoDecoderQuickSync::init(const std::string& filepath) {
if (0 != initBitstream(filepath)) {
return -1;
}
if (0 != initVideoSession()) {
return -3;
}
if (0 != initIntelAdapter()) {
return -4;
}
if (0 != initDirect3DContext()) {
return -5;
}
if (0 != initAllocators()) {
return -6;
}
if (0 != allocateFrames()) {
return -7;
}
return 0;
}
int VideoDecoderQuickSync::initBitstream(const std::string& filepath) {
size_t nal_start = 0;
size_t nal_end = 0;
ZeroMemory(&mfx_bitstream, sizeof(mfx_bitstream));
if (0 != h264_file.open(filepath)) {
return -1;
}
if (0 != h264_bitstream.setDelegate(&h264_file)) {
return -2;
}
if (0 > h264_bitstream.read(1024 * 1024)) {
return -3;
}
if (0 != h264_bitstream.findNal(nal_start, nal_end)) {
return -4;
}
mfx_bitstream.Data = h264_bitstream.ptr();
mfx_bitstream.DataOffset = 0;
mfx_bitstream.DataLength = h264_bitstream.size();
mfx_bitstream.MaxLength = h264_bitstream.size();
return 0;
}
int VideoDecoderQuickSync::initVideoSession() {
mfxStatus status = MFX_ERR_NONE;
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11;
if (NULL != video_decode) {
printf("Error: failed to init the video session because it seems that we already create the decoder.\n");
return -1;
}
status = video_session.Init(impl, &version);
if (MFX_ERR_NONE != status) {
printf("Error: failed to initialize the mfxSession for hardware decoding. Does you system support GPU decoding?\n");
return -2;
}
/* @todo .. we call video_decode->Init() in ... */
video_decode = new MFXVideoDECODE(video_session);
return 0;
}
int VideoDecoderQuickSync::initIntelAdapter() {
bool found = false;
mfxU32 adapter_num = 0;
mfxIMPL impl;
mfxIMPL base_impl;
mfxStatus status = MFX_ERR_NONE;
HRESULT hr = S_OK;
status = video_session.QueryIMPL(&impl);
if (MFX_ERR_NONE != status) {
printf("Error: failed to MFXQueryIMPL while trying to find the Intel adapter. Status: %d.\n", status);
return -1;
}
const struct {
mfxIMPL impl;
mfxU32 adapter_id;
} impl_types[] = {
{MFX_IMPL_HARDWARE, 0},
{MFX_IMPL_SOFTWARE, 0},
{MFX_IMPL_HARDWARE2, 1},
{MFX_IMPL_HARDWARE3, 2},
{MFX_IMPL_HARDWARE4, 3}
};
base_impl = MFX_IMPL_BASETYPE(impl);
for (mfxU8 i = 0; i < (sizeof(impl_types)/sizeof(impl_types[0])); ++i) {
if (impl_types[i].impl == base_impl) {
adapter_num = impl_types[i].adapter_id;
found = true;
break;
}
}
if (false == found) {
printf("Error: we didn't find the adapter number that should be used for decoding using Quick Sync.\n");
return -2;
}
hr = CreateDXGIFactory(__uuidof(IDXGIFactory2),(void**)&factory);
if (S_OK != hr) {
printf("Error: Failed to create the DXGI factory\n");
return -3;
}
if (NULL == factory) {
printf("Error: not supposed to happen, but we found a IDXGIFactory2, but it returned NULL. Cannot get the Intel adapter.\n");
return -4;
}
hr = factory->EnumAdapters(adapter_num, &intel_adapter);
if (S_OK != hr) {
printf("Error: failed to find the Intel adapter.\n");
shutdown();
return -5;
}
printf("Adapter ID: %u, ptr: %p.\n", adapter_num, intel_adapter);
return 0;
}
int VideoDecoderQuickSync::initDirect3DContext() {
if (NULL == intel_adapter) {
printf("Error: cannot initialize a Direct3D context because the intel_adapter member is NULL.\n");
return -1;
}
if (NULL != d3d_device) {
printf("Error: we already have an initialized D3D11 Device. First call shutdown.\n");
return -2;
}
/* Supported feature levels. */
D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
HRESULT hr = S_OK;
D3D_FEATURE_LEVEL selected_feature;
int num_features = sizeof(feature_levels) / sizeof(feature_levels[0]);
hr = D3D11CreateDevice(intel_adapter, /* The adapter on which we create the context. */
D3D_DRIVER_TYPE_UNKNOWN, /* DriverType: we use GPU as a backing device, when passing an non-null adapter this must be D3D_DRIVER_TYPE_UNKNOWN, see remarks on msdn for this function. */
NULL, /* Software: we're using D3D_DRIVER_TYPE_HARDWARE, so it's not applicable. */
0, /* D3D11_CREATE_DEVICE_FLAG. */
feature_levels, /* The feature levels we want to use. Pick the best. */
num_features, /* How many feature levels are stored in 'feature_levels'. */
D3D11_SDK_VERSION, /* The SDK version, use D3D11_SDK_VERSION. */
&d3d_device, /* OUT: the ID3D11Device object. */
&selected_feature, /* OUT: the selected feature level. */
&d3d_context); /* OUT: the ID3D11DeviceContext that represents the above features. */
if (S_OK != hr) {
printf("Error: failed to create the D3D11 device context: %04X\n", hr);
shutdown();
return -3;
}
CComQIPtr<ID3D10Multithread> mt(d3d_context);
if (mt) {
/*
@todo the code example from the Quick Sync guide doesn't check the result.
though our return value is FALSE. It's not clear from the docs if we should
stop here or continue with a non-multithreaded context.
@todo see page 22 of the SDK API Reference, this contains another example
of how to set multithreaded.
*/
mt->SetMultithreadProtected(true);
/*
if (false == mt->SetMultithreadProtected(true)) {
printf("Error: failed to set multi thread protected on the d3d context.\n");
shutdown();
return -4;
}
*/
}
else {
printf("Error: failed to query the ID3D10Multithread interface.\n");
shutdown();
return -5;
}
return 0;
}
int VideoDecoderQuickSync::initAllocators() {
mfxStatus status = MFX_ERR_NONE;
if (NULL == d3d_context) {
printf("Error: cannot init allocators because d3d_context is NULL.\n");
shutdown();
return -1;
}
if (NULL == d3d_device) {
printf("Error: cannot init allocators because d3d_device is NULL.\n");
shutdown();
return -2;
}
status = video_session.SetHandle(MFX_HANDLE_D3D11_DEVICE, d3d_device);
if (MFX_ERR_NONE != status) {
printf("Error: failed to set the handle for the MFXVideoSession.\n");
shutdown();
return -3;
}
if (0 != frame_allocator.init(d3d_context, d3d_device)) {
printf("Error: failed to init the frame allocator.\n");
shutdown();
return -4;
}
printf("Setting frame allocator.\n");
status = video_session.SetFrameAllocator(&frame_allocator);
if (MFX_ERR_NONE != status) {
printf("Error: failed to set the frame allocator on the video_session member.\n");
shutdown();
return -5;
}
return 0;
}
int VideoDecoderQuickSync::allocateFrames() {
mfxStatus status = MFX_ERR_NONE;
mfxFrameAllocRequest alloc_request;
mfxU16 num_surfaces = 0;
ZeroMemory(&alloc_request, sizeof(alloc_request));
ZeroMemory(&video_param, sizeof(video_param));
if (NULL == video_decode) {
printf("Error: cannot allocate frames because the video_decode is NULL.\n");
shutdown();
return -1;
}
/* This is just a check to make sure that initBitstream has been called.*/
if (0 == h264_bitstream.size()) {
printf("Error: cannot allocate frames because the h264 bitstream hasn't got any data.\n");
shutdown();
return -2;
}
/* @todo at some point we may want to support MJPEG too. */
video_param.mfx.CodecId = MFX_CODEC_AVC;
video_param.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; /* Must be set otherwise I got a INVALID_VIDEO_PARAM error, see developers guide section 4.4.1 . */
/* We use DecodeHeader to fill our video params. */
status = video_decode->DecodeHeader(&mfx_bitstream, &video_param);
if (MFX_ERR_NONE != status) {
printf("Error: failed to decode the header, err: %d\n", status);
shutdown();
return -3;
}
/* The mfxVideoParam.mfx.CodecId is must be set. * Query() is optional. */
#if 1
status = video_decode->Query(&video_param, &video_param);
if (MFX_ERR_NONE != status) {
printf("Error: failed to query the Video Params while trying to allocateFrames(): %d.\n", status);
shutdown();
return -2;
}
#endif
printf("video_param.AsyncDepth: %u\n", video_param.AsyncDepth);
printf("video_param.NumExtParam: %u\n", video_param.NumExtParam);
printf("video_param.Protected: %u\n", video_param.Protected);
printf("video_param.IOPattern: %u\n", video_param.IOPattern);
printf("video_param.mfx.CodecId: %04X\n", video_param.mfx.CodecId);
printf("video_param.mfx.FrameInfo.Width: %d\n", video_param.mfx.FrameInfo.Width);
printf("video_param.mfx.FrameInfo.Height: %d\n", video_param.mfx.FrameInfo.Height);
printf("video_param.mfx.FrameInfo.FourCC: %04X\n", video_param.mfx.FrameInfo.FourCC);
printf("video_param.mfx.FrameInfo.ChromaFormat: %u\n", video_param.mfx.FrameInfo.ChromaFormat);
/* Query how many frames we should allocate. */
status = video_decode->QueryIOSurf(&video_param, &alloc_request);
if (MFX_ERR_NONE != status) {
printf("Error: failed to QueryIOSurf, when trying to allocate the backing frames in allocFrames(): %d.\n", status);
shutdown();
return -3;
}
printf("alloc_request.NumFrameMin: %u\n", alloc_request.NumFrameMin);
printf("alloc_request.NumFrameSuggested: %u\n", alloc_request.NumFrameSuggested);
printf("Init decoder.\n");
status = video_decode->Init(&video_param);
if (MFX_ERR_NONE != status) {
printf("Error: failed to initialize the video decoder: %d\n", status);
shutdown();
return -4;
}
/* Create the surfaces */
if (0 == frame_allocator.textures.size()) {
printf("Error: the frame allocator didn't allocate any textures after video_decode->Init() was called. Not supposed to happen.\n");
shutdown();
return -5;
}
/* @todo we should use the NumFrameActual from the alloc response. */
/*
Slightly based on:
https://github.com/KJCondron/VideoWatcher/blob/master/src/simple_2_decode%20-%20d3d/src/simple_decode_d3d.cpp
*/
printf("Created %lu frames\n", frame_allocator.textures.size());
for (size_t i = 0; i < frame_allocator.textures.size(); ++i) {
VideoDecoderQuickSyncTexture* tex = frame_allocator.textures[i];
mfxFrameSurface1* fs = new mfxFrameSurface1();
memset(fs, 0x00, sizeof(fs));
memcpy(&fs->Info, &video_param.mfx.FrameInfo, sizeof(fs->Info));
fs->Data.MemId = tex->memory_id;
surfaces.push_back(fs);
}
return 0;
}
int VideoDecoderQuickSync::getFreeSurfaceIndex(size_t& index) {
for (size_t i = 0; i < surfaces.size(); ++i) {
if (0 == surfaces[i]->Data.Locked) {
index = i;
return 0;
}
}
return -1;
}
int VideoDecoderQuickSync::decode() {
if (NULL == d3d_device) {
printf("Error: cannot decode; we're not initialized.\n");
return -1;
}
if (NULL == video_decode) {
printf("Error: cannot decode; the VideoDecoderQuickSync is not initialized. video_decode member is NULL.\n");
return -2;
}
int i = 0;
size_t nal_start = 0;
size_t nal_end = 0;
size_t free_surface_dx = 0;
mfxStatus status = MFX_ERR_NONE;
while (0 == h264_bitstream.findNal(nal_start, nal_end)) {
printf("nal_start: %lu, nal_end: %lu\n", nal_start, nal_end);
/* Update mfx_bitstream. */
mfx_bitstream.Data = h264_bitstream.ptr(); /* @FIXME Should this point to the start of the NAL? Or to the header? Pointer to the current byte offset. */
mfx_bitstream.DataOffset = 0;
mfx_bitstream.DataLength = nal_end - nal_start;
mfx_bitstream.MaxLength = h264_bitstream.size();
if (NULL == mfx_bitstream.Data) {
printf("-- Error: failed to get a correct bitstream pointer. Currently NULL after finding a NAL.\n");
break;
}
if (0 != getFreeSurfaceIndex(free_surface_dx)) {
printf("-- Error: failed to find a free surface index.\n");
break;
}
printf("-- Free surface index: %lu\n", free_surface_dx);
status = video_decode->DecodeFrameAsync(&mfx_bitstream,
surfaces[free_surface_dx],
&output_surface,
&sync_point);
if (MFX_ERR_NONE != status) {
printf("-- Error while trying to decode: %d\n", status);
}
/* Move byte offset. So the next findNal() will get the next nal. */
h264_bitstream.bs.byte_offset = nal_end;
if (i >= 10) {
break;
}
++i;
}
return 0;
}
int VideoDecoderQuickSync::shutdown() {
printf("Shutdown().\n");
/* @todo cleanup video session. */
/* @todo cleanup allocators. */
/* @todo delete video_decode */
/* @todo we're passing the device handle using SetHandle which increases the count. See page 40 of the reference. */
if (NULL != factory) {
factory->Release();
factory = NULL;
}
if (NULL != intel_adapter) {
intel_adapter->Release();
intel_adapter = NULL;
}
if (NULL != d3d_device) {
d3d_device->Release();
d3d_device = NULL;
}
if (NULL != d3d_context) {
d3d_context->Release();
d3d_context = NULL;
}
return 0;
}
/* --------------------------------------------------------- */
static mfxStatus dummy_Alloc(mfxHDL pthis, mfxFrameAllocRequest* req, mfxFrameAllocResponse* resp) {
printf(">> Alloc()\n");
return MFX_ERR_NONE;
}
static mfxStatus dummy_Lock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr) {
printf(">> Lock()\n");
return MFX_ERR_NONE;
}
static mfxStatus dummy_Unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData* ptr) {
printf(">> Unlock()\n");
return MFX_ERR_NONE;
}
static mfxStatus dummy_GetHDL(mfxHDL pthis, mfxMemId mid, mfxHDL* handle) {
printf(">> GetHDL()\n");
return MFX_ERR_NONE;
}
static mfxStatus dummy_Free(mfxHDL pthis, mfxFrameAllocResponse* resp) {
printf(">> Free()\n");
return MFX_ERR_NONE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment