refactor(hwcodec): 简化 hwcodec 库以适配 IP-KVM 场景

移除 IP-KVM 场景不需要的功能模块:
- 移除 VRAM 模块 (GPU 显存直接编解码)
- 移除 Mux 模块 (视频混流)
- 移除 macOS/Android 平台支持
- 移除外部 SDK 依赖 (~9MB)
- 移除开发工具和示例程序

简化解码器为仅支持 MJPEG (采集卡输出格式)
简化 NVIDIA 检测代码 (使用 dlopen 替代 SDK)
更新版本号至 0.8.0
更新相关技术文档
This commit is contained in:
mofeng-git
2025-12-31 19:47:08 +08:00
parent a8a3b6c66b
commit d0e2e13b35
441 changed files with 467 additions and 143421 deletions

View File

@@ -1,34 +0,0 @@
#include "common.h"
#include <iostream>
#include <public/common/TraceAdapter.h>
#include <stdio.h>
#ifndef AMF_FACILITY
#define AMF_FACILITY L"AMFCommon"
#endif
static bool convert_api(amf::AMF_MEMORY_TYPE &rhs) {
// Always use DX11 since it's the only supported API
rhs = amf::AMF_MEMORY_DX11;
return true;
}
static bool convert_surface_format(SurfaceFormat lhs,
amf::AMF_SURFACE_FORMAT &rhs) {
switch (lhs) {
case SURFACE_FORMAT_NV12:
rhs = amf::AMF_SURFACE_NV12;
break;
case SURFACE_FORMAT_RGBA:
rhs = amf::AMF_SURFACE_RGBA;
break;
case SURFACE_FORMAT_BGRA:
rhs = amf::AMF_SURFACE_BGRA;
break;
default:
std::cerr << "unsupported surface format: " << static_cast<int>(lhs)
<< "\n";
return false;
}
return true;
}

View File

@@ -1,451 +0,0 @@
#include <public/common/AMFFactory.h>
#include <public/common/AMFSTL.h>
#include <public/common/ByteArray.h>
#include <public/common/Thread.h>
#include <public/common/TraceAdapter.h>
#include <public/include/components/VideoConverter.h>
#include <public/include/components/VideoDecoderUVD.h>
#include <cstring>
#include <iostream>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "AMFDEC"
#include "log.h"
#define AMF_FACILITY L"AMFDecoder"
#define AMF_CHECK_RETURN(res, msg) \
if (res != AMF_OK) { \
LOG_ERROR(std::string(msg) + ", result code: " + std::to_string(int(res))); \
return res; \
}
namespace {
class AMFDecoder {
private:
// system
void *device_;
int64_t luid_;
std::unique_ptr<NativeDevice> nativeDevice_ = nullptr;
// amf
AMFFactoryHelper AMFFactory_;
amf::AMFContextPtr AMFContext_ = NULL;
amf::AMFComponentPtr AMFDecoder_ = NULL;
amf::AMF_MEMORY_TYPE AMFMemoryType_;
amf::AMF_SURFACE_FORMAT decodeFormatOut_ = amf::AMF_SURFACE_NV12;
amf::AMF_SURFACE_FORMAT textureFormatOut_;
amf::AMFComponentPtr AMFConverter_ = NULL;
int last_width_ = 0;
int last_height_ = 0;
amf_wstring codec_;
bool full_range_ = false;
bool bt709_ = false;
// buffer
std::vector<std::vector<uint8_t>> buffer_;
public:
AMFDecoder(void *device, int64_t luid, amf::AMF_MEMORY_TYPE memoryTypeOut,
amf_wstring codec, amf::AMF_SURFACE_FORMAT textureFormatOut) {
device_ = device;
luid_ = luid;
AMFMemoryType_ = memoryTypeOut;
textureFormatOut_ = textureFormatOut;
codec_ = codec;
}
~AMFDecoder() {}
AMF_RESULT decode(uint8_t *iData, uint32_t iDataSize, DecodeCallback callback,
void *obj) {
AMF_RESULT res = AMF_FAIL;
bool decoded = false;
amf::AMFBufferPtr iDataWrapBuffer = NULL;
res = AMFContext_->CreateBufferFromHostNative(iData, iDataSize,
&iDataWrapBuffer, NULL);
AMF_CHECK_RETURN(res, "CreateBufferFromHostNative failed");
res = AMFDecoder_->SubmitInput(iDataWrapBuffer);
if (res == AMF_RESOLUTION_CHANGED) {
iDataWrapBuffer = NULL;
LOG_INFO(std::string("resolution changed"));
res = AMFDecoder_->Drain();
AMF_CHECK_RETURN(res, "Drain failed");
res = AMFDecoder_->Terminate();
AMF_CHECK_RETURN(res, "Terminate failed");
res = AMFDecoder_->Init(decodeFormatOut_, 0, 0);
AMF_CHECK_RETURN(res, "Init failed");
res = AMFContext_->CreateBufferFromHostNative(iData, iDataSize,
&iDataWrapBuffer, NULL);
AMF_CHECK_RETURN(res, "CreateBufferFromHostNative failed");
res = AMFDecoder_->SubmitInput(iDataWrapBuffer);
}
AMF_CHECK_RETURN(res, "SubmitInput failed");
amf::AMFDataPtr oData = NULL;
auto start = util::now();
do {
res = AMFDecoder_->QueryOutput(&oData);
if (res == AMF_REPEAT) {
amf_sleep(1);
}
} while (res == AMF_REPEAT && util::elapsed_ms(start) < DECODE_TIMEOUT_MS);
if (res == AMF_OK && oData != NULL) {
amf::AMFSurfacePtr surface(oData);
AMF_RETURN_IF_INVALID_POINTER(surface, L"surface is NULL");
if (surface->GetPlanesCount() == 0)
return AMF_FAIL;
// convert texture
amf::AMFDataPtr convertData;
res = Convert(surface, convertData);
AMF_CHECK_RETURN(res, "Convert failed");
amf::AMFSurfacePtr convertSurface(convertData);
if (!convertSurface || convertSurface->GetPlanesCount() == 0)
return AMF_FAIL;
// For DirectX objects, when a pointer to a COM interface is returned,
// GetNative does not call IUnknown::AddRef on the interface being
// returned.
void *native = convertSurface->GetPlaneAt(0)->GetNative();
if (!native)
return AMF_FAIL;
switch (convertSurface->GetMemoryType()) {
case amf::AMF_MEMORY_DX11: {
{
ID3D11Texture2D *src = (ID3D11Texture2D *)native;
D3D11_TEXTURE2D_DESC desc;
src->GetDesc(&desc);
nativeDevice_->EnsureTexture(desc.Width, desc.Height);
nativeDevice_->next();
ID3D11Texture2D *dst = nativeDevice_->GetCurrentTexture();
nativeDevice_->context_->CopyResource(dst, src);
nativeDevice_->context_->Flush();
if (callback)
callback(dst, obj);
decoded = true;
}
break;
} break;
case amf::AMF_MEMORY_OPENCL: {
uint8_t *buf = (uint8_t *)native;
} break;
}
surface = NULL;
convertData = NULL;
convertSurface = NULL;
}
oData = NULL;
iDataWrapBuffer = NULL;
return decoded ? AMF_OK : AMF_FAIL;
return AMF_OK;
}
AMF_RESULT destroy() {
// Terminate converter before terminate decoder get "[AMFDeviceDX11Impl]
// Warning: Possible memory leak detected: DX11 device is being destroyed,
// but has 6 surfaces associated with it. This is OK if there are references
// to the device outside AMF"
if (AMFConverter_ != NULL) {
AMFConverter_->Drain();
AMFConverter_->Terminate();
AMFConverter_ = NULL;
}
if (AMFDecoder_ != NULL) {
AMFDecoder_->Drain();
AMFDecoder_->Terminate();
AMFDecoder_ = NULL;
}
if (AMFContext_ != NULL) {
AMFContext_->Terminate();
AMFContext_ = NULL; // context is the last
}
AMFFactory_.Terminate();
return AMF_OK;
}
AMF_RESULT initialize() {
AMF_RESULT res;
res = AMFFactory_.Init();
AMF_CHECK_RETURN(res, "AMFFactory Init failed");
amf::AMFSetCustomTracer(AMFFactory_.GetTrace());
amf::AMFTraceEnableWriter(AMF_TRACE_WRITER_CONSOLE, true);
amf::AMFTraceSetWriterLevel(AMF_TRACE_WRITER_CONSOLE, AMF_TRACE_WARNING);
res = AMFFactory_.GetFactory()->CreateContext(&AMFContext_);
AMF_CHECK_RETURN(res, "CreateContext failed");
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
nativeDevice_ = std::make_unique<NativeDevice>();
if (!nativeDevice_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Init NativeDevice failed"));
return AMF_FAIL;
}
res = AMFContext_->InitDX11(
nativeDevice_->device_.Get()); // can be DX11 device
AMF_CHECK_RETURN(res, "InitDX11 failed");
break;
default:
LOG_ERROR(std::string("unsupported memory type: ") +
std::to_string((int)AMFMemoryType_));
return AMF_FAIL;
}
res = AMFFactory_.GetFactory()->CreateComponent(AMFContext_, codec_.c_str(),
&AMFDecoder_);
AMF_CHECK_RETURN(res, "CreateComponent failed");
res = setParameters();
AMF_CHECK_RETURN(res, "setParameters failed");
res = AMFDecoder_->Init(decodeFormatOut_, 0, 0);
AMF_CHECK_RETURN(res, "Init decoder failed");
return AMF_OK;
}
private:
AMF_RESULT setParameters() {
AMF_RESULT res;
res =
AMFDecoder_->SetProperty(AMF_TIMESTAMP_MODE, amf_int64(AMF_TS_DECODE));
AMF_RETURN_IF_FAILED(
res, L"SetProperty AMF_TIMESTAMP_MODE to AMF_TS_DECODE failed");
res =
AMFDecoder_->SetProperty(AMF_VIDEO_DECODER_REORDER_MODE,
amf_int64(AMF_VIDEO_DECODER_MODE_LOW_LATENCY));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_REORDER_MODE failed");
// color
res = AMFDecoder_->SetProperty<amf_int64>(
AMF_VIDEO_DECODER_COLOR_RANGE,
full_range_ ? AMF_COLOR_RANGE_FULL : AMF_COLOR_RANGE_STUDIO);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_COLOR_RANGE failed");
res = AMFDecoder_->SetProperty<amf_int64>(
AMF_VIDEO_DECODER_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_COLOR_PROFILE failed");
// res = AMFDecoder_->SetProperty<amf_int64>(
// AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC,
// bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
// : AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
// AMF_CHECK_RETURN(
// res,
// "SetProperty AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC
// failed");
// res = AMFDecoder_->SetProperty<amf_int64>(
// AMF_VIDEO_DECODER_COLOR_PRIMARIES,
// bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
// AMF_CHECK_RETURN(res,
// "SetProperty AMF_VIDEO_DECODER_COLOR_PRIMARIES failed");
return AMF_OK;
}
AMF_RESULT Convert(IN amf::AMFSurfacePtr &surface,
OUT amf::AMFDataPtr &convertData) {
if (decodeFormatOut_ == textureFormatOut_)
return AMF_OK;
AMF_RESULT res;
int width = surface->GetPlaneAt(0)->GetWidth();
int height = surface->GetPlaneAt(0)->GetHeight();
if (AMFConverter_ != NULL) {
if (width != last_width_ || height != last_height_) {
LOG_INFO(std::string("Convert size changed, (") + std::to_string(last_width_) + "x" +
std::to_string(last_height_) + ") -> (" +
std::to_string(width) + "x" + std::to_string(width) + ")");
AMFConverter_->Terminate();
AMFConverter_ = NULL;
}
}
if (!AMFConverter_) {
res = AMFFactory_.GetFactory()->CreateComponent(
AMFContext_, AMFVideoConverter, &AMFConverter_);
AMF_CHECK_RETURN(res, "Convert CreateComponent failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_MEMORY_TYPE,
AMFMemoryType_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_MEMORY_TYPE failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_FORMAT,
textureFormatOut_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_OUTPUT_FORMAT failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_SIZE,
::AMFConstructSize(width, height));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_OUTPUT_SIZE failed");
res = AMFConverter_->Init(decodeFormatOut_, width, height);
AMF_CHECK_RETURN(res, "Init converter failed");
// color
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE,
full_range_ ? AMF_COLOR_RANGE_FULL : AMF_COLOR_RANGE_STUDIO);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE, AMF_COLOR_RANGE_FULL);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_COLOR_PROFILE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC "
"failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES failed");
}
last_width_ = width;
last_height_ = height;
res = AMFConverter_->SubmitInput(surface);
AMF_CHECK_RETURN(res, "Convert SubmitInput failed");
res = AMFConverter_->QueryOutput(&convertData);
AMF_CHECK_RETURN(res, "Convert QueryOutput failed");
return AMF_OK;
}
};
bool convert_codec(DataFormat lhs, amf_wstring &rhs) {
switch (lhs) {
case H264:
rhs = AMFVideoDecoderUVD_H264_AVC;
break;
case H265:
rhs = AMFVideoDecoderHW_H265_HEVC;
break;
default:
LOG_ERROR(std::string("unsupported codec: ") + std::to_string(lhs));
return false;
}
return true;
}
} // namespace
#include "amf_common.cpp"
extern "C" {
int amf_destroy_decoder(void *decoder) {
try {
AMFDecoder *dec = (AMFDecoder *)decoder;
if (dec) {
dec->destroy();
delete dec;
dec = NULL;
return 0;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *amf_new_decoder(void *device, int64_t luid,
DataFormat dataFormat) {
AMFDecoder *dec = NULL;
try {
amf_wstring codecStr;
amf::AMF_MEMORY_TYPE memory;
amf::AMF_SURFACE_FORMAT surfaceFormat;
if (!convert_api(memory)) {
return NULL;
}
if (!convert_codec(dataFormat, codecStr)) {
return NULL;
}
dec = new AMFDecoder(device, luid, memory, codecStr, amf::AMF_SURFACE_BGRA);
if (dec) {
if (dec->initialize() == AMF_OK) {
return dec;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (dec) {
dec->destroy();
delete dec;
dec = NULL;
}
return NULL;
}
int amf_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj) {
try {
AMFDecoder *dec = (AMFDecoder *)decoder;
if (dec->decode(data, length, callback, obj) == AMF_OK) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int amf_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_AMD))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
AMFDecoder *p = (AMFDecoder *)amf_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = p->decode(data, length, nullptr, nullptr) == AMF_OK;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_AMD;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -1,611 +0,0 @@
#include <public/common/AMFFactory.h>
#include <public/common/AMFSTL.h>
#include <public/common/Thread.h>
#include <public/common/TraceAdapter.h>
#include <public/include/components/VideoEncoderAV1.h>
#include <public/include/components/VideoEncoderHEVC.h>
#include <public/include/components/VideoEncoderVCE.h>
#include <public/include/core/Platform.h>
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <math.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "AMFENC"
#include "log.h"
#define AMF_FACILITY L"AMFEncoder"
#define MILLISEC_TIME 10000
namespace {
#define AMF_CHECK_RETURN(res, msg) \
if (res != AMF_OK) { \
LOG_ERROR(std::string(msg) + ", result code: " + std::to_string(int(res))); \
return res; \
}
/** Encoder output packet */
struct encoder_packet {
uint8_t *data; /**< Packet data */
size_t size; /**< Packet size */
int64_t pts; /**< Presentation timestamp */
int64_t dts; /**< Decode timestamp */
int32_t timebase_num; /**< Timebase numerator */
int32_t timebase_den; /**< Timebase denominator */
bool keyframe; /**< Is a keyframe */
/* ---------------------------------------------------------------- */
/* Internal video variables (will be parsed automatically) */
/* DTS in microseconds */
int64_t dts_usec;
/* System DTS in microseconds */
int64_t sys_dts_usec;
};
class AMFEncoder {
public:
DataFormat dataFormat_;
amf::AMFComponentPtr AMFEncoder_ = NULL;
amf::AMFContextPtr AMFContext_ = NULL;
private:
// system
void *handle_;
// AMF Internals
AMFFactoryHelper AMFFactory_;
amf::AMF_MEMORY_TYPE AMFMemoryType_;
amf::AMF_SURFACE_FORMAT AMFSurfaceFormat_ = amf::AMF_SURFACE_BGRA;
std::pair<int32_t, int32_t> resolution_;
amf_wstring codec_;
// const
AMF_COLOR_BIT_DEPTH_ENUM eDepth_ = AMF_COLOR_BIT_DEPTH_8;
int query_timeout_ = ENCODE_TIMEOUT_MS;
int32_t bitRateIn_;
int32_t frameRate_;
int32_t gop_;
bool enable4K_ = false;
bool full_range_ = false;
bool bt709_ = false;
// Buffers
std::vector<uint8_t> packetDataBuffer_;
public:
AMFEncoder(void *handle, amf::AMF_MEMORY_TYPE memoryType, amf_wstring codec,
DataFormat dataFormat, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop) {
handle_ = handle;
dataFormat_ = dataFormat;
AMFMemoryType_ = memoryType;
resolution_ = std::make_pair(width, height);
codec_ = codec;
bitRateIn_ = bitrate;
frameRate_ = framerate;
gop_ = (gop > 0 && gop < MAX_GOP) ? gop : MAX_GOP;
enable4K_ = width > 1920 && height > 1080;
}
~AMFEncoder() {}
AMF_RESULT encode(void *tex, EncodeCallback callback, void *obj, int64_t ms) {
amf::AMFSurfacePtr surface = NULL;
amf::AMFComputeSyncPointPtr pSyncPoint = NULL;
AMF_RESULT res;
bool encoded = false;
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
// https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/280
// AMF will not copy the surface during the CreateSurfaceFromDX11Native
// call
res = AMFContext_->CreateSurfaceFromDX11Native(tex, &surface, NULL);
AMF_CHECK_RETURN(res, "CreateSurfaceFromDX11Native failed");
{
amf::AMFDataPtr data1;
surface->Duplicate(surface->GetMemoryType(), &data1);
surface = amf::AMFSurfacePtr(data1);
}
break;
default:
LOG_ERROR(std::string("Unsupported memory type"));
return AMF_NOT_IMPLEMENTED;
break;
}
surface->SetPts(ms * AMF_MILLISECOND);
res = AMFEncoder_->SubmitInput(surface);
AMF_CHECK_RETURN(res, "SubmitInput failed");
amf::AMFDataPtr data = NULL;
res = AMFEncoder_->QueryOutput(&data);
if (res == AMF_OK && data != NULL) {
struct encoder_packet packet;
PacketKeyframe(data, &packet);
amf::AMFBufferPtr pBuffer = amf::AMFBufferPtr(data);
packet.size = pBuffer->GetSize();
if (packet.size > 0) {
if (packetDataBuffer_.size() < packet.size) {
size_t newBufferSize = (size_t)exp2(ceil(log2((double)packet.size)));
packetDataBuffer_.resize(newBufferSize);
}
packet.data = packetDataBuffer_.data();
std::memcpy(packet.data, pBuffer->GetNative(), packet.size);
if (callback)
callback(packet.data, packet.size, packet.keyframe, obj, ms);
encoded = true;
}
pBuffer = NULL;
}
data = NULL;
pSyncPoint = NULL;
surface = NULL;
return encoded ? AMF_OK : AMF_FAIL;
}
AMF_RESULT destroy() {
if (AMFEncoder_) {
AMFEncoder_->Terminate();
AMFEncoder_ = NULL;
}
if (AMFContext_) {
AMFContext_->Terminate();
AMFContext_ = NULL; // AMFContext_ is the last
}
AMFFactory_.Terminate();
return AMF_OK;
}
AMF_RESULT test() {
AMF_RESULT res = AMF_OK;
amf::AMFSurfacePtr surface = nullptr;
res = AMFContext_->AllocSurface(AMFMemoryType_, AMFSurfaceFormat_,
resolution_.first, resolution_.second,
&surface);
AMF_CHECK_RETURN(res, "AllocSurface failed");
if (surface->GetPlanesCount() < 1)
return AMF_FAIL;
void *native = surface->GetPlaneAt(0)->GetNative();
if (!native)
return AMF_FAIL;
int32_t key_obj = 0;
auto start = util::now();
res = encode(native, util_encode::vram_encode_test_callback, &key_obj, 0);
int64_t elapsed = util::elapsed_ms(start);
if (res == AMF_OK && key_obj == 1 && elapsed < TEST_TIMEOUT_MS) {
return AMF_OK;
}
return AMF_FAIL;
}
AMF_RESULT initialize() {
AMF_RESULT res;
res = AMFFactory_.Init();
if (res != AMF_OK) {
std::cerr << "AMF init failed, error code = " << res << "\n";
return res;
}
amf::AMFSetCustomTracer(AMFFactory_.GetTrace());
amf::AMFTraceEnableWriter(AMF_TRACE_WRITER_CONSOLE, true);
amf::AMFTraceSetWriterLevel(AMF_TRACE_WRITER_CONSOLE, AMF_TRACE_WARNING);
// AMFContext_
res = AMFFactory_.GetFactory()->CreateContext(&AMFContext_);
AMF_CHECK_RETURN(res, "CreateContext failed");
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
res = AMFContext_->InitDX11(handle_); // can be DX11 device
AMF_CHECK_RETURN(res, "InitDX11 failed");
break;
default:
LOG_ERROR(std::string("unsupported amf memory type"));
return AMF_FAIL;
}
// component: encoder
res = AMFFactory_.GetFactory()->CreateComponent(AMFContext_, codec_.c_str(),
&AMFEncoder_);
AMF_CHECK_RETURN(res, "CreateComponent failed");
res = SetParams(codec_);
AMF_CHECK_RETURN(res, "Could not set params in encoder.");
res = AMFEncoder_->Init(AMFSurfaceFormat_, resolution_.first,
resolution_.second);
AMF_CHECK_RETURN(res, "encoder->Init() failed");
return AMF_OK;
}
private:
AMF_RESULT SetParams(const amf_wstring &codecStr) {
AMF_RESULT res;
if (codecStr == amf_wstring(AMFVideoEncoderVCE_AVC)) {
// ------------- Encoder params usage---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_USAGE,
AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_USAGE failed");
// ------------- Encoder params static---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_FRAMESIZE,
::AMFConstructSize(resolution_.first, resolution_.second));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_FRAMESIZE failed, (" +
std::to_string(resolution_.first) + "," +
std::to_string(resolution_.second) + ")");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, true);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_LOWLATENCY_MODE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET,
AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_QUALITY_PRESET failed");
res =
AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, eDepth_);
AMF_CHECK_RETURN(res,
"SetProperty(AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD,
AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD");
if (enable4K_) {
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_PROFILE,
AMF_VIDEO_ENCODER_PROFILE_HIGH);
AMF_CHECK_RETURN(res, "SetProperty(AMF_VIDEO_ENCODER_PROFILE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_PROFILE_LEVEL,
AMF_H264_LEVEL__5_1);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_PROFILE_LEVEL failed");
}
// color
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FULL_RANGE_COLOR,
full_range_);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_FULL_RANGE_COLOR");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE");
// https://github.com/obsproject/obs-studio/blob/e27b013d4754e0e81119ab237ffedce8fcebcbbf/plugins/obs-ffmpeg/texture-amf.cpp#L924
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES");
// ------------- Encoder params dynamic ---------------
AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, 0);
// do not check error for AMF_VIDEO_ENCODER_B_PIC_PATTERN
// - can be not supported - check Capability Manager
// sample
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT,
query_timeout_); // ms
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_QUERY_TIMEOUT failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE,
bitRateIn_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_TARGET_BITRATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE,
::AMFConstructRate(frameRate_, 1));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_FRAMERATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, gop_);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_IDR_PERIOD failed");
} else if (codecStr == amf_wstring(AMFVideoEncoder_HEVC)) {
// ------------- Encoder params usage---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_USAGE,
AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_HEVC_USAGE failed");
// ------------- Encoder params static---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_FRAMESIZE,
::AMFConstructSize(resolution_.first, resolution_.second));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_FRAMESIZE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_LOWLATENCY_MODE,
true);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_LOWLATENCY_MODE failed");
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET,
AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH,
eDepth_);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH failed");
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD,
AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD failed");
if (enable4K_) {
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TIER,
AMF_VIDEO_ENCODER_HEVC_TIER_HIGH);
AMF_CHECK_RETURN(res, "SetProperty(AMF_VIDEO_ENCODER_HEVC_TIER failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL,
AMF_LEVEL_5_1);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL failed");
}
// color
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE,
full_range_ ? AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL
: AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_STUDIO);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(
res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty "
"AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(
res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES failed");
// ------------- Encoder params dynamic ---------------
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT,
query_timeout_); // ms
AMF_CHECK_RETURN(
res, "SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE,
bitRateIn_);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE,
::AMFConstructRate(frameRate_, 1));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_FRAMERATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_GOP_SIZE,
gop_); // todo
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_GOP_SIZE failed");
} else {
return AMF_FAIL;
}
return AMF_OK;
}
void PacketKeyframe(amf::AMFDataPtr &pData, struct encoder_packet *packet) {
if (AMFVideoEncoderVCE_AVC == codec_) {
uint64_t pktType;
pData->GetProperty(AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &pktType);
packet->keyframe = AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR == pktType ||
AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_I == pktType;
} else if (AMFVideoEncoder_HEVC == codec_) {
uint64_t pktType;
pData->GetProperty(AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &pktType);
packet->keyframe =
AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR == pktType ||
AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_I == pktType;
}
}
};
bool convert_codec(DataFormat lhs, amf_wstring &rhs) {
switch (lhs) {
case H264:
rhs = AMFVideoEncoderVCE_AVC;
break;
case H265:
rhs = AMFVideoEncoder_HEVC;
break;
default:
LOG_ERROR(std::string("unsupported codec: ") + std::to_string((int)lhs));
return false;
}
return true;
}
} // namespace
#include "amf_common.cpp"
extern "C" {
int amf_destroy_encoder(void *encoder) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
enc->destroy();
delete enc;
enc = NULL;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *amf_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop) {
AMFEncoder *enc = NULL;
try {
amf_wstring codecStr;
if (!convert_codec(dataFormat, codecStr)) {
return NULL;
}
amf::AMF_MEMORY_TYPE memoryType;
if (!convert_api(memoryType)) {
return NULL;
}
enc = new AMFEncoder(handle, memoryType, codecStr, dataFormat, width,
height, kbs * 1000, framerate, gop);
if (enc) {
if (AMF_OK == enc->initialize()) {
return enc;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (enc) {
enc->destroy();
delete enc;
enc = NULL;
}
return NULL;
}
int amf_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
return -enc->encode(tex, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("encode failed: ") + e.what());
}
return -1;
}
int amf_driver_support() {
try {
AMFFactoryHelper factory;
AMF_RESULT res = factory.Init();
if (res == AMF_OK) {
factory.Terminate();
return 0;
}
} catch (const std::exception &e) {
}
return -1;
}
int amf_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_AMD))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
AMFEncoder *e = (AMFEncoder *)amf_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->test() == AMF_OK) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_AMD;
count += 1;
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test ") + std::to_string(kbs) + " failed: " + e.what());
}
return -1;
}
int amf_set_bitrate(void *encoder, int32_t kbs) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
AMF_RESULT res = AMF_FAIL;
switch (enc->dataFormat_) {
case H264:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE,
kbs * 1000);
break;
case H265:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE,
kbs * 1000);
break;
}
return res == AMF_OK ? 0 : -1;
} catch (const std::exception &e) {
LOG_ERROR(std::string("set bitrate to ") + std::to_string(kbs) +
"k failed: " + e.what());
}
return -1;
}
int amf_set_framerate(void *encoder, int32_t framerate) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
AMF_RESULT res = AMF_FAIL;
AMFRate rate = ::AMFConstructRate(framerate, 1);
switch (enc->dataFormat_) {
case H264:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, rate);
break;
case H265:
res =
enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE, rate);
break;
}
return res == AMF_OK ? 0 : -1;
} catch (const std::exception &e) {
LOG_ERROR(std::string("set framerate to ") + std::to_string(framerate) +
" failed: " + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -1,39 +0,0 @@
#ifndef AMF_FFI_H
#define AMF_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int amf_driver_support();
void *amf_new_encoder(void *handle, int64_t luid,
int32_t data_format, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop);
int amf_encode(void *encoder, void *texture, EncodeCallback callback, void *obj,
int64_t ms);
int amf_destroy_encoder(void *encoder);
void *amf_new_decoder(void *device, int64_t luid,
int32_t dataFormat);
int amf_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj);
int amf_destroy_decoder(void *decoder);
int amf_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int amf_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int amf_set_bitrate(void *encoder, int32_t kbs);
int amf_set_framerate(void *encoder, int32_t framerate);
#endif // AMF_FFI_H

View File

@@ -2,83 +2,50 @@
#include "../../log.h"
#include <cstring>
#include <dlfcn.h>
#include <dynlink_cuda.h>
#include <dynlink_loader.h>
#include <errno.h>
#include <exception> // Include the necessary header file
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <fcntl.h>
namespace
{
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (cuda_load_functions(pp_cuda_dl, NULL) < 0)
{
LOG_TRACE(std::string("cuda_load_functions failed"));
throw "cuda_load_functions failed";
}
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0)
{
LOG_TRACE(std::string("nvenc_load_functions failed"));
throw "nvenc_load_functions failed";
}
if (cuvid_load_functions(pp_cvdl, NULL) < 0)
{
LOG_TRACE(std::string("cuvid_load_functions failed"));
throw "cuvid_load_functions failed";
}
}
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (*pp_cvdl)
{
cuvid_free_functions(pp_cvdl);
*pp_cvdl = NULL;
}
if (*pp_nvenc_dl)
{
nvenc_free_functions(pp_nvenc_dl);
*pp_nvenc_dl = NULL;
}
if (*pp_cuda_dl)
{
cuda_free_functions(pp_cuda_dl);
*pp_cuda_dl = NULL;
}
}
} // namespace
// Check for NVIDIA driver support by loading CUDA libraries
int linux_support_nv()
{
try
// Try to load NVIDIA CUDA runtime library
void *handle = dlopen("libcuda.so.1", RTLD_LAZY);
if (!handle)
{
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cuda_dl, &nvenc_dl, &cvdl);
free_driver(&cuda_dl, &nvenc_dl, &cvdl);
return 0;
handle = dlopen("libcuda.so", RTLD_LAZY);
}
catch (...)
if (!handle)
{
LOG_TRACE(std::string("nvidia driver not support"));
LOG_TRACE(std::string("NVIDIA: libcuda.so not found"));
return -1;
}
return -1;
dlclose(handle);
// Also check for nvenc library
handle = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
if (!handle)
{
handle = dlopen("libnvidia-encode.so", RTLD_LAZY);
}
if (!handle)
{
LOG_TRACE(std::string("NVIDIA: libnvidia-encode.so not found"));
return -1;
}
dlclose(handle);
LOG_TRACE(std::string("NVIDIA: driver support detected"));
return 0;
}
int linux_support_amd()
{
#if defined(__x86_64__) || defined(__aarch64__)
#define AMF_DLL_NAME L"libamfrt64.so.1"
#define AMF_DLL_NAMEA "libamfrt64.so.1"
#else
#define AMF_DLL_NAME L"libamfrt32.so.1"
#define AMF_DLL_NAMEA "libamfrt32.so.1"
#endif
void *handle = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
@@ -160,4 +127,4 @@ int linux_support_v4l2m2m() {
LOG_TRACE(std::string("V4L2 M2M: No M2M device found"));
return -1;
}
}

View File

@@ -1,167 +0,0 @@
#include <AVFoundation/AVFoundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreMedia/CoreMedia.h>
#include <MacTypes.h>
#include <VideoToolbox/VideoToolbox.h>
#include <cstdlib>
#include <pthread.h>
#include <ratio>
#include <sys/_types/_int32_t.h>
#include <sys/event.h>
#include <unistd.h>
#include "../../log.h"
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
// ---------------------- Core: More Robust Hardware Encoder Detection ----------------------
static int32_t hasHardwareEncoder(bool h265) {
CMVideoCodecType codecType = h265 ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264;
// ---------- Path A: Quick Query with Enable + Require ----------
// Note: Require implies Enable, but setting both here makes it easier to bypass the strategy on some models that default to a software encoder.
CFMutableDictionaryRef spec = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionaryRef properties = NULL;
CFStringRef outID = NULL;
// Use 1280x720 for capability detection to reduce the probability of "no hardware encoding" due to resolution/level issues.
OSStatus result = VTCopySupportedPropertyDictionaryForEncoder(1280, 720, codecType, spec, &outID, &properties);
if (properties) CFRelease(properties);
if (outID) CFRelease(outID);
if (spec) CFRelease(spec);
if (result == noErr) {
// Explicitly found an encoder that meets the "hardware-only" specification.
return 1;
}
// Reaching here means either no encoder satisfying Require was found (common), or another error occurred.
// For all failure cases, continue with the safer "session-level confirmation" path to avoid misjudgment.
// ---------- Path B: Create Session and Read UsingHardwareAcceleratedVideoEncoder ----------
CFMutableDictionaryRef enableOnly = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(enableOnly, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
VTCompressionSessionRef session = NULL;
// Also use 1280x720 to reduce profile/level interference
OSStatus st = VTCompressionSessionCreate(kCFAllocatorDefault,
1280, 720, codecType,
enableOnly, /* encoderSpecification */
NULL, /* sourceImageBufferAttributes */
NULL, /* compressedDataAllocator */
NULL, /* outputCallback */
NULL, /* outputRefCon */
&session);
if (enableOnly) CFRelease(enableOnly);
if (st != noErr || !session) {
// Creation failed, considered no hardware available.
return 0;
}
// First, explicitly prepare the encoding process to give VideoToolbox a chance to choose between software/hardware.
OSStatus prepareStatus = VTCompressionSessionPrepareToEncodeFrames(session);
if (prepareStatus != noErr) {
VTCompressionSessionInvalidate(session);
CFRelease(session);
return 0;
}
// Query the session's read-only property: whether it is using a hardware encoder.
CFBooleanRef usingHW = NULL;
st = VTSessionCopyProperty(session,
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder,
kCFAllocatorDefault,
(void **)&usingHW);
Boolean isHW = (st == noErr && usingHW && CFBooleanGetValue(usingHW));
if (usingHW) CFRelease(usingHW);
VTCompressionSessionInvalidate(session);
CFRelease(session);
return isHW ? 1 : 0;
}
// -------------- Your Public Interface: Unchanged ------------------
extern "C" void checkVideoToolboxSupport(int32_t *h264Encoder, int32_t *h265Encoder, int32_t *h264Decoder, int32_t *h265Decoder) {
// https://stackoverflow.com/questions/50956097/determine-if-ios-device-can-support-hevc-encoding
*h264Encoder = 0; // H.264 encoder support is disabled due to frequent reliability issues (see encode.rs)
*h265Encoder = hasHardwareEncoder(true);
*h264Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_H264);
*h265Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
return;
}
extern "C" uint64_t GetHwcodecGpuSignature() {
int32_t h264Encoder = 0;
int32_t h265Encoder = 0;
int32_t h264Decoder = 0;
int32_t h265Decoder = 0;
checkVideoToolboxSupport(&h264Encoder, &h265Encoder, &h264Decoder, &h265Decoder);
return (uint64_t)h264Encoder << 24 | (uint64_t)h265Encoder << 16 | (uint64_t)h264Decoder << 8 | (uint64_t)h265Decoder;
}
static void *parent_death_monitor_thread(void *arg) {
int kq = (intptr_t)arg;
struct kevent events[1];
int ret = kevent(kq, NULL, 0, events, 1, NULL);
if (ret > 0) {
// Parent process died, terminate this process
LOG_INFO("Parent process died, terminating hwcodec check process");
exit(1);
}
return NULL;
}
extern "C" int setup_parent_death_signal() {
// On macOS, use kqueue to monitor parent process death
pid_t parent_pid = getppid();
int kq = kqueue();
if (kq == -1) {
LOG_DEBUG("Failed to create kqueue for parent monitoring");
return -1;
}
struct kevent event;
EV_SET(&event, parent_pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
NULL);
int ret = kevent(kq, &event, 1, NULL, 0, NULL);
if (ret == -1) {
LOG_ERROR("Failed to register parent death monitoring on macOS\n");
close(kq);
return -1;
} else {
// Spawn a thread to monitor parent death
pthread_t monitor_thread;
ret = pthread_create(&monitor_thread, NULL, parent_death_monitor_thread,
(void *)(intptr_t)kq);
if (ret != 0) {
LOG_ERROR("Failed to create parent death monitor thread");
close(kq);
return -1;
}
// Detach the thread so it can run independently
pthread_detach(monitor_thread);
return 0;
}
}

View File

@@ -1,410 +0,0 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/hw_decode.c
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/decode_video.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/hwcontext.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
}
#include <libavutil/hwcontext_d3d11va.h>
#include <memory>
#include <mutex>
#include <stdbool.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#define LOG_MODULE "FFMPEG_VRAM_DEC"
#include <log.h>
#include <util.h>
namespace {
#define USE_SHADER
void lockContext(void *lock_ctx);
void unlockContext(void *lock_ctx);
class FFmpegVRamDecoder {
public:
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVCodecParserContext *sw_parser_ctx_ = NULL;
AVFrame *frame_ = NULL;
AVPacket *pkt_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
ID3D11Device *d3d11Device_ = NULL;
ID3D11DeviceContext *d3d11DeviceContext_ = NULL;
void *device_ = nullptr;
int64_t luid_ = 0;
DataFormat dataFormat_;
std::string name_;
AVHWDeviceType device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
bool bt709_ = false;
bool full_range_ = false;
FFmpegVRamDecoder(void *device, int64_t luid, DataFormat dataFormat) {
device_ = device;
luid_ = luid;
dataFormat_ = dataFormat;
switch (dataFormat) {
case H264:
name_ = "h264";
break;
case H265:
name_ = "hevc";
break;
default:
LOG_ERROR(std::string("unsupported data format"));
break;
}
// Always use DX11 since it's the only API
device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
}
~FFmpegVRamDecoder() {}
void destroy() {
if (frame_)
av_frame_free(&frame_);
if (pkt_)
av_packet_free(&pkt_);
if (c_)
avcodec_free_context(&c_);
if (hw_device_ctx_) {
av_buffer_unref(&hw_device_ctx_);
// AVHWDeviceContext takes ownership of d3d11 object
d3d11Device_ = nullptr;
d3d11DeviceContext_ = nullptr;
} else {
SAFE_RELEASE(d3d11Device_);
SAFE_RELEASE(d3d11DeviceContext_);
}
frame_ = NULL;
pkt_ = NULL;
c_ = NULL;
hw_device_ctx_ = NULL;
}
int reset() {
destroy();
if (!native_) {
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to init native device"));
return -1;
}
}
if (!native_->support_decode(dataFormat_)) {
LOG_ERROR(std::string("unsupported data format"));
return -1;
}
d3d11Device_ = native_->device_.Get();
d3d11Device_->AddRef();
d3d11DeviceContext_ = native_->context_.Get();
d3d11DeviceContext_->AddRef();
const AVCodec *codec = NULL;
int ret;
if (!(codec = avcodec_find_decoder_by_name(name_.c_str()))) {
LOG_ERROR(std::string("avcodec_find_decoder_by_name ") + name_ + " failed");
return -1;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return -1;
}
c_->flags |= AV_CODEC_FLAG_LOW_DELAY;
hw_device_ctx_ = av_hwdevice_ctx_alloc(device_type_);
if (!hw_device_ctx_) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
return -1;
}
AVHWDeviceContext *deviceContext =
(AVHWDeviceContext *)hw_device_ctx_->data;
AVD3D11VADeviceContext *d3d11vaDeviceContext =
(AVD3D11VADeviceContext *)deviceContext->hwctx;
d3d11vaDeviceContext->device = d3d11Device_;
d3d11vaDeviceContext->device_context = d3d11DeviceContext_;
d3d11vaDeviceContext->lock = lockContext;
d3d11vaDeviceContext->unlock = unlockContext;
d3d11vaDeviceContext->lock_ctx = this;
ret = av_hwdevice_ctx_init(hw_device_ctx_);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_init failed, ret = ") + av_err2str(ret));
return -1;
}
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("av_packet_alloc failed"));
return -1;
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("av_frame_alloc failed"));
return -1;
}
if ((ret = avcodec_open2(c_, codec, NULL)) != 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
", name=" + name_);
return -1;
}
return 0;
}
int decode(const uint8_t *data, int length, DecodeCallback callback,
const void *obj) {
int ret = -1;
if (!data || !length) {
LOG_ERROR(std::string("illegal decode parameter"));
return -1;
}
pkt_->data = (uint8_t *)data;
pkt_->size = length;
ret = do_decode(callback, obj);
return ret;
}
private:
int do_decode(DecodeCallback callback, const void *obj) {
int ret;
bool decoded = false;
bool locked = false;
ret = avcodec_send_packet(c_, pkt_);
if (ret < 0) {
LOG_ERROR(std::string("avcodec_send_packet failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < DECODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_frame(c_, frame_)) != 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_frame failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (frame_->format != AV_PIX_FMT_D3D11) {
LOG_ERROR(std::string("only AV_PIX_FMT_D3D11 is supported"));
goto _exit;
}
lockContext(this);
locked = true;
if (!convert(frame_, callback, obj)) {
LOG_ERROR(std::string("Failed to convert"));
goto _exit;
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
}
_exit:
if (locked) {
unlockContext(this);
}
av_packet_unref(pkt_);
return decoded ? 0 : -1;
}
bool convert(AVFrame *frame, DecodeCallback callback, const void *obj) {
ID3D11Texture2D *texture = (ID3D11Texture2D *)frame->data[0];
if (!texture) {
LOG_ERROR(std::string("texture is NULL"));
return false;
}
D3D11_TEXTURE2D_DESC desc2D;
texture->GetDesc(&desc2D);
if (desc2D.Format != DXGI_FORMAT_NV12) {
LOG_ERROR(std::string("only DXGI_FORMAT_NV12 is supported"));
return false;
}
if (!native_->EnsureTexture(frame->width, frame->height)) {
LOG_ERROR(std::string("Failed to EnsureTexture"));
return false;
}
native_->next(); // comment out to remove picture shaking
#ifdef USE_SHADER
native_->BeginQuery();
if (!native_->Nv12ToBgra(frame->width, frame->height, texture,
native_->GetCurrentTexture(),
(int)frame->data[1])) {
LOG_ERROR(std::string("Failed to Nv12ToBgra"));
native_->EndQuery();
return false;
}
native_->EndQuery();
native_->Query();
#else
native_->BeginQuery();
// nv12 -> bgra
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 60;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: aligned width, height or crop width, height
contentDesc.InputWidth = frame->width;
contentDesc.InputHeight = frame->height;
contentDesc.OutputWidth = frame->width;
contentDesc.OutputHeight = frame->height;
contentDesc.OutputFrameRate.Numerator = 60;
contentDesc.OutputFrameRate.Denominator = 1;
DXGI_COLOR_SPACE_TYPE colorSpace_out =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_in;
if (bt709_) {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->Process(texture, native_->GetCurrentTexture(), contentDesc,
colorSpace_in, colorSpace_out, (int)frame->data[1])) {
LOG_ERROR(std::string("Failed to process"));
native_->EndQuery();
return false;
}
native_->context_->Flush();
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Failed to query"));
return false;
}
#endif
return true;
}
};
void lockContext(void *lock_ctx) { (void)lock_ctx; }
void unlockContext(void *lock_ctx) { (void)lock_ctx; }
} // namespace
extern "C" int ffmpeg_vram_destroy_decoder(FFmpegVRamDecoder *decoder) {
try {
if (!decoder)
return 0;
decoder->destroy();
delete decoder;
decoder = NULL;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_free_decoder exception:") + e.what());
}
return -1;
}
extern "C" FFmpegVRamDecoder *ffmpeg_vram_new_decoder(void *device,
int64_t luid,
DataFormat dataFormat) {
FFmpegVRamDecoder *decoder = NULL;
try {
decoder = new FFmpegVRamDecoder(device, luid, dataFormat);
if (decoder) {
if (decoder->reset() == 0) {
return decoder;
}
}
} catch (std::exception &e) {
LOG_ERROR(std::string("new decoder exception:") + e.what());
}
if (decoder) {
decoder->destroy();
delete decoder;
decoder = NULL;
}
return NULL;
}
extern "C" int ffmpeg_vram_decode(FFmpegVRamDecoder *decoder,
const uint8_t *data, int length,
DecodeCallback callback, const void *obj) {
try {
int ret = decoder->decode(data, length, callback, obj);
if (DataFormat::H265 == decoder->dataFormat_ && util_decode::has_flag_could_not_find_ref_with_poc()) {
return HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC;
} else {
return ret == 0 ? HWCODEC_SUCCESS : HWCODEC_ERR_COMMON;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_decode exception:") + e.what());
}
return HWCODEC_ERR_COMMON;
}
extern "C" int ffmpeg_vram_test_decode(int64_t *outLuids, int32_t *outVendors,
int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat,
uint8_t *data, int32_t length,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
int count = 0;
struct VendorMapping {
AdapterVendor adapter_vendor;
int driver_vendor;
};
VendorMapping vendors[] = {
{ADAPTER_VENDOR_INTEL, VENDOR_INTEL},
{ADAPTER_VENDOR_NVIDIA, VENDOR_NV},
{ADAPTER_VENDOR_AMD, VENDOR_AMD}
};
for (auto vendorMap : vendors) {
Adapters adapters;
if (!adapters.Init(vendorMap.adapter_vendor))
continue;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
FFmpegVRamDecoder *p = (FFmpegVRamDecoder *)ffmpeg_vram_new_decoder(
nullptr, LUID(adapter.get()->desc1_), dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = ffmpeg_vram_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = LUID(adapter.get()->desc1_);
outVendors[count] = (int32_t)vendorMap.driver_vendor; // Map adapter vendor to driver vendor
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
}
return -1;
}

View File

@@ -1,558 +0,0 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/hwcontext.h>
#include <libavutil/imgutils.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
}
#ifdef _WIN32
#include <libavutil/hwcontext_d3d11va.h>
#endif
#include <memory>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#define LOG_MODULE "FFMPEG_VRAM_ENC"
#include <log.h>
#include <util.h>
namespace {
void lockContext(void *lock_ctx);
void unlockContext(void *lock_ctx);
enum class EncoderDriver {
NVENC,
AMF,
QSV,
};
class Encoder {
public:
Encoder(EncoderDriver driver, const char *name, AVHWDeviceType device_type,
AVHWDeviceType derived_device_type, AVPixelFormat hw_pixfmt,
AVPixelFormat sw_pixfmt) {
driver_ = driver;
name_ = name;
device_type_ = device_type;
derived_device_type_ = derived_device_type;
hw_pixfmt_ = hw_pixfmt;
sw_pixfmt_ = sw_pixfmt;
};
EncoderDriver driver_;
std::string name_;
AVHWDeviceType device_type_;
AVHWDeviceType derived_device_type_;
AVPixelFormat hw_pixfmt_;
AVPixelFormat sw_pixfmt_;
};
class FFmpegVRamEncoder {
public:
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVFrame *frame_ = NULL;
AVFrame *mapped_frame_ = NULL;
ID3D11Texture2D *encode_texture_ = NULL; // no free
AVPacket *pkt_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
ID3D11Device *d3d11Device_ = NULL;
ID3D11DeviceContext *d3d11DeviceContext_ = NULL;
std::unique_ptr<Encoder> encoder_ = nullptr;
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_ = 0;
int32_t height_ = 0;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
const int align_ = 0;
const bool full_range_ = false;
const bool bt709_ = false;
FFmpegVRamEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
}
~FFmpegVRamEncoder() {}
bool init() {
const AVCodec *codec = NULL;
int ret;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("NativeDevice init failed"));
return false;
}
d3d11Device_ = native_->device_.Get();
d3d11Device_->AddRef();
d3d11DeviceContext_ = native_->context_.Get();
d3d11DeviceContext_->AddRef();
AdapterVendor vendor = native_->GetVendor();
if (!choose_encoder(vendor)) {
return false;
}
LOG_INFO(std::string("encoder name: ") + encoder_->name_);
if (!(codec = avcodec_find_encoder_by_name(encoder_->name_.c_str()))) {
LOG_ERROR(std::string("Codec ") + encoder_->name_ + " not found");
return false;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return false;
}
/* resolution must be a multiple of two */
c_->width = width_;
c_->height = height_;
c_->pix_fmt = encoder_->hw_pixfmt_;
c_->sw_pix_fmt = encoder_->sw_pixfmt_;
util_encode::set_av_codec_ctx(c_, encoder_->name_, kbs_, gop_, framerate_);
if (!util_encode::set_lantency_free(c_->priv_data, encoder_->name_)) {
return false;
}
// util_encode::set_quality(c_->priv_data, encoder_->name_, Quality_Default);
util_encode::set_rate_control(c_, encoder_->name_, RC_CBR, -1);
util_encode::set_others(c_->priv_data, encoder_->name_);
hw_device_ctx_ = av_hwdevice_ctx_alloc(encoder_->device_type_);
if (!hw_device_ctx_) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
return false;
}
AVHWDeviceContext *deviceContext =
(AVHWDeviceContext *)hw_device_ctx_->data;
AVD3D11VADeviceContext *d3d11vaDeviceContext =
(AVD3D11VADeviceContext *)deviceContext->hwctx;
d3d11vaDeviceContext->device = d3d11Device_;
d3d11vaDeviceContext->device_context = d3d11DeviceContext_;
d3d11vaDeviceContext->lock = lockContext;
d3d11vaDeviceContext->unlock = unlockContext;
d3d11vaDeviceContext->lock_ctx = this;
ret = av_hwdevice_ctx_init(hw_device_ctx_);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_init failed, ret = ") + av_err2str(ret));
return false;
}
if (encoder_->derived_device_type_ != AV_HWDEVICE_TYPE_NONE) {
AVBufferRef *derived_context = nullptr;
ret = av_hwdevice_ctx_create_derived(
&derived_context, encoder_->derived_device_type_, hw_device_ctx_, 0);
if (ret) {
LOG_ERROR(std::string("av_hwdevice_ctx_create_derived failed, err = ") +
av_err2str(ret));
return false;
}
av_buffer_unref(&hw_device_ctx_);
hw_device_ctx_ = derived_context;
}
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
if (!set_hwframe_ctx()) {
return false;
}
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("Could not allocate video packet"));
return false;
}
if ((ret = avcodec_open2(c_, codec, NULL)) < 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
", name: " + encoder_->name_);
return false;
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("Could not allocate video frame"));
return false;
}
frame_->format = c_->pix_fmt;
frame_->width = c_->width;
frame_->height = c_->height;
frame_->color_range = c_->color_range;
frame_->color_primaries = c_->color_primaries;
frame_->color_trc = c_->color_trc;
frame_->colorspace = c_->colorspace;
frame_->chroma_location = c_->chroma_sample_location;
if ((ret = av_hwframe_get_buffer(c_->hw_frames_ctx, frame_, 0)) < 0) {
LOG_ERROR(std::string("av_frame_get_buffer failed, ret = ") + av_err2str(ret));
return false;
}
if (frame_->format == AV_PIX_FMT_QSV) {
mapped_frame_ = av_frame_alloc();
if (!mapped_frame_) {
LOG_ERROR(std::string("Could not allocate mapped video frame"));
return false;
}
mapped_frame_->format = AV_PIX_FMT_D3D11;
ret = av_hwframe_map(mapped_frame_, frame_,
AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
if (ret) {
LOG_ERROR(std::string("av_hwframe_map failed, err = ") + av_err2str(ret));
return false;
}
encode_texture_ = (ID3D11Texture2D *)mapped_frame_->data[0];
} else {
encode_texture_ = (ID3D11Texture2D *)frame_->data[0];
}
return true;
}
int encode(void *texture, EncodeCallback callback, void *obj, int64_t ms) {
if (!convert(texture))
return -1;
return do_encode(callback, obj, ms);
}
void destroy() {
if (pkt_)
av_packet_free(&pkt_);
if (frame_)
av_frame_free(&frame_);
if (mapped_frame_)
av_frame_free(&mapped_frame_);
if (c_)
avcodec_free_context(&c_);
if (hw_device_ctx_) {
av_buffer_unref(&hw_device_ctx_);
// AVHWDeviceContext takes ownership of d3d11 object
d3d11Device_ = nullptr;
d3d11DeviceContext_ = nullptr;
} else {
SAFE_RELEASE(d3d11Device_);
SAFE_RELEASE(d3d11DeviceContext_);
}
}
int set_bitrate(int kbs) {
return util_encode::change_bit_rate(c_, encoder_->name_, kbs) ? 0 : -1;
}
int set_framerate(int framerate) {
c_->time_base = av_make_q(1, framerate);
c_->framerate = av_inv_q(c_->time_base);
return 0;
}
private:
bool choose_encoder(AdapterVendor vendor) {
if (ADAPTER_VENDOR_NVIDIA == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_nvenc";
} else if (dataFormat_ == H265) {
name = "hevc_nvenc";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::NVENC, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12);
return true;
} else if (ADAPTER_VENDOR_AMD == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_amf";
} else if (dataFormat_ == H265) {
name = "hevc_amf";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::AMF, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12);
return true;
} else if (ADAPTER_VENDOR_INTEL == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_qsv";
} else if (dataFormat_ == H265) {
name = "hevc_qsv";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::QSV, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12);
return true;
} else {
LOG_ERROR(std::string("Unsupported vendor: ") + std::to_string(vendor));
return false;
}
return false;
}
int do_encode(EncodeCallback callback, const void *obj, int64_t ms) {
int ret;
bool encoded = false;
frame_->pts = ms;
if ((ret = avcodec_send_frame(c_, frame_)) < 0) {
LOG_ERROR(std::string("avcodec_send_frame failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < ENCODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_packet(c_, pkt_)) < 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_packet failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (!pkt_->data || !pkt_->size) {
LOG_ERROR(std::string("avcodec_receive_packet failed, pkt size is 0"));
goto _exit;
}
encoded = true;
if (callback)
callback(pkt_->data, pkt_->size, pkt_->flags & AV_PKT_FLAG_KEY, obj,
pkt_->pts);
}
_exit:
av_packet_unref(pkt_);
return encoded ? 0 : -1;
}
bool convert(void *texture) {
if (frame_->format == AV_PIX_FMT_D3D11 ||
frame_->format == AV_PIX_FMT_QSV) {
ID3D11Texture2D *texture2D = (ID3D11Texture2D *)encode_texture_;
D3D11_TEXTURE2D_DESC desc;
texture2D->GetDesc(&desc);
if (desc.Format != DXGI_FORMAT_NV12) {
LOG_ERROR(std::string("convert: texture format mismatch, ") +
std::to_string(desc.Format) +
" != " + std::to_string(DXGI_FORMAT_NV12));
return false;
}
DXGI_COLOR_SPACE_TYPE colorSpace_in =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_out;
if (bt709_) {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->BgraToNv12((ID3D11Texture2D *)texture, texture2D, width_,
height_, colorSpace_in, colorSpace_out)) {
LOG_ERROR(std::string("convert: BgraToNv12 failed"));
return false;
}
return true;
} else {
LOG_ERROR(std::string("convert: unsupported format, ") +
std::to_string(frame_->format));
return false;
}
}
bool set_hwframe_ctx() {
AVBufferRef *hw_frames_ref;
AVHWFramesContext *frames_ctx = NULL;
int err = 0;
bool ret = true;
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx_))) {
LOG_ERROR(std::string("av_hwframe_ctx_alloc failed."));
return false;
}
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
frames_ctx->format = encoder_->hw_pixfmt_;
frames_ctx->sw_format = encoder_->sw_pixfmt_;
frames_ctx->width = width_;
frames_ctx->height = height_;
frames_ctx->initial_pool_size = 0;
if (encoder_->device_type_ == AV_HWDEVICE_TYPE_D3D11VA) {
frames_ctx->initial_pool_size = 1;
AVD3D11VAFramesContext *frames_hwctx =
(AVD3D11VAFramesContext *)frames_ctx->hwctx;
frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
frames_hwctx->MiscFlags = 0;
}
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
LOG_ERROR(std::string("av_hwframe_ctx_init failed."));
av_buffer_unref(&hw_frames_ref);
return false;
}
c_->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
if (!c_->hw_frames_ctx) {
LOG_ERROR(std::string("av_buffer_ref failed"));
ret = false;
}
av_buffer_unref(&hw_frames_ref);
return ret;
}
};
void lockContext(void *lock_ctx) { (void)lock_ctx; }
void unlockContext(void *lock_ctx) { (void)lock_ctx; }
} // namespace
extern "C" {
FFmpegVRamEncoder *ffmpeg_vram_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
FFmpegVRamEncoder *encoder = NULL;
try {
encoder = new FFmpegVRamEncoder(handle, luid, dataFormat, width,
height, kbs, framerate, gop);
if (encoder) {
if (encoder->init()) {
return encoder;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new FFmpegVRamEncoder failed, ") + std::string(e.what()));
}
if (encoder) {
encoder->destroy();
delete encoder;
encoder = NULL;
}
return NULL;
}
int ffmpeg_vram_encode(FFmpegVRamEncoder *encoder, void *texture,
EncodeCallback callback, void *obj, int64_t ms) {
try {
return encoder->encode(texture, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_vram_encode failed, ") + std::string(e.what()));
}
return -1;
}
void ffmpeg_vram_destroy_encoder(FFmpegVRamEncoder *encoder) {
try {
if (!encoder)
return;
encoder->destroy();
delete encoder;
encoder = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("free encoder failed, ") + std::string(e.what()));
}
}
int ffmpeg_vram_set_bitrate(FFmpegVRamEncoder *encoder, int kbs) {
try {
return encoder->set_bitrate(kbs);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_set_bitrate failed, ") + std::string(e.what()));
}
return -1;
}
int ffmpeg_vram_set_framerate(FFmpegVRamEncoder *encoder, int32_t framerate) {
try {
return encoder->set_bitrate(framerate);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_vram_set_framerate failed, ") + std::string(e.what()));
}
return -1;
}
int ffmpeg_vram_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
int count = 0;
struct VendorMapping {
AdapterVendor adapter_vendor;
int driver_vendor;
};
VendorMapping vendors[] = {
{ADAPTER_VENDOR_INTEL, VENDOR_INTEL},
{ADAPTER_VENDOR_NVIDIA, VENDOR_NV},
{ADAPTER_VENDOR_AMD, VENDOR_AMD}
};
for (auto vendorMap : vendors) {
Adapters adapters;
if (!adapters.Init(vendorMap.adapter_vendor))
continue;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
FFmpegVRamEncoder *e = (FFmpegVRamEncoder *)ffmpeg_vram_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = ffmpeg_vram_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback,
&key_obj, 0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = (int32_t)vendorMap.driver_vendor; // Map adapter vendor to driver vendor
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -1,32 +0,0 @@
#ifndef FFMPEG_VRAM_FFI_H
#define FFMPEG_VRAM_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
void *ffmpeg_vram_new_decoder(void *device, int64_t luid,
int32_t codecID);
int ffmpeg_vram_decode(void *decoder, uint8_t *data, int len,
DecodeCallback callback, void *obj);
int ffmpeg_vram_destroy_decoder(void *decoder);
int ffmpeg_vram_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum,
int32_t dataFormat, uint8_t *data, int32_t length,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
void *ffmpeg_vram_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop);
int ffmpeg_vram_encode(void *encoder, void *tex, EncodeCallback callback,
void *obj, int64_t ms);
int ffmpeg_vram_destroy_encoder(void *encoder);
int ffmpeg_vram_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int ffmpeg_vram_set_bitrate(void *encoder, int32_t kbs);
int ffmpeg_vram_set_framerate(void *encoder, int32_t framerate);
#endif // FFMPEG_VRAM_FFI_H

View File

@@ -1,481 +0,0 @@
#include <cstring>
#include <d3d11_allocator.h>
#include <libavutil/pixfmt.h>
#include <sample_defs.h>
#include <sample_utils.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "MFXDEC"
#include "log.h"
#define CHECK_STATUS(X, MSG) \
{ \
mfxStatus __sts = (X); \
if (__sts != MFX_ERR_NONE) { \
MSDK_PRINT_RET_MSG(__sts, MSG); \
LOG_ERROR(std::string(MSG) + "failed, sts=" + std::to_string((int)__sts)); \
return __sts; \
} \
}
#define USE_SHADER
namespace {
class VplDecoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
MFXVideoSession session_;
MFXVideoDECODE *mfxDEC_ = NULL;
std::vector<mfxFrameSurface1> pmfxSurfaces_;
mfxVideoParam mfxVideoParams_;
bool initialized_ = false;
D3D11FrameAllocator d3d11FrameAllocator_;
mfxFrameAllocResponse mfxResponse_;
void *device_;
int64_t luid_;
DataFormat codecID_;
bool bt709_ = false;
bool full_range_ = false;
VplDecoder(void *device, int64_t luid, DataFormat codecID) {
device_ = device;
luid_ = luid;
codecID_ = codecID;
ZeroMemory(&mfxVideoParams_, sizeof(mfxVideoParams_));
ZeroMemory(&mfxResponse_, sizeof(mfxResponse_));
}
~VplDecoder() {}
int destroy() {
if (mfxDEC_) {
mfxDEC_->Close();
delete mfxDEC_;
mfxDEC_ = NULL;
}
return 0;
}
mfxStatus init() {
mfxStatus sts = MFX_ERR_NONE;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to initialize native device"));
return MFX_ERR_DEVICE_FAILED;
}
sts = InitializeMFX();
CHECK_STATUS(sts, "InitializeMFX");
// Create Media SDK decoder
mfxDEC_ = new MFXVideoDECODE(session_);
if (!mfxDEC_) {
LOG_ERROR(std::string("Failed to create MFXVideoDECODE"));
return MFX_ERR_NOT_INITIALIZED;
}
memset(&mfxVideoParams_, 0, sizeof(mfxVideoParams_));
if (!convert_codec(codecID_, mfxVideoParams_.mfx.CodecId)) {
LOG_ERROR(std::string("Unsupported codec"));
return MFX_ERR_UNSUPPORTED;
}
mfxVideoParams_.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
// AsyncDepth: sSpecifies how many asynchronous operations an
// application performs before the application explicitly synchronizes the
// result. If zero, the value is not specified
mfxVideoParams_.AsyncDepth = 1; // Not important.
// DecodedOrder: For AVC and HEVC, used to instruct the decoder
// to return output frames in the decoded order. Must be zero for all other
// decoders.
mfxVideoParams_.mfx.DecodedOrder = true; // Not important.
mfxVideoParams_.mfx.FrameInfo.FrameRateExtN = 30;
mfxVideoParams_.mfx.FrameInfo.FrameRateExtD = 1;
mfxVideoParams_.mfx.FrameInfo.AspectRatioW = 1;
mfxVideoParams_.mfx.FrameInfo.AspectRatioH = 1;
mfxVideoParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxVideoParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
// Validate video decode parameters (optional)
sts = mfxDEC_->Query(&mfxVideoParams_, &mfxVideoParams_);
CHECK_STATUS(sts, "Query");
return MFX_ERR_NONE;
}
int decode(uint8_t *data, int len, DecodeCallback callback, void *obj) {
mfxStatus sts = MFX_ERR_NONE;
mfxSyncPoint syncp;
mfxFrameSurface1 *pmfxOutSurface = NULL;
bool decoded = false;
mfxBitstream mfxBS;
setBitStream(&mfxBS, data, len);
if (!initialized_) {
sts = initializeDecode(&mfxBS, false);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("initializeDecode failed, sts=") + std::to_string((int)sts));
return -1;
}
initialized_ = true;
}
setBitStream(&mfxBS, data, len);
auto start = util::now();
do {
if (util::elapsed_ms(start) > DECODE_TIMEOUT_MS) {
LOG_ERROR(std::string("decode timeout"));
break;
}
int nIndex =
GetFreeSurfaceIndex(pmfxSurfaces_.data(),
pmfxSurfaces_.size()); // Find free frame surface
if (nIndex >= pmfxSurfaces_.size()) {
LOG_ERROR(std::string("GetFreeSurfaceIndex failed, nIndex=") +
std::to_string(nIndex));
break;
}
sts = mfxDEC_->DecodeFrameAsync(&mfxBS, &pmfxSurfaces_[nIndex],
&pmfxOutSurface, &syncp);
if (MFX_ERR_NONE == sts) {
if (!syncp) {
LOG_ERROR(std::string("should not happen, syncp is NULL while error is none"));
break;
}
sts = session_.SyncOperation(syncp, 1000);
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("SyncOperation failed, sts=") + std::to_string((int)sts));
break;
}
if (!pmfxOutSurface) {
LOG_ERROR(std::string("pmfxOutSurface is null"));
break;
}
if (!convert(pmfxOutSurface)) {
LOG_ERROR(std::string("Failed to convert"));
break;
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
break;
} else if (MFX_WRN_DEVICE_BUSY == sts) {
LOG_INFO(std::string("Device busy"));
Sleep(1);
continue;
} else if (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM == sts) {
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#multiple-sequence-headers
LOG_INFO(std::string("Incompatible video param, reset decoder"));
// https://github.com/FFmpeg/FFmpeg/blob/f84412d6f4e9c1f1d1a2491f9337d7e789c688ba/libavcodec/qsvdec.c#L736
setBitStream(&mfxBS, data, len);
sts = initializeDecode(&mfxBS, true);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("initializeDecode failed, sts=") + std::to_string((int)sts));
break;
}
Sleep(1);
continue;
} else if (MFX_WRN_VIDEO_PARAM_CHANGED == sts) {
LOG_TRACE(std::string("new sequence header"));
sts = mfxDEC_->GetVideoParam(&mfxVideoParams_);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("GetVideoParam failed, sts=") + std::to_string((int)sts));
}
continue;
} else if (MFX_ERR_MORE_SURFACE == sts) {
LOG_INFO(std::string("More surface"));
Sleep(1);
continue;
} else {
LOG_ERROR(std::string("DecodeFrameAsync failed, sts=") + std::to_string(sts));
break;
}
// double confirm, check continue
} while (MFX_ERR_NONE == sts || MFX_WRN_DEVICE_BUSY == sts ||
MFX_ERR_INCOMPATIBLE_VIDEO_PARAM == sts ||
MFX_WRN_VIDEO_PARAM_CHANGED == sts || MFX_ERR_MORE_SURFACE == sts);
if (!decoded) {
LOG_ERROR(std::string("decode failed, sts=") + std::to_string(sts));
}
return decoded ? 0 : -1;
}
private:
mfxStatus InitializeMFX() {
mfxStatus sts = MFX_ERR_NONE;
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11;
mfxVersion ver = {{0, 1}};
D3D11AllocatorParams allocParams;
sts = session_.Init(impl, &ver);
CHECK_STATUS(sts, "session Init");
sts = session_.SetHandle(MFX_HANDLE_D3D11_DEVICE, native_->device_.Get());
CHECK_STATUS(sts, "SetHandle");
allocParams.bUseSingleTexture = false; // important
allocParams.pDevice = native_->device_.Get();
allocParams.uncompressedResourceMiscFlags = 0;
sts = d3d11FrameAllocator_.Init(&allocParams);
CHECK_STATUS(sts, "init D3D11FrameAllocator");
sts = session_.SetFrameAllocator(&d3d11FrameAllocator_);
CHECK_STATUS(sts, "SetFrameAllocator");
return MFX_ERR_NONE;
}
bool convert_codec(DataFormat dataFormat, mfxU32 &CodecId) {
switch (dataFormat) {
case H264:
CodecId = MFX_CODEC_AVC;
return true;
case H265:
CodecId = MFX_CODEC_HEVC;
return true;
}
return false;
}
mfxStatus initializeDecode(mfxBitstream *mfxBS, bool reinit) {
mfxStatus sts = MFX_ERR_NONE;
mfxFrameAllocRequest Request;
memset(&Request, 0, sizeof(Request));
mfxU16 numSurfaces;
mfxU16 width, height;
mfxU8 bitsPerPixel = 12; // NV12
mfxU32 surfaceSize;
mfxU8 *surfaceBuffers;
// mfxExtVideoSignalInfo got MFX_ERR_INVALID_VIDEO_PARAM
// mfxExtVideoSignalInfo video_signal_info = {0};
// https://spec.oneapi.io/versions/1.1-rev-1/elements/oneVPL/source/API_ref/VPL_func_vid_decode.html#mfxvideodecode-decodeheader
sts = mfxDEC_->DecodeHeader(mfxBS, &mfxVideoParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
sts = mfxDEC_->QueryIOSurf(&mfxVideoParams_, &Request);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
numSurfaces = Request.NumFrameSuggested;
// Request.Type |= WILL_READ; // This line is only required for Windows
// DirectX11 to ensure that surfaces can be retrieved by the application
// Allocate surfaces for decoder
if (reinit) {
sts = d3d11FrameAllocator_.FreeFrames(&mfxResponse_);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
}
sts = d3d11FrameAllocator_.AllocFrames(&Request, &mfxResponse_);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
// Allocate surface headers (mfxFrameSurface1) for decoder
pmfxSurfaces_.resize(numSurfaces);
for (int i = 0; i < numSurfaces; i++) {
memset(&pmfxSurfaces_[i], 0, sizeof(mfxFrameSurface1));
pmfxSurfaces_[i].Info = mfxVideoParams_.mfx.FrameInfo;
pmfxSurfaces_[i].Data.MemId =
mfxResponse_
.mids[i]; // MID (memory id) represents one video NV12 surface
}
// Initialize the Media SDK decoder
if (reinit) {
// https://github.com/FFmpeg/FFmpeg/blob/f84412d6f4e9c1f1d1a2491f9337d7e789c688ba/libavcodec/qsvdec.c#L181
sts = mfxDEC_->Close();
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
}
sts = mfxDEC_->Init(&mfxVideoParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
return MFX_ERR_NONE;
}
void setBitStream(mfxBitstream *mfxBS, uint8_t *data, int len) {
memset(mfxBS, 0, sizeof(mfxBitstream));
mfxBS->Data = data;
mfxBS->DataLength = len;
mfxBS->MaxLength = len;
mfxBS->DataFlag = MFX_BITSTREAM_COMPLETE_FRAME;
}
bool convert(mfxFrameSurface1 *pmfxOutSurface) {
mfxStatus sts = MFX_ERR_NONE;
mfxHDLPair pair = {NULL};
sts = d3d11FrameAllocator_.GetFrameHDL(pmfxOutSurface->Data.MemId,
(mfxHDL *)&pair);
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("Failed to GetFrameHDL"));
return false;
}
ID3D11Texture2D *texture = (ID3D11Texture2D *)pair.first;
D3D11_TEXTURE2D_DESC desc2D;
texture->GetDesc(&desc2D);
if (!native_->EnsureTexture(pmfxOutSurface->Info.CropW,
pmfxOutSurface->Info.CropH)) {
LOG_ERROR(std::string("Failed to EnsureTexture"));
return false;
}
native_->next(); // comment out to remove picture shaking
#ifdef USE_SHADER
native_->BeginQuery();
if (!native_->Nv12ToBgra(pmfxOutSurface->Info.CropW,
pmfxOutSurface->Info.CropH, texture,
native_->GetCurrentTexture(), 0)) {
LOG_ERROR(std::string("Failed to Nv12ToBgra"));
native_->EndQuery();
return false;
}
native_->EndQuery();
native_->Query();
#else
native_->BeginQuery();
// nv12 -> bgra
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 60;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: aligned width, height or crop width, height
contentDesc.InputWidth = pmfxOutSurface->Info.CropW;
contentDesc.InputHeight = pmfxOutSurface->Info.CropH;
contentDesc.OutputWidth = pmfxOutSurface->Info.CropW;
contentDesc.OutputHeight = pmfxOutSurface->Info.CropH;
contentDesc.OutputFrameRate.Numerator = 60;
contentDesc.OutputFrameRate.Denominator = 1;
DXGI_COLOR_SPACE_TYPE colorSpace_out =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_in;
if (bt709_) {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->Process(texture, native_->GetCurrentTexture(), contentDesc,
colorSpace_in, colorSpace_out, 0)) {
LOG_ERROR(std::string("Failed to process"));
native_->EndQuery();
return false;
}
native_->context_->Flush();
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Failed to query"));
return false;
}
#endif
return true;
}
};
} // namespace
extern "C" {
int mfx_destroy_decoder(void *decoder) {
VplDecoder *p = (VplDecoder *)decoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
}
void *mfx_new_decoder(void *device, int64_t luid, DataFormat codecID) {
VplDecoder *p = NULL;
try {
p = new VplDecoder(device, luid, codecID);
if (p) {
if (p->init() == MFX_ERR_NONE) {
return p;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int mfx_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj) {
try {
VplDecoder *p = (VplDecoder *)decoder;
if (p->decode(data, len, callback, obj) == 0) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int mfx_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_INTEL))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
VplDecoder *p = (VplDecoder *)mfx_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = mfx_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_INTEL;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
}
return -1;
}
} // extern "C"

View File

@@ -1,709 +0,0 @@
#include <cstring>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <limits>
#include <sample_defs.h>
#include <sample_utils.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "MFXENC"
#include "log.h"
// #define CONFIG_USE_VPP
#define CONFIG_USE_D3D_CONVERT
#define CHECK_STATUS(X, MSG) \
{ \
mfxStatus __sts = (X); \
if (__sts != MFX_ERR_NONE) { \
LOG_ERROR(std::string(MSG) + " failed, sts=" + std::to_string((int)__sts)); \
return __sts; \
} \
}
namespace {
mfxStatus MFX_CDECL simple_getHDL(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) {
mfxHDLPair *pair = (mfxHDLPair *)handle;
pair->first = mid;
pair->second = (mfxHDL)(UINT)0;
return MFX_ERR_NONE;
}
mfxFrameAllocator frameAllocator{{}, NULL, NULL, NULL,
NULL, simple_getHDL, NULL};
mfxStatus InitSession(MFXVideoSession &session) {
mfxInitParam mfxparams{};
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11;
mfxparams.Implementation = impl;
mfxparams.Version.Major = 1;
mfxparams.Version.Minor = 0;
mfxparams.GPUCopy = MFX_GPUCOPY_OFF;
return session.InitEx(mfxparams);
}
class VplEncoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
MFXVideoSession session_;
MFXVideoENCODE *mfxENC_ = nullptr;
std::vector<mfxFrameSurface1> encSurfaces_;
std::vector<mfxU8> bstData_;
mfxBitstream mfxBS_;
mfxVideoParam mfxEncParams_;
mfxExtBuffer *extbuffers_[4] = {NULL, NULL, NULL, NULL};
mfxExtCodingOption coding_option_;
mfxExtCodingOption2 coding_option2_;
mfxExtCodingOption3 coding_option3_;
mfxExtVideoSignalInfo signal_info_;
ComPtr<ID3D11Texture2D> nv12Texture_ = nullptr;
// vpp
#ifdef CONFIG_USE_VPP
MFXVideoVPP *mfxVPP_ = nullptr;
mfxVideoParam vppParams_;
mfxExtBuffer *vppExtBuffers_[1] = {NULL};
mfxExtVPPDoNotUse vppDontUse_;
mfxU32 vppDontUseArgList_[4];
std::vector<mfxFrameSurface1> vppSurfaces_;
#endif
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_ = 0;
int32_t height_ = 0;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
bool full_range_ = false;
bool bt709_ = false;
VplEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs, int32_t framerate,
int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
}
~VplEncoder() {}
mfxStatus Reset() {
mfxStatus sts = MFX_ERR_NONE;
if (!native_) {
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("failed to init native device"));
return MFX_ERR_DEVICE_FAILED;
}
}
sts = resetMFX();
CHECK_STATUS(sts, "resetMFX");
#ifdef CONFIG_USE_VPP
sts = resetVpp();
CHECK_STATUS(sts, "resetVpp");
#endif
sts = resetEnc();
CHECK_STATUS(sts, "resetEnc");
return MFX_ERR_NONE;
}
int encode(ID3D11Texture2D *tex, EncodeCallback callback, void *obj,
int64_t ms) {
mfxStatus sts = MFX_ERR_NONE;
int nEncSurfIdx =
GetFreeSurfaceIndex(encSurfaces_.data(), encSurfaces_.size());
if (nEncSurfIdx >= encSurfaces_.size()) {
LOG_ERROR(std::string("no free enc surface"));
return -1;
}
mfxFrameSurface1 *encSurf = &encSurfaces_[nEncSurfIdx];
#ifdef CONFIG_USE_VPP
mfxSyncPoint syncp;
sts = vppOneFrame(tex, encSurf, syncp);
syncp = NULL;
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("vppOneFrame failed, sts=") + std::to_string((int)sts));
return -1;
}
#elif defined(CONFIG_USE_D3D_CONVERT)
DXGI_COLOR_SPACE_TYPE colorSpace_in =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_out;
if (bt709_) {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!nv12Texture_) {
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
tex->GetDesc(&desc);
desc.Format = DXGI_FORMAT_NV12;
desc.MiscFlags = 0;
HRI(native_->device_->CreateTexture2D(
&desc, NULL, nv12Texture_.ReleaseAndGetAddressOf()));
}
if (!native_->BgraToNv12(tex, nv12Texture_.Get(), width_, height_,
colorSpace_in, colorSpace_out)) {
LOG_ERROR(std::string("failed to convert to NV12"));
return -1;
}
encSurf->Data.MemId = nv12Texture_.Get();
#else
encSurf->Data.MemId = tex;
#endif
return encodeOneFrame(encSurf, callback, obj, ms);
}
void destroy() {
if (mfxENC_) {
// - It is recommended to close Media SDK components first, before
// releasing allocated surfaces, since
// some surfaces may still be locked by internal Media SDK resources.
mfxENC_->Close();
delete mfxENC_;
mfxENC_ = NULL;
}
#ifdef CONFIG_USE_VPP
if (mfxVPP_) {
mfxVPP_->Close();
delete mfxVPP_;
mfxVPP_ = NULL;
}
#endif
// session closed automatically on destruction
}
private:
mfxStatus resetMFX() {
mfxStatus sts = MFX_ERR_NONE;
sts = InitSession(session_);
CHECK_STATUS(sts, "InitSession");
sts = session_.SetHandle(MFX_HANDLE_D3D11_DEVICE, native_->device_.Get());
CHECK_STATUS(sts, "SetHandle");
sts = session_.SetFrameAllocator(&frameAllocator);
CHECK_STATUS(sts, "SetFrameAllocator");
return MFX_ERR_NONE;
}
#ifdef CONFIG_USE_VPP
mfxStatus resetVpp() {
mfxStatus sts = MFX_ERR_NONE;
memset(&vppParams_, 0, sizeof(vppParams_));
vppParams_.IOPattern =
MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
vppParams_.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
vppParams_.vpp.In.FrameRateExtN = framerate_;
vppParams_.vpp.In.FrameRateExtD = 1;
vppParams_.vpp.In.Width = MSDK_ALIGN16(width_);
vppParams_.vpp.In.Height =
(MFX_PICSTRUCT_PROGRESSIVE == vppParams_.vpp.In.PicStruct)
? MSDK_ALIGN16(height_)
: MSDK_ALIGN32(height_);
vppParams_.vpp.In.CropX = 0;
vppParams_.vpp.In.CropY = 0;
vppParams_.vpp.In.CropW = width_;
vppParams_.vpp.In.CropH = height_;
vppParams_.vpp.In.Shift = 0;
memcpy(&vppParams_.vpp.Out, &vppParams_.vpp.In, sizeof(vppParams_.vpp.Out));
vppParams_.vpp.In.FourCC = MFX_FOURCC_RGB4;
vppParams_.vpp.Out.FourCC = MFX_FOURCC_NV12;
vppParams_.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
vppParams_.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
vppParams_.AsyncDepth = 1;
vppParams_.ExtParam = vppExtBuffers_;
vppParams_.NumExtParam = 1;
vppExtBuffers_[0] = (mfxExtBuffer *)&vppDontUse_;
vppDontUse_.Header.BufferId = MFX_EXTBUFF_VPP_DONOTUSE;
vppDontUse_.Header.BufferSz = sizeof(vppDontUse_);
vppDontUse_.AlgList = vppDontUseArgList_;
vppDontUse_.NumAlg = 4;
vppDontUseArgList_[0] = MFX_EXTBUFF_VPP_DENOISE;
vppDontUseArgList_[1] = MFX_EXTBUFF_VPP_SCENE_ANALYSIS;
vppDontUseArgList_[2] = MFX_EXTBUFF_VPP_DETAIL;
vppDontUseArgList_[3] = MFX_EXTBUFF_VPP_PROCAMP;
if (mfxVPP_) {
mfxVPP_->Close();
delete mfxVPP_;
mfxVPP_ = NULL;
}
mfxVPP_ = new MFXVideoVPP(session_);
if (!mfxVPP_) {
LOG_ERROR(std::string("Failed to create MFXVideoVPP"));
return MFX_ERR_MEMORY_ALLOC;
}
sts = mfxVPP_->Query(&vppParams_, &vppParams_);
CHECK_STATUS(sts, "vpp query");
mfxFrameAllocRequest vppAllocRequest;
ZeroMemory(&vppAllocRequest, sizeof(vppAllocRequest));
memcpy(&vppAllocRequest.Info, &vppParams_.vpp.In, sizeof(mfxFrameInfo));
sts = mfxVPP_->QueryIOSurf(&vppParams_, &vppAllocRequest);
CHECK_STATUS(sts, "vpp QueryIOSurf");
vppSurfaces_.resize(vppAllocRequest.NumFrameSuggested);
for (int i = 0; i < vppAllocRequest.NumFrameSuggested; i++) {
memset(&vppSurfaces_[i], 0, sizeof(mfxFrameSurface1));
memcpy(&vppSurfaces_[i].Info, &vppParams_.vpp.In, sizeof(mfxFrameInfo));
}
sts = mfxVPP_->Init(&vppParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
CHECK_STATUS(sts, "vpp init");
return MFX_ERR_NONE;
}
#endif
mfxStatus resetEnc() {
mfxStatus sts = MFX_ERR_NONE;
memset(&mfxEncParams_, 0, sizeof(mfxEncParams_));
// Basic
if (!convert_codec(dataFormat_, mfxEncParams_.mfx.CodecId)) {
LOG_ERROR(std::string("unsupported dataFormat: ") + std::to_string(dataFormat_));
return MFX_ERR_UNSUPPORTED;
}
// mfxEncParams_.mfx.LowPower = MFX_CODINGOPTION_ON;
mfxEncParams_.mfx.BRCParamMultiplier = 0;
// Frame Info
mfxEncParams_.mfx.FrameInfo.FrameRateExtN = framerate_;
mfxEncParams_.mfx.FrameInfo.FrameRateExtD = 1;
#ifdef CONFIG_USE_VPP
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
#elif defined(CONFIG_USE_D3D_CONVERT)
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
#else
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_BGR4;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
#endif
mfxEncParams_.mfx.FrameInfo.BitDepthLuma = 8;
mfxEncParams_.mfx.FrameInfo.BitDepthChroma = 8;
mfxEncParams_.mfx.FrameInfo.Shift = 0;
mfxEncParams_.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
mfxEncParams_.mfx.FrameInfo.CropX = 0;
mfxEncParams_.mfx.FrameInfo.CropY = 0;
mfxEncParams_.mfx.FrameInfo.CropW = width_;
mfxEncParams_.mfx.FrameInfo.CropH = height_;
// Width must be a multiple of 16
// Height must be a multiple of 16 in case of frame picture and a multiple
// of 32 in case of field picture
mfxEncParams_.mfx.FrameInfo.Width = MSDK_ALIGN16(width_);
mfxEncParams_.mfx.FrameInfo.Height =
(MFX_PICSTRUCT_PROGRESSIVE == mfxEncParams_.mfx.FrameInfo.PicStruct)
? MSDK_ALIGN16(height_)
: MSDK_ALIGN32(height_);
// Encoding Options
mfxEncParams_.mfx.EncodedOrder = 0;
mfxEncParams_.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
// Configuration for low latency
mfxEncParams_.AsyncDepth = 1; // 1 is best for low latency
mfxEncParams_.mfx.GopRefDist =
1; // 1 is best for low latency, I and P frames only
mfxEncParams_.mfx.GopPicSize = (gop_ > 0 && gop_ < 0xFFFF) ? gop_ : 0xFFFF;
// quality
// https://www.intel.com/content/www/us/en/developer/articles/technical/common-bitrate-control-methods-in-intel-media-sdk.html
mfxEncParams_.mfx.TargetUsage = MFX_TARGETUSAGE_BEST_SPEED;
mfxEncParams_.mfx.RateControlMethod = MFX_RATECONTROL_VBR;
mfxEncParams_.mfx.InitialDelayInKB = 0;
mfxEncParams_.mfx.BufferSizeInKB = 512;
mfxEncParams_.mfx.TargetKbps = kbs_;
mfxEncParams_.mfx.MaxKbps = kbs_;
mfxEncParams_.mfx.NumSlice = 1;
mfxEncParams_.mfx.NumRefFrame = 0;
if (H264 == dataFormat_) {
mfxEncParams_.mfx.CodecLevel = MFX_LEVEL_AVC_51;
mfxEncParams_.mfx.CodecProfile = MFX_PROFILE_AVC_MAIN;
} else if (H265 == dataFormat_) {
mfxEncParams_.mfx.CodecLevel = MFX_LEVEL_HEVC_51;
mfxEncParams_.mfx.CodecProfile = MFX_PROFILE_HEVC_MAIN;
}
resetEncExtParams();
// Create Media SDK encoder
if (mfxENC_) {
mfxENC_->Close();
delete mfxENC_;
mfxENC_ = NULL;
}
mfxENC_ = new MFXVideoENCODE(session_);
if (!mfxENC_) {
LOG_ERROR(std::string("failed to create MFXVideoENCODE"));
return MFX_ERR_NOT_INITIALIZED;
}
// Validate video encode parameters (optional)
// - In this example the validation result is written to same structure
// - MFX_WRN_INCOMPATIBLE_VIDEO_PARAM is returned if some of the video
// parameters are not supported,
// instead the encoder will select suitable parameters closest matching
// the requested configuration
sts = mfxENC_->Query(&mfxEncParams_, &mfxEncParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_INCOMPATIBLE_VIDEO_PARAM);
CHECK_STATUS(sts, "Query");
mfxFrameAllocRequest EncRequest;
memset(&EncRequest, 0, sizeof(EncRequest));
sts = mfxENC_->QueryIOSurf(&mfxEncParams_, &EncRequest);
CHECK_STATUS(sts, "QueryIOSurf");
// Allocate surface headers (mfxFrameSurface1) for encoder
encSurfaces_.resize(EncRequest.NumFrameSuggested);
for (int i = 0; i < EncRequest.NumFrameSuggested; i++) {
memset(&encSurfaces_[i], 0, sizeof(mfxFrameSurface1));
memcpy(&encSurfaces_[i].Info, &mfxEncParams_.mfx.FrameInfo,
sizeof(mfxFrameInfo));
}
// Initialize the Media SDK encoder
sts = mfxENC_->Init(&mfxEncParams_);
CHECK_STATUS(sts, "Init");
// Retrieve video parameters selected by encoder.
// - BufferSizeInKB parameter is required to set bit stream buffer size
sts = mfxENC_->GetVideoParam(&mfxEncParams_);
CHECK_STATUS(sts, "GetVideoParam");
// Prepare Media SDK bit stream buffer
memset(&mfxBS_, 0, sizeof(mfxBS_));
mfxBS_.MaxLength = mfxEncParams_.mfx.BufferSizeInKB * 1024;
bstData_.resize(mfxBS_.MaxLength);
mfxBS_.Data = bstData_.data();
return MFX_ERR_NONE;
}
#ifdef CONFIG_USE_VPP
mfxStatus vppOneFrame(void *texture, mfxFrameSurface1 *out,
mfxSyncPoint syncp) {
mfxStatus sts = MFX_ERR_NONE;
int surfIdx =
GetFreeSurfaceIndex(vppSurfaces_.data(),
vppSurfaces_.size()); // Find free frame surface
if (surfIdx >= vppSurfaces_.size()) {
LOG_ERROR(std::string("No free vpp surface"));
return MFX_ERR_MORE_SURFACE;
}
mfxFrameSurface1 *in = &vppSurfaces_[surfIdx];
in->Data.MemId = texture;
for (;;) {
sts = mfxVPP_->RunFrameVPPAsync(in, out, NULL, &syncp);
if (MFX_ERR_NONE < sts &&
!syncp) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
} else if (MFX_ERR_NONE < sts && syncp) {
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
} else {
break; // not a warning
}
}
if (MFX_ERR_NONE == sts) {
sts = session_.SyncOperation(
syncp, 1000); // Synchronize. Wait until encoded frame is ready
CHECK_STATUS(sts, "SyncOperation");
}
return sts;
}
#endif
int encodeOneFrame(mfxFrameSurface1 *in, EncodeCallback callback, void *obj,
int64_t ms) {
mfxStatus sts = MFX_ERR_NONE;
mfxSyncPoint syncp;
bool encoded = false;
auto start = util::now();
do {
if (util::elapsed_ms(start) > ENCODE_TIMEOUT_MS) {
LOG_ERROR(std::string("encode timeout"));
break;
}
mfxBS_.DataLength = 0;
mfxBS_.DataOffset = 0;
mfxBS_.TimeStamp = ms * 90; // ms to 90KHZ
mfxBS_.DecodeTimeStamp = mfxBS_.TimeStamp;
sts = mfxENC_->EncodeFrameAsync(NULL, in, &mfxBS_, &syncp);
if (MFX_ERR_NONE == sts) {
if (!syncp) {
LOG_ERROR(std::string("should not happen, error is none while syncp is null"));
break;
}
sts = session_.SyncOperation(
syncp, 1000); // Synchronize. Wait until encoded frame is ready
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("SyncOperation failed, sts=") + std::to_string(sts));
break;
}
if (mfxBS_.DataLength <= 0) {
LOG_ERROR(std::string("mfxBS_.DataLength <= 0"));
break;
}
int key = (mfxBS_.FrameType & MFX_FRAMETYPE_I) ||
(mfxBS_.FrameType & MFX_FRAMETYPE_IDR);
if (callback)
callback(mfxBS_.Data + mfxBS_.DataOffset, mfxBS_.DataLength, key, obj,
ms);
encoded = true;
break;
} else if (MFX_WRN_DEVICE_BUSY == sts) {
LOG_INFO(std::string("device busy"));
Sleep(1);
continue;
} else if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) {
LOG_ERROR(std::string("not enough buffer, size=") +
std::to_string(mfxBS_.MaxLength));
if (mfxBS_.MaxLength < 10 * 1024 * 1024) {
mfxBS_.MaxLength *= 2;
bstData_.resize(mfxBS_.MaxLength);
mfxBS_.Data = bstData_.data();
Sleep(1);
continue;
} else {
break;
}
} else {
LOG_ERROR(std::string("EncodeFrameAsync failed, sts=") + std::to_string(sts));
break;
}
// double confirm, check continue
} while (MFX_WRN_DEVICE_BUSY == sts || MFX_ERR_NOT_ENOUGH_BUFFER == sts);
if (!encoded) {
LOG_ERROR(std::string("encode failed, sts=") + std::to_string(sts));
}
return encoded ? 0 : -1;
}
void resetEncExtParams() {
// coding option
memset(&coding_option_, 0, sizeof(mfxExtCodingOption));
coding_option_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
coding_option_.Header.BufferSz = sizeof(mfxExtCodingOption);
coding_option_.NalHrdConformance = MFX_CODINGOPTION_OFF;
extbuffers_[0] = (mfxExtBuffer *)&coding_option_;
// coding option2
memset(&coding_option2_, 0, sizeof(mfxExtCodingOption2));
coding_option2_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
coding_option2_.Header.BufferSz = sizeof(mfxExtCodingOption2);
coding_option2_.RepeatPPS = MFX_CODINGOPTION_OFF;
extbuffers_[1] = (mfxExtBuffer *)&coding_option2_;
// coding option3
memset(&coding_option3_, 0, sizeof(mfxExtCodingOption3));
coding_option3_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
coding_option3_.Header.BufferSz = sizeof(mfxExtCodingOption3);
extbuffers_[2] = (mfxExtBuffer *)&coding_option3_;
// signal info
memset(&signal_info_, 0, sizeof(mfxExtVideoSignalInfo));
signal_info_.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
signal_info_.Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
signal_info_.VideoFormat = 5;
signal_info_.ColourDescriptionPresent = 1;
signal_info_.VideoFullRange = !!full_range_;
signal_info_.MatrixCoefficients =
bt709_ ? AVCOL_SPC_BT709 : AVCOL_SPC_SMPTE170M;
signal_info_.ColourPrimaries =
bt709_ ? AVCOL_PRI_BT709 : AVCOL_PRI_SMPTE170M;
signal_info_.TransferCharacteristics =
bt709_ ? AVCOL_TRC_BT709 : AVCOL_TRC_SMPTE170M;
// https://github.com/GStreamer/gstreamer/blob/651dcb49123ec516e7c582e4a49a5f3f15c10f93/subprojects/gst-plugins-bad/sys/qsv/gstqsvh264enc.cpp#L1647
extbuffers_[3] = (mfxExtBuffer *)&signal_info_;
mfxEncParams_.ExtParam = extbuffers_;
mfxEncParams_.NumExtParam = 4;
}
bool convert_codec(DataFormat dataFormat, mfxU32 &CodecId) {
switch (dataFormat) {
case H264:
CodecId = MFX_CODEC_AVC;
return true;
case H265:
CodecId = MFX_CODEC_HEVC;
return true;
}
return false;
}
};
} // namespace
extern "C" {
int mfx_driver_support() {
MFXVideoSession session;
return InitSession(session) == MFX_ERR_NONE ? 0 : -1;
}
int mfx_destroy_encoder(void *encoder) {
VplEncoder *p = (VplEncoder *)encoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
}
void *mfx_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t w, int32_t h, int32_t kbs,
int32_t framerate, int32_t gop) {
VplEncoder *p = NULL;
try {
p = new VplEncoder(handle, luid, dataFormat, w, h, kbs, framerate,
gop);
if (!p) {
return NULL;
}
mfxStatus sts = p->Reset();
if (sts == MFX_ERR_NONE) {
return p;
} else {
LOG_ERROR(std::string("Init failed, sts=") + std::to_string(sts));
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int mfx_encode(void *encoder, ID3D11Texture2D *tex, EncodeCallback callback,
void *obj, int64_t ms) {
try {
return ((VplEncoder *)encoder)->encode(tex, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
return -1;
}
int mfx_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_INTEL))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
VplEncoder *e = (VplEncoder *)mfx_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = mfx_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback, &key_obj,
0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_INTEL;
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#dynamic-bitrate-change
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#mfxinfomfx
// https://spec.oneapi.io/onevpl/2.4.0/programming_guide/VPL_prg_encoding.html#configuration-change
int mfx_set_bitrate(void *encoder, int32_t kbs) {
try {
VplEncoder *p = (VplEncoder *)encoder;
mfxStatus sts = MFX_ERR_NONE;
// https://github.com/GStreamer/gstreamer/blob/e19428a802c2f4ee9773818aeb0833f93509a1c0/subprojects/gst-plugins-bad/sys/qsv/gstqsvencoder.cpp#L1312
p->kbs_ = kbs;
p->mfxENC_->GetVideoParam(&p->mfxEncParams_);
p->mfxEncParams_.mfx.TargetKbps = kbs;
p->mfxEncParams_.mfx.MaxKbps = kbs;
sts = p->mfxENC_->Reset(&p->mfxEncParams_);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("reset failed, sts=") + std::to_string(sts));
return -1;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
return -1;
}
int mfx_set_framerate(void *encoder, int32_t framerate) {
LOG_WARN("not support change framerate");
return -1;
}
}

View File

@@ -1,39 +0,0 @@
#ifndef MFX_FFI_H
#define MFX_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int mfx_driver_support();
void *mfx_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop);
int mfx_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms);
int mfx_destroy_encoder(void *encoder);
void *mfx_new_decoder(void *device, int64_t luid,
int32_t dataFormat);
int mfx_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj);
int mfx_destroy_decoder(void *decoder);
int mfx_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int mfx_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int mfx_set_bitrate(void *encoder, int32_t kbs);
int mfx_set_framerate(void *encoder, int32_t framerate);
#endif // MFX_FFI_H

View File

@@ -1,188 +0,0 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/muxing.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/timestamp.h>
}
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LOG_MODULE "MUX"
#include <log.h>
namespace {
typedef struct OutputStream {
AVStream *st;
AVPacket *tmp_pkt;
} OutputStream;
class Muxer {
public:
OutputStream video_st;
AVFormatContext *oc = NULL;
int framerate;
int64_t start_ms;
int64_t last_pts;
int got_first;
Muxer() {}
void destroy() {
OutputStream *ost = &video_st;
if (ost && ost->tmp_pkt)
av_packet_free(&ost->tmp_pkt);
if (oc && oc->pb && !(oc->oformat->flags & AVFMT_NOFILE))
avio_closep(&oc->pb);
if (oc)
avformat_free_context(oc);
}
bool init(const char *filename, int width, int height, int is265,
int framerate) {
OutputStream *ost = &video_st;
ost->st = NULL;
ost->tmp_pkt = NULL;
int ret;
if ((ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename)) < 0) {
LOG_ERROR(std::string("avformat_alloc_output_context2 failed, ret = ") +
std::to_string(ret));
return false;
}
ost->st = avformat_new_stream(oc, NULL);
if (!ost->st) {
LOG_ERROR(std::string("avformat_new_stream failed"));
return false;
}
ost->st->id = oc->nb_streams - 1;
ost->st->codecpar->codec_id = is265 ? AV_CODEC_ID_H265 : AV_CODEC_ID_H264;
ost->st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
ost->st->codecpar->width = width;
ost->st->codecpar->height = height;
if (!(oc->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
LOG_ERROR(std::string("avio_open failed, ret = ") + std::to_string(ret));
return false;
}
}
ost->tmp_pkt = av_packet_alloc();
if (!ost->tmp_pkt) {
LOG_ERROR(std::string("av_packet_alloc failed"));
return false;
}
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
LOG_ERROR(std::string("avformat_write_header failed"));
return false;
}
this->framerate = framerate;
this->start_ms = 0;
this->last_pts = 0;
this->got_first = 0;
return true;
}
int write_video_frame(const uint8_t *data, int len, int64_t pts_ms, int key) {
OutputStream *ost = &video_st;
AVPacket *pkt = ost->tmp_pkt;
AVFormatContext *fmt_ctx = oc;
int ret;
if (framerate <= 0)
return -3;
if (!got_first) {
if (key != 1)
return -2;
start_ms = pts_ms;
}
int64_t pts = (pts_ms - start_ms); // use write timestamp
if (pts <= last_pts && got_first) {
pts = last_pts + 1000 / framerate;
}
got_first = 1;
pkt->data = (uint8_t *)data;
pkt->size = len;
pkt->pts = pts;
pkt->dts = pkt->pts; // no B-frame
int64_t duration = pkt->pts - last_pts;
last_pts = pkt->pts;
pkt->duration = duration > 0 ? duration : 1000 / framerate; // predict
AVRational rational;
rational.num = 1;
rational.den = 1000;
av_packet_rescale_ts(pkt, rational,
ost->st->time_base); // ms -> stream timebase
pkt->stream_index = ost->st->index;
if (key == 1) {
pkt->flags |= AV_PKT_FLAG_KEY;
} else {
pkt->flags &= ~AV_PKT_FLAG_KEY;
}
ret = av_write_frame(fmt_ctx, pkt);
if (ret < 0) {
LOG_ERROR(std::string("av_write_frame failed, ret = ") + std::to_string(ret));
return -1;
}
return 0;
}
};
} // namespace
extern "C" Muxer *hwcodec_new_muxer(const char *filename, int width, int height,
int is265, int framerate) {
Muxer *muxer = NULL;
try {
muxer = new Muxer();
if (muxer) {
if (muxer->init(filename, width, height, is265, framerate)) {
return muxer;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new muxer exception: ") + std::string(e.what()));
}
if (muxer) {
muxer->destroy();
delete muxer;
muxer = NULL;
}
return NULL;
}
extern "C" int hwcodec_write_video_frame(Muxer *muxer, const uint8_t *data,
int len, int64_t pts_ms, int key) {
try {
return muxer->write_video_frame(data, len, pts_ms, key);
} catch (const std::exception &e) {
LOG_ERROR(std::string("write_video_frame exception: ") + std::string(e.what()));
}
return -1;
}
extern "C" int hwcodec_write_tail(Muxer *muxer) {
return av_write_trailer(muxer->oc);
}
extern "C" void hwcodec_free_muxer(Muxer *muxer) {
try {
if (!muxer)
return;
muxer->destroy();
delete muxer;
muxer = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("free_muxer exception: ") + std::string(e.what()));
}
}

View File

@@ -1,15 +0,0 @@
#ifndef MUX_FFI_H
#define MUX_FFI_H
#include <stdint.h>
void *hwcodec_new_muxer(const char *filename, int width, int height, int is265,
int framerate);
int hwcodec_write_video_frame(void *muxer, const uint8_t *data, int len,
int64_t pts_ms, int key);
int hwcodec_write_tail(void *muxer);
void hwcodec_free_muxer(void *muxer);
#endif // FFI_H

View File

@@ -1,693 +0,0 @@
#define FFNV_LOG_FUNC
#define FFNV_DEBUG_LOG_FUNC
#include <DirectXMath.h>
#include <Samples/NvCodec/NvDecoder/NvDecoder.h>
#include <Samples/Utils/NvCodecUtils.h>
#include <algorithm>
#include <array>
#include <d3dcompiler.h>
#include <directxcolors.h>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <thread>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "CUVID"
#include "log.h"
#define NUMVERTICES 6
using namespace DirectX;
namespace {
#define succ(call) ((call) == 0)
class CUVIDAutoUnmapper {
CudaFunctions *cudl_ = NULL;
CUgraphicsResource *pCuResource_ = NULL;
public:
CUVIDAutoUnmapper(CudaFunctions *cudl, CUgraphicsResource *pCuResource)
: cudl_(cudl), pCuResource_(pCuResource) {
if (!succ(cudl->cuGraphicsMapResources(1, pCuResource, 0))) {
LOG_TRACE(std::string("cuGraphicsMapResources failed"));
NVDEC_THROW_ERROR("cuGraphicsMapResources failed", CUDA_ERROR_UNKNOWN);
}
}
~CUVIDAutoUnmapper() {
if (!succ(cudl_->cuGraphicsUnmapResources(1, pCuResource_, 0))) {
LOG_TRACE(std::string("cuGraphicsUnmapResources failed"));
// NVDEC_THROW_ERROR("cuGraphicsUnmapResources failed",
// CUDA_ERROR_UNKNOWN);
}
}
};
class CUVIDAutoCtxPopper {
CudaFunctions *cudl_ = NULL;
public:
CUVIDAutoCtxPopper(CudaFunctions *cudl, CUcontext cuContext) : cudl_(cudl) {
if (!succ(cudl->cuCtxPushCurrent(cuContext))) {
LOG_TRACE(std::string("cuCtxPushCurrent failed"));
NVDEC_THROW_ERROR("cuCtxPopCurrent failed", CUDA_ERROR_UNKNOWN);
}
}
~CUVIDAutoCtxPopper() {
if (!succ(cudl_->cuCtxPopCurrent(NULL))) {
LOG_TRACE(std::string("cuCtxPopCurrent failed"));
// NVDEC_THROW_ERROR("cuCtxPopCurrent failed", CUDA_ERROR_UNKNOWN);
}
}
};
void load_driver(CudaFunctions **pp_cudl, CuvidFunctions **pp_cvdl) {
if (cuda_load_functions(pp_cudl, NULL) < 0) {
LOG_TRACE(std::string("cuda_load_functions failed"));
NVDEC_THROW_ERROR("cuda_load_functions failed", CUDA_ERROR_UNKNOWN);
}
if (cuvid_load_functions(pp_cvdl, NULL) < 0) {
LOG_TRACE(std::string("cuvid_load_functions failed"));
NVDEC_THROW_ERROR("cuvid_load_functions failed", CUDA_ERROR_UNKNOWN);
}
}
void free_driver(CudaFunctions **pp_cudl, CuvidFunctions **pp_cvdl) {
if (*pp_cvdl) {
cuvid_free_functions(pp_cvdl);
*pp_cvdl = NULL;
}
if (*pp_cudl) {
cuda_free_functions(pp_cudl);
*pp_cudl = NULL;
}
}
typedef struct _VERTEX {
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
class CuvidDecoder {
public:
CudaFunctions *cudl_ = NULL;
CuvidFunctions *cvdl_ = NULL;
NvDecoder *dec_ = NULL;
CUcontext cuContext_ = NULL;
CUgraphicsResource cuResource_[2] = {NULL, NULL}; // r8, r8g8
ComPtr<ID3D11Texture2D> textures_[2] = {NULL, NULL};
ComPtr<ID3D11RenderTargetView> RTV_ = NULL;
ComPtr<ID3D11ShaderResourceView> SRV_[2] = {NULL, NULL};
ComPtr<ID3D11VertexShader> vertexShader_ = NULL;
ComPtr<ID3D11PixelShader> pixelShader_ = NULL;
ComPtr<ID3D11SamplerState> samplerLinear_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
void *device_;
int64_t luid_;
DataFormat dataFormat_;
bool prepare_tried_ = false;
bool prepare_ok_ = false;
int width_ = 0;
int height_ = 0;
CUVIDEOFORMAT last_video_format_ = {};
public:
CuvidDecoder(void *device, int64_t luid, DataFormat dataFormat) {
device_ = device;
luid_ = luid;
dataFormat_ = dataFormat;
ZeroMemory(&last_video_format_, sizeof(last_video_format_));
load_driver(&cudl_, &cvdl_);
}
~CuvidDecoder() {}
bool init() {
if (!succ(cudl_->cuInit(0))) {
LOG_ERROR(std::string("cuInit failed"));
return false;
}
CUdevice cuDevice = 0;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to init native device"));
return false;
}
if (!succ(cudl_->cuD3D11GetDevice(&cuDevice, native_->adapter_.Get()))) {
LOG_ERROR(std::string("Failed to get cuDevice"));
return false;
}
if (!succ(cudl_->cuCtxCreate(&cuContext_, 0, cuDevice))) {
LOG_ERROR(std::string("Failed to create cuContext"));
return false;
}
if (!create_nvdecoder()) {
LOG_ERROR(std::string("Failed to create nvdecoder"));
return false;
}
return true;
}
// ref: HandlePictureDisplay
int decode(uint8_t *data, int len, DecodeCallback callback, void *obj) {
int nFrameReturned = decode_and_recreate(data, len);
if (nFrameReturned == -2) {
nFrameReturned = dec_->Decode(data, len, CUVID_PKT_ENDOFPICTURE);
}
if (nFrameReturned <= 0) {
return -1;
}
last_video_format_ = dec_->GetLatestVideoFormat();
cudaVideoSurfaceFormat format = dec_->GetOutputFormat();
int width = dec_->GetWidth();
int height = dec_->GetHeight();
if (prepare_tried_ && (width != width_ || height != height_)) {
LOG_INFO(std::string("resolution changed, (") + std::to_string(width_) + "," +
std::to_string(height_) + ") -> (" + std::to_string(width) +
"," + std::to_string(height) + ")");
reset_prepare();
width_ = width;
height_ = height;
}
if (!prepare()) {
LOG_ERROR(std::string("prepare failed"));
return -1;
}
bool decoded = false;
for (int i = 0; i < nFrameReturned; i++) {
uint8_t *pFrame = dec_->GetFrame();
native_->BeginQuery();
if (!copy_cuda_frame(pFrame)) {
LOG_ERROR(std::string("copy_cuda_frame failed"));
native_->EndQuery();
return -1;
}
if (!native_->EnsureTexture(width, height)) {
LOG_ERROR(std::string("EnsureTexture failed"));
native_->EndQuery();
return -1;
}
native_->next();
if (!set_rtv(native_->GetCurrentTexture())) {
LOG_ERROR(std::string("set_rtv failed"));
native_->EndQuery();
return -1;
}
if (!draw()) {
LOG_ERROR(std::string("draw failed"));
native_->EndQuery();
return -1;
}
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Query failed"));
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
}
return decoded ? 0 : -1;
}
void destroy() {
if (dec_) {
delete dec_;
dec_ = nullptr;
}
if (cudl_ && cuContext_) {
cudl_->cuCtxPushCurrent(cuContext_);
for (int i = 0; i < 2; i++) {
if (cuResource_[i]) {
cudl_->cuGraphicsUnregisterResource(cuResource_[i]);
cuResource_[i] = NULL;
}
}
cudl_->cuCtxPopCurrent(NULL);
cudl_->cuCtxDestroy(cuContext_);
cuContext_ = NULL;
}
free_driver(&cudl_, &cvdl_);
}
private:
void reset_prepare() {
prepare_tried_ = false;
prepare_ok_ = false;
if (cudl_ && cuContext_) {
cudl_->cuCtxPushCurrent(cuContext_);
for (int i = 0; i < 2; i++) {
if (cuResource_[i])
cudl_->cuGraphicsUnregisterResource(cuResource_[i]);
}
cudl_->cuCtxPopCurrent(NULL);
}
for (int i = 0; i < 2; i++) {
textures_[i].Reset();
SRV_[i].Reset();
}
RTV_.Reset();
vertexShader_.Reset();
pixelShader_.Reset();
samplerLinear_.Reset();
}
bool prepare() {
if (prepare_tried_) {
return prepare_ok_;
}
prepare_tried_ = true;
if (!set_srv())
return false;
if (!set_view_port())
return false;
if (!set_sample())
return false;
if (!set_shader())
return false;
if (!set_vertex_buffer())
return false;
if (!register_texture())
return false;
prepare_ok_ = true;
return true;
}
bool copy_cuda_frame(unsigned char *dpNv12) {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
int chromaHeight = dec_->GetChromaHeight();
CUVIDAutoCtxPopper ctxPoper(cudl_, cuContext_);
for (int i = 0; i < 2; i++) {
CUarray dstArray;
CUVIDAutoUnmapper unmapper(cudl_, &cuResource_[i]);
if (!succ(cudl_->cuGraphicsSubResourceGetMappedArray(
&dstArray, cuResource_[i], 0, 0)))
return false;
CUDA_MEMCPY2D m = {0};
m.srcMemoryType = CU_MEMORYTYPE_DEVICE;
m.srcDevice = (CUdeviceptr)(dpNv12 + (width * height) * i);
m.srcPitch = width; // pitch
m.dstMemoryType = CU_MEMORYTYPE_ARRAY;
m.dstArray = dstArray;
m.WidthInBytes = width;
m.Height = i == 0 ? height : chromaHeight;
if (!succ(cudl_->cuMemcpy2D(&m)))
return false;
}
return true;
}
bool draw() {
native_->context_->Draw(NUMVERTICES, 0);
native_->context_->Flush();
return true;
}
// return:
// >=0: nFrameReturned
// -1: failed
// -2: recreated, please decode again
int decode_and_recreate(uint8_t *data, int len) {
try {
int nFrameReturned = dec_->Decode(data, len, CUVID_PKT_ENDOFPICTURE);
if (nFrameReturned <= 0)
return -1;
CUVIDEOFORMAT video_format = dec_->GetLatestVideoFormat();
auto d1 = last_video_format_.display_area;
auto d2 = video_format.display_area;
// reconfigure may cause wrong display area
if (last_video_format_.coded_width != 0 &&
(d1.left != d2.left || d1.right != d2.right || d1.top != d2.top ||
d1.bottom != d2.bottom)) {
LOG_INFO(
std::string("recreate, display area changed from (") + std::to_string(d1.left) +
", " + std::to_string(d1.top) + ", " + std::to_string(d1.right) +
", " + std::to_string(d1.bottom) + ") to (" +
std::to_string(d2.left) + ", " + std::to_string(d2.top) + ", " +
std::to_string(d2.right) + ", " + std::to_string(d2.bottom) + ")");
if (create_nvdecoder()) {
return -2;
} else {
LOG_ERROR(std::string("create_nvdecoder failed"));
}
return -1;
} else {
return nFrameReturned;
}
} catch (const std::exception &e) {
unsigned int maxWidth = dec_->GetMaxWidth();
unsigned int maxHeight = dec_->GetMaxHeight();
CUVIDEOFORMAT video_format = dec_->GetLatestVideoFormat();
// https://github.com/NVIDIA/DALI/blob/4f5ee72b287cfbbe0d400734416ff37bd8027099/dali/operators/reader/loader/video/frames_decoder_gpu.cc#L212
if (maxWidth > 0 && (video_format.coded_width > maxWidth ||
video_format.coded_height > maxHeight)) {
LOG_INFO(std::string("recreate, exceed maxWidth/maxHeight: (") +
std::to_string(video_format.coded_width) + ", " +
std::to_string(video_format.coded_height) + " > (" +
std::to_string(maxWidth) + ", " + std::to_string(maxHeight) +
")");
if (create_nvdecoder()) {
return -2;
} else {
LOG_ERROR(std::string("create_nvdecoder failed"));
}
} else {
LOG_ERROR(std::string("Exception decode_and_recreate: ") + e.what());
}
}
return -1;
}
bool set_srv() {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
int chromaHeight = dec_->GetChromaHeight();
LOG_TRACE(std::string("width:") + std::to_string(width) +
", height:" + std::to_string(height) +
", chromaHeight:" + std::to_string(chromaHeight));
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
HRB(native_->device_->CreateTexture2D(
&desc, nullptr, textures_[0].ReleaseAndGetAddressOf()));
desc.Format = DXGI_FORMAT_R8G8_UNORM;
desc.Width = width / 2;
desc.Height = chromaHeight;
HRB(native_->device_->CreateTexture2D(
&desc, nullptr, textures_[1].ReleaseAndGetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(textures_[0].Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8_UNORM);
HRB(native_->device_->CreateShaderResourceView(
textures_[0].Get(), &srvDesc, SRV_[0].ReleaseAndGetAddressOf()));
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(textures_[1].Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8_UNORM);
HRB(native_->device_->CreateShaderResourceView(
textures_[1].Get(), &srvDesc, SRV_[1].ReleaseAndGetAddressOf()));
// set SRV
std::array<ID3D11ShaderResourceView *, 2> const textureViews = {
SRV_[0].Get(), SRV_[1].Get()};
native_->context_->PSSetShaderResources(0, textureViews.size(),
textureViews.data());
return true;
}
bool set_rtv(ID3D11Texture2D *texture) {
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
HRB(native_->device_->CreateRenderTargetView(
texture, &rtDesc, RTV_.ReleaseAndGetAddressOf()));
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // clear as black
native_->context_->ClearRenderTargetView(RTV_.Get(), clearColor);
native_->context_->OMSetRenderTargets(1, RTV_.GetAddressOf(), NULL);
return true;
}
bool set_view_port() {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)(width);
vp.Height = (FLOAT)(height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
native_->context_->RSSetViewports(1, &vp);
return true;
}
bool set_sample() {
D3D11_SAMPLER_DESC sampleDesc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
HRB(native_->device_->CreateSamplerState(
&sampleDesc, samplerLinear_.ReleaseAndGetAddressOf()));
native_->context_->PSSetSamplers(0, 1, samplerLinear_.GetAddressOf());
return true;
}
bool set_shader() {
// https://gist.github.com/RomiTT/9c05d36fe339b899793a3252297a5624
#include "pixel_shader_601.h"
#include "vertex_shader.h"
native_->device_->CreateVertexShader(
g_VS, ARRAYSIZE(g_VS), nullptr, vertexShader_.ReleaseAndGetAddressOf());
native_->device_->CreatePixelShader(g_PS, ARRAYSIZE(g_PS), nullptr,
pixelShader_.ReleaseAndGetAddressOf());
// set InputLayout
constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 2> Layout = {{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
}};
ComPtr<ID3D11InputLayout> inputLayout = NULL;
HRB(native_->device_->CreateInputLayout(Layout.data(), Layout.size(), g_VS,
ARRAYSIZE(g_VS),
inputLayout.GetAddressOf()));
native_->context_->IASetInputLayout(inputLayout.Get());
native_->context_->VSSetShader(vertexShader_.Get(), NULL, 0);
native_->context_->PSSetShader(pixelShader_.Get(), NULL, 0);
return true;
}
bool set_vertex_buffer() {
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
native_->context_->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
native_->context_->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// set VertexBuffers
VERTEX Vertices[NUMVERTICES] = {
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ComPtr<ID3D11Buffer> VertexBuffer = nullptr;
// Create vertex buffer
HRB(native_->device_->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer));
native_->context_->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(),
&Stride, &Offset);
return true;
}
bool register_texture() {
CUVIDAutoCtxPopper ctxPoper(cudl_, cuContext_);
bool ret = true;
for (int i = 0; i < 2; i++) {
if (!succ(cudl_->cuGraphicsD3D11RegisterResource(
&cuResource_[i], textures_[i].Get(),
CU_GRAPHICS_REGISTER_FLAGS_NONE))) {
ret = false;
break;
}
if (!succ(cudl_->cuGraphicsResourceSetMapFlags(
cuResource_[i], CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD))) {
ret = false;
break;
}
}
return ret;
}
bool dataFormat_to_cuCodecID(DataFormat dataFormat, cudaVideoCodec &cuda) {
switch (dataFormat) {
case H264:
cuda = cudaVideoCodec_H264;
break;
case H265:
cuda = cudaVideoCodec_HEVC;
break;
default:
return false;
}
return true;
}
bool create_nvdecoder() {
LOG_TRACE(std::string("create nvdecoder"));
bool bUseDeviceFrame = true;
bool bLowLatency = true;
bool bDeviceFramePitched = false; // width=pitch
cudaVideoCodec cudaCodecID;
if (!dataFormat_to_cuCodecID(dataFormat_, cudaCodecID)) {
return false;
}
if (dec_) {
delete dec_;
dec_ = nullptr;
}
dec_ = new NvDecoder(cudl_, cvdl_, cuContext_, bUseDeviceFrame, cudaCodecID,
bLowLatency, bDeviceFramePitched);
return true;
}
};
} // namespace
extern "C" {
int nv_decode_driver_support() {
try {
CudaFunctions *cudl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cudl, &cvdl);
free_driver(&cudl, &cvdl);
return 0;
} catch (const std::exception &e) {
}
return -1;
}
int nv_destroy_decoder(void *decoder) {
try {
CuvidDecoder *p = (CuvidDecoder *)decoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *nv_new_decoder(void *device, int64_t luid,
DataFormat dataFormat) {
CuvidDecoder *p = NULL;
try {
p = new CuvidDecoder(device, luid, dataFormat);
if (!p) {
goto _exit;
}
if (p->init())
return p;
} catch (const std::exception &ex) {
LOG_ERROR(std::string("destroy failed: ") + ex.what());
goto _exit;
}
_exit:
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int nv_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj) {
try {
CuvidDecoder *p = (CuvidDecoder *)decoder;
if (p->decode(data, len, callback, obj) == 0 ) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int nv_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_NVIDIA))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
CuvidDecoder *p = (CuvidDecoder *)nv_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = nv_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_NV;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -1,464 +0,0 @@
#define FFNV_LOG_FUNC
#define FFNV_DEBUG_LOG_FUNC
#include <Samples/NvCodec/NvEncoder/NvEncoderD3D11.h>
#include <Samples/Utils/Logger.h>
#include <Samples/Utils/NvCodecUtils.h>
#include <Samples/Utils/NvEncoderCLIOptions.h>
#include <dynlink_cuda.h>
#include <dynlink_loader.h>
#include <fstream>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <memory>
#include <d3d11.h>
#include <d3d9.h>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "NVENC"
#include "log.h"
simplelogger::Logger *logger =
simplelogger::LoggerFactory::CreateConsoleLogger();
namespace {
// #define CONFIG_NV_OPTIMUS_FOR_DEV
#define succ(call) ((call) == 0)
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl) {
if (cuda_load_functions(pp_cuda_dl, NULL) < 0) {
LOG_TRACE(std::string("cuda_load_functions failed"));
NVENC_THROW_ERROR("cuda_load_functions failed", NV_ENC_ERR_GENERIC);
}
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0) {
LOG_TRACE(std::string("nvenc_load_functions failed"));
NVENC_THROW_ERROR("nvenc_load_functions failed", NV_ENC_ERR_GENERIC);
}
}
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl) {
if (*pp_nvenc_dl) {
nvenc_free_functions(pp_nvenc_dl);
*pp_nvenc_dl = NULL;
}
if (*pp_cuda_dl) {
cuda_free_functions(pp_cuda_dl);
*pp_cuda_dl = NULL;
}
}
class NvencEncoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
NvEncoderD3D11 *pEnc_ = nullptr;
CudaFunctions *cuda_dl_ = nullptr;
NvencFunctions *nvenc_dl_ = nullptr;
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_;
int32_t height_;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
bool full_range_ = false;
bool bt709_ = false;
NV_ENC_CONFIG encodeConfig_ = {0};
NvencEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs, int32_t framerate,
int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
load_driver(&cuda_dl_, &nvenc_dl_);
}
~NvencEncoder() {}
bool init() {
GUID guidCodec;
switch (dataFormat_) {
case H264:
guidCodec = NV_ENC_CODEC_H264_GUID;
break;
case H265:
guidCodec = NV_ENC_CODEC_HEVC_GUID;
break;
default:
LOG_ERROR(std::string("dataFormat not support, dataFormat: ") +
std::to_string(dataFormat_));
return false;
}
if (!succ(cuda_dl_->cuInit(0))) {
LOG_TRACE(std::string("cuInit failed"));
return false;
}
native_ = std::make_unique<NativeDevice>();
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
if (!native_->Init(luid_, nullptr))
return false;
#else
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("d3d device init failed"));
return false;
}
#endif
CUdevice cuDevice = 0;
if (!succ(cuda_dl_->cuD3D11GetDevice(&cuDevice, native_->adapter_.Get()))) {
LOG_ERROR(std::string("Failed to get cuDevice"));
return false;
}
int nExtraOutputDelay = 0;
pEnc_ = new NvEncoderD3D11(cuda_dl_, nvenc_dl_, native_->device_.Get(),
width_, height_, NV_ENC_BUFFER_FORMAT_ARGB,
nExtraOutputDelay, false, false); // no delay
NV_ENC_INITIALIZE_PARAMS initializeParams = {0};
ZeroMemory(&initializeParams, sizeof(initializeParams));
ZeroMemory(&encodeConfig_, sizeof(encodeConfig_));
initializeParams.encodeConfig = &encodeConfig_;
pEnc_->CreateDefaultEncoderParams(
&initializeParams, guidCodec,
NV_ENC_PRESET_P3_GUID /*NV_ENC_PRESET_LOW_LATENCY_HP_GUID*/,
NV_ENC_TUNING_INFO_LOW_LATENCY);
// no delay
initializeParams.encodeConfig->frameIntervalP = 1;
initializeParams.encodeConfig->rcParams.lookaheadDepth = 0;
// bitrate
initializeParams.encodeConfig->rcParams.averageBitRate = kbs_ * 1000;
// framerate
initializeParams.frameRateNum = framerate_;
initializeParams.frameRateDen = 1;
// gop
initializeParams.encodeConfig->gopLength =
(gop_ > 0 && gop_ < MAX_GOP) ? gop_ : NVENC_INFINITE_GOPLENGTH;
// rc method
initializeParams.encodeConfig->rcParams.rateControlMode =
NV_ENC_PARAMS_RC_CBR;
// color
if (dataFormat_ == H264) {
setup_h264(initializeParams.encodeConfig);
} else {
setup_hevc(initializeParams.encodeConfig);
}
pEnc_->CreateEncoder(&initializeParams);
return true;
}
int encode(void *texture, EncodeCallback callback, void *obj, int64_t ms) {
bool encoded = false;
std::vector<NvPacket> vPacket;
const NvEncInputFrame *pEncInput = pEnc_->GetNextInputFrame();
// TODO: sdk can ensure the inputPtr's width, height same as width_,
// height_, does capture's frame can ensure width height same with width_,
// height_ ?
ID3D11Texture2D *pBgraTextyure =
reinterpret_cast<ID3D11Texture2D *>(pEncInput->inputPtr);
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
copy_texture(texture, pBgraTextyure);
#else
native_->context_->CopyResource(
pBgraTextyure, reinterpret_cast<ID3D11Texture2D *>(texture));
#endif
NV_ENC_PIC_PARAMS picParams = {0};
picParams.inputTimeStamp = ms;
pEnc_->EncodeFrame(vPacket);
for (NvPacket &packet : vPacket) {
int32_t key = (packet.pictureType == NV_ENC_PIC_TYPE_IDR ||
packet.pictureType == NV_ENC_PIC_TYPE_I)
? 1
: 0;
if (packet.data.size() > 0) {
if (callback)
callback(packet.data.data(), packet.data.size(), key, obj, ms);
encoded = true;
}
}
return encoded ? 0 : -1;
}
void destroy() {
if (pEnc_) {
pEnc_->DestroyEncoder();
delete pEnc_;
pEnc_ = nullptr;
}
free_driver(&cuda_dl_, &nvenc_dl_);
}
void setup_h264(NV_ENC_CONFIG *encodeConfig) {
NV_ENC_CODEC_CONFIG *encodeCodecConfig = &encodeConfig->encodeCodecConfig;
NV_ENC_CONFIG_H264 *h264 = &encodeCodecConfig->h264Config;
NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264->h264VUIParameters;
vui->videoFullRangeFlag = !!full_range_;
vui->colourMatrix = bt709_ ? NV_ENC_VUI_MATRIX_COEFFS_BT709 : NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M;
vui->colourPrimaries = bt709_ ? NV_ENC_VUI_COLOR_PRIMARIES_BT709 : NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M;
vui->transferCharacteristics =
bt709_ ? NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 : NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M;
vui->colourDescriptionPresentFlag = 1;
vui->videoSignalTypePresentFlag = 1;
h264->sliceMode = 3;
h264->sliceModeData = 1;
h264->repeatSPSPPS = 1;
// Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for
// yuv444 input
h264->chromaFormatIDC = 1;
h264->level = NV_ENC_LEVEL_AUTOSELECT;
encodeConfig->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
}
void setup_hevc(NV_ENC_CONFIG *encodeConfig) {
NV_ENC_CODEC_CONFIG *encodeCodecConfig = &encodeConfig->encodeCodecConfig;
NV_ENC_CONFIG_HEVC *hevc = &encodeCodecConfig->hevcConfig;
NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc->hevcVUIParameters;
vui->videoFullRangeFlag = !!full_range_;
vui->colourMatrix = bt709_ ? NV_ENC_VUI_MATRIX_COEFFS_BT709 : NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M;
vui->colourPrimaries = bt709_ ? NV_ENC_VUI_COLOR_PRIMARIES_BT709 : NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M;
vui->transferCharacteristics =
bt709_ ? NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 : NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M;
vui->colourDescriptionPresentFlag = 1;
vui->videoSignalTypePresentFlag = 1;
hevc->sliceMode = 3;
hevc->sliceModeData = 1;
hevc->repeatSPSPPS = 1;
// Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for
// yuv444 input
hevc->chromaFormatIDC = 1;
hevc->level = NV_ENC_LEVEL_AUTOSELECT;
hevc->outputPictureTimingSEI = 1;
hevc->tier = NV_ENC_TIER_HEVC_MAIN;
encodeConfig->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
}
private:
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
int copy_texture(void *src, void *dst) {
ComPtr<ID3D11Device> src_device = (ID3D11Device *)handle_;
ComPtr<ID3D11DeviceContext> src_deviceContext;
src_device->GetImmediateContext(src_deviceContext.ReleaseAndGetAddressOf());
ComPtr<ID3D11Texture2D> src_tex = (ID3D11Texture2D *)src;
ComPtr<ID3D11Texture2D> dst_tex = (ID3D11Texture2D *)dst;
HRESULT hr;
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
src_tex->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
desc.MiscFlags = 0;
ComPtr<ID3D11Texture2D> staging_tex;
src_device->CreateTexture2D(&desc, NULL,
staging_tex.ReleaseAndGetAddressOf());
src_deviceContext->CopyResource(staging_tex.Get(), src_tex.Get());
D3D11_MAPPED_SUBRESOURCE map;
src_deviceContext->Map(staging_tex.Get(), 0, D3D11_MAP_READ, 0, &map);
std::unique_ptr<uint8_t[]> buffer(
new uint8_t[desc.Width * desc.Height * 4]);
memcpy(buffer.get(), map.pData, desc.Width * desc.Height * 4);
src_deviceContext->Unmap(staging_tex.Get(), 0);
D3D11_BOX Box;
Box.left = 0;
Box.right = desc.Width;
Box.top = 0;
Box.bottom = desc.Height;
Box.front = 0;
Box.back = 1;
native_->context_->UpdateSubresource(dst_tex.Get(), 0, &Box, buffer.get(),
desc.Width * 4,
desc.Width * desc.Height * 4);
return 0;
}
#endif
};
} // namespace
extern "C" {
int nv_encode_driver_support() {
try {
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
load_driver(&cuda_dl, &nvenc_dl);
free_driver(&cuda_dl, &nvenc_dl);
return 0;
} catch (const std::exception &e) {
LOG_TRACE(std::string("driver not support, ") + e.what());
}
return -1;
}
int nv_destroy_encoder(void *encoder) {
try {
NvencEncoder *e = (NvencEncoder *)encoder;
if (e) {
e->destroy();
delete e;
e = NULL;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *nv_new_encoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
NvencEncoder *e = NULL;
try {
e = new NvencEncoder(handle, luid, dataFormat, width, height, kbs,
framerate, gop);
if (!e->init()) {
goto _exit;
}
return e;
} catch (const std::exception &ex) {
LOG_ERROR(std::string("new failed: ") + ex.what());
goto _exit;
}
_exit:
if (e) {
e->destroy();
delete e;
e = NULL;
}
return NULL;
}
int nv_encode(void *encoder, void *texture, EncodeCallback callback, void *obj,
int64_t ms) {
try {
NvencEncoder *e = (NvencEncoder *)encoder;
return e->encode(texture, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("encode failed: ") + e.what());
}
return -1;
}
// ref: Reconfigure API
#define RECONFIGURE_HEAD \
NvencEncoder *enc = (NvencEncoder *)e; \
NV_ENC_CONFIG sEncodeConfig = {0}; \
NV_ENC_INITIALIZE_PARAMS sInitializeParams = {0}; \
sInitializeParams.encodeConfig = &sEncodeConfig; \
enc->pEnc_->GetInitializeParams(&sInitializeParams); \
NV_ENC_RECONFIGURE_PARAMS params = {0}; \
params.version = NV_ENC_RECONFIGURE_PARAMS_VER; \
params.reInitEncodeParams = sInitializeParams;
#define RECONFIGURE_TAIL \
if (enc->pEnc_->Reconfigure(&params)) { \
return 0; \
}
int nv_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_NVIDIA))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
NvencEncoder *e = (NvencEncoder *)nv_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = nv_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback, &key_obj,
0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_NV;
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
int nv_set_bitrate(void *e, int32_t kbs) {
try {
RECONFIGURE_HEAD
params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate =
kbs * 1000;
RECONFIGURE_TAIL
} catch (const std::exception &e) {
LOG_ERROR(std::string("set bitrate to ") + std::to_string(kbs) +
"k failed: " + e.what());
}
return -1;
}
int nv_set_framerate(void *e, int32_t framerate) {
try {
RECONFIGURE_HEAD
params.reInitEncodeParams.frameRateNum = framerate;
params.reInitEncodeParams.frameRateDen = 1;
RECONFIGURE_TAIL
} catch (const std::exception &e) {
LOG_ERROR(std::string("set framerate failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -1,40 +0,0 @@
#ifndef NV_FFI_H
#define NV_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int nv_encode_driver_support();
int nv_decode_driver_support();
void *nv_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop);
int nv_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms);
int nv_destroy_encoder(void *encoder);
void *nv_new_decoder(void *device, int64_t luid, int32_t codecID);
int nv_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj);
int nv_destroy_decoder(void *decoder);
int nv_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int nv_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int nv_set_bitrate(void *encoder, int32_t kbs);
int nv_set_framerate(void *encoder, int32_t framerate);
#endif // NV_FFI_H