Use VAAPI for hardware encoding on Linux

This commit is contained in:
loki
2021-05-29 16:25:37 +02:00
parent fe5375f17b
commit ff1ea1a63e
5 changed files with 223 additions and 101 deletions
+4 -4
View File
@@ -67,8 +67,8 @@ constexpr std::uint8_t map_surround71[] {
}; };
} // namespace speaker } // namespace speaker
enum class dev_type_e { enum class mem_type_e {
none, system,
dxgi, dxgi,
unknown unknown
}; };
@@ -155,7 +155,7 @@ struct sink_t {
struct hwdevice_t { struct hwdevice_t {
void *data {}; void *data {};
platf::img_t *img {}; void *img {};
virtual int convert(platf::img_t &img) { virtual int convert(platf::img_t &img) {
return -1; return -1;
@@ -221,7 +221,7 @@ std::string from_sockaddr(const sockaddr *const);
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const); std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const);
std::unique_ptr<audio_control_t> audio_control(); std::unique_ptr<audio_control_t> audio_control();
std::shared_ptr<display_t> display(dev_type_e hwdevice_type); std::shared_ptr<display_t> display(mem_type_e hwdevice_type);
input_t input(); input_t input();
void move_mouse(input_t &input, int deltaX, int deltaY); void move_mouse(input_t &input, int deltaX, int deltaY);
+2 -2
View File
@@ -355,8 +355,8 @@ struct shm_attr_t : public x11_attr_t {
} }
}; };
std::shared_ptr<display_t> display(platf::dev_type_e hwdevice_type) { std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type) {
if(hwdevice_type != platf::dev_type_e::none) { if(hwdevice_type != platf::mem_type_e::system) {
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
return nullptr; return nullptr;
} }
+3 -3
View File
@@ -417,15 +417,15 @@ const char *format_str[] = {
} // namespace platf::dxgi } // namespace platf::dxgi
namespace platf { namespace platf {
std::shared_ptr<display_t> display(dev_type_e hwdevice_type) { std::shared_ptr<display_t> display(mem_type_e hwdevice_type) {
if(hwdevice_type == dev_type_e::dxgi) { if(hwdevice_type == mem_type_e::dxgi) {
auto disp = std::make_shared<dxgi::display_vram_t>(); auto disp = std::make_shared<dxgi::display_vram_t>();
if(!disp->init()) { if(!disp->init()) {
return disp; return disp;
} }
} }
else if(hwdevice_type == dev_type_e::none) { else if(hwdevice_type == mem_type_e::system) {
auto disp = std::make_shared<dxgi::display_ram_t>(); auto disp = std::make_shared<dxgi::display_ram_t>();
if(!disp->init()) { if(!disp->init()) {
+1
View File
@@ -619,6 +619,7 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
// make sure moonlight recognizes the nalu code for IDR frames // make sure moonlight recognizes the nalu code for IDR frames
if(packet->flags & AV_PKT_FLAG_KEY) { if(packet->flags & AV_PKT_FLAG_KEY) {
BOOST_LOG(debug) << "Sending IDR frame"sv;
// TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix // TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix
std::string_view frame_old = "\000\000\001e"sv; std::string_view frame_old = "\000\000\001e"sv;
std::string_view frame_new = "\000\000\000\001e"sv; std::string_view frame_new = "\000\000\000\001e"sv;
+212 -91
View File
@@ -60,20 +60,21 @@ using buffer_t = util::safe_ptr<AVBufferRef, free_buffer>;
using sws_t = util::safe_ptr<SwsContext, sws_freeContext>; using sws_t = util::safe_ptr<SwsContext, sws_freeContext>;
using img_event_t = std::shared_ptr<safe::event_t<std::shared_ptr<platf::img_t>>>; using img_event_t = std::shared_ptr<safe::event_t<std::shared_ptr<platf::img_t>>>;
platf::dev_type_e map_dev_type(AVHWDeviceType type); platf::mem_type_e map_dev_type(AVHWDeviceType type);
platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt); platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt);
void sw_img_to_frame(const platf::img_t &img, frame_t &frame); int sw_img_to_frame(const void *img, frame_t &frame);
void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame); int vaapi_img_to_frame(const void *img, frame_t &frame);
int dxgi_img_to_frame(const void *img, frame_t &frame);
util::Either<buffer_t, int> dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); util::Either<buffer_t, int> dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx);
util::Either<buffer_t, int> vaapi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx);
util::Either<buffer_t, int> make_hwdevice_ctx(AVHWDeviceType type, void *hwdevice_ctx);
int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format); int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format);
class swdevice_t : public platf::hwdevice_t { class swdevice_t : public platf::hwdevice_t {
public: public:
int convert(platf::img_t &img) override { int convert(platf::img_t &img) override {
auto frame = (AVFrame *)data; auto frame = (AVFrame *)this->img;
av_frame_make_writable(frame); av_frame_make_writable(frame);
@@ -81,12 +82,19 @@ public:
img.row_pitch, 0 img.row_pitch, 0
}; };
std::uint8_t *const data[] {
frame->data[0] + offset, std::uint8_t *data[4];
frame->data[1] + offset / 2,
frame->data[2] + offset / 2, data[0] = frame->data[0] + offset;
0 if(frame->format == AV_PIX_FMT_NV12) {
}; data[1] = frame->data[1] + offset;
data[2] = nullptr;
}
else {
data[1] = frame->data[1] + offset / 2;
data[2] = frame->data[2] + offset / 2;
data[3] = nullptr;
}
int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, data, frame->linesize); int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, data, frame->linesize);
if(ret <= 0) { if(ret <= 0) {
@@ -108,14 +116,16 @@ public:
/** /**
* When preserving aspect ratio, ensure that padding is black * When preserving aspect ratio, ensure that padding is black
*/ */
int prefill(AVFrame *frame, AVPixelFormat format) { int prefill() {
auto frame = (frame_t::pointer)img;
auto width = frame->width; auto width = frame->width;
auto height = frame->height; auto height = frame->height;
sws_t sws { sws_t sws {
sws_getContext( sws_getContext(
width, height, AV_PIX_FMT_BGR0, width, height, AV_PIX_FMT_BGR0,
width, height, format, width, height, (AVPixelFormat)frame->format,
SWS_LANCZOS | SWS_ACCURATE_RND, SWS_LANCZOS | SWS_ACCURATE_RND,
nullptr, nullptr, nullptr) nullptr, nullptr, nullptr)
}; };
@@ -145,7 +155,27 @@ public:
} }
int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format) { int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format) {
if(prefill(frame, format)) { // If the device used is hardware, yet the image resides on main memory
if(frame->hw_frames_ctx) {
if(av_hwframe_get_buffer(frame->hw_frames_ctx, frame, 0)) {
return -1;
}
img = av_frame_alloc();
auto sw_frame = (frame_t::pointer)img;
sw_frame->width = frame->width;
sw_frame->height = frame->height;
sw_frame->format = format;
}
else {
img = frame;
}
av_frame_get_buffer((frame_t::pointer)img, 0);
if(prefill()) {
return -1; return -1;
} }
@@ -167,12 +197,15 @@ public:
out_width, out_height, format, out_width, out_height, format,
SWS_LANCZOS | SWS_ACCURATE_RND, SWS_LANCZOS | SWS_ACCURATE_RND,
nullptr, nullptr, nullptr)); nullptr, nullptr, nullptr));
data = frame;
return sws ? 0 : -1; return sws ? 0 : -1;
} }
~swdevice_t() override {} ~swdevice_t() override {
if(img) {
av_frame_unref((frame_t::pointer)img);
}
}
sws_t sws; sws_t sws;
@@ -180,12 +213,20 @@ public:
int offset; int offset;
}; };
enum flag_e {
DEFAULT = 0x00,
SYSTEM_MEMORY = 0x01,
H264_ONLY = 0x02,
LIMITED_GOP_SIZE = 0x04,
};
struct encoder_t { struct encoder_t {
std::string_view name; std::string_view name;
enum flag_e { enum flag_e {
PASSED, // Is supported PASSED, // Is supported
REF_FRAMES_RESTRICT, // Set maximum reference frames REF_FRAMES_RESTRICT, // Set maximum reference frames
REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT) REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT)
SLICE, // Allow frame to be partitioned into multiple slices
DYNAMIC_RANGE, DYNAMIC_RANGE,
MAX_FLAGS MAX_FLAGS
}; };
@@ -228,10 +269,9 @@ struct encoder_t {
} }
} hevc, h264; } hevc, h264;
bool system_memory; int flags;
bool hevc_mode;
std::function<void(const platf::img_t &, frame_t &)> img_to_frame; std::function<int(const void *, frame_t &)> img_to_frame;
std::function<util::Either<buffer_t, int>(platf::hwdevice_t *hwdevice)> make_hwdevice_ctx; std::function<util::Either<buffer_t, int>(platf::hwdevice_t *hwdevice)> make_hwdevice_ctx;
}; };
@@ -316,23 +356,29 @@ static encoder_t nvenc {
AV_PIX_FMT_D3D11, AV_PIX_FMT_D3D11,
AV_PIX_FMT_NV12, AV_PIX_FMT_P010, AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
{ {
{ { "forced-idr"s, 1 }, {
{ "forced-idr"s, 1 },
{ "zerolatency"s, 1 }, { "zerolatency"s, 1 },
{ "preset"s, &config::video.nv.preset }, { "preset"s, &config::video.nv.preset },
{ "rc"s, &config::video.nv.rc } }, { "rc"s, &config::video.nv.rc },
},
std::nullopt, std::nullopt,
std::nullopt, std::nullopt,
"hevc_nvenc"s, "hevc_nvenc"s,
}, },
{ { { "forced-idr"s, 1 }, {
{
{ "forced-idr"s, 1 },
{ "zerolatency"s, 1 }, { "zerolatency"s, 1 },
{ "preset"s, &config::video.nv.preset }, { "preset"s, &config::video.nv.preset },
{ "rc"s, &config::video.nv.rc }, { "rc"s, &config::video.nv.rc },
{ "coder"s, &config::video.nv.coder } }, { "coder"s, &config::video.nv.coder },
std::nullopt, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }), },
"h264_nvenc"s }, std::nullopt,
false, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
true, "h264_nvenc"s,
},
DEFAULT,
dxgi_img_to_frame, dxgi_img_to_frame,
dxgi_make_hwdevice_ctx dxgi_make_hwdevice_ctx
@@ -345,25 +391,29 @@ static encoder_t amdvce {
AV_PIX_FMT_D3D11, AV_PIX_FMT_D3D11,
AV_PIX_FMT_NV12, AV_PIX_FMT_P010, AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
{ {
{ { "header_insertion_mode"s, "idr"s }, {
{ "header_insertion_mode"s, "idr"s },
{ "gops_per_idr"s, 30 }, { "gops_per_idr"s, 30 },
{ "usage"s, "ultralowlatency"s }, { "usage"s, "ultralowlatency"s },
{ "quality"s, &config::video.amd.quality }, { "quality"s, &config::video.amd.quality },
{ "rc"s, &config::video.amd.rc } }, { "rc"s, &config::video.amd.rc },
},
std::nullopt, std::nullopt,
std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }), std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
"hevc_amf"s, "hevc_amf"s,
}, },
{ { {
{
{ "usage"s, "ultralowlatency"s }, { "usage"s, "ultralowlatency"s },
{ "quality"s, &config::video.amd.quality }, { "quality"s, &config::video.amd.quality },
{ "rc"s, &config::video.amd.rc }, { "rc"s, &config::video.amd.rc },
{ "log_to_dbg"s, "1"s }, { "log_to_dbg"s, "1"s },
}, },
std::nullopt, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }), std::nullopt,
"h264_amf"s }, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
false, "h264_amf"s,
true, },
DEFAULT
dxgi_img_to_frame, dxgi_img_to_frame,
dxgi_make_hwdevice_ctx dxgi_make_hwdevice_ctx
@@ -376,7 +426,8 @@ static encoder_t software {
AV_HWDEVICE_TYPE_NONE, AV_HWDEVICE_TYPE_NONE,
AV_PIX_FMT_NONE, AV_PIX_FMT_NONE,
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10,
{ // x265's Info SEI is so long that it causes the IDR picture data to be {
// x265's Info SEI is so long that it causes the IDR picture data to be
// kicked to the 2nd packet in the frame, breaking Moonlight's parsing logic. // kicked to the 2nd packet in the frame, breaking Moonlight's parsing logic.
// It also looks like gop_size isn't passed on to x265, so we have to set // It also looks like gop_size isn't passed on to x265, so we have to set
// 'keyint=-1' in the parameters ourselves. // 'keyint=-1' in the parameters ourselves.
@@ -384,24 +435,68 @@ static encoder_t software {
{ "forced-idr"s, 1 }, { "forced-idr"s, 1 },
{ "x265-params"s, "info=0:keyint=-1"s }, { "x265-params"s, "info=0:keyint=-1"s },
{ "preset"s, &config::video.sw.preset }, { "preset"s, &config::video.sw.preset },
{ "tune"s, &config::video.sw.tune } }, { "tune"s, &config::video.sw.tune },
std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf), std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp), },
"libx265"s }, std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf),
{ { { "preset"s, &config::video.sw.preset }, std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
{ "tune"s, &config::video.sw.tune } }, "libx265"s,
std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf), std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp), },
"libx264"s }, {
true, {
false, { "preset"s, &config::video.sw.preset },
{ "tune"s, &config::video.sw.tune },
},
std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf),
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
"libx264"s,
},
H264_ONLY | SYSTEM_MEMORY,
sw_img_to_frame, sw_img_to_frame,
nullptr nullptr
}; };
#ifdef __linux__
static encoder_t vaapi {
"vaapi"sv,
{ FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10 },
AV_HWDEVICE_TYPE_VAAPI,
AV_PIX_FMT_VAAPI,
AV_PIX_FMT_NV12,
AV_PIX_FMT_YUV420P10,
{
{
{ "sei"s, 0 },
{ "idr_interval"s, std::numeric_limits<int>::max() },
},
std::nullopt,
std::nullopt,
"hevc_vaapi"s,
},
{
{
{ "sei"s, 0 },
{ "idr_interval"s, std::numeric_limits<int>::max() },
// { "quality"s, 10 },
},
std::nullopt,
std::nullopt,
"h264_vaapi"s,
},
LIMITED_GOP_SIZE | SYSTEM_MEMORY,
vaapi_img_to_frame,
vaapi_make_hwdevice_ctx
};
#endif
static std::vector<encoder_t> encoders { static std::vector<encoder_t> encoders {
#ifdef _WIN32 #ifdef _WIN32
nvenc, nvenc,
amdvce, amdvce,
#endif
#ifdef __linux__
vaapi,
#endif #endif
software software
}; };
@@ -626,8 +721,11 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
ctx->max_b_frames = 0; ctx->max_b_frames = 0;
// Use an infinite GOP length since I-frames are generated on demand // Use an infinite GOP length since I-frames are generated on demand
ctx->gop_size = std::numeric_limits<int>::max(); ctx->gop_size = encoder.flags & LIMITED_GOP_SIZE ?
ctx->keyint_min = ctx->gop_size; std::numeric_limits<std::int16_t>::max() :
std::numeric_limits<int>::max();
ctx->keyint_min = std::numeric_limits<int>::max();
if(config.numRefFrames == 0) { if(config.numRefFrames == 0) {
ctx->refs = video_format[encoder_t::REF_FRAMES_AUTOSELECT] ? 0 : 16; ctx->refs = video_format[encoder_t::REF_FRAMES_AUTOSELECT] ? 0 : 16;
@@ -707,6 +805,10 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
ctx->slices = std::max(config.slicesPerFrame, config::video.min_threads); ctx->slices = std::max(config.slicesPerFrame, config::video.min_threads);
} }
if(!video_format[encoder_t::SLICE]) {
ctx->slices = 1;
}
ctx->thread_type = FF_THREAD_SLICE; ctx->thread_type = FF_THREAD_SLICE;
ctx->thread_count = ctx->slices; ctx->thread_count = ctx->slices;
@@ -744,7 +846,15 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
return std::nullopt; return std::nullopt;
} }
avcodec_open2(ctx.get(), codec, &options); if(auto status = avcodec_open2(ctx.get(), codec, &options)) {
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
BOOST_LOG(error)
<< "Could not open codec ["sv
<< video_format.name << "]: "sv
<< av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status);
return std::nullopt;
}
frame_t frame { av_frame_alloc() }; frame_t frame { av_frame_alloc() };
frame->format = ctx->pix_fmt; frame->format = ctx->pix_fmt;
@@ -755,9 +865,6 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
if(hardware) { if(hardware) {
frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx); frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx);
} }
else /* software */ {
av_frame_get_buffer(frame.get(), 0);
}
util::wrap_ptr<platf::hwdevice_t> device; util::wrap_ptr<platf::hwdevice_t> device;
@@ -778,7 +885,8 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
return std::make_optional(session_t { return std::make_optional(session_t {
std::move(ctx), std::move(ctx),
std::move(frame), std::move(frame),
std::move(device) }); std::move(device),
});
} }
void encode_run( void encode_run(
@@ -832,7 +940,9 @@ void encode_run(
if(auto img = images->pop(delay)) { if(auto img = images->pop(delay)) {
session->device->convert(*img); session->device->convert(*img);
encoder.img_to_frame(*session->device->img, session->frame); if(encoder.img_to_frame(session->device->img, session->frame)) {
return;
}
} }
else if(images->running()) { else if(images->running()) {
continue; continue;
@@ -1011,7 +1121,9 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
} }
pos->img_tmp = nullptr; pos->img_tmp = nullptr;
encoder.img_to_frame(*pos->hwdevice->img, pos->session.frame); if(encoder.img_to_frame(pos->hwdevice->img, pos->session.frame)) {
return encode_e::error;
}
} }
if(encode(ctx->frame_nr++, pos->session.ctx, pos->session.frame, ctx->packets, ctx->channel_data)) { if(encode(ctx->frame_nr++, pos->session.ctx, pos->session.frame, ctx->packets, ctx->channel_data)) {
@@ -1053,8 +1165,7 @@ void captureThreadSync() {
} }
}); });
while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) {}
;
} }
void capture_async( void capture_async(
@@ -1138,7 +1249,7 @@ void capture(
void *channel_data) { void *channel_data) {
idr_events->raise(std::make_pair(0, 1)); idr_events->raise(std::make_pair(0, 1));
if(encoders.front().system_memory) { if(encoders.front().flags & SYSTEM_MEMORY) {
capture_async(shutdown_event, packets, idr_events, config, channel_data); capture_async(shutdown_event, packets, idr_events, config, channel_data);
} }
else { else {
@@ -1177,7 +1288,9 @@ bool validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &e
return false; return false;
} }
encoder.img_to_frame(*hwdevice->img, session->frame); if(encoder.img_to_frame(session->device->img, session->frame)) {
return false;
}
session->frame->pict_type = AV_PICTURE_TYPE_I; session->frame->pict_type = AV_PICTURE_TYPE_I;
@@ -1198,11 +1311,13 @@ bool validate_encoder(encoder_t &encoder) {
}); });
auto force_hevc = config::video.hevc_mode >= 2; auto force_hevc = config::video.hevc_mode >= 2;
auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode); auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && !(encoder.flags & H264_ONLY));
encoder.h264.capabilities.set(); encoder.h264.capabilities.set();
encoder.hevc.capabilities.set(); encoder.hevc.capabilities.set();
encoder.hevc[encoder_t::PASSED] = test_hevc;
// First, test encoder viability // First, test encoder viability
config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 }; config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 };
config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 }; config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 };
@@ -1218,6 +1333,7 @@ bool validate_encoder(encoder_t &encoder) {
encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264; encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264;
encoder.h264[encoder_t::PASSED] = true; encoder.h264[encoder_t::PASSED] = true;
encoder.h264[encoder_t::SLICE] = validate_config(disp, encoder, config_max_ref_frames);
if(test_hevc) { if(test_hevc) {
config_max_ref_frames.videoFormat = 1; config_max_ref_frames.videoFormat = 1;
config_autoselect.videoFormat = 1; config_autoselect.videoFormat = 1;
@@ -1232,11 +1348,13 @@ bool validate_encoder(encoder_t &encoder) {
encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc; encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc;
encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc; encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc;
encoder.hevc[encoder_t::PASSED] = max_ref_frames_hevc || autoselect_hevc;
} }
encoder.hevc[encoder_t::PASSED] = test_hevc;
std::vector<std::pair<encoder_t::flag_e, config_t>> configs { std::vector<std::pair<encoder_t::flag_e, config_t>> configs {
{ encoder_t::DYNAMIC_RANGE, { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1 } } { encoder_t::DYNAMIC_RANGE, { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1 } },
{ encoder_t::SLICE, { 1920, 1080, 60, 1000, 2, 1, 1, 0, 0 } },
}; };
for(auto &[flag, config] : configs) { for(auto &[flag, config] : configs) {
auto h264 = config; auto h264 = config;
@@ -1246,7 +1364,7 @@ bool validate_encoder(encoder_t &encoder) {
hevc.videoFormat = 1; hevc.videoFormat = 1;
encoder.h264[flag] = validate_config(disp, encoder, h264); encoder.h264[flag] = validate_config(disp, encoder, h264);
if(test_hevc && encoder.hevc[encoder_t::PASSED]) { if(encoder.hevc[encoder_t::PASSED]) {
encoder.hevc[flag] = validate_config(disp, encoder, hevc); encoder.hevc[flag] = validate_config(disp, encoder, hevc);
} }
} }
@@ -1313,29 +1431,6 @@ int init() {
return 0; return 0;
} }
util::Either<buffer_t, int> make_hwdevice_ctx(AVHWDeviceType type, void *hwdevice) {
buffer_t ctx;
int err;
if(hwdevice) {
ctx.reset(av_hwdevice_ctx_alloc(type));
((AVHWDeviceContext *)ctx.get())->hwctx = hwdevice;
err = av_hwdevice_ctx_init(ctx.get());
}
else {
AVBufferRef *ref {};
err = av_hwdevice_ctx_create(&ref, type, nullptr, nullptr, 0);
ctx.reset(ref);
}
if(err < 0) {
return err;
}
return ctx;
}
int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) { int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) }; buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) };
@@ -1344,7 +1439,7 @@ int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
frame_ctx->sw_format = format; frame_ctx->sw_format = format;
frame_ctx->height = ctx->height; frame_ctx->height = ctx->height;
frame_ctx->width = ctx->width; frame_ctx->width = ctx->width;
frame_ctx->initial_pool_size = 0; frame_ctx->initial_pool_size = 20;
if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) { if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) {
return err; return err;
@@ -1355,7 +1450,32 @@ int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
return 0; return 0;
} }
void sw_img_to_frame(const platf::img_t &img, frame_t &frame) {} int sw_img_to_frame(const void *img, frame_t &frame) {
return 0;
}
int vaapi_img_to_frame(const void *img, frame_t &frame) {
auto status = av_hwframe_transfer_data(frame.get(), (frame_t::pointer)img, 0);
if(status < 0) {
char string[AV_ERROR_MAX_STRING_SIZE];
BOOST_LOG(error) << "Failed to transfer image data to hardware frame: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
return -1;
}
return 0;
}
util::Either<buffer_t, int> vaapi_make_hwdevice_ctx(platf::hwdevice_t *) {
buffer_t hw_device_buf;
auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VAAPI, "/dev/dri/renderD129", nullptr, 0);
if(status < 0) {
char string[AV_ERROR_MAX_STRING_SIZE];
BOOST_LOG(error) << "Failed to create a VAAPI device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
return -1;
}
return hw_device_buf;
}
#ifdef _WIN32 #ifdef _WIN32
} }
@@ -1445,17 +1565,18 @@ int start_capture_sync(capture_thread_sync_ctx_t &ctx) {
} }
void end_capture_sync(capture_thread_sync_ctx_t &ctx) {} void end_capture_sync(capture_thread_sync_ctx_t &ctx) {}
platf::dev_type_e map_dev_type(AVHWDeviceType type) { platf::mem_type_e map_dev_type(AVHWDeviceType type) {
switch(type) { switch(type) {
case AV_HWDEVICE_TYPE_D3D11VA: case AV_HWDEVICE_TYPE_D3D11VA:
return platf::dev_type_e::dxgi; return platf::mem_type_e::dxgi;
case AV_PICTURE_TYPE_NONE: case AV_PICTURE_TYPE_NONE:
return platf::dev_type_e::none; case AV_HWDEVICE_TYPE_VAAPI:
return platf::mem_type_e::system;
default: default:
return platf::dev_type_e::unknown; return platf::mem_type_e::unknown;
} }
return platf::dev_type_e::unknown; return platf::mem_type_e::unknown;
} }
platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) { platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) {