diff options
Diffstat (limited to 'tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx')
-rw-r--r-- | tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx | 2221 |
1 files changed, 2221 insertions, 0 deletions
diff --git a/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx b/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx new file mode 100644 index 0000000..49f9e1c --- /dev/null +++ b/tinyDAV/src/codecs/h264/tdav_codec_h264_intel.cxx @@ -0,0 +1,2221 @@ +/* +* Copyright (C) 2014-2015 Mamadou DIOP. +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO 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 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO 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 DOUBANGO. +* +*/ + +/**@file tdav_codec_h264_intel.cxx +* @brief H.264 codec plugin using Intel Media SDK 2014 R2 for Clients (https://software.intel.com/en-us/media-solutions-portal) v1.1 for encoding/decoding. +* Low latency encoding/decoding: https://software.intel.com/en-us/articles/video-conferencing-features-of-intel-media-software-development-kit +*/ +#include "tinydav/codecs/h264/tdav_codec_h264_intel.h" + +#if HAVE_INTEL_MEDIA_SDK + +#include "tinydav/codecs/h264/tdav_codec_h264_common.h" + +#include "tinyrtp/rtp/trtp_rtp_packet.h" + +#include "tinymedia/tmedia_codec.h" +#include "tinymedia/tmedia_params.h" +#include "tinymedia/tmedia_defaults.h" + +#include "tsk_thread.h" +#include "tsk_params.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + +#include <mfxvideo++.h> + +#if defined(_MSC_VER) +# pragma comment(lib, "libmfx.lib") +#endif /* _MSC_VER */ + +#if !defined(INTEL_DX11_D3D) +# define INTEL_DX11_D3D 1 +#endif /* INTEL_DX11_D3D */ + +#if INTEL_DX11_D3D +#include <d3d11.h> +#include <dxgi1_2.h> +#include <atlbase.h> +# if defined(_MSC_VER) +# pragma comment(lib, "d3d11.lib") +# pragma comment(lib, "dxgi.lib") +# endif /* _MSC_VER */ + +#endif /* INTEL_DX11_D3D */ + +#define INTEL_DEBUG_INFO(FMT, ...) TSK_DEBUG_INFO("[MSDK H264 Codec] " FMT, ##__VA_ARGS__) +#define INTEL_DEBUG_WARN(FMT, ...) TSK_DEBUG_WARN("[MSDK H264 Codec] " FMT, ##__VA_ARGS__) +#define INTEL_DEBUG_ERROR(FMT, ...) TSK_DEBUG_ERROR("[MSDK H264 Codec] " FMT, ##__VA_ARGS__) +#define INTEL_DEBUG_FATAL(FMT, ...) TSK_DEBUG_FATAL("[MSDK H264 Codec] " FMT, ##__VA_ARGS__) + +#define INTEL_ALIGN16(i) ((((i) + 15) >> 4) << 4) +#define INTEL_ALIGN32(i) (((mfxU32)((i)+31)) & (~ (mfxU32)31)) +#define INTEL_SAFE_RELEASE(X) {if ((X)) { (X)->Release(); (X) = NULL; }} + +#define INTEL_CHECK_STATUS(x) { mfxStatus __status__ = (x); if (__status__ != MFX_ERR_NONE) { INTEL_DEBUG_ERROR("Operation Failed (%d)", __status__); goto bail; } } +#define INTEL_BREAK(msg) { INTEL_DEBUG_ERROR("%s", msg); goto bail; } + +#define INTEL_ENABLE_REALTIME 1 + +static mfxIMPL __IntelDefaultImpl = MFX_IMPL_AUTO_ANY +#if INTEL_DX11_D3D +| MFX_IMPL_VIA_D3D11 +#endif +; +static mfxVersion __IntelDefaultVer = { 0, 1 }; + +// TODO: Test against FFmpeg, CUDA, OpenH264 and Microsoft implementations +// TODO: When Bandwidth change (or any other event) Reset() fails + +class IntelCodecEncoder; +class IntelCodecDecoder; + +typedef struct tdav_codec_h264_intel_s +{ + TDAV_DECLARE_CODEC_H264_COMMON; + + MFXVideoSession* mfxSession; + + // DX11_D3D +#if INTEL_DX11_D3D + mfxFrameAllocResponse D3D11SavedAllocResponses[2/*Encode=0, Decode=1*/]; + mfxFrameAllocator D3D11Allocator; + + CComPtr<ID3D11Device> pD3D11Device; + CComPtr<ID3D11DeviceContext> pD3D11Ctx; + CComPtr<IDXGIAdapter>pAdapter; + CComPtr<IDXGIFactory1> pDXGIFactory; + CComPtr<IDXGIAdapter> hAdapter; +#endif + + // Encoder + struct{ + IntelCodecEncoder *pInst; + int64_t frame_count; + tsk_bool_t force_idr; + int rotation; + int neg_width; + int neg_height; + int neg_fps; + int max_bitrate_bps; + } encoder; + + // decoder + struct{ + IntelCodecDecoder *pInst; + } decoder; +} +tdav_codec_h264_intel_t; + +#if !defined(INTEL_H264_GOP_SIZE_IN_SECONDS) +# define INTEL_H264_GOP_SIZE_IN_SECONDS 25 +#endif + +static int tdav_codec_h264_intel_init(tdav_codec_h264_intel_t* self, profile_idc_t profile); +static int tdav_codec_h264_intel_deinit(tdav_codec_h264_intel_t* self); +static int tdav_codec_h264_intel_open_encoder(tdav_codec_h264_intel_t* self); +static int tdav_codec_h264_intel_close_encoder(tdav_codec_h264_intel_t* self); +static int tdav_codec_h264_intel_open_decoder(tdav_codec_h264_intel_t* self); +static int tdav_codec_h264_intel_close_decoder(tdav_codec_h264_intel_t* self); + +#if INTEL_DX11_D3D +#define D3D11_WILL_READ 0x1000 +#define D3D11_WILL_WRITE 0x2000 + +typedef struct { + mfxMemId memId; + mfxMemId memIdStage; + mfxU16 rw; +} CustomMemId; + +const struct { + mfxIMPL impl; // actual implementation + mfxU32 adapterID; // device adapter number +} implTypes[] = { + { MFX_IMPL_HARDWARE, 0 }, + { MFX_IMPL_HARDWARE2, 1 }, + { MFX_IMPL_HARDWARE3, 2 }, + { MFX_IMPL_HARDWARE4, 3 } +}; + +static mfxStatus D3D11_CreateHWDevice(mfxHDL pthis, mfxSession session, mfxHDL* deviceHandle, HWND hWnd); +static void D3D11_CleanupHWDevice(mfxHDL pthis); +static void D3D11_SetHWDeviceContext(mfxHDL pthis, CComPtr<ID3D11DeviceContext> devCtx); + +// Intel Media SDK memory allocator entrypoints.... +// - A slightly different allocation procedure is used for encode, decode and VPP +static mfxStatus D3D11_SimpleAlloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); +static mfxStatus D3D11_SimpleLock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); +static mfxStatus D3D11_SimpleUnlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); +static mfxStatus D3D11_SimpleGethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle); +static mfxStatus D3D11_SimpleFree(mfxHDL pthis, mfxFrameAllocResponse *response); +#endif /* INTEL_DX11_D3D */ + +// +// IntelCodec +// +class IntelCodec +{ +protected: + IntelCodec(MFXVideoSession* pSession) + : m_bOpened(false) + , m_pSession(pSession) + , n_nNumSurfaces(0) + , m_nSurfaceWidth(0) + , m_nSurfaceHeight(0) + , m_nSurfaceBitsPerPixel(0) + , m_nSurfaceSize(0) + , m_pSurfaceBuffers(NULL) + , m_ppSurfacePtrs(NULL) + { + memset(&m_sBitstream, 0, sizeof(m_sBitstream)); + memset(&m_sParamReq, 0, sizeof(m_sParamReq)); + memset(&m_sParamSel, 0, sizeof(m_sParamSel)); + memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest)); + } +public: + virtual ~IntelCodec() + { + Close(); + } + virtual mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec) = 0; + + virtual mfxStatus Close() + { + DeAllocSurfaces(); + DeAllocateBitstream(); + + memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest)); + + m_bOpened = false; + + return MFX_ERR_NONE; + } + +protected: + int GetFreeSurfaceIndex() + { + if (m_ppSurfacePtrs) + { + for (mfxU16 i = 0; i < n_nNumSurfaces; i++) + { + if (0 == m_ppSurfacePtrs[i]->Data.Locked) + { + return i; + } + } + } + return MFX_ERR_NOT_FOUND; + } + + mfxStatus ReadPlaneData(mfxU16 w, mfxU16 h, mfxU8 *buf, mfxU8 *ptr, mfxU16 pitch, mfxU16 offset, const mfxU8* &src) + { + for (mfxU16 i = 0; i < h; i++) + { + memcpy(buf, src, w); + src += w; + + for (mfxU16 j = 0; j < w; j++) + ptr[i * pitch + j * 2 + offset] = buf[j]; + } + return MFX_ERR_NONE; + } + + mfxStatus LoadRawFrame(int nSurfaceIndex, const mfxU8* src) + { + mfxFrameSurface1* pSurface = (m_ppSurfacePtrs && nSurfaceIndex >= 0 && nSurfaceIndex < n_nNumSurfaces) ? m_ppSurfacePtrs[nSurfaceIndex] : NULL; + if (!pSurface) + { + INTEL_DEBUG_ERROR("Failed to find surface at index=%d", nSurfaceIndex); + return MFX_ERR_NOT_FOUND; + } + + mfxStatus sts = MFX_ERR_NONE; + mfxU16 w, h, i, pitch; + mfxU8 *ptr; + mfxFrameInfo* pInfo = &pSurface->Info; + mfxFrameData* pData = &pSurface->Data; + + if (pInfo->CropH > 0 && pInfo->CropW > 0) { + w = pInfo->CropW; + h = pInfo->CropH; + } + else { + w = pInfo->Width; + h = pInfo->Height; + } + + pitch = pData->Pitch; + ptr = pData->Y + pInfo->CropX + pInfo->CropY * pData->Pitch; + + // read luminance plane + for (i = 0; i < h; i++) + { + memcpy(ptr + i * pitch, src, w); + src += w; + } + + mfxU8 buf[2048]; // maximum supported chroma width for nv12 + w /= 2; + h /= 2; + ptr = pData->UV + pInfo->CropX + (pInfo->CropY / 2) * pitch; + if (w > 2048) + return MFX_ERR_UNSUPPORTED; + + // load U + sts = ReadPlaneData(w, h, buf, ptr, pitch, 0, src); + if (MFX_ERR_NONE != sts) return sts; + // load V + sts = ReadPlaneData(w, h, buf, ptr, pitch, 1, src); + if (MFX_ERR_NONE != sts) return sts; + + return MFX_ERR_NONE; + } + + virtual mfxStatus AllocSurfaces(mfxU16 nNumSurfaces, mfxU16 nSurfaceWidth, mfxU16 nSurfaceHeight, const mfxFrameInfo* pcFrameInfo) + { + mfxStatus status = MFX_ERR_UNKNOWN; + + INTEL_DEBUG_INFO("Alloc surfaces: num=%u, width=%u, height=%u", nNumSurfaces, nSurfaceWidth, nSurfaceHeight); + + DeAllocSurfaces(); + + n_nNumSurfaces = nNumSurfaces; + m_nSurfaceWidth = (mfxU16)INTEL_ALIGN32(nSurfaceWidth); + m_nSurfaceHeight = (mfxU16)INTEL_ALIGN32(nSurfaceHeight); + m_nSurfaceBitsPerPixel = 12; // NV12 format is a 12 bits per pixel format + m_nSurfaceSize = m_nSurfaceWidth * m_nSurfaceHeight * m_nSurfaceBitsPerPixel / 8; + +#if !INTEL_DX11_D3D + if (!(m_pSurfaceBuffers = (mfxU8 *)new mfxU8[m_nSurfaceSize * n_nNumSurfaces])) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } +#endif + + if (!(m_ppSurfacePtrs = new mfxFrameSurface1*[n_nNumSurfaces])) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + for (mfxU16 i = 0; i < n_nNumSurfaces; i++) + { + if (!(m_ppSurfacePtrs[i] = new mfxFrameSurface1)) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + memset(m_ppSurfacePtrs[i], 0, sizeof(mfxFrameSurface1)); + memcpy(&(m_ppSurfacePtrs[i]->Info), pcFrameInfo, sizeof(mfxFrameInfo)); +#if INTEL_DX11_D3D + m_ppSurfacePtrs[i]->Data.MemId = m_sD3D11Response.mids[i]; // MID (memory id) represent one D3D NV12 surface +#else + m_ppSurfacePtrs[i]->Data.Y = &m_pSurfaceBuffers[m_nSurfaceSize * i]; + m_ppSurfacePtrs[i]->Data.U = m_ppSurfacePtrs[i]->Data.Y + m_nSurfaceWidth * m_nSurfaceHeight; + m_ppSurfacePtrs[i]->Data.V = m_ppSurfacePtrs[i]->Data.U + 1; + m_ppSurfacePtrs[i]->Data.Pitch = m_nSurfaceWidth; +#endif + } + + return MFX_ERR_NONE; + + bail: + DeAllocSurfaces(); + return status; + } + + mfxStatus AllocateBitstream(mfxU32 nMaxLength) + { + DeAllocateBitstream(); + + m_sBitstream.MaxLength = nMaxLength; + if (!(m_sBitstream.Data = new mfxU8[nMaxLength])) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + + return MFX_ERR_NONE; + + bail: + return MFX_ERR_MEMORY_ALLOC; + } +private: + mfxStatus DeAllocSurfaces() + { + if (m_ppSurfacePtrs) + { + for (mfxU16 i = 0; i < n_nNumSurfaces; i++) + { + if (m_ppSurfacePtrs[i]) + { + delete m_ppSurfacePtrs[i]; + } + } + delete[] m_ppSurfacePtrs; + m_ppSurfacePtrs = NULL; + } + n_nNumSurfaces = 0; + + if (m_pSurfaceBuffers) + { + delete[] m_pSurfaceBuffers; + m_pSurfaceBuffers = NULL; + } + + m_nSurfaceWidth = 0; + m_nSurfaceHeight = 0; + m_nSurfaceBitsPerPixel = 0; + m_nSurfaceSize = 0; + + return MFX_ERR_NONE; + } + + mfxStatus DeAllocateBitstream() + { + if (m_sBitstream.Data) + { + delete[]m_sBitstream.Data; + } + memset(&m_sBitstream, 0, sizeof(m_sBitstream)); + + return MFX_ERR_NONE; + } + + +protected: + bool m_bOpened; + MFXVideoSession* m_pSession; + mfxU16 n_nNumSurfaces; + mfxU16 m_nSurfaceWidth; + mfxU16 m_nSurfaceHeight; + mfxU8 m_nSurfaceBitsPerPixel; + mfxU32 m_nSurfaceSize; + mfxU8* m_pSurfaceBuffers; // mfxU8[]; + mfxFrameSurface1** m_ppSurfacePtrs; // mfxFrameSurface1[] + mfxBitstream m_sBitstream; + mfxVideoParam m_sParamReq; // requested params + mfxVideoParam m_sParamSel; // selected params + mfxFrameAllocRequest m_sAllocRequest; +#if INTEL_DX11_D3D + mfxFrameAllocResponse m_sD3D11Response; +#endif +}; + + +// +// IntelCodecEncoder +// +class IntelCodecEncoder : public IntelCodec +{ +public: + IntelCodecEncoder(MFXVideoSession* pSession) + : IntelCodec(pSession) + , m_Inst(*pSession) + { + memset(&m_sFrameCtrl, 0, sizeof(m_sFrameCtrl)); + } + virtual ~IntelCodecEncoder() + { + Close(); + } + + virtual mfxStatus Close() + { + m_Inst.Close(); + memset(&m_sFrameCtrl, 0, sizeof(m_sFrameCtrl)); + return IntelCodec::Close(); + } + + mfxStatus Reset() + { + if (m_bOpened) + { + return m_Inst.Reset(&m_sParamSel); + } + return MFX_ERR_NONE; + } + + mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec) + { + int32_t max_bw_kpbs; + tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec; + mfxStatus status = MFX_ERR_UNKNOWN; + mfxU16 uSelWidth, uSelHeight; + + pWrappedCodec->encoder.neg_width = (pWrappedCodec->encoder.rotation == 90 || pWrappedCodec->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height : TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width; + pWrappedCodec->encoder.neg_height = (pWrappedCodec->encoder.rotation == 90 || pWrappedCodec->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width : TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height; + pWrappedCodec->encoder.neg_fps = TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.fps; + max_bw_kpbs = TSK_CLAMP( + 0, + tmedia_get_video_bandwidth_kbps_2(pWrappedCodec->encoder.neg_width, pWrappedCodec->encoder.neg_height, pWrappedCodec->encoder.neg_fps), + TMEDIA_CODEC(pWrappedCodec)->bandwidth_max_upload + ); + pWrappedCodec->encoder.max_bitrate_bps = (max_bw_kpbs * 1024); + + INTEL_DEBUG_INFO("neg_width=%d, neg_height=%d, neg_fps=%d, max_bitrate_bps=%d", + pWrappedCodec->encoder.neg_width, + pWrappedCodec->encoder.neg_height, + pWrappedCodec->encoder.neg_fps, + pWrappedCodec->encoder.max_bitrate_bps + ); + + // Initialize encoder parameters + memset(&m_sParamReq, 0, sizeof(m_sParamReq)); + m_sParamReq.mfx.CodecId = MFX_CODEC_AVC; + m_sParamReq.mfx.CodecProfile = pWrappedCodecCommon->profile == profile_idc_main ? MFX_PROFILE_AVC_MAIN : MFX_PROFILE_AVC_BASELINE; + m_sParamReq.mfx.CodecLevel = (mfxU16)pWrappedCodecCommon->level; + // TODO: Update "CodecProfile" based on "common->profile_iop" + m_sParamReq.mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED; + m_sParamReq.mfx.TargetKbps = max_bw_kpbs; + m_sParamReq.mfx.RateControlMethod = MFX_RATECONTROL_CBR; + m_sParamReq.mfx.IdrInterval = (pWrappedCodec->encoder.neg_fps * INTEL_H264_GOP_SIZE_IN_SECONDS); + m_sParamReq.mfx.FrameInfo.FrameRateExtN = pWrappedCodec->encoder.neg_fps; + m_sParamReq.mfx.FrameInfo.FrameRateExtD = 1; + m_sParamReq.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + m_sParamReq.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + m_sParamReq.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + m_sParamReq.mfx.FrameInfo.CropX = 0; + m_sParamReq.mfx.FrameInfo.CropY = 0; + m_sParamReq.mfx.FrameInfo.CropW = pWrappedCodec->encoder.neg_width; + m_sParamReq.mfx.FrameInfo.CropH = pWrappedCodec->encoder.neg_height; + m_sParamReq.mfx.FrameInfo.Width = INTEL_ALIGN16(pWrappedCodec->encoder.neg_width); // must be a multiple of 16 + m_sParamReq.mfx.FrameInfo.Height = INTEL_ALIGN16(pWrappedCodec->encoder.neg_height); // must be a multiple of 16 +#if INTEL_DX11_D3D + m_sParamReq.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY; +#else + m_sParamReq.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; +#endif + + memset(&m_sOpt2MaxFrameSize, 0, sizeof(m_sOpt2MaxFrameSize)); + m_sOpt2MaxFrameSize.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + m_sOpt2MaxFrameSize.Header.BufferSz = sizeof(m_sOpt2MaxFrameSize); + m_sOpt2MaxFrameSize.MaxSliceSize = (H264_RTP_PAYLOAD_SIZE - 100); + m_sOpt2MaxFrameSize.RepeatPPS = MFX_CODINGOPTION_OFF; + m_pExtendedBuffers[0] = (mfxExtBuffer*)&m_sOpt2MaxFrameSize; +#if INTEL_ENABLE_REALTIME + m_sParamReq.AsyncDepth = 1; // limits internal frame buffering + m_sParamReq.mfx.GopRefDist = 1; // No B-Frames + m_sParamReq.mfx.NumRefFrame = 1; + memset(&m_sOptLowLatency, 0, sizeof(m_sOptLowLatency)); + m_sOptLowLatency.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + m_sOptLowLatency.Header.BufferSz = sizeof(m_sOptLowLatency); + m_sOptLowLatency.MaxDecFrameBuffering = 1; + m_pExtendedBuffers[1] = (mfxExtBuffer*)&m_sOptLowLatency; + m_sParamReq.NumExtParam = 2; +#else + m_sParamReq.NumExtParam = 1; +#endif + m_sParamReq.ExtParam = m_pExtendedBuffers; + + // Check parameters + status = m_Inst.Query(&m_sParamReq, &m_sParamReq); + if (status != MFX_ERR_NONE && status != MFX_WRN_INCOMPATIBLE_VIDEO_PARAM /* Best one will be selected by the encoder */) { + INTEL_CHECK_STATUS(status); + } + if (m_sOpt2MaxFrameSize.MaxSliceSize == 0) + { + INTEL_DEBUG_INFO("The encoder doesn't support setting 'MaxSliceSize' :("); + } + + // Query number required surfaces for encoder + memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest)); + INTEL_CHECK_STATUS(status = m_Inst.QueryIOSurf(&m_sParamReq, &m_sAllocRequest)); + INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sAllocRequest.NumFrameSuggested); +#if INTEL_DX11_D3D + m_sAllocRequest.Type |= D3D11_WILL_WRITE; // Hint to DX11 memory handler that application will write data to input surfaces +#endif + + // Allocate surfaces for encoder +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodec->D3D11Allocator.Alloc(pWrappedCodec->D3D11Allocator.pthis, &m_sAllocRequest, &m_sD3D11Response)); + if (m_sD3D11Response.NumFrameActual == 0) + { + INTEL_CHECK_STATUS(status = MFX_ERR_UNKNOWN); + } + INTEL_CHECK_STATUS(status = AllocSurfaces(m_sD3D11Response.NumFrameActual, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo)); +#else + INTEL_CHECK_STATUS(status = AllocSurfaces(m_sAllocRequest.NumFrameSuggested, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo)); +#endif + + // Initialize the Media SDK encoder + status = m_Inst.Init(&m_sParamReq); + if (status != MFX_ERR_NONE && status != MFX_WRN_PARTIAL_ACCELERATION) { + INTEL_CHECK_STATUS(status); + } + INTEL_DEBUG_INFO("Encoder->Init() returned: %d", status); + + // Retrieve video parameters selected by encoder. + memset(&m_sParamSel, 0, sizeof(m_sParamSel)); + INTEL_CHECK_STATUS(status = m_Inst.GetVideoParam(&m_sParamSel)); + INTEL_DEBUG_INFO("sel_width=%u.crop=%u, sel_height=%u.crop=%u, sel_fps=%u/%u", + m_sParamSel.mfx.FrameInfo.Width, m_sParamSel.mfx.FrameInfo.CropW, + m_sParamSel.mfx.FrameInfo.Height, m_sParamSel.mfx.FrameInfo.CropH, + m_sParamReq.mfx.FrameInfo.FrameRateExtN, + m_sParamReq.mfx.FrameInfo.FrameRateExtD + ); + if (m_sParamSel.mfx.FrameInfo.CropW > 0 && m_sParamSel.mfx.FrameInfo.CropH > 0) + { + uSelWidth = m_sParamSel.mfx.FrameInfo.CropW; + uSelHeight = m_sParamSel.mfx.FrameInfo.CropH; + } + else + { + uSelWidth = m_sParamSel.mfx.FrameInfo.Width; + uSelHeight = m_sParamSel.mfx.FrameInfo.Height; + } + if (pWrappedCodec->encoder.neg_width != uSelWidth || pWrappedCodec->encoder.neg_height != uSelHeight) { + INTEL_DEBUG_INFO("Encoder neg size <> sel size: %dx%d<>%dx%d", pWrappedCodec->encoder.neg_width, pWrappedCodec->encoder.neg_height, uSelWidth, uSelHeight); + pWrappedCodec->encoder.neg_width = uSelWidth; + pWrappedCodec->encoder.neg_height = uSelHeight; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.width = pWrappedCodec->encoder.neg_width; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->out.height = pWrappedCodec->encoder.neg_height; + } + + // Allocate BitStream + INTEL_CHECK_STATUS(status = AllocateBitstream(m_sParamSel.mfx.BufferSizeInKB * 1000)); + + m_bOpened = true; + return MFX_ERR_NONE; + + bail: + Close(); + return status; + } + + mfxStatus UpdateBandwidth(bool bUp, mfxU16 max) + { + if (bUp) + { + m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, (mfxU16)((m_sParamSel.mfx.TargetKbps * 3) >> 1), max); + } + else + { + m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, (mfxU16)((m_sParamSel.mfx.TargetKbps << 1) / 3), max); + } + m_sParamReq.mfx.TargetKbps = m_sParamSel.mfx.TargetKbps; + INTEL_DEBUG_INFO("Setting new target bandwidth to %ukbps", m_sParamSel.mfx.TargetKbps); + return m_Inst.Reset(&m_sParamSel); + } + + mfxStatus SetMaxBandwidth(mfxU16 max) + { + m_sParamSel.mfx.TargetKbps = TSK_CLAMP(0, m_sParamSel.mfx.TargetKbps, max); + m_sParamReq.mfx.TargetKbps = m_sParamSel.mfx.TargetKbps; + INTEL_DEBUG_INFO("Setting new target bandwidth to %ukbps", m_sParamSel.mfx.TargetKbps); + return m_Inst.Reset(&m_sParamSel); + } + + mfxStatus Encode(struct tmedia_codec_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize) + { + tdav_codec_h264_intel_t* pWrappedCodecH264 = (tdav_codec_h264_intel_t*)pWrappedCodec; + tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec; + mfxU32 nInDataXSize; + tsk_bool_t bSendIDR; + int nEncSurfIdx = 0; + mfxSyncPoint syncp; + mfxStatus status = MFX_ERR_UNKNOWN; + + if (!pWrappedCodec || !pcInDataPtr || !nInDataSize) { + INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR); + } + + if (!m_bOpened) { + INTEL_CHECK_STATUS(MFX_ERR_NOT_INITIALIZED); + } + + nInDataXSize = (pWrappedCodecH264->encoder.neg_width * pWrappedCodecH264->encoder.neg_height * 3) >> 1; + if (nInDataXSize != nInDataSize) + { + /* guard */ + INTEL_DEBUG_ERROR("Invalid size: %u<>%u", nInDataXSize, nInDataSize); + goto bail; + } + + bSendIDR = (pWrappedCodecH264->encoder.frame_count++ == 0 || pWrappedCodecH264->encoder.force_idr); + + nEncSurfIdx = GetFreeSurfaceIndex(); + if (MFX_ERR_NOT_FOUND == nEncSurfIdx) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + + // Surface locking required when read/write D3D surfaces + +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Lock(pWrappedCodecH264->D3D11Allocator.pthis, m_ppSurfacePtrs[nEncSurfIdx]->Data.MemId, &(m_ppSurfacePtrs[nEncSurfIdx]->Data))); +#endif + + INTEL_CHECK_STATUS(status = LoadRawFrame(nEncSurfIdx, pcInDataPtr)); + +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, m_ppSurfacePtrs[nEncSurfIdx]->Data.MemId, &(m_ppSurfacePtrs[nEncSurfIdx]->Data))); +#endif + + m_sFrameCtrl.FrameType = bSendIDR ? (MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF | MFX_FRAMETYPE_IDR) : MFX_FRAMETYPE_UNKNOWN; + + // + // Stage 1: Main encoding loop + // + do + { + for (;;) + { + // Encode a frame asychronously (returns immediately) + status = m_Inst.EncodeFrameAsync(&m_sFrameCtrl, m_ppSurfacePtrs[nEncSurfIdx], &m_sBitstream, &syncp); + + if (MFX_ERR_NONE < status && !syncp) // Repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == status) + { + tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call + } + } + else if (MFX_ERR_NONE < status && syncp) + { + status = MFX_ERR_NONE; // Ignore warnings if output is available + break; + } + else if (MFX_ERR_NOT_ENOUGH_BUFFER == status) + { + // Allocate more bitstream buffer memory here if needed... + break; + } + else + { + if (status != MFX_ERR_MORE_DATA) + { + INTEL_CHECK_STATUS(status); + } + break; + } + } + if (MFX_ERR_NONE == status) + { + INTEL_CHECK_STATUS(m_pSession->SyncOperation(syncp, 60000)); // Synchronize. Wait until encoded frame is ready + if (m_sBitstream.DataLength > 0) + { + tdav_codec_h264_rtp_encap(pWrappedCodecCommon, (const uint8_t*)(m_sBitstream.Data + m_sBitstream.DataOffset), (tsk_size_t)m_sBitstream.DataLength); + m_sBitstream.DataLength = 0; + pWrappedCodecH264->encoder.force_idr = tsk_false; // reset + } + } + } while (0); + + // + // Stage 2: Retrieve the buffered encoded frames + // + while (MFX_ERR_NONE <= status) + { + for (;;) + { + // Encode a frame asychronously (returns immediately) + status = m_Inst.EncodeFrameAsync(&m_sFrameCtrl, NULL, &m_sBitstream, &syncp); + + if (MFX_ERR_NONE < status && !syncp) // Repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == status) + { + tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call + } + } + else if (MFX_ERR_NONE < status && syncp) + { + status = MFX_ERR_NONE; // Ignore warnings if output is available + break; + } + else + { + break; + } + } + + if (MFX_ERR_NONE == status) + { + INTEL_CHECK_STATUS(m_pSession->SyncOperation(syncp, 60000)); // Synchronize. Wait until encoded frame is + if (m_sBitstream.DataLength > 0) + { + tdav_codec_h264_rtp_encap(pWrappedCodecCommon, (const uint8_t*)(m_sBitstream.Data + m_sBitstream.DataOffset), (tsk_size_t)m_sBitstream.DataLength); + m_sBitstream.DataLength = 0; + pWrappedCodecH264->encoder.force_idr = tsk_false; // reset + } + } + } + + bail: + return MFX_ERR_NONE; + } + +private: + MFXVideoENCODE m_Inst; + mfxEncodeCtrl m_sFrameCtrl; + mfxExtCodingOption m_sOptLowLatency; + mfxExtCodingOption2 m_sOpt2MaxFrameSize; + mfxExtBuffer* m_pExtendedBuffers[2]; // Not allocated +}; + + +// +// IntelCodecDecoder +// +class IntelCodecDecoder : public IntelCodec +{ +public: + IntelCodecDecoder(MFXVideoSession* pSession) + : IntelCodec(pSession) + , m_Inst(*pSession) + , m_pAccumulatorPtr(NULL) + , m_nAccumulatorSize(0) + , m_nAccumulatorPos(0) + , m_bInit(false) + { + + } + virtual ~IntelCodecDecoder() + { + Close(); + } + + virtual mfxStatus Close() + { + m_Inst.Close(); + TSK_FREE(m_pAccumulatorPtr); + m_nAccumulatorSize = 0; + m_nAccumulatorPos = 0; + m_bInit = false; + return IntelCodec::Close(); + } + + mfxStatus Open(struct tdav_codec_h264_intel_s* pWrappedCodec) + { + tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec; + + INTEL_DEBUG_INFO("Decoder.Open width=%d, height=%d, fps=%d", + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.width, + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.height, + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.fps + ); + + // Allocation will be done each time we decode the SPS:PPS header + + m_bOpened = true; + return MFX_ERR_NONE; + } + + mfxU32 Decode(struct tmedia_codec_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize, void **ppOutDataPtr, tsk_size_t *pOutDataMaxSize, const trtp_rtp_header_t* pcRtpHdr) + { + mfxU32 nRetSize = 0, nOutXSize; + mfxStatus status = MFX_ERR_NONE; + tsk_bool_t append_scp, end_of_unit; + tsk_bool_t sps_or_pps; + const uint8_t* pay_ptr = tsk_null; + tsk_size_t pay_size = 0, size_to_copy = 0; + bool bGotFrame = false; + mfxFrameSurface1* pmfxOutSurface = NULL; + static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error) + static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX); + + tdav_codec_h264_intel_t* pWrappedCodecH264 = (tdav_codec_h264_intel_t*)pWrappedCodec; + tdav_codec_h264_common_t* pWrappedCodecCommon = (tdav_codec_h264_common_t*)pWrappedCodec; + + if (!pWrappedCodec || !pcInDataPtr || !nInDataSize || !ppOutDataPtr) + { + INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR); + } + //INTEL_DEBUG_INFO("Size=%u", nInDataSize); + if (!m_bOpened) + { + INTEL_CHECK_STATUS(MFX_ERR_NOT_INITIALIZED); + } + + + /* 5.3. NAL Unit Octet Usage + +---------------+ + |0|1|2|3|4|5|6|7| + +-+-+-+-+-+-+-+-+ + |F|NRI| Type | + +---------------+ + */ + if (pcInDataPtr[0] & 0x80) // F ?== 1 + { + /* reset accumulator */ + m_nAccumulatorPos = 0; + INTEL_CHECK_STATUS(status = MFX_ERR_UNDEFINED_BEHAVIOR); + } + + // New frame? + if (m_nLastRtpTimestamp != pcRtpHdr->timestamp) + { + m_nAccumulatorPos = 0; + m_nLastRtpTimestamp = pcRtpHdr->timestamp; + } + + /* get payload */ + if ((tdav_codec_h264_get_pay(pcInDataPtr, nInDataSize, (const void**)&pay_ptr, &pay_size, &append_scp, &end_of_unit) != 0) || !pay_ptr || !pay_size) + { + INTEL_BREAK("Depayloader failed to get H.264 content"); + } +#if 1 // TODO: MSDK cannot decode slices + end_of_unit = pcRtpHdr->marker; +#endif + //append_scp = tsk_true; + size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0); + // whether it's SPS or PPS (append_scp is false for subsequent FUA chuncks) + sps_or_pps = append_scp && pay_ptr && ((pay_ptr[0] & 0x1F) == 7 || (pay_ptr[0] & 0x1F) == 8); + + // start-accumulator + if (!m_pAccumulatorPtr) + { + if (size_to_copy > xmax_size) + { + INTEL_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size); + m_nAccumulatorPos = 0; + return 0; + } + if (!(m_pAccumulatorPtr = (mfxU8*)tsk_calloc(size_to_copy, sizeof(mfxU8)))) + { + INTEL_DEBUG_ERROR("Failed to allocated new buffer"); + m_nAccumulatorPos = 0; + return 0; + } + m_nAccumulatorSize = (mfxU32)size_to_copy; + } + if ((m_nAccumulatorPos + size_to_copy) >= xmax_size) + { + INTEL_DEBUG_ERROR("BufferOverflow"); + m_nAccumulatorPos = 0; + return 0; + } + if ((m_nAccumulatorPos + size_to_copy) > m_nAccumulatorSize) + { + if (!(m_pAccumulatorPtr = (mfxU8*)tsk_realloc(m_pAccumulatorPtr, (m_nAccumulatorPos + size_to_copy)))) + { + INTEL_DEBUG_ERROR("Failed to reallocated new buffer"); + m_nAccumulatorPos = 0; + m_nAccumulatorSize = 0; + return 0; + } + m_nAccumulatorSize = (mfxU32)(m_nAccumulatorPos + size_to_copy); + } + + if (append_scp) + { + memcpy(&m_pAccumulatorPtr[m_nAccumulatorPos], H264_START_CODE_PREFIX, start_code_prefix_size); + m_nAccumulatorPos += (mfxU32)start_code_prefix_size; + } + memcpy(&m_pAccumulatorPtr[m_nAccumulatorPos], pay_ptr, pay_size); + m_nAccumulatorPos += (mfxU32)pay_size; + // end-accumulator + + if (/*rtp_hdr->marker*/end_of_unit) + { + /* decode the picture */ + mfxU32 nOutWidth, nOutHeight; + + // Decode a Unit + status = DecodeFrame(pWrappedCodecH264, m_pAccumulatorPtr, m_nAccumulatorPos, !!sps_or_pps, &pmfxOutSurface, bGotFrame); + if (status != MFX_ERR_NONE) + { + INTEL_DEBUG_WARN("DecodeFrame failed: %d", status); + goto bail; + } + + // Do we have a complete frame? + if (!bGotFrame || !pmfxOutSurface) + { + goto bail; + } + +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Lock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data))); +#endif + if (!pmfxOutSurface->Data.Y || !pmfxOutSurface->Data.U || !pmfxOutSurface->Data.V) + { +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data))); +#endif + goto bail; + } + + if (pmfxOutSurface->Info.CropW > 0 && pmfxOutSurface->Info.CropH > 0) + { + nOutWidth = pmfxOutSurface->Info.CropW; + nOutHeight = pmfxOutSurface->Info.CropH; + } + else + { + nOutWidth = pmfxOutSurface->Info.Width; + nOutHeight = pmfxOutSurface->Info.Height; + } + + nOutXSize = (nOutWidth * nOutHeight * 3) >> 1; // I420 + /* IDR ? */ + if (((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback) + { + INTEL_DEBUG_INFO("Decoded H.264 IDR"); + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.type = tmedia_video_decode_result_type_idr; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.proto_hdr = pcRtpHdr; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback(&TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result); + } + /* fill out */ + if (*pOutDataMaxSize < nOutXSize) + { + if ((*ppOutDataPtr = tsk_realloc(*ppOutDataPtr, nOutXSize))) + { + *pOutDataMaxSize = nOutXSize; + } + else + { + *pOutDataMaxSize = 0; + return 0; + } + } + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.width = nOutWidth; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.height = nOutHeight; + + /* layout picture */ + INTEL_CHECK_STATUS(status = IntelCodecDecoder::LayoutPicture(pmfxOutSurface, (mfxU8 *)*ppOutDataPtr)); + nRetSize = nOutXSize; +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodecH264->D3D11Allocator.Unlock(pWrappedCodecH264->D3D11Allocator.pthis, pmfxOutSurface->Data.MemId, &(pmfxOutSurface->Data))); +#endif + } // else if(rtp_hdr->marker) + + bail: + if (end_of_unit) + { + /* reset accumulator */ + m_nAccumulatorPos = 0; + } + + if (status != MFX_ERR_NONE) + { + INTEL_DEBUG_INFO("Failed to decode the buffer with error code =%d, size=%u, append=%s", status, m_nAccumulatorPos, append_scp ? "yes" : "no"); + if (TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback) + { + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.type = tmedia_video_decode_result_type_error; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result.proto_hdr = pcRtpHdr; + TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.callback(&TMEDIA_CODEC_VIDEO(pWrappedCodec)->in.result); + } + } + return nRetSize; + } + +private: +#if 0 + static mfxStatus WriteSection(mfxU8* plane, mfxU16 factor, mfxU16 chunksize, mfxFrameInfo *pInfo, mfxFrameData *pData, mfxU32 i, mfxU32 j, mfxU8 *pDstPtr) + { + memcpy(pDstPtr, plane + (pInfo->CropY * pData->Pitch / factor + pInfo->CropX) + i * pData->Pitch + j, chunksize); + return MFX_ERR_NONE; + } +#else +#define WriteSection(_plane, _factor, _chunksize, _pInfo, _pData, _i, _j, _pDstPtr) \ + memcpy((_pDstPtr), (_plane) + ((_pInfo)->CropY * (_pData)->Pitch / (_factor) + (_pInfo)->CropX) + (_i) * (_pData)->Pitch + (_j), (_chunksize)) +#define WriteSection1(_plane, _factor, _pInfo, _pData, _i, _j, _pDstPtr) \ + *(_pDstPtr) = *((_plane) + ((_pInfo)->CropY * (_pData)->Pitch / (_factor) + (_pInfo)->CropX) + (_i) * (_pData)->Pitch + (_j)); +#endif + + static mfxStatus LayoutPicture(mfxFrameSurface1 *pSurface, mfxU8 *pDstPtr) + { +#if 1 // ->YUV420 + mfxFrameInfo *pInfo = &pSurface->Info; + mfxFrameData *pData = &pSurface->Data; + mfxU32 i, j, h, w; + + if (pSurface->Info.CropW > 0 && pSurface->Info.CropH > 0) + { + w = pSurface->Info.CropW; + h = pSurface->Info.CropH; + } + else + { + w = pSurface->Info.Width; + h = pSurface->Info.Height; + } + + for (i = 0; i < h; i++) + { + WriteSection(pData->Y, 1, w, pInfo, pData, i, 0, pDstPtr); + pDstPtr += w; + } + + h >>= 1; + for (i = 0; i < h; i++) + { + for (j = 0; j < w; j += 2) + { + WriteSection1(pData->UV, 2, pInfo, pData, i, j, pDstPtr); + pDstPtr += 1; + } + } + + for (i = 0; i < h; i++) + { + for (j = 1; j < w; j += 2) + { + WriteSection1(pData->UV, 2, pInfo, pData, i, j, pDstPtr); + pDstPtr += 1; + } + } +#elif 1 // ->NV12 + mfxFrameInfo *pInfo = &pSurface->Info; + mfxFrameData *pData = &pSurface->Data; + mfxU32 i, j, h, w; + + if (pSurface->Info.CropW > 0 && pSurface->Info.CropH > 0) + { + w = pSurface->Info.CropW; + h = pSurface->Info.CropH; + } + else + { + w = pSurface->Info.Width; + h = pSurface->Info.Height; + } + + for (i = 0; i < h; i++) + { + WriteSection(pData->Y, 1, w, pInfo, pData, i, 0, pDstPtr); + pDstPtr += w; + } + + h >>= 1; + for (i = 0; i < h; i++) + { + for (j = 0; j < w; j += 2) + { + WriteSection1(pData->UV, 2, pInfo, pData, i, j, &pDstPtr[0]); + WriteSection1(pData->UV, 2, pInfo, pData, i, j + 1, &pDstPtr[1]); + pDstPtr += 2; + } + } +#endif + + return MFX_ERR_NONE; + } + + mfxStatus DecodeFrame(struct tdav_codec_h264_intel_s* pWrappedCodec, const mfxU8* pcInDataPtr, mfxU32 nInDataSize, bool bSpsOrPps, mfxFrameSurface1** ppmfxOutSurface, bool &bGotFrame) + { + mfxStatus status = MFX_ERR_NONE; + int nSurfaceIndex; + mfxSyncPoint syncp; + bGotFrame = false; + *ppmfxOutSurface = NULL; + mfxFrameSurface1* pmfxOutSurface = NULL; + +#if 0 + if (!bSpsOrPps && !m_bInit) + { + INTEL_CHECK_STATUS(status = MFX_ERR_NOT_INITIALIZED); + } +#endif + + if (m_sBitstream.DataLength < nInDataSize) + { + INTEL_CHECK_STATUS(status = AllocateBitstream(nInDataSize)); + } + memcpy(m_sBitstream.Data, pcInDataPtr, nInDataSize); + m_sBitstream.DataOffset = 0; + m_sBitstream.DataLength = nInDataSize; + m_sBitstream.DataFlag = MFX_BITSTREAM_COMPLETE_FRAME; + + if (bSpsOrPps || !m_bInit) + { + memset(&m_sParamReq, 0, sizeof(m_sParamReq)); + m_sParamReq.mfx.CodecId = MFX_CODEC_AVC; + m_sParamReq.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + m_sParamReq.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; +#if INTEL_DX11_D3D + m_sParamReq.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; +#else + m_sParamReq.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; +#endif + m_sParamReq.AsyncDepth = 1; // Low latency: limits internal frame buffering + + status = m_Inst.DecodeHeader(&m_sBitstream, &m_sParamReq); + if (status == MFX_WRN_PARTIAL_ACCELERATION) + { + status = MFX_ERR_NONE; + } + INTEL_CHECK_STATUS(status); + + memcpy(&m_sParamSel, &m_sParamReq, sizeof(m_sParamSel)); + + // Check parameters + status = m_Inst.Query(&m_sParamReq, &m_sParamReq); + if (status != MFX_ERR_NONE && status != MFX_WRN_INCOMPATIBLE_VIDEO_PARAM /* Best one will be selected by the encoder */) + { + INTEL_CHECK_STATUS(status); + } + + // Query number required surfaces for encoder + memset(&m_sAllocRequest, 0, sizeof(m_sAllocRequest)); + INTEL_CHECK_STATUS(status = m_Inst.QueryIOSurf(&m_sParamReq, &m_sAllocRequest)); +#if INTEL_DX11_D3D + m_sAllocRequest.Type |= D3D11_WILL_READ; // Hint to DX11 memory handler that application will read data from output surfaces +#endif + + // Allocate surfaces for decoder +#if INTEL_DX11_D3D + INTEL_CHECK_STATUS(status = pWrappedCodec->D3D11Allocator.Alloc(pWrappedCodec->D3D11Allocator.pthis, &m_sAllocRequest, &m_sD3D11Response)); + if (m_sD3D11Response.NumFrameActual == 0) + { + INTEL_CHECK_STATUS(status = MFX_ERR_UNKNOWN); + } + INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sD3D11Response.NumFrameActual); + INTEL_CHECK_STATUS(status = AllocSurfaces(m_sD3D11Response.NumFrameActual, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo)); +#else + INTEL_DEBUG_INFO("nEncSurfNum = %hu", m_sAllocRequest.NumFrameSuggested); + INTEL_CHECK_STATUS(status = AllocSurfaces(m_sAllocRequest.NumFrameSuggested, m_sAllocRequest.Info.Width, m_sAllocRequest.Info.Height, &m_sParamReq.mfx.FrameInfo)); +#endif + // Initialize the Media SDK decoder + status = m_Inst.Init(&m_sParamReq); + if (status != MFX_ERR_NONE && status != MFX_WRN_PARTIAL_ACCELERATION) + { + INTEL_CHECK_STATUS(status); + } + INTEL_DEBUG_INFO("Decoder->Init() returned: %d", status); + + m_bInit = true; + } + + // + // Stage 1: Main decoding loop + // + while (MFX_ERR_NONE <= status || MFX_ERR_MORE_DATA == status || MFX_ERR_MORE_SURFACE == status) + { + if (MFX_WRN_DEVICE_BUSY == status) + { + tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call to DecodeFrameAsync + } + + if (MFX_ERR_MORE_DATA == status) + { + return MFX_ERR_NONE; + } + + if (MFX_ERR_MORE_SURFACE == status || MFX_ERR_NONE == status) + { + nSurfaceIndex = GetFreeSurfaceIndex(); // Find free frame surface + if (MFX_ERR_NOT_FOUND == nSurfaceIndex) + { + INTEL_CHECK_STATUS((status = MFX_ERR_MEMORY_ALLOC)); + } + } + + // Decode a frame asychronously (returns immediately) + // - If input bitstream contains multiple frames DecodeFrameAsync will start decoding multiple frames, and remove them from bitstream + status = m_Inst.DecodeFrameAsync(&m_sBitstream, m_ppSurfacePtrs[nSurfaceIndex], &pmfxOutSurface, &syncp); + + // Ignore warnings if output is available, + // if no output and no action required just repeat the DecodeFrameAsync call + if (MFX_ERR_NONE < status && syncp) + { + status = MFX_ERR_NONE; + } + + if (MFX_ERR_NONE == status) + { + status = m_pSession->SyncOperation(syncp, 60000); // Synchronize. Wait until decoded frame is ready + } + + if (MFX_ERR_NONE == status) + { + bGotFrame = true; + if (pmfxOutSurface) + { + *ppmfxOutSurface = pmfxOutSurface; + } + } + } + + // + // Stage 2: Retrieve the buffered decoded frames + // + while (MFX_ERR_NONE <= status || MFX_ERR_MORE_SURFACE == status) + { + if (MFX_WRN_DEVICE_BUSY == status) + { + tsk_thread_sleep(1); // Wait if device is busy, then repeat the same call to DecodeFrameAsync + } + + nSurfaceIndex = GetFreeSurfaceIndex(); // Find free frame surface + if (MFX_ERR_NOT_FOUND == nSurfaceIndex) + { + INTEL_CHECK_STATUS((status = MFX_ERR_MEMORY_ALLOC)); + } + + // Decode a frame asychronously (returns immediately) + status = m_Inst.DecodeFrameAsync(NULL, m_ppSurfacePtrs[nSurfaceIndex], ppmfxOutSurface, &syncp); + + // Ignore warnings if output is available, + // if no output and no action required just repeat the DecodeFrameAsync call + if (MFX_ERR_NONE < status && syncp) + { + status = MFX_ERR_NONE; + } + + if (MFX_ERR_NONE == status) + { + status = m_pSession->SyncOperation(syncp, 60000); // Synchronize. Waits until decoded frame is ready + } + + if (MFX_ERR_NONE == status) + { + bGotFrame = true; + if (pmfxOutSurface) + { + *ppmfxOutSurface = pmfxOutSurface; + } + } + } + + status = MFX_ERR_NONE; + + bail: + return status; + } +private: + mfxU8 *m_pAccumulatorPtr; + mfxU32 m_nAccumulatorSize; + mfxU32 m_nAccumulatorPos; + mfxU32 m_nLastRtpTimestamp; + MFXVideoDECODE m_Inst; + bool m_bInit; +}; + +/* ============ H.264 Base/Main Profile X.X Plugin interface functions ================= */ + +static int tdav_codec_h264_intel_set(tmedia_codec_t* self, const tmedia_param_t* param) +{ + tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self; + if (param->value_type == tmedia_pvt_int32) + { + if (tsk_striequals(param->key, "action")) + { + tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value); + switch (action) { + case tmedia_codec_action_encode_idr: + { + h264->encoder.force_idr = tsk_true; + return 0; + } + case tmedia_codec_action_bw_up: + case tmedia_codec_action_bw_down: + { + if (self->opened) + { + INTEL_CHECK_STATUS(h264->encoder.pInst->UpdateBandwidth(action == tmedia_codec_action_bw_up, TMEDIA_CODEC(h264)->bandwidth_max_upload)); + } + break; + } + } + } + else if (tsk_striequals(param->key, "bw_kbps")) // both up and down (from the SDP) + { + int32_t max_bw_userdefine = tmedia_defaults_get_bandwidth_video_upload_max(); + int32_t max_bw_new = *((int32_t*)param->value); + if (max_bw_userdefine > 0) + { + // do not use more than what the user defined in it's configuration + TMEDIA_CODEC(h264)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine); + } + else + { + TMEDIA_CODEC(h264)->bandwidth_max_upload = max_bw_new; + } + INTEL_DEBUG_INFO("bandwidth-max-upload=%d", TMEDIA_CODEC(h264)->bandwidth_max_upload); + if (self->opened) + { + INTEL_CHECK_STATUS(h264->encoder.pInst->SetMaxBandwidth(TMEDIA_CODEC(h264)->bandwidth_max_upload)); + } + return 0; + } + else if (tsk_striequals(param->key, "bandwidth-max-upload")) + { + int32_t bw_max_upload = *((int32_t*)param->value); + TSK_DEBUG_INFO("OpenH264 codec: bandwidth-max-upload=%d", bw_max_upload); + TMEDIA_CODEC(h264)->bandwidth_max_upload = bw_max_upload; + if (self->opened) + { + INTEL_CHECK_STATUS(h264->encoder.pInst->SetMaxBandwidth(TMEDIA_CODEC(h264)->bandwidth_max_upload)); + } + return 0; + } + else if (tsk_striequals(param->key, "rotation")) + { + int rotation = *((int32_t*)param->value); + if (h264->encoder.rotation != rotation) + { + if (self->opened) + { + int ret; + h264->encoder.rotation = rotation; + if ((ret = tdav_codec_h264_intel_close_encoder(h264))) + { + return ret; + } + if ((ret = tdav_codec_h264_intel_open_encoder(h264))) + { + return ret; + } + } + } + return 0; + } + } +bail: + + return -1; +} + + +static int tdav_codec_h264_intel_open(tmedia_codec_t* self) +{ + int ret; + tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self; + + if (!h264) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + /* the caller (base class) already checked that the codec is not opened */ + + // Encoder + if ((ret = tdav_codec_h264_intel_open_encoder(h264))) { + return ret; + } + + // Decoder + if ((ret = tdav_codec_h264_intel_open_decoder(h264))) { + return ret; + } + + return 0; +} + +static int tdav_codec_h264_intel_close(tmedia_codec_t* self) +{ + tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self; + + if (!h264) + { + INTEL_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + /* the caller (base class) alreasy checked that the codec is opened */ + + // Encoder + tdav_codec_h264_intel_close_encoder(h264); + + // Decoder + tdav_codec_h264_intel_close_decoder(h264); + + return 0; +} + +static tsk_size_t tdav_codec_h264_intel_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size) +{ + tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self; + (void)(out_data); + (void)(out_max_size); + + if (!h264 || !h264->encoder.pInst) + { + INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR); + } + INTEL_CHECK_STATUS(h264->encoder.pInst->Encode(self, (const mfxU8*)in_data, (mfxU32)in_size)); +bail: + return 0; +} + +static tsk_size_t tdav_codec_h264_intel_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr) +{ + tdav_codec_h264_intel_t* h264 = (tdav_codec_h264_intel_t*)self; + + if (!h264 || !h264->decoder.pInst) + { + INTEL_CHECK_STATUS(MFX_ERR_NULL_PTR); + } + return (tsk_size_t)h264->decoder.pInst->Decode(self, (const mfxU8*)in_data, (mfxU32)in_size, out_data, out_max_size, (const trtp_rtp_header_t*)proto_hdr); +bail: + return 0; +} + +static tsk_bool_t tdav_codec_h264_intel_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value) +{ + return tdav_codec_h264_common_sdp_att_match((tdav_codec_h264_common_t*)self, att_name, att_value); +} + +static char* tdav_codec_h264_intel_sdp_att_get(const tmedia_codec_t* self, const char* att_name) +{ + char* att = tdav_codec_h264_common_sdp_att_get((const tdav_codec_h264_common_t*)self, att_name); + if (att && tsk_striequals(att_name, "fmtp")) { + tsk_strcat(&att, "; impl=intel"); + } + return att; +} + +/* ============ H.264 Base Profile Plugin interface ================= */ + +/* constructor */ +static tsk_object_t* tdav_codec_h264_intel_base_ctor(tsk_object_t * self, va_list * app) +{ + tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self; + if (h264) { + /* init base: called by tmedia_codec_create() */ + /* init self */ + if (tdav_codec_h264_intel_init(h264, profile_idc_baseline) != 0) { + return tsk_null; + } + } + return self; +} +/* destructor */ +static tsk_object_t* tdav_codec_h264_intel_base_dtor(tsk_object_t * self) +{ + tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self; + if (h264) { + /* deinit base */ + tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self)); + /* deinit self */ + tdav_codec_h264_intel_deinit(h264); + } + + return self; +} +/* object definition */ +static const tsk_object_def_t tdav_codec_h264_intel_base_def_s = +{ + sizeof(tdav_codec_h264_intel_t), + tdav_codec_h264_intel_base_ctor, + tdav_codec_h264_intel_base_dtor, + tmedia_codec_cmp, +}; +/* plugin definition*/ +static const tmedia_codec_plugin_def_t tdav_codec_h264_intel_base_plugin_def_s = +{ + &tdav_codec_h264_intel_base_def_s, + + tmedia_video, + tmedia_codec_id_h264_bp, + "H264", + "H264 Base Profile (Intel Media SDK)", + TMEDIA_CODEC_FORMAT_H264_BP, + tsk_true, + 90000, // rate + + /* audio */ + { 0 }, + + /* video (width, height, fps) */ + { 176, 144, 0 }, // fps is @deprecated + + tdav_codec_h264_intel_set, + tdav_codec_h264_intel_open, + tdav_codec_h264_intel_close, + tdav_codec_h264_intel_encode, + tdav_codec_h264_intel_decode, + tdav_codec_h264_intel_sdp_att_match, + tdav_codec_h264_intel_sdp_att_get +}; +const tmedia_codec_plugin_def_t *tdav_codec_h264_intel_base_plugin_def_t = &tdav_codec_h264_intel_base_plugin_def_s; + + +/* ============ H.264 Main Profile Plugin interface ================= */ + +/* constructor */ +static tsk_object_t* tdav_codec_h264_intel_main_ctor(tsk_object_t * self, va_list * app) +{ + tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self; + if (h264) { + /* init main: called by tmedia_codec_create() */ + /* init self */ + if (tdav_codec_h264_intel_init(h264, profile_idc_main) != 0) { + return tsk_null; + } + } + return self; +} +/* destructor */ +static tsk_object_t* tdav_codec_h264_intel_main_dtor(tsk_object_t * self) +{ + tdav_codec_h264_intel_t *h264 = (tdav_codec_h264_intel_t*)self; + if (h264) + { + /* deinit main */ + tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self)); + /* deinit self */ + tdav_codec_h264_intel_deinit(h264); + } + + return self; +} +/* object definition */ +static const tsk_object_def_t tdav_codec_h264_intel_main_def_s = +{ + sizeof(tdav_codec_h264_intel_t), + tdav_codec_h264_intel_main_ctor, + tdav_codec_h264_intel_main_dtor, + tmedia_codec_cmp, +}; +/* plugin definition*/ +static const tmedia_codec_plugin_def_t tdav_codec_h264_intel_main_plugin_def_s = +{ + &tdav_codec_h264_intel_main_def_s, + + tmedia_video, + tmedia_codec_id_h264_mp, + "H264", + "H264 main Profile (Intel Media SDK)", + TMEDIA_CODEC_FORMAT_H264_MP, + tsk_true, + 90000, // rate + + /* audio */ + { 0 }, + + /* video (width, height, fps) */ + { 176, 144, 0 }, // fps is @deprecated + + tdav_codec_h264_intel_set, + tdav_codec_h264_intel_open, + tdav_codec_h264_intel_close, + tdav_codec_h264_intel_encode, + tdav_codec_h264_intel_decode, + tdav_codec_h264_intel_sdp_att_match, + tdav_codec_h264_intel_sdp_att_get +}; +const tmedia_codec_plugin_def_t *tdav_codec_h264_intel_main_plugin_def_t = &tdav_codec_h264_intel_main_plugin_def_s; + +/* ============ Common To all H264 profiles ================= */ + +static int tdav_codec_h264_intel_open_encoder(tdav_codec_h264_intel_t* self) +{ + if (self->encoder.pInst) + { + INTEL_BREAK("Already initialized"); + } + + if (!(self->encoder.pInst = new IntelCodecEncoder(self->mfxSession))) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + INTEL_CHECK_STATUS(self->encoder.pInst->Open(self)); + + return 0; + +bail: + if (self->encoder.pInst) + { + delete self->encoder.pInst, self->encoder.pInst = NULL; + } + return -1; +} + +static int tdav_codec_h264_intel_close_encoder(tdav_codec_h264_intel_t* self) +{ + if (self) + { + if (self->encoder.pInst) + { + delete self->encoder.pInst; + self->encoder.pInst = NULL; + } + self->encoder.frame_count = 0; + } + return 0; +} + +int tdav_codec_h264_intel_open_decoder(tdav_codec_h264_intel_t* self) +{ + if (self->decoder.pInst) + { + INTEL_BREAK("Already initialized"); + } + + if (!(self->decoder.pInst = new IntelCodecDecoder(self->mfxSession))) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + INTEL_CHECK_STATUS(self->decoder.pInst->Open(self)); + + return 0; + +bail: + if (self->decoder.pInst) + { + delete self->decoder.pInst, self->decoder.pInst = NULL; + } + return -1; +} + +static int tdav_codec_h264_intel_close_decoder(tdav_codec_h264_intel_t* self) +{ + if (self) + { + if (self->decoder.pInst) + { + delete self->decoder.pInst; + self->decoder.pInst = NULL; + } + } + return 0; +} + +static int tdav_codec_h264_intel_init(tdav_codec_h264_intel_t* self, profile_idc_t profile) +{ + int ret = -1; + level_idc_t level; + tdav_codec_h264_common_t* common = (tdav_codec_h264_common_t*)self; +#if INTEL_DX11_D3D + mfxHDL deviceHandle = NULL; +#endif + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + goto bail; + } + + if ((ret = tdav_codec_h264_common_init(common))) { + TSK_DEBUG_ERROR("tdav_codec_h264_intel_common_init() faile with error code=%d", ret); + goto bail; + } + + if ((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))) { + TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height); + goto bail; + } + + if (/*MFUtils::IsLowLatencyH264SupportsMaxSliceSize()*/0) { // TODO: MSDK doesn't support PM=0. Neg. PM=1 but try to do the best to produce SingleNalUnits + common->pack_mode_local = H264_PACKETIZATION_MODE; + } + else { + common->pack_mode_local = Non_Interleaved_Mode; + } + common->profile = profile; + common->level = level; +#if 0 + // A.2.1.1 Constrained Baseline profile + // Conformance of a bitstream to the Constrained Baseline profile is indicated by profile_idc being equal to 66 with + // constraint_set1_flag being equal to 1. + common->profile_iop = 0xe0; // "constraint_set0_flag=1 and constraint_set1_flag=1" -> Constrained Baseline profile +#endif + TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS * 1000; + TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR * 1000; + + TMEDIA_CODEC_VIDEO(self)->in.chroma = tmedia_chroma_yuv420p; // decoded + TMEDIA_CODEC_VIDEO(self)->out.chroma = tmedia_chroma_yuv420p; // encoded + + if (!self->mfxSession && !(self->mfxSession = new MFXVideoSession())) + { + INTEL_CHECK_STATUS(MFX_ERR_MEMORY_ALLOC); + } + INTEL_CHECK_STATUS(self->mfxSession->Init(__IntelDefaultImpl, &__IntelDefaultVer)); + +#if INTEL_DX11_D3D + // Create DirectX device context + INTEL_CHECK_STATUS(D3D11_CreateHWDevice(self, *self->mfxSession, &deviceHandle, NULL)); + + // Provide device manager to Media SDK + INTEL_CHECK_STATUS(self->mfxSession->SetHandle(MFX_HANDLE_D3D11_DEVICE, deviceHandle)); + + self->D3D11Allocator.Alloc = D3D11_SimpleAlloc; + self->D3D11Allocator.Free = D3D11_SimpleFree; + self->D3D11Allocator.Lock = D3D11_SimpleLock; + self->D3D11Allocator.Unlock = D3D11_SimpleUnlock; + self->D3D11Allocator.GetHDL = D3D11_SimpleGethdl; + self->D3D11Allocator.pthis = self; + + // Since we are using video memory we must provide Media SDK with an external allocator + INTEL_CHECK_STATUS(self->mfxSession->SetFrameAllocator(&self->D3D11Allocator)); +#endif /* INTEL_DX11_D3D */ + + ret = 0; + +bail: + return ret; +} + +static int tdav_codec_h264_intel_deinit(tdav_codec_h264_intel_t* self) +{ + if (!self) + { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tdav_codec_h264_intel_close((tmedia_codec_t*)self); + + if (self->mfxSession) + { + delete self->mfxSession, self->mfxSession = NULL; + } + +#if INTEL_DX11_D3D + D3D11_CleanupHWDevice(self); +#endif /* INTEL_DX11_D3D */ + + return 0; +} + + +#if INTEL_DX11_D3D + +static IDXGIAdapter* D3D11_GetIntelDeviceAdapterHandle(mfxHDL _pthis, mfxSession session) +{ + mfxU32 adapterNum = 0; + mfxIMPL impl; + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + + MFXQueryIMPL(session, &impl); + + mfxIMPL baseImpl = MFX_IMPL_BASETYPE(impl); // Extract Media SDK base implementation type + + // get corresponding adapter number + for (mfxU8 i = 0; i < sizeof(implTypes) / sizeof(implTypes[0]); i++) + { + if (implTypes[i].impl == baseImpl) + { + adapterNum = implTypes[i].adapterID; + break; + } + } + + HRESULT hres = CreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)(&pthis->pDXGIFactory)); + if (FAILED(hres)) + { + INTEL_DEBUG_ERROR("CreateDXGIFactory failed: %ld", hres); + return NULL; + } + + IDXGIAdapter* adapter = NULL; + hres = pthis->pDXGIFactory->EnumAdapters(adapterNum, &adapter); + if (FAILED(hres)) + { + INTEL_DEBUG_ERROR("EnumAdapters failed: %ld", hres); + return NULL; + } + + return adapter; +} + +// Create DirectX 11 device context +// - Required when using D3D surfaces. +// - D3D Device created and handed to Intel Media SDK +// - Intel graphics device adapter will be determined automatically (does not have to be primary), +// but with the following caveats: +// - Device must be active (but monitor does NOT have to be attached) +// - Device must be enabled in BIOS. Required for the case when used together with a discrete graphics card +// - For switchable graphics solutions (mobile) make sure that Intel device is the active device +static mfxStatus D3D11_CreateHWDevice(mfxHDL _pthis, mfxSession session, mfxHDL* deviceHandle, HWND hWnd) +{ + hWnd; // Window handle not required by DX11 since we do not showcase rendering. + + HRESULT hres = S_OK; + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + + static D3D_FEATURE_LEVEL FeatureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0 + }; + D3D_FEATURE_LEVEL pFeatureLevelsOut; + + pthis->hAdapter = D3D11_GetIntelDeviceAdapterHandle(_pthis, session); + if (NULL == pthis->hAdapter) + { + INTEL_DEBUG_ERROR("D3D11_GetIntelDeviceAdapterHandle failed"); + return MFX_ERR_DEVICE_FAILED; + } + + hres = D3D11CreateDevice(pthis->hAdapter, + D3D_DRIVER_TYPE_UNKNOWN, + NULL, + 0, + FeatureLevels, + (sizeof(FeatureLevels) / sizeof(FeatureLevels[0])), + D3D11_SDK_VERSION, + &pthis->pD3D11Device, + &pFeatureLevelsOut, + &pthis->pD3D11Ctx); + if (FAILED(hres)) + { + INTEL_DEBUG_ERROR("D3D11CreateDevice failed: %ld", hres); + return MFX_ERR_DEVICE_FAILED; + } + + // turn on multithreading for the DX11 cntext + CComQIPtr<ID3D10Multithread> p_mt(pthis->pD3D11Ctx); + if (p_mt) + { + p_mt->SetMultithreadProtected(true); + } + else + { + INTEL_DEBUG_ERROR("Failed to create ID3D10Multithread object"); + return MFX_ERR_DEVICE_FAILED; + } + + *deviceHandle = (mfxHDL)pthis->pD3D11Device; + return MFX_ERR_NONE; +} + +static void D3D11_CleanupHWDevice(mfxHDL _pthis) +{ + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + pthis->pD3D11Device = NULL; + pthis->pDXGIFactory = NULL; + pthis->pD3D11Ctx = NULL; + pthis->hAdapter = NULL; + pthis->pAdapter = NULL; +} + +static void D3D11_SetHWDeviceContext(mfxHDL _pthis, CComPtr<ID3D11DeviceContext> devCtx) +{ + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + pthis->pD3D11Ctx = devCtx; + devCtx->GetDevice(&pthis->pD3D11Device); +} + +// Intel Media SDK memory allocator entrypoints.... +// - A slightly different allocation procedure is used for encode, decode and VPP +static mfxStatus _D3D11_SimpleAlloc(mfxHDL _pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + HRESULT hRes; + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + + // Determine surface format (current simple implementation only supports NV12 and RGB4(32)) + DXGI_FORMAT format; + if (MFX_FOURCC_NV12 == request->Info.FourCC) + { + format = DXGI_FORMAT_NV12; + } + else if (MFX_FOURCC_RGB4 == request->Info.FourCC) + { + format = DXGI_FORMAT_B8G8R8A8_UNORM; + } + else + { + format = DXGI_FORMAT_P8; + } + + // Allocate custom container to keep texture and stage buffers for each surface + // Container also stores the intended read and/or write operation. + CustomMemId** mids = new CustomMemId *[request->NumFrameSuggested]; + if (!mids) return MFX_ERR_MEMORY_ALLOC; + for (int i = 0; i < request->NumFrameSuggested; i++) + { + mids[i] = new CustomMemId; + memset(mids[i], 0, sizeof(CustomMemId)); + mids[i]->rw = request->Type & 0xF000; // Set intended read/write operation + } + + request->Type = request->Type & 0x0FFF; + + // because P8 data (bitstream) for h264 encoder should be allocated by CreateBuffer() + // but P8 data (MBData) for MPEG2 encoder should be allocated by CreateTexture2D() + if (request->Info.FourCC == MFX_FOURCC_P8) + { + D3D11_BUFFER_DESC desc = { 0 }; + + desc.ByteWidth = request->Info.Width * request->Info.Height; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + + ID3D11Buffer * buffer = 0; + hRes = pthis->pD3D11Device->CreateBuffer(&desc, 0, &buffer); + if (FAILED(hRes)) + { + INTEL_DEBUG_ERROR("CreateBuffer failed:%ld", hRes); + return MFX_ERR_MEMORY_ALLOC; + } + + mids[0]->memId = reinterpret_cast<ID3D11Texture2D *>(buffer); + } + else + { + D3D11_TEXTURE2D_DESC desc = { 0 }; + + desc.Width = request->Info.Width; + desc.Height = request->Info.Height; + desc.MipLevels = 1; + desc.ArraySize = 1; // number of subresources is 1 in this case + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_DECODER; + desc.MiscFlags = 0; + + if ((MFX_MEMTYPE_FROM_VPPIN & request->Type) && + (DXGI_FORMAT_B8G8R8A8_UNORM == desc.Format)) + { + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (desc.ArraySize > 2) + return MFX_ERR_MEMORY_ALLOC; + } + + if ((MFX_MEMTYPE_FROM_VPPOUT & request->Type) || + (MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET & request->Type)) + { + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (desc.ArraySize > 2) + { + return MFX_ERR_MEMORY_ALLOC; + } + } + + if (DXGI_FORMAT_P8 == desc.Format) + desc.BindFlags = 0; + + ID3D11Texture2D* pTexture2D; + + // Create surface textures + for (size_t i = 0; i < request->NumFrameSuggested / desc.ArraySize; i++) + { + hRes = pthis->pD3D11Device->CreateTexture2D(&desc, NULL, &pTexture2D); + + if (FAILED(hRes)) + { + INTEL_DEBUG_ERROR("CreateTexture2D failed:%ld", hRes); + return MFX_ERR_MEMORY_ALLOC; + } + + mids[i]->memId = pTexture2D; + } + + desc.ArraySize = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ /*| D3D11_CPU_ACCESS_WRITE*/; + desc.BindFlags = 0; + desc.MiscFlags = 0; + + // Create surface staging textures + for (size_t i = 0; i < request->NumFrameSuggested; i++) + { + hRes = pthis->pD3D11Device->CreateTexture2D(&desc, NULL, &pTexture2D); + + if (FAILED(hRes)) + { + INTEL_DEBUG_ERROR("CreateTexture2D failed:%ld", hRes); + return MFX_ERR_MEMORY_ALLOC; + } + + mids[i]->memIdStage = pTexture2D; + } + } + + response->mids = (mfxMemId*)mids; + response->NumFrameActual = request->NumFrameSuggested; + + return MFX_ERR_NONE; +} + +static mfxStatus D3D11_SimpleAlloc(mfxHDL _pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + mfxStatus sts = MFX_ERR_NONE; + int idx = (MFX_MEMTYPE_FROM_DECODE & request->Type) ? 1 : 0; + + if (request->NumFrameSuggested <= pthis->D3D11SavedAllocResponses[idx].NumFrameActual && + MFX_MEMTYPE_EXTERNAL_FRAME & request->Type && + MFX_MEMTYPE_FROM_DECODE & request->Type && + pthis->D3D11SavedAllocResponses[idx].NumFrameActual != 0) + { + // Memory for this request was already allocated during manual allocation stage. Return saved response + // When decode acceleration device (DXVA) is created it requires a list of d3d surfaces to be passed. + // Therefore Media SDK will ask for the surface info/mids again at Init() stage, thus requiring us to return the saved response + // (No such restriction applies to Encode or VPP) + + *response = pthis->D3D11SavedAllocResponses[idx]; + } + else + { + sts = _D3D11_SimpleAlloc(_pthis, request, response); + pthis->D3D11SavedAllocResponses[idx] = *response; + } + + return sts; +} + +static mfxStatus D3D11_SimpleLock(mfxHDL _pthis, mfxMemId mid, mfxFrameData *ptr) +{ + HRESULT hRes = S_OK; + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + + D3D11_TEXTURE2D_DESC desc = { 0 }; + D3D11_MAPPED_SUBRESOURCE lockedRect = { 0 }; + + CustomMemId* memId = (CustomMemId*)mid; + ID3D11Texture2D* pSurface = (ID3D11Texture2D *)memId->memId; + ID3D11Texture2D* pStage = (ID3D11Texture2D *)memId->memIdStage; + + D3D11_MAP mapType = D3D11_MAP_READ; + UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT; + + if (NULL == pStage) + { + hRes = pthis->pD3D11Ctx->Map(pSurface, 0, mapType, mapFlags, &lockedRect); + desc.Format = DXGI_FORMAT_P8; + } + else + { + pSurface->GetDesc(&desc); + + // copy data only in case of user wants o read from stored surface + if (memId->rw & D3D11_WILL_READ) + { + pthis->pD3D11Ctx->CopySubresourceRegion(pStage, 0, 0, 0, 0, pSurface, 0, NULL); + } + + do + { + hRes = pthis->pD3D11Ctx->Map(pStage, 0, mapType, mapFlags, &lockedRect); + if (S_OK != hRes && DXGI_ERROR_WAS_STILL_DRAWING != hRes) + { + return MFX_ERR_LOCK_MEMORY; + } + } while (DXGI_ERROR_WAS_STILL_DRAWING == hRes); + } + + if (FAILED(hRes)) + { + return MFX_ERR_LOCK_MEMORY; + } + + switch (desc.Format) + { + case DXGI_FORMAT_NV12: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch; + ptr->V = ptr->U + 1; + break; + case DXGI_FORMAT_B8G8R8A8_UNORM: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->B = (mfxU8 *)lockedRect.pData; + ptr->G = ptr->B + 1; + ptr->R = ptr->B + 2; + ptr->A = ptr->B + 3; + break; + case DXGI_FORMAT_P8: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = 0; + ptr->V = 0; + break; + default: + return MFX_ERR_LOCK_MEMORY; + } + + return MFX_ERR_NONE; +} + +static mfxStatus D3D11_SimpleUnlock(mfxHDL _pthis, mfxMemId mid, mfxFrameData *ptr) +{ + tdav_codec_h264_intel_t* pthis = (tdav_codec_h264_intel_t*)_pthis; + + CustomMemId* memId = (CustomMemId*)mid; + ID3D11Texture2D* pSurface = (ID3D11Texture2D *)memId->memId; + ID3D11Texture2D* pStage = (ID3D11Texture2D *)memId->memIdStage; + + if (NULL == pStage) + { + pthis->pD3D11Ctx->Unmap(pSurface, 0); + } + else + { + pthis->pD3D11Ctx->Unmap(pStage, 0); + // copy data only in case of user wants to write to stored surface + if (memId->rw & D3D11_WILL_WRITE) + { + pthis->pD3D11Ctx->CopySubresourceRegion(pSurface, 0, 0, 0, 0, pStage, 0, NULL); + } + } + + if (ptr) + { + ptr->Pitch = 0; + ptr->U = ptr->V = ptr->Y = 0; + ptr->A = ptr->R = ptr->G = ptr->B = 0; + } + + return MFX_ERR_NONE; +} + +static mfxStatus D3D11_SimpleGethdl(mfxHDL _pthis, mfxMemId mid, mfxHDL *handle) +{ + _pthis; + if (NULL == handle) + { + return MFX_ERR_INVALID_HANDLE; + } + + mfxHDLPair* pPair = (mfxHDLPair*)handle; + CustomMemId* memId = (CustomMemId*)mid; + + pPair->first = memId->memId; // surface texture + pPair->second = 0; + + return MFX_ERR_NONE; +} + +static mfxStatus D3D11_SimpleFree(mfxHDL _pthis, mfxFrameAllocResponse *response) +{ + _pthis; + if (NULL == response) + { + return MFX_ERR_NULL_PTR; + } + + if (response->mids) + { + for (mfxU32 i = 0; i < response->NumFrameActual; i++) + { + if (response->mids[i]) + { + CustomMemId* mid = (CustomMemId*)response->mids[i]; + ID3D11Texture2D* pSurface = (ID3D11Texture2D *)mid->memId; + ID3D11Texture2D* pStage = (ID3D11Texture2D *)mid->memIdStage; + if (pSurface) + { + pSurface->Release(); + } + if (pStage) + { + pStage->Release(); + } + delete mid; + } + } + } + + delete[] response->mids; + response->mids = 0; + + return MFX_ERR_NONE; +} + +#endif /* INTEL_DX11_D3D */ + +#endif /* HAVE_INTEL_MEDIA_SDK */ |