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:
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
|
||||
Reference in New Issue
Block a user