mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
init
This commit is contained in:
34
libs/hwcodec/cpp/amf/amf_common.cpp
Normal file
34
libs/hwcodec/cpp/amf/amf_common.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#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;
|
||||
}
|
||||
451
libs/hwcodec/cpp/amf/amf_decode.cpp
Normal file
451
libs/hwcodec/cpp/amf/amf_decode.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
#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"
|
||||
611
libs/hwcodec/cpp/amf/amf_encode.cpp
Normal file
611
libs/hwcodec/cpp/amf/amf_encode.cpp
Normal file
@@ -0,0 +1,611 @@
|
||||
#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"
|
||||
39
libs/hwcodec/cpp/amf/amf_ffi.h
Normal file
39
libs/hwcodec/cpp/amf/amf_ffi.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#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
|
||||
11
libs/hwcodec/cpp/common/callback.h
Normal file
11
libs/hwcodec/cpp/common/callback.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef CALLBACK_H
|
||||
#define CALLBACK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*EncodeCallback)(const uint8_t *data, int32_t len, int32_t key,
|
||||
const void *obj, int64_t pts);
|
||||
|
||||
typedef void (*DecodeCallback)(void *opaque, const void *obj);
|
||||
|
||||
#endif // CALLBACK_H
|
||||
57
libs/hwcodec/cpp/common/common.h
Normal file
57
libs/hwcodec/cpp/common/common.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_GOP 0x7FFFFFFF // i32 max
|
||||
|
||||
#define TEST_TIMEOUT_MS 1000
|
||||
#define ENCODE_TIMEOUT_MS 1000
|
||||
#define DECODE_TIMEOUT_MS 1000
|
||||
|
||||
enum AdapterVendor {
|
||||
ADAPTER_VENDOR_AMD = 0x1002,
|
||||
ADAPTER_VENDOR_INTEL = 0x8086,
|
||||
ADAPTER_VENDOR_NVIDIA = 0x10DE,
|
||||
ADAPTER_VENDOR_UNKNOWN = 0,
|
||||
};
|
||||
|
||||
enum SurfaceFormat {
|
||||
SURFACE_FORMAT_BGRA,
|
||||
SURFACE_FORMAT_RGBA,
|
||||
SURFACE_FORMAT_NV12,
|
||||
};
|
||||
|
||||
enum DataFormat {
|
||||
H264,
|
||||
H265,
|
||||
VP8,
|
||||
VP9,
|
||||
AV1,
|
||||
MJPEG,
|
||||
};
|
||||
|
||||
// same as Driver
|
||||
enum Vendor {
|
||||
VENDOR_NV = 0,
|
||||
VENDOR_AMD = 1,
|
||||
VENDOR_INTEL = 2,
|
||||
VENDOR_FFMPEG = 3
|
||||
};
|
||||
|
||||
enum Quality { Quality_Default, Quality_High, Quality_Medium, Quality_Low };
|
||||
|
||||
enum RateControl {
|
||||
RC_DEFAULT,
|
||||
RC_CBR,
|
||||
RC_VBR,
|
||||
RC_CQ,
|
||||
};
|
||||
|
||||
enum HwcodecErrno {
|
||||
HWCODEC_SUCCESS = 0,
|
||||
HWCODEC_ERR_COMMON = -1,
|
||||
HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC = -2,
|
||||
};
|
||||
|
||||
#endif // COMMON_H
|
||||
29
libs/hwcodec/cpp/common/ffmpeg_ffi.h
Normal file
29
libs/hwcodec/cpp/common/ffmpeg_ffi.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef FFMPEG_H
|
||||
#define FFMPEG_H
|
||||
|
||||
#define AV_LOG_QUIET -8
|
||||
#define AV_LOG_PANIC 0
|
||||
#define AV_LOG_FATAL 8
|
||||
#define AV_LOG_ERROR 16
|
||||
#define AV_LOG_WARNING 24
|
||||
#define AV_LOG_INFO 32
|
||||
#define AV_LOG_VERBOSE 40
|
||||
#define AV_LOG_DEBUG 48
|
||||
#define AV_LOG_TRACE 56
|
||||
|
||||
enum AVPixelFormat {
|
||||
AV_PIX_FMT_YUV420P = 0,
|
||||
AV_PIX_FMT_YUYV422 = 1,
|
||||
AV_PIX_FMT_YUV422P = 4, // planar YUV 4:2:2
|
||||
AV_PIX_FMT_YUVJ420P = 12, // JPEG full-range YUV420P (same layout as YUV420P)
|
||||
AV_PIX_FMT_YUVJ422P = 13, // JPEG full-range YUV422P (same layout as YUV422P)
|
||||
AV_PIX_FMT_NV12 = 23,
|
||||
AV_PIX_FMT_NV21 = 24,
|
||||
};
|
||||
|
||||
int av_log_get_level(void);
|
||||
void av_log_set_level(int level);
|
||||
void hwcodec_set_av_log_callback();
|
||||
void hwcodec_set_flag_could_not_find_ref_with_poc();
|
||||
|
||||
#endif
|
||||
54
libs/hwcodec/cpp/common/log.cpp
Normal file
54
libs/hwcodec/cpp/common/log.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
#include "log.h"
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
namespace gol {
|
||||
enum {
|
||||
LOG_LEVEL_ERROR = 0,
|
||||
LOG_LEVEL_WARN = 1,
|
||||
LOG_LEVEL_INFO = 2,
|
||||
LOG_LEVEL_DEBUG = 3,
|
||||
LOG_LEVEL_TRACE = 4,
|
||||
};
|
||||
|
||||
extern "C" void hwcodec_log(int level, const char *message);
|
||||
extern "C" void hwcodec_av_log_callback(int level, const char *message);
|
||||
|
||||
void log_to_rust(int level, const std::string &message) {
|
||||
const char *cstr = message.c_str();
|
||||
hwcodec_log(level, cstr);
|
||||
}
|
||||
|
||||
void error(const std::string &message) {
|
||||
log_to_rust(LOG_LEVEL_ERROR, message);
|
||||
}
|
||||
|
||||
void warn(const std::string &message) { log_to_rust(LOG_LEVEL_WARN, message); }
|
||||
|
||||
void info(const std::string &message) { log_to_rust(LOG_LEVEL_INFO, message); }
|
||||
|
||||
void debug(const std::string &message) {
|
||||
log_to_rust(LOG_LEVEL_DEBUG, message);
|
||||
}
|
||||
|
||||
void trace(const std::string &message) {
|
||||
log_to_rust(LOG_LEVEL_TRACE, message);
|
||||
}
|
||||
|
||||
void av_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
|
||||
(void)ptr;
|
||||
if (level > av_log_get_level()) {
|
||||
return;
|
||||
}
|
||||
char line[1024] = {0};
|
||||
vsnprintf(line, sizeof(line), fmt, vl);
|
||||
hwcodec_av_log_callback(level, line);
|
||||
};
|
||||
|
||||
} // namespace gol
|
||||
|
||||
|
||||
extern "C" void hwcodec_set_av_log_callback() {
|
||||
av_log_set_callback(gol::av_log_callback);
|
||||
}
|
||||
66
libs/hwcodec/cpp/common/log.h
Normal file
66
libs/hwcodec/cpp/common/log.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/attributes.h>
|
||||
#include <libavutil/error.h>
|
||||
}
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifndef LOG_MODULE
|
||||
#define LOG_MODULE "*"
|
||||
#endif
|
||||
|
||||
namespace gol {
|
||||
void error(const std::string &message);
|
||||
void warn(const std::string &message);
|
||||
void info(const std::string &message);
|
||||
void debug(const std::string &message);
|
||||
void trace(const std::string &message);
|
||||
} // namespace gol
|
||||
|
||||
#define LOG_ERROR(message) \
|
||||
gol::error(std::string("[") + LOG_MODULE + "] " + message)
|
||||
#define LOG_WARN(message) \
|
||||
gol::warn(std::string("[") + LOG_MODULE + "] " + message)
|
||||
#define LOG_INFO(message) \
|
||||
gol::info(std::string("[") + LOG_MODULE + "] " + message)
|
||||
#define LOG_DEBUG(message) \
|
||||
gol::debug(std::string("[") + LOG_MODULE + "] " + message)
|
||||
#define LOG_TRACE(message) \
|
||||
gol::trace(std::string("[") + LOG_MODULE + "] " + message)
|
||||
|
||||
// https://github.com/joncampbell123/composite-video-simulator/issues/5#issuecomment-611885908
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char str[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define HRB(f) MS_CHECK(f, return false;)
|
||||
#define HRI(f) MS_CHECK(f, return -1;)
|
||||
#define HRP(f) MS_CHECK(f, return nullptr;)
|
||||
#define MS_CHECK(f, ...) \
|
||||
do { \
|
||||
HRESULT __ms_hr__ = (f); \
|
||||
if (FAILED(__ms_hr__)) { \
|
||||
std::stringstream ss; \
|
||||
ss << "ERROR@" << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ \
|
||||
<< " hr=0x" << std::hex << __ms_hr__ << std::dec << " " \
|
||||
<< std::error_code(__ms_hr__, std::system_category()).message(); \
|
||||
std::string result = ss.str(); \
|
||||
LOG_ERROR(result); \
|
||||
__VA_ARGS__ \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
163
libs/hwcodec/cpp/common/platform/linux/linux.cpp
Normal file
163
libs/hwcodec/cpp/common/platform/linux/linux.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "linux.h"
|
||||
#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
|
||||
|
||||
int linux_support_nv()
|
||||
{
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_TRACE(std::string("nvidia driver not support"));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!handle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
dlclose(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int linux_support_intel()
|
||||
{
|
||||
const char *libs[] =
|
||||
{"libvpl.so", "libmfx.so", "libmfx-gen.so.1.2", "libmfxhw64.so.1"};
|
||||
for (size_t i = 0; i < sizeof(libs) / sizeof(libs[0]); i++)
|
||||
{
|
||||
void *handle = dlopen(libs[i], RTLD_LAZY);
|
||||
if (handle)
|
||||
{
|
||||
dlclose(handle);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int setup_parent_death_signal() {
|
||||
// Set up parent death signal to ensure this process dies if parent dies
|
||||
// This prevents orphaned processes especially when running with different
|
||||
// user permissions
|
||||
int ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR(std::string("Failed to set parent death signal:") + std::to_string(errno));
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Rockchip MPP (Media Process Platform) support
|
||||
// Returns 0 if supported, -1 otherwise
|
||||
int linux_support_rkmpp() {
|
||||
// Check for MPP service device (primary method)
|
||||
if (access("/dev/mpp_service", F_OK) == 0) {
|
||||
LOG_TRACE(std::string("RKMPP: Found /dev/mpp_service"));
|
||||
return 0;
|
||||
}
|
||||
// Fallback: check for RGA (Rockchip Graphics Acceleration) device
|
||||
if (access("/dev/rga", F_OK) == 0) {
|
||||
LOG_TRACE(std::string("RKMPP: Found /dev/rga"));
|
||||
return 0;
|
||||
}
|
||||
LOG_TRACE(std::string("RKMPP: No Rockchip MPP device found"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for V4L2 Memory-to-Memory (M2M) codec support
|
||||
// Returns 0 if a M2M capable device is found, -1 otherwise
|
||||
int linux_support_v4l2m2m() {
|
||||
// Check common V4L2 M2M device paths used by various ARM SoCs
|
||||
const char *m2m_devices[] = {
|
||||
"/dev/video10", // Common M2M encoder device
|
||||
"/dev/video11", // Common M2M decoder device
|
||||
"/dev/video0", // Some SoCs use video0 for M2M
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
|
||||
if (access(m2m_devices[i], F_OK) == 0) {
|
||||
// Device exists, check if it's an M2M device by trying to open it
|
||||
int fd = open(m2m_devices[i], O_RDWR | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
LOG_TRACE(std::string("V4L2 M2M: Found device ") + m2m_devices[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE(std::string("V4L2 M2M: No M2M device found"));
|
||||
return -1;
|
||||
}
|
||||
11
libs/hwcodec/cpp/common/platform/linux/linux.h
Normal file
11
libs/hwcodec/cpp/common/platform/linux/linux.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef LINUX_H
|
||||
#define LINUX_H
|
||||
|
||||
extern "C" int linux_support_nv();
|
||||
extern "C" int linux_support_amd();
|
||||
extern "C" int linux_support_intel();
|
||||
extern "C" int linux_support_rkmpp();
|
||||
extern "C" int linux_support_v4l2m2m();
|
||||
extern "C" int setup_parent_death_signal();
|
||||
|
||||
#endif
|
||||
167
libs/hwcodec/cpp/common/platform/mac/mac.mm
Normal file
167
libs/hwcodec/cpp/common/platform/mac/mac.mm
Normal file
@@ -0,0 +1,167 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
187
libs/hwcodec/cpp/common/platform/win/bmp.cpp
Normal file
187
libs/hwcodec/cpp/common/platform/win/bmp.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "../../common.h"
|
||||
#include "win.h"
|
||||
|
||||
#define IF_FAILED_THROW(X) \
|
||||
if (FAILED(hr = (X))) { \
|
||||
throw hr; \
|
||||
}
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
static HRESULT CreateBmpFile(LPCWSTR wszBmpFile, BYTE *pData,
|
||||
const UINT uiFrameSize, const UINT uiWidth,
|
||||
const UINT uiHeight) {
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||
DWORD dwWritten;
|
||||
UINT uiStride;
|
||||
|
||||
BYTE header24[54] = {0x42, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
DWORD dwSizeFile = uiWidth * uiHeight * 3;
|
||||
dwSizeFile += 54;
|
||||
header24[2] = dwSizeFile & 0x000000ff;
|
||||
header24[3] = static_cast<BYTE>((dwSizeFile & 0x0000ff00) >> 8);
|
||||
header24[4] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
|
||||
header24[5] = (dwSizeFile & 0xff000000) >> 24;
|
||||
dwSizeFile -= 54;
|
||||
header24[18] = uiWidth & 0x000000ff;
|
||||
header24[19] = (uiWidth & 0x0000ff00) >> 8;
|
||||
header24[20] = static_cast<BYTE>((uiWidth & 0x00ff0000) >> 16);
|
||||
header24[21] = (uiWidth & 0xff000000) >> 24;
|
||||
|
||||
header24[22] = uiHeight & 0x000000ff;
|
||||
header24[23] = (uiHeight & 0x0000ff00) >> 8;
|
||||
header24[24] = static_cast<BYTE>((uiHeight & 0x00ff0000) >> 16);
|
||||
header24[25] = (uiHeight & 0xff000000) >> 24;
|
||||
|
||||
header24[34] = dwSizeFile & 0x000000ff;
|
||||
header24[35] = (dwSizeFile & 0x0000ff00) >> 8;
|
||||
header24[36] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
|
||||
header24[37] = static_cast<BYTE>((dwSizeFile & 0xff000000) >> 24);
|
||||
|
||||
try {
|
||||
hFile = CreateFileW(wszBmpFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
IF_FAILED_THROW(hFile == INVALID_HANDLE_VALUE ? E_FAIL : S_OK);
|
||||
|
||||
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)header24, 54, &dwWritten, 0) ==
|
||||
FALSE);
|
||||
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
|
||||
|
||||
uiStride = uiWidth * 3;
|
||||
BYTE *Tmpbufsrc = pData + (uiFrameSize - uiStride);
|
||||
|
||||
for (UINT i = 0; i < uiHeight; i++) {
|
||||
|
||||
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)Tmpbufsrc, uiStride, &dwWritten,
|
||||
0) == FALSE);
|
||||
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
|
||||
|
||||
Tmpbufsrc -= uiStride;
|
||||
}
|
||||
} catch (HRESULT) {
|
||||
}
|
||||
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hFile);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static std::string GetDirectoryFromFilename(const std::string &filename) {
|
||||
size_t lastSeparator = filename.find_last_of("/\\");
|
||||
|
||||
if (lastSeparator != std::string::npos) {
|
||||
return filename.substr(0, lastSeparator);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool createBgraBmpFile(ID3D11Device *device, ID3D11Texture2D *texture,
|
||||
const std::string &filename) {
|
||||
D3D11_TEXTURE2D_DESC desc = {};
|
||||
ComPtr<ID3D11DeviceContext> deviceContext;
|
||||
HRESULT hr;
|
||||
texture->GetDesc(&desc);
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.BindFlags = 0;
|
||||
ComPtr<ID3D11Texture2D> bgraStagingTexture;
|
||||
hr = device->CreateTexture2D(&desc, nullptr,
|
||||
bgraStagingTexture.GetAddressOf());
|
||||
IF_FAILED_THROW(hr);
|
||||
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
|
||||
deviceContext->CopyResource(bgraStagingTexture.Get(), texture);
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE ResourceDesc = {};
|
||||
deviceContext->Map(bgraStagingTexture.Get(), 0, D3D11_MAP_READ, 0,
|
||||
&ResourceDesc);
|
||||
|
||||
UINT uiImageSize = desc.Width * desc.Height * 3;
|
||||
BYTE *pDataRgb = new (std::nothrow) BYTE[uiImageSize];
|
||||
BYTE *pDataRgbaColor = (BYTE *)ResourceDesc.pData;
|
||||
BYTE *pDataRgbColor = pDataRgb;
|
||||
for (UINT i = 0; i < desc.Height; i++) {
|
||||
for (UINT j = 0; j < desc.Width; j++) {
|
||||
if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) {
|
||||
// bgr bgra
|
||||
*pDataRgbColor++ = *pDataRgbaColor++;
|
||||
*pDataRgbColor++ = *pDataRgbaColor++;
|
||||
*pDataRgbColor++ = *pDataRgbaColor++;
|
||||
pDataRgbaColor++;
|
||||
} else {
|
||||
// bgr rgba
|
||||
pDataRgbColor[0] = pDataRgbaColor[2];
|
||||
pDataRgbColor[1] = pDataRgbaColor[1];
|
||||
pDataRgbColor[2] = pDataRgbaColor[0];
|
||||
pDataRgbColor += 3;
|
||||
pDataRgbaColor += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto dir = GetDirectoryFromFilename(filename);
|
||||
DWORD attrib = GetFileAttributesA(dir.c_str());
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES ||
|
||||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
if (!CreateDirectoryA(dir.c_str(), NULL)) {
|
||||
std::cout << "Failed to create directory: " << dir << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
std::cout << "Directory created: " << dir << std::endl;
|
||||
}
|
||||
} else {
|
||||
// already exists
|
||||
}
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
|
||||
wchar_t *wszBmpFile = new wchar_t[size];
|
||||
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wszBmpFile, size);
|
||||
|
||||
hr =
|
||||
CreateBmpFile(wszBmpFile, pDataRgb, uiImageSize, desc.Width, desc.Height);
|
||||
delete[] pDataRgb;
|
||||
delete[] wszBmpFile;
|
||||
IF_FAILED_THROW(hr);
|
||||
deviceContext->Unmap(bgraStagingTexture.Get(), 0);
|
||||
}
|
||||
|
||||
void SaveBgraBmps(ID3D11Device *device, void *texture, int cycle) {
|
||||
if (!texture)
|
||||
return;
|
||||
static int index = 0;
|
||||
if (index++ % cycle == 0) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time_t_now = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm local_tm;
|
||||
localtime_s(&local_tm, &time_t_now);
|
||||
|
||||
char buffer[80];
|
||||
std::strftime(buffer, 80, "%H_%M_%S", &local_tm);
|
||||
std::string filename = std::string("bmps") + "/" + std::to_string(index) +
|
||||
"_" + buffer + ".bmp";
|
||||
createBgraBmpFile(device, (ID3D11Texture2D *)texture, filename);
|
||||
}
|
||||
}
|
||||
58
libs/hwcodec/cpp/common/platform/win/dump.cpp
Normal file
58
libs/hwcodec/cpp/common/platform/win/dump.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "win.h"
|
||||
#include <fstream>
|
||||
bool dumpTexture(ID3D11Device *device, ID3D11Texture2D *texture, int cropW,
|
||||
int cropH, const string &filename) {
|
||||
const char *dir = "texture";
|
||||
DWORD attrib = GetFileAttributesA(dir);
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES ||
|
||||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
if (!CreateDirectoryA(dir, NULL)) {
|
||||
std::cout << "Failed to create directory: " << dir << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
std::cout << "Directory created: " << dir << std::endl;
|
||||
}
|
||||
} else {
|
||||
// already exists
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc = {};
|
||||
ComPtr<ID3D11DeviceContext> deviceContext;
|
||||
HRESULT hr;
|
||||
texture->GetDesc(&desc);
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.BindFlags = 0;
|
||||
ComPtr<ID3D11Texture2D> stagingTexture;
|
||||
hr = device->CreateTexture2D(&desc, nullptr, stagingTexture.GetAddressOf());
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
|
||||
deviceContext->CopyResource(stagingTexture.Get(), texture);
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mappedResource = {};
|
||||
deviceContext->Map(stagingTexture.Get(), 0, D3D11_MAP_READ, 0,
|
||||
&mappedResource);
|
||||
string path = string(dir) + "/" + filename;
|
||||
std::ofstream file(path, std::ios::binary | std::ios::app);
|
||||
if (desc.Format == DXGI_FORMAT_NV12) {
|
||||
int Pitch = mappedResource.RowPitch;
|
||||
uint8_t *Y = (uint8_t *)mappedResource.pData;
|
||||
uint8_t *U =
|
||||
(uint8_t *)mappedResource.pData + desc.Height * mappedResource.RowPitch;
|
||||
uint8_t *V = (desc.Format == DXGI_FORMAT_P010) ? U + 2 : U + 1;
|
||||
for (int i = 0; i < cropH; i++) {
|
||||
file.write((const char *)(Y + i * Pitch), cropW);
|
||||
}
|
||||
int ChromaH = cropH / 2;
|
||||
int ChromaW = cropW;
|
||||
for (int i = 0; i < ChromaH; i++) {
|
||||
file.write((const char *)(U + i * Pitch), ChromaW);
|
||||
}
|
||||
}
|
||||
deviceContext->Unmap(stagingTexture.Get(), 0);
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
2729
libs/hwcodec/cpp/common/platform/win/pixel_shader_601.h
Normal file
2729
libs/hwcodec/cpp/common/platform/win/pixel_shader_601.h
Normal file
File diff suppressed because it is too large
Load Diff
919
libs/hwcodec/cpp/common/platform/win/vertex_shader.h
Normal file
919
libs/hwcodec/cpp/common/platform/win/vertex_shader.h
Normal file
@@ -0,0 +1,919 @@
|
||||
#if 0
|
||||
//
|
||||
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
|
||||
//
|
||||
//
|
||||
//
|
||||
// Input signature:
|
||||
//
|
||||
// Name Index Mask Register SysValue Format Used
|
||||
// -------------------- ----- ------ -------- -------- ------- ------
|
||||
// POSITION 0 xyzw 0 NONE float xyzw
|
||||
// TEXCOORD 0 xy 1 NONE float xy
|
||||
//
|
||||
//
|
||||
// Output signature:
|
||||
//
|
||||
// Name Index Mask Register SysValue Format Used
|
||||
// -------------------- ----- ------ -------- -------- ------- ------
|
||||
// SV_POSITION 0 xyzw 0 POS float xyzw
|
||||
// TEXCOORD 0 xy 1 NONE float xy
|
||||
//
|
||||
//
|
||||
// Runtime generated constant mappings:
|
||||
//
|
||||
// Target Reg Constant Description
|
||||
// ---------- --------------------------------------------------
|
||||
// c0 Vertex Shader position offset
|
||||
//
|
||||
//
|
||||
// Level9 shader bytecode:
|
||||
//
|
||||
vs_2_x
|
||||
dcl_texcoord v0 // input<0,1,2,3>
|
||||
dcl_texcoord1 v1 // input<4,5>
|
||||
|
||||
#line 12 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
|
||||
mad oPos.xy, v0.w, c0, v0 // ::VS<0,1>
|
||||
mov oPos.zw, v0 // ::VS<2,3>
|
||||
mov oT0.xy, v1 // ::VS<4,5>
|
||||
|
||||
// approximately 3 instruction slots used
|
||||
vs_4_0
|
||||
dcl_input v0.xyzw
|
||||
dcl_input v1.xy
|
||||
dcl_output_siv o0.xyzw, position
|
||||
dcl_output o1.xy
|
||||
//
|
||||
// Initial variable locations:
|
||||
// v0.x <- input.Pos.x; v0.y <- input.Pos.y; v0.z <- input.Pos.z; v0.w <- input.Pos.w;
|
||||
// v1.x <- input.Tex.x; v1.y <- input.Tex.y;
|
||||
// o1.x <- <VS return value>.Tex.x; o1.y <- <VS return value>.Tex.y;
|
||||
// o0.x <- <VS return value>.Pos.x; o0.y <- <VS return value>.Pos.y; o0.z <- <VS return value>.Pos.z; o0.w <- <VS return value>.Pos.w
|
||||
//
|
||||
#line 14 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
|
||||
mov o0.xyzw, v0.xyzw
|
||||
mov o1.xy, v1.xyxx
|
||||
ret
|
||||
// Approximately 3 instruction slots used
|
||||
#endif
|
||||
|
||||
const BYTE g_VS[] = {
|
||||
68, 88, 66, 67, 17, 156, 6, 174, 73, 86, 39, 50, 168, 176, 148,
|
||||
24, 52, 224, 94, 107, 1, 0, 0, 0, 56, 50, 0, 0, 7, 0,
|
||||
0, 0, 60, 0, 0, 0, 76, 2, 0, 0, 188, 2, 0, 0, 196,
|
||||
48, 0, 0, 64, 49, 0, 0, 140, 49, 0, 0, 224, 49, 0, 0,
|
||||
65, 111, 110, 57, 8, 2, 0, 0, 8, 2, 0, 0, 0, 2, 254,
|
||||
255, 224, 1, 0, 0, 40, 0, 0, 0, 0, 0, 36, 0, 0, 0,
|
||||
36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 1, 0, 36, 0, 0,
|
||||
0, 0, 0, 1, 2, 254, 255, 254, 255, 100, 0, 68, 66, 85, 71,
|
||||
40, 0, 0, 0, 100, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 112, 0, 0, 0, 5, 0, 0, 0, 116, 0, 0, 0, 2, 0,
|
||||
0, 0, 60, 1, 0, 0, 156, 0, 0, 0, 68, 58, 92, 114, 117,
|
||||
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
|
||||
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
|
||||
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
|
||||
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
|
||||
46, 104, 108, 115, 108, 0, 171, 40, 0, 0, 0, 0, 0, 255, 255,
|
||||
152, 1, 0, 0, 0, 0, 255, 255, 164, 1, 0, 0, 12, 0, 0,
|
||||
0, 176, 1, 0, 0, 12, 0, 0, 0, 196, 1, 0, 0, 14, 0,
|
||||
0, 0, 208, 1, 0, 0, 86, 83, 0, 80, 111, 115, 0, 171, 1,
|
||||
0, 3, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 101, 120, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 159, 0, 0, 0, 164, 0, 0, 0, 180, 0,
|
||||
0, 0, 184, 0, 0, 0, 5, 0, 0, 0, 1, 0, 6, 0, 1,
|
||||
0, 2, 0, 200, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0,
|
||||
255, 255, 255, 255, 3, 0, 0, 0, 255, 255, 255, 255, 2, 0, 3,
|
||||
0, 4, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255, 255, 105, 110,
|
||||
112, 117, 116, 0, 171, 171, 5, 0, 0, 0, 1, 0, 6, 0, 1,
|
||||
0, 2, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
|
||||
2, 0, 3, 0, 1, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255,
|
||||
255, 0, 0, 0, 0, 156, 0, 0, 0, 216, 0, 0, 0, 3, 0,
|
||||
0, 0, 232, 0, 0, 0, 156, 0, 0, 0, 12, 1, 0, 0, 20,
|
||||
1, 0, 0, 2, 0, 0, 0, 36, 1, 0, 0, 77, 105, 99, 114,
|
||||
111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32,
|
||||
83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114,
|
||||
32, 49, 48, 46, 49, 0, 31, 0, 0, 2, 5, 0, 0, 128, 0,
|
||||
0, 15, 144, 31, 0, 0, 2, 5, 0, 1, 128, 1, 0, 15, 144,
|
||||
4, 0, 0, 4, 0, 0, 3, 192, 0, 0, 255, 144, 0, 0, 228,
|
||||
160, 0, 0, 228, 144, 1, 0, 0, 2, 0, 0, 12, 192, 0, 0,
|
||||
228, 144, 1, 0, 0, 2, 0, 0, 3, 224, 1, 0, 228, 144, 255,
|
||||
255, 0, 0, 83, 72, 68, 82, 104, 0, 0, 0, 64, 0, 1, 0,
|
||||
26, 0, 0, 0, 95, 0, 0, 3, 242, 16, 16, 0, 0, 0, 0,
|
||||
0, 95, 0, 0, 3, 50, 16, 16, 0, 1, 0, 0, 0, 103, 0,
|
||||
0, 4, 242, 32, 16, 0, 0, 0, 0, 0, 1, 0, 0, 0, 101,
|
||||
0, 0, 3, 50, 32, 16, 0, 1, 0, 0, 0, 54, 0, 0, 5,
|
||||
242, 32, 16, 0, 0, 0, 0, 0, 70, 30, 16, 0, 0, 0, 0,
|
||||
0, 54, 0, 0, 5, 50, 32, 16, 0, 1, 0, 0, 0, 70, 16,
|
||||
16, 0, 1, 0, 0, 0, 62, 0, 0, 1, 83, 80, 68, 66, 0,
|
||||
46, 0, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 67, 47,
|
||||
67, 43, 43, 32, 77, 83, 70, 32, 55, 46, 48, 48, 13, 10, 26,
|
||||
68, 83, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 23, 0,
|
||||
0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 56, 0, 128, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, 0, 32, 0,
|
||||
0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
|
||||
0, 0, 0, 6, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
|
||||
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
|
||||
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
68, 51, 68, 83, 72, 68, 82, 0, 104, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 32, 0, 0, 96, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 198, 90, 0, 0, 117, 131, 1, 0, 156, 39, 3, 0, 156,
|
||||
202, 1, 0, 38, 247, 2, 0, 69, 103, 0, 0, 109, 24, 1, 0,
|
||||
248, 34, 2, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 73,
|
||||
78, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102, 108,
|
||||
111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 80, 79, 83, 73,
|
||||
84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97,
|
||||
116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79,
|
||||
82, 68, 59, 13, 10, 125, 59, 13, 10, 13, 10, 115, 116, 114, 117,
|
||||
99, 116, 32, 86, 83, 95, 79, 85, 84, 80, 85, 84, 13, 10, 123,
|
||||
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
|
||||
115, 32, 58, 32, 83, 86, 95, 80, 79, 83, 73, 84, 73, 79, 78,
|
||||
59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84,
|
||||
101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13,
|
||||
10, 125, 59, 13, 10, 86, 83, 95, 79, 85, 84, 80, 85, 84, 32,
|
||||
86, 83, 40, 86, 83, 95, 73, 78, 80, 85, 84, 32, 105, 110, 112,
|
||||
117, 116, 41, 13, 10, 123, 13, 10, 32, 32, 32, 32, 114, 101, 116,
|
||||
117, 114, 110, 32, 105, 110, 112, 117, 116, 59, 13, 10, 125, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0, 108,
|
||||
1, 0, 0, 0, 68, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107,
|
||||
92, 103, 112, 117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101,
|
||||
99, 92, 118, 115, 92, 83, 104, 97, 100, 101, 114, 67, 111, 109, 112,
|
||||
105, 108, 101, 84, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116,
|
||||
101, 120, 95, 115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0,
|
||||
0, 100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112,
|
||||
117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118,
|
||||
115, 92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101,
|
||||
116, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95,
|
||||
115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 115, 116, 114,
|
||||
117, 99, 116, 32, 86, 83, 95, 73, 78, 80, 85, 84, 13, 10, 123,
|
||||
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
|
||||
115, 32, 58, 32, 80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10,
|
||||
32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32,
|
||||
58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59,
|
||||
13, 10, 13, 10, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 79,
|
||||
85, 84, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102,
|
||||
108, 111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 83, 86, 95,
|
||||
80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32,
|
||||
102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69,
|
||||
88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59, 13, 10, 86, 83,
|
||||
95, 79, 85, 84, 80, 85, 84, 32, 86, 83, 40, 86, 83, 95, 73,
|
||||
78, 80, 85, 84, 32, 105, 110, 112, 117, 116, 41, 13, 10, 123, 13,
|
||||
10, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 105, 110, 112,
|
||||
117, 116, 59, 13, 10, 125, 0, 7, 0, 0, 0, 0, 0, 0, 0,
|
||||
72, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 73, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 27, 226, 48, 1, 128, 0, 0,
|
||||
0, 11, 23, 208, 112, 24, 61, 218, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0,
|
||||
0, 0, 73, 0, 0, 0, 40, 0, 0, 0, 27, 226, 48, 1, 78,
|
||||
31, 42, 3, 219, 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0,
|
||||
73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 66,
|
||||
0, 60, 17, 16, 1, 0, 0, 0, 1, 10, 0, 1, 0, 243, 2,
|
||||
93, 88, 10, 0, 1, 0, 243, 2, 93, 88, 77, 105, 99, 114, 111,
|
||||
115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83,
|
||||
104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114, 32,
|
||||
49, 48, 46, 49, 0, 0, 0, 62, 0, 61, 17, 1, 104, 108, 115,
|
||||
108, 70, 108, 97, 103, 115, 0, 48, 120, 49, 0, 104, 108, 115, 108,
|
||||
84, 97, 114, 103, 101, 116, 0, 118, 115, 95, 52, 95, 48, 95, 108,
|
||||
101, 118, 101, 108, 95, 57, 95, 51, 0, 104, 108, 115, 108, 69, 110,
|
||||
116, 114, 121, 0, 86, 83, 0, 0, 0, 0, 0, 42, 0, 16, 17,
|
||||
0, 0, 0, 0, 64, 2, 0, 0, 0, 0, 0, 0, 44, 0, 0,
|
||||
0, 0, 0, 0, 0, 44, 0, 0, 0, 7, 16, 0, 0, 60, 0,
|
||||
0, 0, 1, 0, 160, 86, 83, 0, 0, 0, 46, 0, 62, 17, 3,
|
||||
16, 0, 0, 9, 0, 105, 110, 112, 117, 116, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0,
|
||||
80, 17, 1, 0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1,
|
||||
0, 44, 0, 0, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0,
|
||||
4, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0,
|
||||
0, 22, 0, 80, 17, 1, 0, 5, 0, 8, 0, 4, 0, 60, 0,
|
||||
0, 0, 1, 0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 1,
|
||||
0, 5, 0, 12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
|
||||
12, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0, 16, 0, 4,
|
||||
0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0, 0, 22, 0,
|
||||
80, 17, 1, 0, 5, 0, 20, 0, 4, 0, 60, 0, 0, 0, 1,
|
||||
0, 44, 0, 20, 0, 0, 0, 58, 0, 62, 17, 6, 16, 0, 0,
|
||||
136, 0, 60, 86, 83, 32, 114, 101, 116, 117, 114, 110, 32, 118, 97,
|
||||
108, 117, 101, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
|
||||
16, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0,
|
||||
0, 22, 0, 80, 17, 2, 0, 5, 0, 20, 0, 4, 0, 60, 0,
|
||||
0, 0, 1, 0, 44, 0, 20, 0, 0, 0, 22, 0, 80, 17, 2,
|
||||
0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
|
||||
0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0, 4, 0, 4,
|
||||
0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0, 0, 22, 0,
|
||||
80, 17, 2, 0, 5, 0, 8, 0, 4, 0, 60, 0, 0, 0, 1,
|
||||
0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
|
||||
12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 12, 0, 0,
|
||||
0, 2, 0, 6, 0, 244, 0, 0, 0, 24, 0, 0, 0, 1, 0,
|
||||
0, 0, 16, 1, 132, 23, 125, 135, 58, 127, 163, 121, 164, 136, 47,
|
||||
191, 133, 155, 215, 75, 0, 0, 242, 0, 0, 0, 96, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 1, 0, 104, 0, 0, 0, 0, 0, 0,
|
||||
0, 6, 0, 0, 0, 84, 0, 0, 0, 60, 0, 0, 0, 14, 0,
|
||||
0, 128, 60, 0, 0, 0, 14, 0, 0, 0, 80, 0, 0, 0, 14,
|
||||
0, 0, 128, 80, 0, 0, 0, 14, 0, 0, 0, 100, 0, 0, 0,
|
||||
14, 0, 0, 128, 100, 0, 0, 0, 14, 0, 0, 0, 5, 0, 17,
|
||||
0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0,
|
||||
17, 0, 5, 0, 17, 0, 246, 0, 0, 0, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
|
||||
202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 8, 16, 0, 0,
|
||||
188, 0, 0, 0, 10, 0, 255, 255, 4, 0, 0, 0, 255, 255, 3,
|
||||
0, 0, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 8, 0,
|
||||
0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 22, 0, 27, 21, 64,
|
||||
0, 0, 0, 4, 0, 0, 0, 16, 0, 102, 108, 111, 97, 116, 52,
|
||||
0, 243, 242, 241, 22, 0, 27, 21, 64, 0, 0, 0, 2, 0, 0,
|
||||
0, 8, 0, 102, 108, 111, 97, 116, 50, 0, 243, 242, 241, 34, 0,
|
||||
3, 18, 13, 21, 3, 0, 0, 16, 0, 0, 0, 0, 80, 111, 115,
|
||||
0, 242, 241, 13, 21, 3, 0, 1, 16, 0, 0, 16, 0, 84, 101,
|
||||
120, 0, 242, 241, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 73,
|
||||
78, 80, 85, 84, 0, 241, 10, 0, 1, 18, 1, 0, 0, 0, 3,
|
||||
16, 0, 0, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 79, 85,
|
||||
84, 80, 85, 84, 0, 10, 0, 24, 21, 5, 16, 0, 0, 1, 0,
|
||||
1, 0, 14, 0, 8, 16, 6, 16, 0, 0, 23, 0, 1, 0, 4,
|
||||
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 11, 202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 0, 16,
|
||||
0, 0, 0, 0, 0, 0, 11, 0, 255, 255, 4, 0, 0, 0, 255,
|
||||
255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 8, 0, 0, 0,
|
||||
8, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 18, 0, 37, 17, 0, 0, 0, 0,
|
||||
136, 0, 0, 0, 1, 0, 86, 83, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
|
||||
119, 9, 49, 1, 1, 0, 0, 0, 13, 0, 20, 142, 14, 0, 20,
|
||||
107, 15, 0, 1, 0, 72, 0, 0, 0, 32, 0, 0, 0, 44, 0,
|
||||
0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22,
|
||||
0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0,
|
||||
0, 32, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 0, 9, 0, 68, 2, 0, 0, 0, 0, 0, 0, 148,
|
||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 86, 83, 0, 110, 111, 110, 101, 0, 45, 186, 46,
|
||||
241, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 32, 0,
|
||||
0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
||||
0, 2, 0, 7, 0, 0, 0, 0, 0, 1, 0, 255, 255, 255, 255,
|
||||
0, 0, 0, 0, 104, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0,
|
||||
0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 68, 58, 92, 114, 117,
|
||||
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
|
||||
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
|
||||
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
|
||||
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
|
||||
46, 104, 108, 115, 108, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
|
||||
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
|
||||
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 116, 0, 0, 0,
|
||||
47, 76, 105, 110, 107, 73, 110, 102, 111, 0, 47, 110, 97, 109, 101,
|
||||
115, 0, 47, 115, 114, 99, 47, 104, 101, 97, 100, 101, 114, 98, 108,
|
||||
111, 99, 107, 0, 47, 115, 114, 99, 47, 102, 105, 108, 101, 115, 47,
|
||||
100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112, 117,
|
||||
95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118, 115,
|
||||
92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101, 116,
|
||||
111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95, 115,
|
||||
104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 4, 0, 0, 0,
|
||||
6, 0, 0, 0, 1, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0,
|
||||
0, 17, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 8, 0,
|
||||
0, 0, 10, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5,
|
||||
0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
16, 0, 0, 0, 32, 0, 0, 0, 208, 0, 0, 0, 244, 0, 0,
|
||||
0, 87, 1, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 156, 1,
|
||||
0, 0, 128, 0, 0, 0, 219, 0, 0, 0, 224, 2, 0, 0, 40,
|
||||
0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 32, 2, 0, 0,
|
||||
44, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0,
|
||||
0, 13, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 9, 0,
|
||||
0, 0, 10, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 12,
|
||||
0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 15, 0, 0, 0,
|
||||
16, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 83, 84, 65, 84, 116, 0, 0, 0, 3, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 82, 68, 69, 70, 68, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0,
|
||||
0, 0, 0, 4, 254, 255, 1, 1, 0, 0, 28, 0, 0, 0, 77,
|
||||
105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76,
|
||||
83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105,
|
||||
108, 101, 114, 32, 49, 48, 46, 49, 0, 73, 83, 71, 78, 76, 0,
|
||||
0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 56, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
|
||||
15, 15, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 3, 0, 0, 80, 79,
|
||||
83, 73, 84, 73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68,
|
||||
0, 171, 171, 79, 83, 71, 78, 80, 0, 0, 0, 2, 0, 0, 0,
|
||||
8, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 3, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 68, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1,
|
||||
0, 0, 0, 3, 12, 0, 0, 83, 86, 95, 80, 79, 83, 73, 84,
|
||||
73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68, 0, 171, 171,
|
||||
171};
|
||||
793
libs/hwcodec/cpp/common/platform/win/win.cpp
Normal file
793
libs/hwcodec/cpp/common/platform/win/win.cpp
Normal file
@@ -0,0 +1,793 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
|
||||
#include "win.h"
|
||||
|
||||
#define LOG_MODULE "WIN"
|
||||
#include "log.h"
|
||||
|
||||
#define NUMVERTICES 6
|
||||
|
||||
typedef struct _VERTEX {
|
||||
DirectX::XMFLOAT3 Pos;
|
||||
DirectX::XMFLOAT2 TexCoord;
|
||||
} VERTEX;
|
||||
|
||||
bool NativeDevice::Init(int64_t luid, ID3D11Device *device, int pool_size) {
|
||||
if (device) {
|
||||
if (!InitFromDevice(device))
|
||||
return false;
|
||||
} else {
|
||||
if (!InitFromLuid(luid))
|
||||
return false;
|
||||
}
|
||||
if (!SetMultithreadProtected())
|
||||
return false;
|
||||
if (!InitQuery())
|
||||
return false;
|
||||
if (!InitVideoDevice())
|
||||
return false;
|
||||
count_ = pool_size;
|
||||
texture_.resize(count_);
|
||||
std::fill(texture_.begin(), texture_.end(), nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::InitFromLuid(int64_t luid) {
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
|
||||
(void **)factory1_.ReleaseAndGetAddressOf()));
|
||||
|
||||
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
|
||||
UINT i = 0;
|
||||
while (!FAILED(
|
||||
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
|
||||
i++;
|
||||
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||
tmpAdapter->GetDesc1(&desc);
|
||||
if (LUID(desc) == luid) {
|
||||
adapter1_.Swap(tmpAdapter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!adapter1_) {
|
||||
LOG_ERROR(std::string("Failed to find adapter1_"));
|
||||
return false;
|
||||
}
|
||||
HRB(adapter1_.As(&adapter_));
|
||||
|
||||
UINT createDeviceFlags =
|
||||
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
D3D_FEATURE_LEVEL featureLevels[] = {
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
};
|
||||
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
|
||||
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
D3D_DRIVER_TYPE d3dDriverType =
|
||||
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
|
||||
HRB(D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
|
||||
createDeviceFlags, featureLevels, numFeatureLevels,
|
||||
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
|
||||
&featureLevel, context_.ReleaseAndGetAddressOf()));
|
||||
|
||||
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
|
||||
LOG_ERROR(std::string("Direct3D Feature Level 11 unsupported."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::InitFromDevice(ID3D11Device *device) {
|
||||
device_ = device;
|
||||
device_->GetImmediateContext(context_.ReleaseAndGetAddressOf());
|
||||
ComPtr<IDXGIDevice> dxgiDevice = nullptr;
|
||||
HRB(device_.As(&dxgiDevice));
|
||||
HRB(dxgiDevice->GetAdapter(adapter_.ReleaseAndGetAddressOf()));
|
||||
HRB(adapter_.As(&adapter1_));
|
||||
HRB(adapter1_->GetParent(IID_PPV_ARGS(&factory1_)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::SetMultithreadProtected() {
|
||||
ComPtr<ID3D10Multithread> hmt = nullptr;
|
||||
HRB(context_.As(&hmt));
|
||||
if (!hmt->SetMultithreadProtected(TRUE)) {
|
||||
if (!hmt->GetMultithreadProtected()) {
|
||||
LOG_ERROR(std::string("Failed to SetMultithreadProtected"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::InitQuery() {
|
||||
D3D11_QUERY_DESC queryDesc;
|
||||
ZeroMemory(&queryDesc, sizeof(queryDesc));
|
||||
queryDesc.Query = D3D11_QUERY_EVENT;
|
||||
queryDesc.MiscFlags = 0;
|
||||
HRB(device_->CreateQuery(&queryDesc, query_.ReleaseAndGetAddressOf()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::InitVideoDevice() {
|
||||
HRB(device_.As(&video_device_));
|
||||
HRB(context_.As(&video_context_));
|
||||
HRB(video_context_.As(&video_context1_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::Nv12ToBgra(int width, int height,
|
||||
ID3D11Texture2D *nv12Texture,
|
||||
ID3D11Texture2D *bgraTexture,
|
||||
int nv12ArrayIndex) {
|
||||
if (width != last_nv12_to_bgra_width_ ||
|
||||
height != last_nv12_to_bgra_height_) {
|
||||
if (!nv12_to_bgra_set_srv(nv12Texture, width, height))
|
||||
return false;
|
||||
if (!nv12_to_bgra_set_view_port(width, height))
|
||||
return false;
|
||||
if (!nv12_to_bgra_set_sample())
|
||||
return false;
|
||||
if (!nv12_to_bgra_set_shader())
|
||||
return false;
|
||||
if (!nv12_to_bgra_set_vertex_buffer())
|
||||
return false;
|
||||
}
|
||||
last_nv12_to_bgra_width_ = width;
|
||||
last_nv12_to_bgra_height_ = height;
|
||||
if (!nv12_to_bgra_set_rtv(bgraTexture, width, height))
|
||||
return false;
|
||||
|
||||
D3D11_BOX srcBox;
|
||||
srcBox.left = 0;
|
||||
srcBox.top = 0;
|
||||
srcBox.right = width;
|
||||
srcBox.bottom = height;
|
||||
srcBox.front = 0;
|
||||
srcBox.back = 1;
|
||||
context_->CopySubresourceRegion(nv12SrvTexture_.Get(), 0, 0, 0, 0,
|
||||
nv12Texture, nv12ArrayIndex, &srcBox);
|
||||
if (!nv12_to_bgra_draw())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
|
||||
int height) {
|
||||
SRV_[0].Reset();
|
||||
SRV_[1].Reset();
|
||||
|
||||
D3D11_TEXTURE2D_DESC texDesc = {};
|
||||
nv12Texture->GetDesc(&texDesc);
|
||||
texDesc.MipLevels = 1;
|
||||
texDesc.ArraySize = 1;
|
||||
texDesc.Format = DXGI_FORMAT_NV12;
|
||||
texDesc.SampleDesc.Quality = 0;
|
||||
texDesc.SampleDesc.Count = 1;
|
||||
texDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
texDesc.CPUAccessFlags = 0;
|
||||
texDesc.MiscFlags = 0;
|
||||
|
||||
texDesc.Width = width;
|
||||
texDesc.Height = height;
|
||||
|
||||
HRB(device_->CreateTexture2D(&texDesc, nullptr,
|
||||
nv12SrvTexture_.ReleaseAndGetAddressOf()));
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
|
||||
D3D11_SRV_DIMENSION_TEXTURE2D,
|
||||
DXGI_FORMAT_R8_UNORM);
|
||||
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
|
||||
SRV_[0].ReleaseAndGetAddressOf()));
|
||||
|
||||
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
|
||||
D3D11_SRV_DIMENSION_TEXTURE2D,
|
||||
DXGI_FORMAT_R8G8_UNORM);
|
||||
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
|
||||
SRV_[1].ReleaseAndGetAddressOf()));
|
||||
|
||||
// set SRV
|
||||
std::array<ID3D11ShaderResourceView *, 2> const textureViews = {
|
||||
SRV_[0].Get(), SRV_[1].Get()};
|
||||
context_->PSSetShaderResources(0, textureViews.size(), textureViews.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
|
||||
int height) {
|
||||
RTV_.Reset();
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
|
||||
rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
rtDesc.Texture2D.MipSlice = 0;
|
||||
HRB(device_->CreateRenderTargetView(bgraTexture, &rtDesc,
|
||||
RTV_.ReleaseAndGetAddressOf()));
|
||||
|
||||
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // clear as black
|
||||
context_->ClearRenderTargetView(RTV_.Get(), clearColor);
|
||||
context_->OMSetRenderTargets(1, RTV_.GetAddressOf(), NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_view_port(int width, int height) {
|
||||
|
||||
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;
|
||||
context_->RSSetViewports(1, &vp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_sample() {
|
||||
samplerLinear_.Reset();
|
||||
|
||||
D3D11_SAMPLER_DESC sampleDesc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
|
||||
HRB(device_->CreateSamplerState(&sampleDesc,
|
||||
samplerLinear_.ReleaseAndGetAddressOf()));
|
||||
context_->PSSetSamplers(0, 1, samplerLinear_.GetAddressOf());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_shader() {
|
||||
vertexShader_.Reset();
|
||||
pixelShader_.Reset();
|
||||
|
||||
// https://gist.github.com/RomiTT/9c05d36fe339b899793a3252297a5624
|
||||
#include "pixel_shader_601.h"
|
||||
#include "vertex_shader.h"
|
||||
device_->CreateVertexShader(g_VS, ARRAYSIZE(g_VS), nullptr,
|
||||
vertexShader_.ReleaseAndGetAddressOf());
|
||||
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(device_->CreateInputLayout(Layout.data(), Layout.size(), g_VS,
|
||||
ARRAYSIZE(g_VS), inputLayout.GetAddressOf()));
|
||||
context_->IASetInputLayout(inputLayout.Get());
|
||||
|
||||
context_->VSSetShader(vertexShader_.Get(), NULL, 0);
|
||||
context_->PSSetShader(pixelShader_.Get(), NULL, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_set_vertex_buffer() {
|
||||
UINT Stride = sizeof(VERTEX);
|
||||
UINT Offset = 0;
|
||||
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
|
||||
context_->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
|
||||
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(device_->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer));
|
||||
context_->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(), &Stride,
|
||||
&Offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::nv12_to_bgra_draw() {
|
||||
context_->Draw(NUMVERTICES, 0);
|
||||
context_->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::EnsureTexture(int width, int height) {
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
if (texture_[0]) {
|
||||
texture_[0]->GetDesc(&desc);
|
||||
if ((int)desc.Width == width && (int)desc.Height == height &&
|
||||
desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM &&
|
||||
desc.MiscFlags == D3D11_RESOURCE_MISC_SHARED &&
|
||||
desc.Usage == D3D11_USAGE_DEFAULT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
for (int i = 0; i < texture_.size(); i++) {
|
||||
HRB(device_->CreateTexture2D(&desc, nullptr,
|
||||
texture_[i].ReleaseAndGetAddressOf()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::SetTexture(ID3D11Texture2D *texture) {
|
||||
texture_[index_].Reset();
|
||||
texture_[index_] = texture;
|
||||
return true;
|
||||
}
|
||||
|
||||
HANDLE NativeDevice::GetSharedHandle() {
|
||||
ComPtr<IDXGIResource> resource = nullptr;
|
||||
HRP(texture_[index_].As(&resource));
|
||||
HANDLE sharedHandle = nullptr;
|
||||
HRP(resource->GetSharedHandle(&sharedHandle));
|
||||
return sharedHandle;
|
||||
}
|
||||
|
||||
ID3D11Texture2D *NativeDevice::GetCurrentTexture() {
|
||||
return texture_[index_].Get();
|
||||
}
|
||||
|
||||
int NativeDevice::next() {
|
||||
index_++;
|
||||
index_ = index_ % count_;
|
||||
return index_;
|
||||
}
|
||||
|
||||
void NativeDevice::BeginQuery() { context_->Begin(query_.Get()); }
|
||||
|
||||
void NativeDevice::EndQuery() { context_->End(query_.Get()); }
|
||||
|
||||
bool NativeDevice::Query() {
|
||||
BOOL bResult = FALSE;
|
||||
int attempts = 0;
|
||||
while (!bResult) {
|
||||
HRESULT hr = context_->GetData(query_.Get(), &bResult, sizeof(BOOL), 0);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (bResult) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
attempts++;
|
||||
if (attempts > 100)
|
||||
Sleep(1);
|
||||
if (attempts > 1000)
|
||||
break;
|
||||
}
|
||||
return bResult == TRUE;
|
||||
}
|
||||
|
||||
bool NativeDevice::Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width,
|
||||
int height,
|
||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_in,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_out,
|
||||
int arraySlice) {
|
||||
D3D11_TEXTURE2D_DESC inDesc = {0};
|
||||
D3D11_TEXTURE2D_DESC outDesc = {0};
|
||||
in->GetDesc(&inDesc);
|
||||
out->GetDesc(&outDesc);
|
||||
if (memcmp(&last_content_desc_, &content_desc, sizeof(content_desc)) != 0) {
|
||||
if (video_processor_enumerator_) {
|
||||
video_processor_enumerator_.Reset();
|
||||
}
|
||||
if (video_processor_) {
|
||||
video_processor_.Reset();
|
||||
}
|
||||
}
|
||||
memcpy(&last_content_desc_, &content_desc, sizeof(content_desc));
|
||||
|
||||
if (!video_processor_enumerator_ || !video_processor_) {
|
||||
HRB(video_device_->CreateVideoProcessorEnumerator(
|
||||
&content_desc, video_processor_enumerator_.ReleaseAndGetAddressOf()));
|
||||
HRB(video_device_->CreateVideoProcessor(
|
||||
video_processor_enumerator_.Get(), 0,
|
||||
video_processor_.ReleaseAndGetAddressOf()));
|
||||
// This fix too dark or too light, and also make in/out colorspace work
|
||||
video_context_->VideoProcessorSetStreamAutoProcessingMode(
|
||||
video_processor_.Get(), 0, FALSE);
|
||||
video_context_->VideoProcessorSetStreamFrameFormat(
|
||||
video_processor_.Get(), 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
|
||||
}
|
||||
|
||||
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#138
|
||||
// https://chromium.googlesource.com/chromium/src/+/a30440e4cfc7016d4f75a4e108025667e130b78b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
|
||||
|
||||
video_context1_->VideoProcessorSetStreamColorSpace1(video_processor_.Get(), 0,
|
||||
colorSpace_in);
|
||||
video_context1_->VideoProcessorSetOutputColorSpace1(video_processor_.Get(),
|
||||
colorSpace_out);
|
||||
|
||||
RECT rect = {0};
|
||||
rect.right = width;
|
||||
rect.bottom = height;
|
||||
video_context_->VideoProcessorSetStreamSourceRect(video_processor_.Get(), 0,
|
||||
true, &rect);
|
||||
video_context1_->VideoProcessorSetStreamDestRect(video_processor_.Get(), 0,
|
||||
true, &rect);
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputViewDesc;
|
||||
ZeroMemory(&InputViewDesc, sizeof(InputViewDesc));
|
||||
InputViewDesc.FourCC = 0;
|
||||
InputViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
|
||||
InputViewDesc.Texture2D.MipSlice = 0;
|
||||
InputViewDesc.Texture2D.ArraySlice = arraySlice;
|
||||
ComPtr<ID3D11VideoProcessorInputView> inputView = nullptr;
|
||||
HRB(video_device_->CreateVideoProcessorInputView(
|
||||
in, video_processor_enumerator_.Get(), &InputViewDesc,
|
||||
inputView.ReleaseAndGetAddressOf()));
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
|
||||
ZeroMemory(&OutputViewDesc, sizeof(OutputViewDesc));
|
||||
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
|
||||
OutputViewDesc.Texture2D.MipSlice = 0;
|
||||
ComPtr<ID3D11VideoProcessorOutputView> outputView = nullptr;
|
||||
video_device_->CreateVideoProcessorOutputView(
|
||||
out, video_processor_enumerator_.Get(), &OutputViewDesc,
|
||||
outputView.ReleaseAndGetAddressOf());
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
|
||||
ZeroMemory(&StreamData, sizeof(StreamData));
|
||||
StreamData.Enable = TRUE;
|
||||
StreamData.pInputSurface = inputView.Get();
|
||||
HRB(video_context_->VideoProcessorBlt(video_processor_.Get(),
|
||||
outputView.Get(), 0, 1, &StreamData));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeDevice::BgraToNv12(ID3D11Texture2D *bgraTexture,
|
||||
ID3D11Texture2D *nv12Texture, int width,
|
||||
int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_out) {
|
||||
D3D11_TEXTURE2D_DESC bgraDesc = {0};
|
||||
D3D11_TEXTURE2D_DESC nv12Desc = {0};
|
||||
bgraTexture->GetDesc(&bgraDesc);
|
||||
nv12Texture->GetDesc(&nv12Desc);
|
||||
if (bgraDesc.Width < width || bgraDesc.Height < height) {
|
||||
LOG_ERROR(std::string("bgraTexture size is smaller than width and height, ") +
|
||||
std::to_string(bgraDesc.Width) + "x" +
|
||||
std::to_string(bgraDesc.Height) + " < " + std::to_string(width) +
|
||||
"x" + std::to_string(height));
|
||||
return false;
|
||||
}
|
||||
if (nv12Desc.Width < width || nv12Desc.Height < height) {
|
||||
LOG_ERROR(std::string("nv12Texture size is smaller than width and height,") +
|
||||
std::to_string(nv12Desc.Width) + "x" +
|
||||
std::to_string(nv12Desc.Height) + " < " + std::to_string(width) +
|
||||
"x" + std::to_string(height));
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
|
||||
ZeroMemory(&contentDesc, sizeof(contentDesc));
|
||||
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
|
||||
contentDesc.InputFrameRate.Numerator = 30;
|
||||
contentDesc.InputFrameRate.Denominator = 1;
|
||||
// TODO: width height always same with desc.Width and desc.Height in test,
|
||||
// need test for decide to use which one
|
||||
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#72
|
||||
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/media_foundation_video_encode_accelerator_win.cc#2170
|
||||
contentDesc.InputWidth = width;
|
||||
contentDesc.InputHeight = height;
|
||||
contentDesc.OutputWidth = width;
|
||||
contentDesc.OutputHeight = height;
|
||||
contentDesc.OutputFrameRate.Numerator = 30;
|
||||
contentDesc.OutputFrameRate.Denominator = 1;
|
||||
|
||||
return Process(bgraTexture, nv12Texture, width, height, contentDesc,
|
||||
colorSpace_in, colorSpace_out, 0);
|
||||
}
|
||||
|
||||
AdapterVendor NativeDevice::GetVendor() {
|
||||
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
|
||||
adapter1_->GetDesc1(&desc1);
|
||||
if (desc1.VendorId == ADAPTER_VENDOR_NVIDIA) {
|
||||
return ADAPTER_VENDOR_NVIDIA;
|
||||
} else if (desc1.VendorId == ADAPTER_VENDOR_AMD) {
|
||||
return ADAPTER_VENDOR_AMD;
|
||||
} else if (desc1.VendorId == ADAPTER_VENDOR_INTEL) {
|
||||
return ADAPTER_VENDOR_INTEL;
|
||||
} else {
|
||||
return ADAPTER_VENDOR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeDevice::support_decode(DataFormat format) {
|
||||
const GUID *guid = nullptr;
|
||||
switch (format) {
|
||||
case H264:
|
||||
guid = &D3D11_DECODER_PROFILE_H264_VLD_NOFGT;
|
||||
break;
|
||||
case H265:
|
||||
guid = &D3D11_DECODER_PROFILE_HEVC_VLD_MAIN;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
BOOL supported = FALSE;
|
||||
if (S_OK != video_device_->CheckVideoDecoderFormat(guid, DXGI_FORMAT_NV12,
|
||||
&supported)) {
|
||||
return false;
|
||||
}
|
||||
if (supported) {
|
||||
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
|
||||
if (FAILED(adapter1_->GetDesc1(&desc1))) {
|
||||
return false;
|
||||
}
|
||||
bool partial =
|
||||
isFormatHybridDecodedByHardware(format, desc1.VendorId, desc1.DeviceId);
|
||||
return partial == false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/moonlight-stream/moonlight-qt/blob/9117f6565e4b2a6ba5417282de6bf9360b681f1a/app/streaming/video/ffmpeg-renderers/dxutil.h#L8
|
||||
bool NativeDevice::isFormatHybridDecodedByHardware(DataFormat format,
|
||||
unsigned int vendorId,
|
||||
unsigned int deviceId) {
|
||||
if (vendorId == ADAPTER_VENDOR_INTEL) {
|
||||
// Intel seems to encode the series in the high byte of
|
||||
// the device ID. We want to avoid the "Partial" acceleration
|
||||
// support explicitly. Those will claim to have HW acceleration
|
||||
// but perform badly.
|
||||
// https://en.wikipedia.org/wiki/Intel_Graphics_Technology#Capabilities_(GPU_video_acceleration)
|
||||
// https://raw.githubusercontent.com/GameTechDev/gpudetect/master/IntelGfx.cfg
|
||||
switch (deviceId & 0xFF00) {
|
||||
case 0x0400: // Haswell
|
||||
case 0x0A00: // Haswell
|
||||
case 0x0D00: // Haswell
|
||||
case 0x1600: // Broadwell
|
||||
case 0x2200: // Cherry Trail and Braswell
|
||||
// Block these for HEVC to avoid hybrid decode
|
||||
return format == H265;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (vendorId == ADAPTER_VENDOR_NVIDIA) {
|
||||
// For NVIDIA, we wait to avoid those GPUs with Feature Set E
|
||||
// for HEVC decoding, since that's hybrid. It appears that Kepler GPUs
|
||||
// also had some hybrid decode support (per DXVA2 Checker) so we'll
|
||||
// blacklist those too.
|
||||
// https://en.wikipedia.org/wiki/Nvidia_PureVideo
|
||||
// https://bluesky23.yukishigure.com/en/dxvac/deviceInfo/decoder.html
|
||||
// http://envytools.readthedocs.io/en/latest/hw/pciid.html (missing GM200)
|
||||
if ((deviceId >= 0x1180 && deviceId <= 0x11BF) || // GK104
|
||||
(deviceId >= 0x11C0 && deviceId <= 0x11FF) || // GK106
|
||||
(deviceId >= 0x0FC0 && deviceId <= 0x0FFF) || // GK107
|
||||
(deviceId >= 0x1000 && deviceId <= 0x103F) || // GK110/GK110B
|
||||
(deviceId >= 0x1280 && deviceId <= 0x12BF) || // GK208
|
||||
(deviceId >= 0x1340 && deviceId <= 0x137F) || // GM108
|
||||
(deviceId >= 0x1380 && deviceId <= 0x13BF) || // GM107
|
||||
(deviceId >= 0x13C0 && deviceId <= 0x13FF) || // GM204
|
||||
(deviceId >= 0x1617 && deviceId <= 0x161A) || // GM204
|
||||
(deviceId == 0x1667) || // GM204
|
||||
(deviceId >= 0x17C0 && deviceId <= 0x17FF)) { // GM200
|
||||
// Avoid HEVC on Feature Set E GPUs
|
||||
return format == H265;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Adapter::Init(IDXGIAdapter1 *adapter1) {
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
adapter1_ = adapter1;
|
||||
HRB(adapter1_.As(&adapter_));
|
||||
|
||||
UINT createDeviceFlags = 0;
|
||||
D3D_FEATURE_LEVEL featureLevels[] = {
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
};
|
||||
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
|
||||
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
D3D_DRIVER_TYPE d3dDriverType =
|
||||
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
|
||||
hr = D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
|
||||
createDeviceFlags, featureLevels, numFeatureLevels,
|
||||
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
|
||||
&featureLevel, context_.ReleaseAndGetAddressOf());
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
|
||||
std::cerr << "Direct3D Feature Level 11 unsupported." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
HRB(adapter1->GetDesc1(&desc1_));
|
||||
if (desc1_.VendorId == ADAPTER_VENDOR_INTEL) {
|
||||
if (!SetMultithreadProtected())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Adapter::SetMultithreadProtected() {
|
||||
ComPtr<ID3D10Multithread> hmt = nullptr;
|
||||
HRB(context_.As(&hmt));
|
||||
if (!hmt->SetMultithreadProtected(TRUE)) {
|
||||
if (!hmt->GetMultithreadProtected()) {
|
||||
std::cerr << "Failed to SetMultithreadProtected" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Adapters::Init(AdapterVendor vendor) {
|
||||
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
|
||||
(void **)factory1_.ReleaseAndGetAddressOf()));
|
||||
|
||||
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
|
||||
UINT i = 0;
|
||||
while (!FAILED(
|
||||
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
|
||||
i++;
|
||||
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||
tmpAdapter->GetDesc1(&desc);
|
||||
if (desc.VendorId == static_cast<UINT>(vendor)) {
|
||||
auto adapter = std::make_unique<Adapter>();
|
||||
if (adapter->Init(tmpAdapter.Get())) {
|
||||
adapters_.push_back(std::move(adapter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Adapters::GetFirstAdapterIndex(AdapterVendor vendor) {
|
||||
ComPtr<IDXGIFactory1> factory1 = nullptr;
|
||||
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
|
||||
(void **)factory1.ReleaseAndGetAddressOf()));
|
||||
|
||||
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
|
||||
UINT i = 0;
|
||||
while (!FAILED(
|
||||
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
|
||||
i++;
|
||||
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
|
||||
tmpAdapter->GetDesc1(&desc);
|
||||
if (desc.VendorId == static_cast<UINT>(vendor)) {
|
||||
return i - 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version
|
||||
// https://github.com/citizenfx/fivem/issues/1121
|
||||
uint64_t GetHwcodecGpuSignature() {
|
||||
uint64_t signature = 0;
|
||||
ComPtr<IDXGIFactory1> factory1 = nullptr;
|
||||
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
|
||||
(void **)factory1.ReleaseAndGetAddressOf()));
|
||||
|
||||
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
|
||||
UINT i = 0;
|
||||
while (!FAILED(
|
||||
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
|
||||
i++;
|
||||
DXGI_ADAPTER_DESC1 desc = {0};
|
||||
if (SUCCEEDED(tmpAdapter->GetDesc1(&desc))) {
|
||||
if (desc.VendorId == ADAPTER_VENDOR_NVIDIA ||
|
||||
desc.VendorId == ADAPTER_VENDOR_AMD ||
|
||||
desc.VendorId == ADAPTER_VENDOR_INTEL) {
|
||||
// hardware
|
||||
signature += desc.VendorId;
|
||||
signature += desc.DeviceId;
|
||||
signature += desc.SubSysId;
|
||||
signature += desc.Revision;
|
||||
// software
|
||||
LARGE_INTEGER umd_version;
|
||||
if SUCCEEDED (tmpAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
|
||||
&umd_version)) {
|
||||
signature += umd_version.QuadPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
|
||||
int *h) {
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
texture->GetDesc(&desc);
|
||||
*w = desc.Width;
|
||||
*h = desc.Height;
|
||||
}
|
||||
|
||||
int32_t add_process_to_new_job(DWORD process_id) {
|
||||
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
|
||||
if (job_handle == nullptr) {
|
||||
LOG_ERROR(std::string("Failed to create job object"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0};
|
||||
job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
|
||||
BOOL result = SetInformationJobObject(
|
||||
job_handle,
|
||||
JobObjectExtendedLimitInformation,
|
||||
&job_info,
|
||||
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
|
||||
);
|
||||
|
||||
if (result == FALSE) {
|
||||
CloseHandle(job_handle);
|
||||
LOG_ERROR(std::string("Failed to set job information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the existing process by ID
|
||||
HANDLE process_handle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, process_id);
|
||||
if (process_handle == nullptr) {
|
||||
CloseHandle(job_handle);
|
||||
LOG_ERROR(std::string("Failed to open process with ID: ") + std::to_string(process_id));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Assign the child process to the Job object
|
||||
BOOL assign_result = AssignProcessToJobObject(job_handle, process_handle);
|
||||
if (assign_result == FALSE) {
|
||||
CloseHandle(process_handle);
|
||||
CloseHandle(job_handle);
|
||||
LOG_ERROR(std::string("Failed to assign process to job"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close process handle (but keep job handle)
|
||||
CloseHandle(process_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
135
libs/hwcodec/cpp/common/platform/win/win.h
Normal file
135
libs/hwcodec/cpp/common/platform/win/win.h
Normal file
@@ -0,0 +1,135 @@
|
||||
#ifndef WIN_H
|
||||
#define WIN_H
|
||||
|
||||
#include <DirectXMath.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <directxcolors.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "../../common.h"
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
using namespace std;
|
||||
using namespace DirectX;
|
||||
|
||||
#define SAFE_RELEASE(p) \
|
||||
{ \
|
||||
if ((p)) { \
|
||||
(p)->Release(); \
|
||||
(p) = nullptr; \
|
||||
} \
|
||||
}
|
||||
#define LUID(desc) \
|
||||
(((int64_t)desc.AdapterLuid.HighPart << 32) | desc.AdapterLuid.LowPart)
|
||||
|
||||
class NativeDevice {
|
||||
public:
|
||||
bool Init(int64_t luid, ID3D11Device *device, int pool_size = 1);
|
||||
bool EnsureTexture(int width, int height);
|
||||
bool SetTexture(ID3D11Texture2D *texture);
|
||||
HANDLE GetSharedHandle();
|
||||
ID3D11Texture2D *GetCurrentTexture();
|
||||
int next();
|
||||
void BeginQuery();
|
||||
void EndQuery();
|
||||
bool Query();
|
||||
bool Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width, int height,
|
||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_in,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_out, int arraySlice);
|
||||
bool BgraToNv12(ID3D11Texture2D *bgraTexture, ID3D11Texture2D *nv12Texture,
|
||||
int width, int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace_outt);
|
||||
bool Nv12ToBgra(int width, int height, ID3D11Texture2D *nv12Texture,
|
||||
ID3D11Texture2D *bgraTexture, int nv12ArrayIndex);
|
||||
AdapterVendor GetVendor();
|
||||
bool support_decode(DataFormat format);
|
||||
|
||||
private:
|
||||
bool InitFromLuid(int64_t luid);
|
||||
bool InitFromDevice(ID3D11Device *device);
|
||||
bool SetMultithreadProtected();
|
||||
bool InitQuery();
|
||||
bool InitVideoDevice();
|
||||
bool isFormatHybridDecodedByHardware(DataFormat format, unsigned int vendorId,
|
||||
unsigned int deviceId);
|
||||
|
||||
// nv12 to bgra
|
||||
bool nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
|
||||
int height);
|
||||
bool nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
|
||||
int height);
|
||||
bool nv12_to_bgra_set_view_port(int width, int height);
|
||||
bool nv12_to_bgra_set_sample();
|
||||
bool nv12_to_bgra_set_shader();
|
||||
bool nv12_to_bgra_set_vertex_buffer();
|
||||
bool nv12_to_bgra_draw();
|
||||
|
||||
public:
|
||||
// Direct3D 11
|
||||
ComPtr<IDXGIFactory1> factory1_ = nullptr;
|
||||
ComPtr<IDXGIAdapter> adapter_ = nullptr;
|
||||
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
|
||||
ComPtr<ID3D11Device> device_ = nullptr;
|
||||
ComPtr<ID3D11DeviceContext> context_ = nullptr;
|
||||
ComPtr<ID3D11Query> query_ = nullptr;
|
||||
ComPtr<ID3D11VideoDevice> video_device_ = nullptr;
|
||||
ComPtr<ID3D11VideoContext> video_context_ = nullptr;
|
||||
ComPtr<ID3D11VideoContext1> video_context1_ = nullptr;
|
||||
ComPtr<ID3D11VideoProcessorEnumerator> video_processor_enumerator_ = nullptr;
|
||||
ComPtr<ID3D11VideoProcessor> video_processor_ = nullptr;
|
||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC last_content_desc_ = {};
|
||||
|
||||
ComPtr<ID3D11RenderTargetView> RTV_ = NULL;
|
||||
ComPtr<ID3D11ShaderResourceView> SRV_[2] = {NULL, NULL};
|
||||
ComPtr<ID3D11VertexShader> vertexShader_ = NULL;
|
||||
ComPtr<ID3D11PixelShader> pixelShader_ = NULL;
|
||||
ComPtr<ID3D11SamplerState> samplerLinear_ = NULL;
|
||||
ComPtr<ID3D11Texture2D> nv12SrvTexture_ = nullptr;
|
||||
|
||||
int count_;
|
||||
int index_ = 0;
|
||||
|
||||
int last_nv12_to_bgra_width_ = 0;
|
||||
int last_nv12_to_bgra_height_ = 0;
|
||||
|
||||
private:
|
||||
std::vector<ComPtr<ID3D11Texture2D>> texture_;
|
||||
};
|
||||
|
||||
class Adapter {
|
||||
public:
|
||||
bool Init(IDXGIAdapter1 *adapter1);
|
||||
|
||||
private:
|
||||
bool SetMultithreadProtected();
|
||||
|
||||
public:
|
||||
ComPtr<IDXGIAdapter> adapter_ = nullptr;
|
||||
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
|
||||
ComPtr<ID3D11Device> device_ = nullptr;
|
||||
ComPtr<ID3D11DeviceContext> context_ = nullptr;
|
||||
DXGI_ADAPTER_DESC1 desc1_;
|
||||
};
|
||||
|
||||
class Adapters {
|
||||
public:
|
||||
bool Init(AdapterVendor vendor);
|
||||
static int GetFirstAdapterIndex(AdapterVendor vendor);
|
||||
|
||||
public:
|
||||
ComPtr<IDXGIFactory1> factory1_ = nullptr;
|
||||
std::vector<std::unique_ptr<Adapter>> adapters_;
|
||||
};
|
||||
|
||||
extern "C" uint64_t GetHwcodecGpuSignature();
|
||||
|
||||
extern "C" void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
|
||||
int *h);
|
||||
|
||||
extern "C" int32_t add_process_to_new_job(DWORD process_id);
|
||||
|
||||
#endif
|
||||
11
libs/hwcodec/cpp/common/system.h
Normal file
11
libs/hwcodec/cpp/common/system.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "platform/win/win.h"
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include "platform/linux/linux.h"
|
||||
#endif
|
||||
|
||||
#endif // SYSTEM_H
|
||||
357
libs/hwcodec/cpp/common/util.cpp
Normal file
357
libs/hwcodec/cpp/common/util.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
extern "C" {
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
#include "util.h"
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define LOG_MODULE "UTIL"
|
||||
#include "log.h"
|
||||
|
||||
namespace util_encode {
|
||||
|
||||
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
|
||||
int gop, int fps) {
|
||||
c->has_b_frames = 0;
|
||||
c->max_b_frames = 0;
|
||||
if (gop > 0 && gop < std::numeric_limits<int16_t>::max()) {
|
||||
c->gop_size = gop;
|
||||
c->keyint_min = gop; // Match keyint_min to gop for consistent keyframe interval
|
||||
} else if (name.find("vaapi") != std::string::npos) {
|
||||
c->gop_size = fps > 0 ? fps : 30; // Default to 1 second keyframe interval
|
||||
c->keyint_min = c->gop_size;
|
||||
} else if (name.find("qsv") != std::string::npos) {
|
||||
c->gop_size = fps > 0 ? fps : 30;
|
||||
c->keyint_min = c->gop_size;
|
||||
} else {
|
||||
c->gop_size = fps > 0 ? fps : 30;
|
||||
c->keyint_min = c->gop_size;
|
||||
}
|
||||
/* put sample parameters */
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/415f012359364a77e8394436f222b74a8641a3ee/libavcodec/encode.c#L581
|
||||
if (kbs > 0) {
|
||||
c->bit_rate = kbs * 1000;
|
||||
if (name.find("qsv") != std::string::npos) {
|
||||
c->rc_max_rate = c->bit_rate;
|
||||
c->bit_rate--; // cbr with vbr
|
||||
}
|
||||
}
|
||||
/* frames per second */
|
||||
c->time_base = av_make_q(1, 1000);
|
||||
c->framerate = av_make_q(fps, 1);
|
||||
c->flags |= AV_CODEC_FLAG2_LOCAL_HEADER;
|
||||
c->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
c->slices = 1;
|
||||
c->thread_type = FF_THREAD_SLICE;
|
||||
c->thread_count = c->slices;
|
||||
|
||||
// https://github.com/obsproject/obs-studio/blob/3cc7dc0e7cf8b01081dc23e432115f7efd0c8877/plugins/obs-ffmpeg/obs-ffmpeg-mux.c#L160
|
||||
c->color_range = AVCOL_RANGE_MPEG;
|
||||
c->colorspace = AVCOL_SPC_SMPTE170M;
|
||||
c->color_primaries = AVCOL_PRI_SMPTE170M;
|
||||
c->color_trc = AVCOL_TRC_SMPTE170M;
|
||||
|
||||
if (name.find("h264") != std::string::npos) {
|
||||
c->profile = FF_PROFILE_H264_HIGH;
|
||||
} else if (name.find("hevc") != std::string::npos) {
|
||||
c->profile = FF_PROFILE_HEVC_MAIN;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_lantency_free(void *priv_data, const std::string &name) {
|
||||
int ret;
|
||||
|
||||
if (name.find("nvenc") != std::string::npos) {
|
||||
if ((ret = av_opt_set(priv_data, "delay", "0", 0)) < 0) {
|
||||
LOG_ERROR(std::string("nvenc set_lantency_free failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("amf") != std::string::npos) {
|
||||
if ((ret = av_opt_set(priv_data, "query_timeout", "1000", 0)) < 0) {
|
||||
LOG_ERROR(std::string("amf set_lantency_free failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("qsv") != std::string::npos) {
|
||||
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("qsv set_lantency_free failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("vaapi") != std::string::npos) {
|
||||
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("vaapi set_lantency_free failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("videotoolbox") != std::string::npos) {
|
||||
if ((ret = av_opt_set_int(priv_data, "realtime", 1, 0)) < 0) {
|
||||
LOG_ERROR(std::string("videotoolbox set realtime failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
if ((ret = av_opt_set_int(priv_data, "prio_speed", 1, 0)) < 0) {
|
||||
LOG_ERROR(std::string("videotoolbox set prio_speed failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// libvpx (VP8/VP9) - set realtime mode to avoid frame lag
|
||||
if (name.find("libvpx") != std::string::npos) {
|
||||
// deadline: realtime for low-latency streaming
|
||||
if ((ret = av_opt_set(priv_data, "deadline", "realtime", 0)) < 0) {
|
||||
LOG_ERROR(std::string("libvpx set deadline realtime failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
// cpu-used: 6 is good balance for real-time (0-8, higher = faster but lower quality)
|
||||
if ((ret = av_opt_set_int(priv_data, "cpu-used", 6, 0)) < 0) {
|
||||
LOG_ERROR(std::string("libvpx set cpu-used failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
// lag-in-frames: 0 disables frame lag (important for real-time)
|
||||
if ((ret = av_opt_set_int(priv_data, "lag-in-frames", 0, 0)) < 0) {
|
||||
LOG_ERROR(std::string("libvpx set lag-in-frames failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
// row-mt: enable row-based multithreading for VP9
|
||||
if (name.find("vp9") != std::string::npos) {
|
||||
if ((ret = av_opt_set_int(priv_data, "row-mt", 1, 0)) < 0) {
|
||||
LOG_ERROR(std::string("libvpx-vp9 set row-mt failed, ret = ") + av_err2str(ret));
|
||||
// row-mt failure is not fatal
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_quality(void *priv_data, const std::string &name, int quality) {
|
||||
int ret = -1;
|
||||
|
||||
if (name.find("nvenc") != std::string::npos) {
|
||||
switch (quality) {
|
||||
// p7 isn't zero lantency
|
||||
case Quality_Medium:
|
||||
if ((ret = av_opt_set(priv_data, "preset", "p4", 0)) < 0) {
|
||||
LOG_ERROR(std::string("nvenc set opt preset p4 failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Quality_Low:
|
||||
if ((ret = av_opt_set(priv_data, "preset", "p1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("nvenc set opt preset p1 failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name.find("amf") != std::string::npos) {
|
||||
switch (quality) {
|
||||
case Quality_High:
|
||||
if ((ret = av_opt_set(priv_data, "quality", "quality", 0)) < 0) {
|
||||
LOG_ERROR(std::string("amf set opt quality quality failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Quality_Medium:
|
||||
if ((ret = av_opt_set(priv_data, "quality", "balanced", 0)) < 0) {
|
||||
LOG_ERROR(std::string("amf set opt quality balanced failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Quality_Low:
|
||||
if ((ret = av_opt_set(priv_data, "quality", "speed", 0)) < 0) {
|
||||
LOG_ERROR(std::string("amf set opt quality speed failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name.find("qsv") != std::string::npos) {
|
||||
switch (quality) {
|
||||
case Quality_High:
|
||||
if ((ret = av_opt_set(priv_data, "preset", "veryslow", 0)) < 0) {
|
||||
LOG_ERROR(std::string("qsv set opt preset veryslow failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Quality_Medium:
|
||||
if ((ret = av_opt_set(priv_data, "preset", "medium", 0)) < 0) {
|
||||
LOG_ERROR(std::string("qsv set opt preset medium failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Quality_Low:
|
||||
if ((ret = av_opt_set(priv_data, "preset", "veryfast", 0)) < 0) {
|
||||
LOG_ERROR(std::string("qsv set opt preset veryfast failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name.find("mediacodec") != std::string::npos) {
|
||||
if (name.find("h264") != std::string::npos) {
|
||||
if ((ret = av_opt_set(priv_data, "level", "5.1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("mediacodec set opt level 5.1 failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("hevc") != std::string::npos) {
|
||||
// https:en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels
|
||||
if ((ret = av_opt_set(priv_data, "level", "h5.1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("mediacodec set opt level h5.1 failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CodecOptions {
|
||||
std::string codec_name;
|
||||
std::string option_name;
|
||||
std::map<int, std::string> rc_values;
|
||||
};
|
||||
|
||||
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
|
||||
int q) {
|
||||
if (name.find("qsv") != std::string::npos) {
|
||||
// https://github.com/LizardByte/Sunshine/blob/3e47cd3cc8fd37a7a88be82444ff4f3c0022856b/src/video.cpp#L1635
|
||||
c->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
|
||||
}
|
||||
std::vector<CodecOptions> codecs = {
|
||||
{"nvenc", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr"}}},
|
||||
{"amf", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr_latency"}}},
|
||||
{"mediacodec",
|
||||
"bitrate_mode",
|
||||
{{RC_CBR, "cbr"}, {RC_VBR, "vbr"}, {RC_CQ, "cq"}}},
|
||||
// {"videotoolbox", "constant_bit_rate", {{RC_CBR, "1"}}},
|
||||
};
|
||||
|
||||
for (const auto &codec : codecs) {
|
||||
if (name.find(codec.codec_name) != std::string::npos) {
|
||||
auto it = codec.rc_values.find(rc);
|
||||
if (it != codec.rc_values.end()) {
|
||||
int ret = av_opt_set(c->priv_data, codec.option_name.c_str(),
|
||||
it->second.c_str(), 0);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(codec.codec_name + " set opt " + codec.option_name + " " +
|
||||
it->second + " failed, ret = " + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
if (name.find("mediacodec") != std::string::npos) {
|
||||
if (rc == RC_CQ) {
|
||||
if (q >= 0 && q <= 51) {
|
||||
c->global_quality = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool set_gpu(void *priv_data, const std::string &name, int gpu) {
|
||||
int ret;
|
||||
if (gpu < 0)
|
||||
return -1;
|
||||
if (name.find("nvenc") != std::string::npos) {
|
||||
if ((ret = av_opt_set_int(priv_data, "gpu", gpu, 0)) < 0) {
|
||||
LOG_ERROR(std::string("nvenc set gpu failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool force_hw(void *priv_data, const std::string &name) {
|
||||
int ret;
|
||||
if (name.find("_mf") != std::string::npos) {
|
||||
if ((ret = av_opt_set_int(priv_data, "hw_encoding", 1, 0)) < 0) {
|
||||
LOG_ERROR(std::string("mediafoundation set hw_encoding failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (name.find("videotoolbox") != std::string::npos) {
|
||||
if ((ret = av_opt_set_int(priv_data, "allow_sw", 0, 0)) < 0) {
|
||||
LOG_ERROR(std::string("mediafoundation set allow_sw failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_others(void *priv_data, const std::string &name) {
|
||||
int ret;
|
||||
if (name.find("_mf") != std::string::npos) {
|
||||
// ff_eAVScenarioInfo_DisplayRemoting = 1
|
||||
if ((ret = av_opt_set_int(priv_data, "scenario", 1, 0)) < 0) {
|
||||
LOG_ERROR(std::string("mediafoundation set scenario failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// NOTE: Removed idr_interval = INT_MAX for VAAPI.
|
||||
// This was disabling automatic keyframe generation.
|
||||
// The encoder should respect c->gop_size for keyframe interval.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs) {
|
||||
if (kbs > 0) {
|
||||
c->bit_rate = kbs * 1000;
|
||||
if (name.find("qsv") != std::string::npos) {
|
||||
c->rc_max_rate = c->bit_rate;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts) {
|
||||
(void)data;
|
||||
(void)len;
|
||||
(void)pts;
|
||||
if (obj) {
|
||||
int32_t *pkey = (int32_t *)obj;
|
||||
*pkey = key;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util_encode
|
||||
|
||||
namespace util_decode {
|
||||
|
||||
static bool g_flag_could_not_find_ref_with_poc = false;
|
||||
|
||||
bool has_flag_could_not_find_ref_with_poc() {
|
||||
bool v = g_flag_could_not_find_ref_with_poc;
|
||||
g_flag_could_not_find_ref_with_poc = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace util_decode
|
||||
|
||||
extern "C" void hwcodec_set_flag_could_not_find_ref_with_poc() {
|
||||
util_decode::g_flag_could_not_find_ref_with_poc = true;
|
||||
}
|
||||
52
libs/hwcodec/cpp/common/util.h
Normal file
52
libs/hwcodec/cpp/common/util.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
namespace util_encode {
|
||||
|
||||
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
|
||||
int gop, int fps);
|
||||
bool set_lantency_free(void *priv_data, const std::string &name);
|
||||
bool set_quality(void *priv_data, const std::string &name, int quality);
|
||||
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
|
||||
int q);
|
||||
bool set_gpu(void *priv_data, const std::string &name, int gpu);
|
||||
bool force_hw(void *priv_data, const std::string &name);
|
||||
bool set_others(void *priv_data, const std::string &name);
|
||||
|
||||
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs);
|
||||
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts);
|
||||
|
||||
} // namespace util
|
||||
|
||||
namespace util_decode {
|
||||
bool has_flag_could_not_find_ref_with_poc();
|
||||
}
|
||||
|
||||
namespace util {
|
||||
|
||||
inline std::chrono::steady_clock::time_point now() {
|
||||
return std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
inline int64_t elapsed_ms(std::chrono::steady_clock::time_point start) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now() - start).count();
|
||||
}
|
||||
|
||||
inline bool skip_test(const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount, int64_t currentLuid, int32_t dataFormat) {
|
||||
for (int32_t i = 0; i < excludeCount; i++) {
|
||||
if (excludedLuids[i] == currentLuid && excludeFormats[i] == dataFormat) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
328
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp
Normal file
328
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
// 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/log.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define LOG_MODULE "FFMPEG_RAM_DEC"
|
||||
#include <log.h>
|
||||
#include <util.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <libavutil/hwcontext_d3d11va.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "system.h"
|
||||
|
||||
// #define CFG_PKG_TRACE
|
||||
|
||||
namespace {
|
||||
typedef void (*RamDecodeCallback)(const void *obj, int width, int height,
|
||||
enum AVPixelFormat pixfmt,
|
||||
int linesize[AV_NUM_DATA_POINTERS],
|
||||
uint8_t *data[AV_NUM_DATA_POINTERS], int key);
|
||||
|
||||
class FFmpegRamDecoder {
|
||||
public:
|
||||
AVCodecContext *c_ = NULL;
|
||||
AVBufferRef *hw_device_ctx_ = NULL;
|
||||
AVFrame *sw_frame_ = NULL;
|
||||
AVFrame *frame_ = NULL;
|
||||
AVPacket *pkt_ = NULL;
|
||||
bool hwaccel_ = true;
|
||||
|
||||
std::string name_;
|
||||
AVHWDeviceType device_type_ = AV_HWDEVICE_TYPE_NONE;
|
||||
int thread_count_ = 1;
|
||||
RamDecodeCallback callback_ = NULL;
|
||||
DataFormat data_format_;
|
||||
|
||||
#ifdef CFG_PKG_TRACE
|
||||
int in_ = 0;
|
||||
int out_ = 0;
|
||||
#endif
|
||||
|
||||
FFmpegRamDecoder(const char *name, int device_type, int thread_count,
|
||||
RamDecodeCallback callback) {
|
||||
this->name_ = name;
|
||||
this->device_type_ = (AVHWDeviceType)device_type;
|
||||
this->thread_count_ = thread_count;
|
||||
this->callback_ = callback;
|
||||
}
|
||||
|
||||
~FFmpegRamDecoder() {}
|
||||
|
||||
void free_decoder() {
|
||||
if (frame_)
|
||||
av_frame_free(&frame_);
|
||||
if (pkt_)
|
||||
av_packet_free(&pkt_);
|
||||
if (sw_frame_)
|
||||
av_frame_free(&sw_frame_);
|
||||
if (c_)
|
||||
avcodec_free_context(&c_);
|
||||
if (hw_device_ctx_)
|
||||
av_buffer_unref(&hw_device_ctx_);
|
||||
|
||||
frame_ = NULL;
|
||||
pkt_ = NULL;
|
||||
sw_frame_ = NULL;
|
||||
c_ = NULL;
|
||||
hw_device_ctx_ = NULL;
|
||||
}
|
||||
int reset() {
|
||||
if (name_.find("h264") != std::string::npos) {
|
||||
data_format_ = DataFormat::H264;
|
||||
} else if (name_.find("hevc") != std::string::npos) {
|
||||
data_format_ = DataFormat::H265;
|
||||
} else if (name_.find("mjpeg") != std::string::npos) {
|
||||
data_format_ = DataFormat::MJPEG;
|
||||
} else {
|
||||
LOG_ERROR(std::string("unsupported data format:") + name_);
|
||||
return -1;
|
||||
}
|
||||
free_decoder();
|
||||
const AVCodec *codec = NULL;
|
||||
hwaccel_ = device_type_ != AV_HWDEVICE_TYPE_NONE;
|
||||
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;
|
||||
c_->thread_count =
|
||||
device_type_ != AV_HWDEVICE_TYPE_NONE ? 1 : thread_count_;
|
||||
c_->thread_type = FF_THREAD_SLICE;
|
||||
|
||||
if (name_.find("qsv") != std::string::npos) {
|
||||
if ((ret = av_opt_set(c_->priv_data, "async_depth", "1", 0)) < 0) {
|
||||
LOG_ERROR(std::string("qsv set opt async_depth 1 failed"));
|
||||
return -1;
|
||||
}
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/c6364b711bad1fe2fbd90e5b2798f87080ddf5ea/libavcodec/qsvdec.c#L932
|
||||
// for disable warning
|
||||
c_->pkt_timebase = av_make_q(1, 30);
|
||||
}
|
||||
|
||||
if (hwaccel_) {
|
||||
ret =
|
||||
av_hwdevice_ctx_create(&hw_device_ctx_, device_type_, NULL, NULL, 0);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(std::string("av_hwdevice_ctx_create failed, ret = ") + av_err2str(ret));
|
||||
return -1;
|
||||
}
|
||||
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
|
||||
if (!check_support()) {
|
||||
LOG_ERROR(std::string("check_support failed"));
|
||||
return -1;
|
||||
}
|
||||
if (!(sw_frame_ = av_frame_alloc())) {
|
||||
LOG_ERROR(std::string("av_frame_alloc failed"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return -1;
|
||||
}
|
||||
#ifdef CFG_PKG_TRACE
|
||||
in_ = 0;
|
||||
out_ = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decode(const uint8_t *data, int length, const void *obj) {
|
||||
int ret = -1;
|
||||
#ifdef CFG_PKG_TRACE
|
||||
in_++;
|
||||
LOG_DEBUG(std::string("delay DI: in:") + in_ + " out:" + out_);
|
||||
#endif
|
||||
|
||||
if (!data || !length) {
|
||||
LOG_ERROR(std::string("illegal decode parameter"));
|
||||
return -1;
|
||||
}
|
||||
pkt_->data = (uint8_t *)data;
|
||||
pkt_->size = length;
|
||||
ret = do_decode(obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
int do_decode(const void *obj) {
|
||||
int ret;
|
||||
AVFrame *tmp_frame = NULL;
|
||||
bool decoded = 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) < ENCODE_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 (hwaccel_) {
|
||||
if (!frame_->hw_frames_ctx) {
|
||||
LOG_ERROR(std::string("hw_frames_ctx is NULL"));
|
||||
goto _exit;
|
||||
}
|
||||
if ((ret = av_hwframe_transfer_data(sw_frame_, frame_, 0)) < 0) {
|
||||
LOG_ERROR(std::string("av_hwframe_transfer_data failed, ret = ") +
|
||||
av_err2str(ret));
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
tmp_frame = sw_frame_;
|
||||
} else {
|
||||
tmp_frame = frame_;
|
||||
}
|
||||
decoded = true;
|
||||
#ifdef CFG_PKG_TRACE
|
||||
out_++;
|
||||
LOG_DEBUG(std::string("delay DO: in:") + in_ + " out:" + out_);
|
||||
#endif
|
||||
#if FF_API_FRAME_KEY
|
||||
int key_frame = frame_->flags & AV_FRAME_FLAG_KEY;
|
||||
#else
|
||||
int key_frame = frame_->key_frame;
|
||||
#endif
|
||||
|
||||
callback_(obj, tmp_frame->width, tmp_frame->height,
|
||||
(AVPixelFormat)tmp_frame->format, tmp_frame->linesize,
|
||||
tmp_frame->data, key_frame);
|
||||
}
|
||||
_exit:
|
||||
av_packet_unref(pkt_);
|
||||
return decoded ? 0 : -1;
|
||||
}
|
||||
|
||||
bool check_support() {
|
||||
#ifdef _WIN32
|
||||
if (device_type_ == AV_HWDEVICE_TYPE_D3D11VA) {
|
||||
if (!c_->hw_device_ctx) {
|
||||
LOG_ERROR(std::string("hw_device_ctx is NULL"));
|
||||
return false;
|
||||
}
|
||||
AVHWDeviceContext *deviceContext =
|
||||
(AVHWDeviceContext *)hw_device_ctx_->data;
|
||||
if (!deviceContext) {
|
||||
LOG_ERROR(std::string("deviceContext is NULL"));
|
||||
return false;
|
||||
}
|
||||
AVD3D11VADeviceContext *d3d11vaDeviceContext =
|
||||
(AVD3D11VADeviceContext *)deviceContext->hwctx;
|
||||
if (!d3d11vaDeviceContext) {
|
||||
LOG_ERROR(std::string("d3d11vaDeviceContext is NULL"));
|
||||
return false;
|
||||
}
|
||||
ID3D11Device *device = d3d11vaDeviceContext->device;
|
||||
if (!device) {
|
||||
LOG_ERROR(std::string("device is NULL"));
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<NativeDevice> native_ = std::make_unique<NativeDevice>();
|
||||
if (!native_) {
|
||||
LOG_ERROR(std::string("Failed to create native device"));
|
||||
return false;
|
||||
}
|
||||
if (!native_->Init(0, (ID3D11Device *)device, 0)) {
|
||||
LOG_ERROR(std::string("Failed to init native device"));
|
||||
return false;
|
||||
}
|
||||
if (!native_->support_decode(data_format_)) {
|
||||
LOG_ERROR(std::string("Failed to check support ") + name_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" void ffmpeg_ram_free_decoder(FFmpegRamDecoder *decoder) {
|
||||
try {
|
||||
if (!decoder)
|
||||
return;
|
||||
decoder->free_decoder();
|
||||
delete decoder;
|
||||
decoder = NULL;
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("ffmpeg_ram_free_decoder exception:") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" FFmpegRamDecoder *
|
||||
ffmpeg_ram_new_decoder(const char *name, int device_type, int thread_count,
|
||||
RamDecodeCallback callback) {
|
||||
FFmpegRamDecoder *decoder = NULL;
|
||||
try {
|
||||
decoder = new FFmpegRamDecoder(name, device_type, thread_count, callback);
|
||||
if (decoder) {
|
||||
if (decoder->reset() == 0) {
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
LOG_ERROR(std::string("new decoder exception:") + e.what());
|
||||
}
|
||||
if (decoder) {
|
||||
decoder->free_decoder();
|
||||
delete decoder;
|
||||
decoder = NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C" int ffmpeg_ram_decode(FFmpegRamDecoder *decoder, const uint8_t *data,
|
||||
int length, const void *obj) {
|
||||
try {
|
||||
int ret = decoder->decode(data, length, obj);
|
||||
if (DataFormat::H265 == decoder->data_format_ && 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;
|
||||
}
|
||||
490
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp
Normal file
490
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_video.c
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/log.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define LOG_MODULE "FFMPEG_RAM_ENC"
|
||||
#include <log.h>
|
||||
#include <util.h>
|
||||
#ifdef _WIN32
|
||||
#include "win.h"
|
||||
#endif
|
||||
|
||||
static int calculate_offset_length(int pix_fmt, int height, const int *linesize,
|
||||
int *offset, int *length) {
|
||||
switch (pix_fmt) {
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
offset[0] = linesize[0] * height;
|
||||
offset[1] = offset[0] + linesize[1] * height / 2;
|
||||
*length = offset[1] + linesize[2] * height / 2;
|
||||
break;
|
||||
case AV_PIX_FMT_NV12:
|
||||
offset[0] = linesize[0] * height;
|
||||
*length = offset[0] + linesize[1] * height / 2;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(std::string("unsupported pixfmt") + std::to_string(pix_fmt));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int ffmpeg_ram_get_linesize_offset_length(int pix_fmt, int width,
|
||||
int height, int align,
|
||||
int *linesize, int *offset,
|
||||
int *length) {
|
||||
AVFrame *frame = NULL;
|
||||
int ioffset[AV_NUM_DATA_POINTERS] = {0};
|
||||
int ilength = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (!(frame = av_frame_alloc())) {
|
||||
LOG_ERROR(std::string("Alloc frame failed"));
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
frame->format = pix_fmt;
|
||||
frame->width = width;
|
||||
frame->height = height;
|
||||
|
||||
if ((ret = av_frame_get_buffer(frame, align)) < 0) {
|
||||
LOG_ERROR(std::string("av_frame_get_buffer, ret = ") + av_err2str(ret));
|
||||
goto _exit;
|
||||
}
|
||||
if (linesize) {
|
||||
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++)
|
||||
linesize[i] = frame->linesize[i];
|
||||
}
|
||||
if (offset || length) {
|
||||
ret = calculate_offset_length(pix_fmt, height, frame->linesize, ioffset,
|
||||
&ilength);
|
||||
if (ret < 0)
|
||||
goto _exit;
|
||||
}
|
||||
if (offset) {
|
||||
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
||||
if (ioffset[i] == 0)
|
||||
break;
|
||||
offset[i] = ioffset[i];
|
||||
}
|
||||
}
|
||||
if (length)
|
||||
*length = ilength;
|
||||
|
||||
ret = 0;
|
||||
_exit:
|
||||
if (frame)
|
||||
av_frame_free(&frame);
|
||||
return ret;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef void (*RamEncodeCallback)(const uint8_t *data, int len, int64_t pts,
|
||||
int key, const void *obj);
|
||||
|
||||
class FFmpegRamEncoder {
|
||||
public:
|
||||
AVCodecContext *c_ = NULL;
|
||||
AVFrame *frame_ = NULL;
|
||||
AVPacket *pkt_ = NULL;
|
||||
std::string name_;
|
||||
std::string mc_name_; // for mediacodec
|
||||
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
AVPixelFormat pixfmt_ = AV_PIX_FMT_NV12;
|
||||
int align_ = 0;
|
||||
int rc_ = 0;
|
||||
int quality_ = 0;
|
||||
int kbs_ = 0;
|
||||
int q_ = 0;
|
||||
int fps_ = 30;
|
||||
int gop_ = 0xFFFF;
|
||||
int thread_count_ = 1;
|
||||
int gpu_ = 0;
|
||||
RamEncodeCallback callback_ = NULL;
|
||||
int offset_[AV_NUM_DATA_POINTERS] = {0};
|
||||
bool force_keyframe_ = false; // Force next frame to be a keyframe
|
||||
|
||||
AVHWDeviceType hw_device_type_ = AV_HWDEVICE_TYPE_NONE;
|
||||
AVPixelFormat hw_pixfmt_ = AV_PIX_FMT_NONE;
|
||||
AVBufferRef *hw_device_ctx_ = NULL;
|
||||
AVFrame *hw_frame_ = NULL;
|
||||
|
||||
FFmpegRamEncoder(const char *name, const char *mc_name, int width, int height,
|
||||
int pixfmt, int align, int fps, int gop, int rc, int quality,
|
||||
int kbs, int q, int thread_count, int gpu,
|
||||
RamEncodeCallback callback) {
|
||||
name_ = name;
|
||||
mc_name_ = mc_name ? mc_name : "";
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
pixfmt_ = (AVPixelFormat)pixfmt;
|
||||
align_ = align;
|
||||
fps_ = fps;
|
||||
gop_ = gop;
|
||||
rc_ = rc;
|
||||
quality_ = quality;
|
||||
kbs_ = kbs;
|
||||
q_ = q;
|
||||
thread_count_ = thread_count;
|
||||
gpu_ = gpu;
|
||||
callback_ = callback;
|
||||
if (name_.find("vaapi") != std::string::npos) {
|
||||
hw_device_type_ = AV_HWDEVICE_TYPE_VAAPI;
|
||||
hw_pixfmt_ = AV_PIX_FMT_VAAPI;
|
||||
} else if (name_.find("nvenc") != std::string::npos) {
|
||||
#ifdef _WIN32
|
||||
hw_device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
|
||||
hw_pixfmt_ = AV_PIX_FMT_D3D11;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
~FFmpegRamEncoder() {}
|
||||
|
||||
void request_keyframe() {
|
||||
force_keyframe_ = true;
|
||||
}
|
||||
|
||||
bool init(int *linesize, int *offset, int *length) {
|
||||
const AVCodec *codec = NULL;
|
||||
|
||||
int ret;
|
||||
|
||||
if (!(codec = avcodec_find_encoder_by_name(name_.c_str()))) {
|
||||
LOG_ERROR(std::string("Codec ") + name_ + " not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(c_ = avcodec_alloc_context3(codec))) {
|
||||
LOG_ERROR(std::string("Could not allocate video codec context"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hw_device_type_ != AV_HWDEVICE_TYPE_NONE) {
|
||||
std::string device = "";
|
||||
#ifdef _WIN32
|
||||
if (name_.find("nvenc") != std::string::npos) {
|
||||
int index = Adapters::GetFirstAdapterIndex(
|
||||
AdapterVendor::ADAPTER_VENDOR_NVIDIA);
|
||||
if (index >= 0) {
|
||||
device = std::to_string(index);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ret = av_hwdevice_ctx_create(&hw_device_ctx_, hw_device_type_,
|
||||
device.length() == 0 ? NULL : device.c_str(),
|
||||
NULL, 0);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
|
||||
return false;
|
||||
}
|
||||
if (set_hwframe_ctx() != 0) {
|
||||
LOG_ERROR(std::string("set_hwframe_ctx failed"));
|
||||
return false;
|
||||
}
|
||||
hw_frame_ = av_frame_alloc();
|
||||
if (!hw_frame_) {
|
||||
LOG_ERROR(std::string("av_frame_alloc failed"));
|
||||
return false;
|
||||
}
|
||||
if ((ret = av_hwframe_get_buffer(c_->hw_frames_ctx, hw_frame_, 0)) < 0) {
|
||||
LOG_ERROR(std::string("av_hwframe_get_buffer failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
if (!hw_frame_->hw_frames_ctx) {
|
||||
LOG_ERROR(std::string("hw_frame_->hw_frames_ctx is NULL"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(frame_ = av_frame_alloc())) {
|
||||
LOG_ERROR(std::string("Could not allocate video frame"));
|
||||
return false;
|
||||
}
|
||||
frame_->format = pixfmt_;
|
||||
frame_->width = width_;
|
||||
frame_->height = height_;
|
||||
|
||||
if ((ret = av_frame_get_buffer(frame_, align_)) < 0) {
|
||||
LOG_ERROR(std::string("av_frame_get_buffer failed, ret = ") + av_err2str(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(pkt_ = av_packet_alloc())) {
|
||||
LOG_ERROR(std::string("Could not allocate video packet"));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* resolution must be a multiple of two */
|
||||
c_->width = width_;
|
||||
c_->height = height_;
|
||||
c_->pix_fmt =
|
||||
hw_pixfmt_ != AV_PIX_FMT_NONE ? hw_pixfmt_ : (AVPixelFormat)pixfmt_;
|
||||
c_->sw_pix_fmt = (AVPixelFormat)pixfmt_;
|
||||
util_encode::set_av_codec_ctx(c_, name_, kbs_, gop_, fps_);
|
||||
if (!util_encode::set_lantency_free(c_->priv_data, name_)) {
|
||||
LOG_ERROR(std::string("set_lantency_free failed, name: ") + name_);
|
||||
return false;
|
||||
}
|
||||
// util_encode::set_quality(c_->priv_data, name_, quality_);
|
||||
util_encode::set_rate_control(c_, name_, rc_, q_);
|
||||
util_encode::set_gpu(c_->priv_data, name_, gpu_);
|
||||
util_encode::force_hw(c_->priv_data, name_);
|
||||
util_encode::set_others(c_->priv_data, name_);
|
||||
if (name_.find("mediacodec") != std::string::npos) {
|
||||
if (mc_name_.length() > 0) {
|
||||
LOG_INFO(std::string("mediacodec codec_name: ") + mc_name_);
|
||||
if ((ret = av_opt_set(c_->priv_data, "codec_name", mc_name_.c_str(),
|
||||
0)) < 0) {
|
||||
LOG_ERROR(std::string("mediacodec codec_name failed, ret = ") + av_err2str(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = avcodec_open2(c_, codec, NULL)) < 0) {
|
||||
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
|
||||
", name: " + name_);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ffmpeg_ram_get_linesize_offset_length(pixfmt_, width_, height_, align_,
|
||||
NULL, offset_, length) != 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
||||
linesize[i] = frame_->linesize[i];
|
||||
offset[i] = offset_[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int encode(const uint8_t *data, int length, const void *obj, uint64_t ms) {
|
||||
int ret;
|
||||
|
||||
if ((ret = av_frame_make_writable(frame_)) != 0) {
|
||||
LOG_ERROR(std::string("av_frame_make_writable failed, ret = ") + av_err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
if ((ret = fill_frame(frame_, (uint8_t *)data, length, offset_)) != 0)
|
||||
return ret;
|
||||
AVFrame *tmp_frame;
|
||||
if (hw_device_type_ != AV_HWDEVICE_TYPE_NONE) {
|
||||
if ((ret = av_hwframe_transfer_data(hw_frame_, frame_, 0)) < 0) {
|
||||
LOG_ERROR(std::string("av_hwframe_transfer_data failed, ret = ") + av_err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
tmp_frame = hw_frame_;
|
||||
} else {
|
||||
tmp_frame = frame_;
|
||||
}
|
||||
|
||||
return do_encode(tmp_frame, obj, ms);
|
||||
}
|
||||
|
||||
void free_encoder() {
|
||||
if (pkt_)
|
||||
av_packet_free(&pkt_);
|
||||
if (frame_)
|
||||
av_frame_free(&frame_);
|
||||
if (hw_frame_)
|
||||
av_frame_free(&hw_frame_);
|
||||
if (hw_device_ctx_)
|
||||
av_buffer_unref(&hw_device_ctx_);
|
||||
if (c_)
|
||||
avcodec_free_context(&c_);
|
||||
}
|
||||
|
||||
int set_bitrate(int kbs) {
|
||||
return util_encode::change_bit_rate(c_, name_, kbs) ? 0 : -1;
|
||||
}
|
||||
|
||||
private:
|
||||
int set_hwframe_ctx() {
|
||||
AVBufferRef *hw_frames_ref;
|
||||
AVHWFramesContext *frames_ctx = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx_))) {
|
||||
LOG_ERROR(std::string("av_hwframe_ctx_alloc failed"));
|
||||
return -1;
|
||||
}
|
||||
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
|
||||
frames_ctx->format = hw_pixfmt_;
|
||||
frames_ctx->sw_format = (AVPixelFormat)pixfmt_;
|
||||
frames_ctx->width = width_;
|
||||
frames_ctx->height = height_;
|
||||
frames_ctx->initial_pool_size = 1;
|
||||
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
|
||||
av_buffer_unref(&hw_frames_ref);
|
||||
return err;
|
||||
}
|
||||
c_->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
|
||||
if (!c_->hw_frames_ctx) {
|
||||
LOG_ERROR(std::string("av_buffer_ref failed"));
|
||||
err = -1;
|
||||
}
|
||||
av_buffer_unref(&hw_frames_ref);
|
||||
return err;
|
||||
}
|
||||
|
||||
int do_encode(AVFrame *frame, const void *obj, int64_t ms) {
|
||||
int ret;
|
||||
bool encoded = false;
|
||||
frame->pts = ms;
|
||||
|
||||
// Force keyframe if requested
|
||||
if (force_keyframe_) {
|
||||
frame->pict_type = AV_PICTURE_TYPE_I;
|
||||
force_keyframe_ = false;
|
||||
} else {
|
||||
frame->pict_type = AV_PICTURE_TYPE_NONE;
|
||||
}
|
||||
|
||||
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) < DECODE_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;
|
||||
callback_(pkt_->data, pkt_->size, pkt_->pts,
|
||||
pkt_->flags & AV_PKT_FLAG_KEY, obj);
|
||||
}
|
||||
_exit:
|
||||
av_packet_unref(pkt_);
|
||||
return encoded ? 0 : -1;
|
||||
}
|
||||
|
||||
int fill_frame(AVFrame *frame, uint8_t *data, int data_length,
|
||||
const int *const offset) {
|
||||
switch (frame->format) {
|
||||
case AV_PIX_FMT_NV12:
|
||||
if (data_length <
|
||||
frame->height * (frame->linesize[0] + frame->linesize[1] / 2)) {
|
||||
LOG_ERROR(std::string("fill_frame: NV12 data length error. data_length:") +
|
||||
std::to_string(data_length) +
|
||||
", linesize[0]:" + std::to_string(frame->linesize[0]) +
|
||||
", linesize[1]:" + std::to_string(frame->linesize[1]));
|
||||
return -1;
|
||||
}
|
||||
frame->data[0] = data;
|
||||
frame->data[1] = data + offset[0];
|
||||
break;
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
if (data_length <
|
||||
frame->height * (frame->linesize[0] + frame->linesize[1] / 2 +
|
||||
frame->linesize[2] / 2)) {
|
||||
LOG_ERROR(std::string("fill_frame: 420P data length error. data_length:") +
|
||||
std::to_string(data_length) +
|
||||
", linesize[0]:" + std::to_string(frame->linesize[0]) +
|
||||
", linesize[1]:" + std::to_string(frame->linesize[1]) +
|
||||
", linesize[2]:" + std::to_string(frame->linesize[2]));
|
||||
return -1;
|
||||
}
|
||||
frame->data[0] = data;
|
||||
frame->data[1] = data + offset[0];
|
||||
frame->data[2] = data + offset[1];
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(std::string("fill_frame: unsupported format, ") +
|
||||
std::to_string(frame->format));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" FFmpegRamEncoder *
|
||||
ffmpeg_ram_new_encoder(const char *name, const char *mc_name, int width,
|
||||
int height, int pixfmt, int align, int fps, int gop,
|
||||
int rc, int quality, int kbs, int q, int thread_count,
|
||||
int gpu, int *linesize, int *offset, int *length,
|
||||
RamEncodeCallback callback) {
|
||||
FFmpegRamEncoder *encoder = NULL;
|
||||
try {
|
||||
encoder = new FFmpegRamEncoder(name, mc_name, width, height, pixfmt, align,
|
||||
fps, gop, rc, quality, kbs, q, thread_count,
|
||||
gpu, callback);
|
||||
if (encoder) {
|
||||
if (encoder->init(linesize, offset, length)) {
|
||||
return encoder;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("new FFmpegRamEncoder failed, ") + std::string(e.what()));
|
||||
}
|
||||
if (encoder) {
|
||||
encoder->free_encoder();
|
||||
delete encoder;
|
||||
encoder = NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C" int ffmpeg_ram_encode(FFmpegRamEncoder *encoder, const uint8_t *data,
|
||||
int length, const void *obj, uint64_t ms) {
|
||||
try {
|
||||
return encoder->encode(data, length, obj, ms);
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("ffmpeg_ram_encode failed, ") + std::string(e.what()));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" void ffmpeg_ram_free_encoder(FFmpegRamEncoder *encoder) {
|
||||
try {
|
||||
if (!encoder)
|
||||
return;
|
||||
encoder->free_encoder();
|
||||
delete encoder;
|
||||
encoder = NULL;
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("free encoder failed, ") + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int ffmpeg_ram_set_bitrate(FFmpegRamEncoder *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;
|
||||
}
|
||||
|
||||
extern "C" void ffmpeg_ram_request_keyframe(FFmpegRamEncoder *encoder) {
|
||||
try {
|
||||
if (encoder) {
|
||||
encoder->request_keyframe();
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("ffmpeg_ram_request_keyframe failed, ") + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
35
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_ffi.h
Normal file
35
libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_ffi.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef FFMPEG_RAM_FFI_H
|
||||
#define FFMPEG_RAM_FFI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define AV_NUM_DATA_POINTERS 8
|
||||
|
||||
typedef void (*RamDecodeCallback)(const void *obj, int width, int height,
|
||||
int pixfmt,
|
||||
int linesize[AV_NUM_DATA_POINTERS],
|
||||
uint8_t *data[AV_NUM_DATA_POINTERS], int key);
|
||||
typedef void (*RamEncodeCallback)(const uint8_t *data, int len, int64_t pts,
|
||||
int key, const void *obj);
|
||||
|
||||
void *ffmpeg_ram_new_encoder(const char *name, const char *mc_name, int width,
|
||||
int height, int pixfmt, int align, int fps,
|
||||
int gop, int rc, int quality, int kbs, int q,
|
||||
int thread_count, int gpu, int *linesize,
|
||||
int *offset, int *length,
|
||||
RamEncodeCallback callback);
|
||||
void *ffmpeg_ram_new_decoder(const char *name, int device_type,
|
||||
int thread_count, RamDecodeCallback callback);
|
||||
int ffmpeg_ram_encode(void *encoder, const uint8_t *data, int length,
|
||||
const void *obj, int64_t ms);
|
||||
int ffmpeg_ram_decode(void *decoder, const uint8_t *data, int length,
|
||||
const void *obj);
|
||||
void ffmpeg_ram_free_encoder(void *encoder);
|
||||
void ffmpeg_ram_free_decoder(void *decoder);
|
||||
int ffmpeg_ram_get_linesize_offset_length(int pix_fmt, int width, int height,
|
||||
int align, int *linesize, int *offset,
|
||||
int *length);
|
||||
int ffmpeg_ram_set_bitrate(void *encoder, int kbs);
|
||||
void ffmpeg_ram_request_keyframe(void *encoder);
|
||||
|
||||
#endif // FFMPEG_RAM_FFI_H
|
||||
410
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_decode.cpp
Normal file
410
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_decode.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
// 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;
|
||||
}
|
||||
558
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_encode.cpp
Normal file
558
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_encode.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
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"
|
||||
32
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_ffi.h
Normal file
32
libs/hwcodec/cpp/ffmpeg_vram/ffmpeg_vram_ffi.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#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
|
||||
481
libs/hwcodec/cpp/mfx/mfx_decode.cpp
Normal file
481
libs/hwcodec/cpp/mfx/mfx_decode.cpp
Normal file
@@ -0,0 +1,481 @@
|
||||
#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"
|
||||
709
libs/hwcodec/cpp/mfx/mfx_encode.cpp
Normal file
709
libs/hwcodec/cpp/mfx/mfx_encode.cpp
Normal file
@@ -0,0 +1,709 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
39
libs/hwcodec/cpp/mfx/mfx_ffi.h
Normal file
39
libs/hwcodec/cpp/mfx/mfx_ffi.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#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
|
||||
188
libs/hwcodec/cpp/mux/mux.cpp
Normal file
188
libs/hwcodec/cpp/mux/mux.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
15
libs/hwcodec/cpp/mux/mux_ffi.h
Normal file
15
libs/hwcodec/cpp/mux/mux_ffi.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#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
|
||||
693
libs/hwcodec/cpp/nv/nv_decode.cpp
Normal file
693
libs/hwcodec/cpp/nv/nv_decode.cpp
Normal file
@@ -0,0 +1,693 @@
|
||||
#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"
|
||||
464
libs/hwcodec/cpp/nv/nv_encode.cpp
Normal file
464
libs/hwcodec/cpp/nv/nv_encode.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
#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(¶ms)) { \
|
||||
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"
|
||||
40
libs/hwcodec/cpp/nv/nv_ffi.h
Normal file
40
libs/hwcodec/cpp/nv/nv_ffi.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user