Intel QuickSync support for Windows (#758)
This commit is contained in:
@@ -739,13 +739,14 @@ encoder
|
|||||||
.. table::
|
.. table::
|
||||||
:widths: auto
|
:widths: auto
|
||||||
|
|
||||||
======== ===========
|
========= ===========
|
||||||
Value Description
|
Value Description
|
||||||
======== ===========
|
========= ===========
|
||||||
nvenc For Nvidia graphics cards
|
nvenc For NVIDIA graphics cards
|
||||||
|
quicksync For Intel graphics cards
|
||||||
amdvce For AMD graphics cards
|
amdvce For AMD graphics cards
|
||||||
software Encoding occurs on the CPU
|
software Encoding occurs on the CPU
|
||||||
======== ===========
|
========= ===========
|
||||||
|
|
||||||
**Default**
|
**Default**
|
||||||
Sunshine will use the first encoder that is available.
|
Sunshine will use the first encoder that is available.
|
||||||
@@ -958,6 +959,68 @@ nv_coder
|
|||||||
|
|
||||||
nv_coder = auto
|
nv_coder = auto
|
||||||
|
|
||||||
|
qsv_preset
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
The encoder preset to use.
|
||||||
|
|
||||||
|
.. Note:: This option only applies when using quicksync `encoder`_.
|
||||||
|
|
||||||
|
**Choices**
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
:widths: auto
|
||||||
|
|
||||||
|
========== ===========
|
||||||
|
Value Description
|
||||||
|
========== ===========
|
||||||
|
veryfast fastest (lowest quality)
|
||||||
|
faster faster (lower quality)
|
||||||
|
fast fast (low quality)
|
||||||
|
medium medium (default)
|
||||||
|
slow slow (good quality)
|
||||||
|
slower slower (better quality)
|
||||||
|
veryslow slowest (best quality)
|
||||||
|
========== ===========
|
||||||
|
|
||||||
|
**Default**
|
||||||
|
``medium``
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
qsv_preset = medium
|
||||||
|
|
||||||
|
qsv_coder
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
The entropy encoding to use.
|
||||||
|
|
||||||
|
.. Note:: This option only applies when using H264 with quicksync `encoder`_.
|
||||||
|
|
||||||
|
**Choices**
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
:widths: auto
|
||||||
|
|
||||||
|
========== ===========
|
||||||
|
Value Description
|
||||||
|
========== ===========
|
||||||
|
auto let ffmpeg decide
|
||||||
|
cabac context adaptive binary arithmetic coding - higher quality
|
||||||
|
cavlc context adaptive variable-length coding - faster decode
|
||||||
|
========== ===========
|
||||||
|
|
||||||
|
**Default**
|
||||||
|
``auto``
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
qsv_coder = auto
|
||||||
|
|
||||||
amd_quality
|
amd_quality
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
|||||||
@@ -200,6 +200,46 @@ int coder_from_view(const std::string_view &coder) {
|
|||||||
}
|
}
|
||||||
} // namespace amd
|
} // namespace amd
|
||||||
|
|
||||||
|
namespace qsv {
|
||||||
|
enum preset_e : int {
|
||||||
|
veryslow = 1,
|
||||||
|
slower = 2,
|
||||||
|
slow = 3,
|
||||||
|
medium = 4,
|
||||||
|
fast = 5,
|
||||||
|
faster = 6,
|
||||||
|
veryfast = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum cavlc_e : int {
|
||||||
|
_auto = false,
|
||||||
|
enabled = true,
|
||||||
|
disabled = false
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<int> preset_from_view(const std::string_view &preset) {
|
||||||
|
#define _CONVERT_(x) \
|
||||||
|
if(preset == #x##sv) return x
|
||||||
|
_CONVERT_(veryslow);
|
||||||
|
_CONVERT_(slower);
|
||||||
|
_CONVERT_(slow);
|
||||||
|
_CONVERT_(medium);
|
||||||
|
_CONVERT_(fast);
|
||||||
|
_CONVERT_(faster);
|
||||||
|
_CONVERT_(veryfast);
|
||||||
|
#undef _CONVERT_
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> coder_from_view(const std::string_view &coder) {
|
||||||
|
if(coder == "auto"sv) return _auto;
|
||||||
|
if(coder == "cabac"sv || coder == "ac"sv) return disabled;
|
||||||
|
if(coder == "cavlc"sv || coder == "vlc"sv) return enabled;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qsv
|
||||||
|
|
||||||
namespace vt {
|
namespace vt {
|
||||||
|
|
||||||
enum coder_e : int {
|
enum coder_e : int {
|
||||||
@@ -254,6 +294,11 @@ video_t video {
|
|||||||
nv::_auto // coder
|
nv::_auto // coder
|
||||||
}, // nv
|
}, // nv
|
||||||
|
|
||||||
|
{
|
||||||
|
qsv::medium, // preset
|
||||||
|
qsv::_auto, // cavlc
|
||||||
|
}, // qsv
|
||||||
|
|
||||||
{
|
{
|
||||||
(int)amd::quality_h264_e::balanced, // quality (h264)
|
(int)amd::quality_h264_e::balanced, // quality (h264)
|
||||||
(int)amd::quality_hevc_e::balanced, // quality (hevc)
|
(int)amd::quality_hevc_e::balanced, // quality (hevc)
|
||||||
@@ -261,11 +306,13 @@ video_t video {
|
|||||||
(int)amd::rc_hevc_e::vbr_latency, // rate control (hevc)
|
(int)amd::rc_hevc_e::vbr_latency, // rate control (hevc)
|
||||||
(int)amd::coder_e::_auto, // coder
|
(int)amd::coder_e::_auto, // coder
|
||||||
}, // amd
|
}, // amd
|
||||||
|
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
-1 }, // vt
|
-1,
|
||||||
|
}, // vt
|
||||||
|
|
||||||
{}, // encoder
|
{}, // encoder
|
||||||
{}, // adapter_name
|
{}, // adapter_name
|
||||||
@@ -761,6 +808,9 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
|||||||
int_f(vars, "nv_rc", video.nv.rc, nv::rc_from_view);
|
int_f(vars, "nv_rc", video.nv.rc, nv::rc_from_view);
|
||||||
int_f(vars, "nv_coder", video.nv.coder, nv::coder_from_view);
|
int_f(vars, "nv_coder", video.nv.coder, nv::coder_from_view);
|
||||||
|
|
||||||
|
int_f(vars, "qsv_preset", video.qsv.preset, qsv::preset_from_view);
|
||||||
|
int_f(vars, "qsv_coder", video.qsv.cavlc, qsv::coder_from_view);
|
||||||
|
|
||||||
std::string quality;
|
std::string quality;
|
||||||
string_f(vars, "amd_quality", quality);
|
string_f(vars, "amd_quality", quality);
|
||||||
if(!quality.empty()) {
|
if(!quality.empty()) {
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ struct video_t {
|
|||||||
int coder;
|
int coder;
|
||||||
} nv;
|
} nv;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::optional<int> preset;
|
||||||
|
std::optional<int> cavlc;
|
||||||
|
} qsv;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::optional<int> quality_h264;
|
std::optional<int> quality_h264;
|
||||||
std::optional<int> quality_hevc;
|
std::optional<int> quality_hevc;
|
||||||
|
|||||||
@@ -210,6 +210,13 @@ struct hwdevice_t {
|
|||||||
*/
|
*/
|
||||||
virtual void init_hwframes(AVHWFramesContext *frames) {};
|
virtual void init_hwframes(AVHWFramesContext *frames) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations may make modifications required before context derivation
|
||||||
|
*/
|
||||||
|
virtual int prepare_to_derive_context(int hw_device_type) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~hwdevice_t() = default;
|
virtual ~hwdevice_t() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -406,6 +406,23 @@ public:
|
|||||||
frames->initial_pool_size = 1;
|
frames->initial_pool_size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int prepare_to_derive_context(int hw_device_type) override {
|
||||||
|
// QuickSync requires our device to be multithread-protected
|
||||||
|
if(hw_device_type == AV_HWDEVICE_TYPE_QSV) {
|
||||||
|
multithread_t mt;
|
||||||
|
|
||||||
|
auto status = device->QueryInterface(IID_ID3D11Multithread, (void **)&mt);
|
||||||
|
if(FAILED(status)) {
|
||||||
|
BOOST_LOG(warning) << "Failed to query ID3D11Multithread interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mt->SetMultithreadProtected(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
||||||
this->hwframe.reset(frame);
|
this->hwframe.reset(frame);
|
||||||
this->frame = frame;
|
this->frame = frame;
|
||||||
|
|||||||
155
src/video.cpp
155
src/video.cpp
@@ -63,8 +63,29 @@ enum class profile_hevc_e : int {
|
|||||||
};
|
};
|
||||||
} // namespace nv
|
} // namespace nv
|
||||||
|
|
||||||
|
namespace qsv {
|
||||||
|
|
||||||
platf::mem_type_e map_dev_type(AVHWDeviceType type);
|
enum class profile_h264_e : int {
|
||||||
|
baseline = 66,
|
||||||
|
main = 77,
|
||||||
|
high = 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class profile_hevc_e : int {
|
||||||
|
main = 1,
|
||||||
|
main_10 = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class multiframe_mode_e : int {
|
||||||
|
mf_default = 0,
|
||||||
|
mf_disabled = 1,
|
||||||
|
mf_auto = 2,
|
||||||
|
mf_manual = 3,
|
||||||
|
};
|
||||||
|
} // namespace qsv
|
||||||
|
|
||||||
|
|
||||||
|
platf::mem_type_e map_base_dev_type(AVHWDeviceType type);
|
||||||
platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt);
|
platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt);
|
||||||
|
|
||||||
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);
|
||||||
@@ -239,6 +260,8 @@ enum flag_e {
|
|||||||
H264_ONLY = 0x02, // When HEVC is too heavy
|
H264_ONLY = 0x02, // When HEVC is too heavy
|
||||||
LIMITED_GOP_SIZE = 0x04, // Some encoders don't like it when you have an infinite GOP_SIZE. *cough* VAAPI *cough*
|
LIMITED_GOP_SIZE = 0x04, // Some encoders don't like it when you have an infinite GOP_SIZE. *cough* VAAPI *cough*
|
||||||
SINGLE_SLICE_ONLY = 0x08, // Never use multiple slices <-- Older intel iGPU's ruin it for everyone else :P
|
SINGLE_SLICE_ONLY = 0x08, // Never use multiple slices <-- Older intel iGPU's ruin it for everyone else :P
|
||||||
|
CBR_WITH_VBR = 0x10, // Use a VBR rate control mode to simulate CBR
|
||||||
|
RELAXED_COMPLIANCE = 0x20, // Use FF_COMPLIANCE_UNOFFICIAL compliance mode
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encoder_t {
|
struct encoder_t {
|
||||||
@@ -285,11 +308,10 @@ struct encoder_t {
|
|||||||
option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {}
|
option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {}
|
||||||
};
|
};
|
||||||
|
|
||||||
AVHWDeviceType dev_type;
|
AVHWDeviceType base_dev_type, derived_dev_type;
|
||||||
AVPixelFormat dev_pix_fmt;
|
AVPixelFormat dev_pix_fmt;
|
||||||
|
|
||||||
AVPixelFormat static_pix_fmt;
|
AVPixelFormat static_pix_fmt, dynamic_pix_fmt;
|
||||||
AVPixelFormat dynamic_pix_fmt;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::vector<option_t> common_options;
|
std::vector<option_t> common_options;
|
||||||
@@ -403,10 +425,10 @@ auto capture_thread_sync = safe::make_shared<capture_thread_sync_ctx_t>(start_c
|
|||||||
static encoder_t nvenc {
|
static encoder_t nvenc {
|
||||||
"nvenc"sv,
|
"nvenc"sv,
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
AV_HWDEVICE_TYPE_D3D11VA,
|
AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_NONE,
|
||||||
AV_PIX_FMT_D3D11,
|
AV_PIX_FMT_D3D11,
|
||||||
#else
|
#else
|
||||||
AV_HWDEVICE_TYPE_CUDA,
|
AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_NONE,
|
||||||
AV_PIX_FMT_CUDA,
|
AV_PIX_FMT_CUDA,
|
||||||
#endif
|
#endif
|
||||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||||
@@ -458,9 +480,66 @@ static encoder_t nvenc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
static encoder_t quicksync {
|
||||||
|
"quicksync"sv,
|
||||||
|
AV_HWDEVICE_TYPE_D3D11VA,
|
||||||
|
AV_HWDEVICE_TYPE_QSV,
|
||||||
|
AV_PIX_FMT_QSV,
|
||||||
|
AV_PIX_FMT_NV12,
|
||||||
|
AV_PIX_FMT_P010,
|
||||||
|
{
|
||||||
|
// Common options
|
||||||
|
{
|
||||||
|
{ "preset"s, &config::video.qsv.preset },
|
||||||
|
{ "forced_idr"s, 1 },
|
||||||
|
{ "async_depth"s, 1 },
|
||||||
|
{ "low_delay_brc"s, 1 },
|
||||||
|
{ "recovery_point_sei"s, 0 },
|
||||||
|
{ "vcm"s, 1 },
|
||||||
|
{ "pic_timing_sei"s, 0 },
|
||||||
|
{ "max_dec_frame_buffering"s, 1 },
|
||||||
|
{ "mfmode"s, (int)qsv::multiframe_mode_e::mf_disabled },
|
||||||
|
},
|
||||||
|
// SDR-specific options
|
||||||
|
{
|
||||||
|
{ "profile"s, (int)qsv::profile_hevc_e::main },
|
||||||
|
},
|
||||||
|
// HDR-specific options
|
||||||
|
{
|
||||||
|
{ "profile"s, (int)qsv::profile_hevc_e::main_10 },
|
||||||
|
},
|
||||||
|
std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||||
|
"hevc_qsv"s,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Common options
|
||||||
|
{
|
||||||
|
{ "preset"s, &config::video.qsv.preset },
|
||||||
|
{ "cavlc"s, &config::video.qsv.cavlc },
|
||||||
|
{ "forced_idr"s, 1 },
|
||||||
|
{ "async_depth"s, 1 },
|
||||||
|
{ "low_delay_brc"s, 1 },
|
||||||
|
{ "recovery_point_sei"s, 0 },
|
||||||
|
{ "vcm"s, 1 },
|
||||||
|
{ "pic_timing_sei"s, 0 },
|
||||||
|
{ "max_dec_frame_buffering"s, 1 },
|
||||||
|
{ "mfmode"s, (int)qsv::multiframe_mode_e::mf_disabled },
|
||||||
|
},
|
||||||
|
// SDR-specific options
|
||||||
|
{
|
||||||
|
{ "profile"s, (int)qsv::profile_h264_e::high },
|
||||||
|
},
|
||||||
|
{}, // HDR-specific options
|
||||||
|
std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||||
|
"h264_qsv"s,
|
||||||
|
},
|
||||||
|
PARALLEL_ENCODING | CBR_WITH_VBR | RELAXED_COMPLIANCE,
|
||||||
|
dxgi_make_hwdevice_ctx,
|
||||||
|
};
|
||||||
|
|
||||||
static encoder_t amdvce {
|
static encoder_t amdvce {
|
||||||
"amdvce"sv,
|
"amdvce"sv,
|
||||||
AV_HWDEVICE_TYPE_D3D11VA,
|
AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_NONE,
|
||||||
AV_PIX_FMT_D3D11,
|
AV_PIX_FMT_D3D11,
|
||||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||||
{
|
{
|
||||||
@@ -505,7 +584,7 @@ static encoder_t amdvce {
|
|||||||
|
|
||||||
static encoder_t software {
|
static encoder_t software {
|
||||||
"software"sv,
|
"software"sv,
|
||||||
AV_HWDEVICE_TYPE_NONE,
|
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,
|
||||||
{
|
{
|
||||||
@@ -543,7 +622,7 @@ static encoder_t software {
|
|||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
static encoder_t vaapi {
|
static encoder_t vaapi {
|
||||||
"vaapi"sv,
|
"vaapi"sv,
|
||||||
AV_HWDEVICE_TYPE_VAAPI,
|
AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_NONE,
|
||||||
AV_PIX_FMT_VAAPI,
|
AV_PIX_FMT_VAAPI,
|
||||||
AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10,
|
AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10,
|
||||||
{
|
{
|
||||||
@@ -579,7 +658,7 @@ static encoder_t vaapi {
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
static encoder_t videotoolbox {
|
static encoder_t videotoolbox {
|
||||||
"videotoolbox"sv,
|
"videotoolbox"sv,
|
||||||
AV_HWDEVICE_TYPE_NONE,
|
AV_HWDEVICE_TYPE_NONE, AV_HWDEVICE_TYPE_NONE,
|
||||||
AV_PIX_FMT_VIDEOTOOLBOX,
|
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||||
AV_PIX_FMT_NV12, AV_PIX_FMT_NV12,
|
AV_PIX_FMT_NV12, AV_PIX_FMT_NV12,
|
||||||
{
|
{
|
||||||
@@ -617,6 +696,7 @@ static std::vector<encoder_t> encoders {
|
|||||||
nvenc,
|
nvenc,
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
quicksync,
|
||||||
amdvce,
|
amdvce,
|
||||||
#endif
|
#endif
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -632,7 +712,7 @@ void reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type,
|
|||||||
// We try this twice, in case we still get an error on reinitialization
|
// We try this twice, in case we still get an error on reinitialization
|
||||||
for(int x = 0; x < 2; ++x) {
|
for(int x = 0; x < 2; ++x) {
|
||||||
disp.reset();
|
disp.reset();
|
||||||
disp = platf::display(map_dev_type(type), display_name, framerate);
|
disp = platf::display(map_base_dev_type(type), display_name, framerate);
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -664,7 +744,7 @@ void captureThread(
|
|||||||
|
|
||||||
// Get all the monitor names now, rather than at boot, to
|
// Get all the monitor names now, rather than at boot, to
|
||||||
// get the most up-to-date list available monitors
|
// get the most up-to-date list available monitors
|
||||||
auto display_names = platf::display_names(map_dev_type(encoder.dev_type));
|
auto display_names = platf::display_names(map_base_dev_type(encoder.base_dev_type));
|
||||||
int display_p = 0;
|
int display_p = 0;
|
||||||
|
|
||||||
if(display_names.empty()) {
|
if(display_names.empty()) {
|
||||||
@@ -683,7 +763,7 @@ void captureThread(
|
|||||||
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto disp = platf::display(map_dev_type(encoder.dev_type), display_names[display_p], capture_ctxs.front().framerate);
|
auto disp = platf::display(map_base_dev_type(encoder.base_dev_type), display_names[display_p], capture_ctxs.front().framerate);
|
||||||
if(!disp) {
|
if(!disp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -769,7 +849,7 @@ void captureThread(
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(capture_ctx_queue->running()) {
|
while(capture_ctx_queue->running()) {
|
||||||
reset_display(disp, encoder.dev_type, display_names[display_p], capture_ctxs.front().framerate);
|
reset_display(disp, encoder.base_dev_type, display_names[display_p], capture_ctxs.front().framerate);
|
||||||
|
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
@@ -868,7 +948,7 @@ int encode(int64_t frame_nr, session_t &session, frame_t::pointer frame, safe::m
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<session_t> make_session(const encoder_t &encoder, const config_t &config, int width, int height, std::shared_ptr<platf::hwdevice_t> &&hwdevice) {
|
std::optional<session_t> make_session(const encoder_t &encoder, const config_t &config, int width, int height, std::shared_ptr<platf::hwdevice_t> &&hwdevice) {
|
||||||
bool hardware = encoder.dev_type != AV_HWDEVICE_TYPE_NONE;
|
bool hardware = encoder.base_dev_type != AV_HWDEVICE_TYPE_NONE;
|
||||||
|
|
||||||
auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc;
|
auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc;
|
||||||
if(!video_format[encoder_t::PASSED]) {
|
if(!video_format[encoder_t::PASSED]) {
|
||||||
@@ -970,16 +1050,38 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
|||||||
// Used by cbs::make_sps_hevc
|
// Used by cbs::make_sps_hevc
|
||||||
ctx->sw_pix_fmt = sw_fmt;
|
ctx->sw_pix_fmt = sw_fmt;
|
||||||
|
|
||||||
buffer_t hwdevice_ctx;
|
|
||||||
if(hardware) {
|
if(hardware) {
|
||||||
|
buffer_t hwdevice_ctx;
|
||||||
|
|
||||||
ctx->pix_fmt = encoder.dev_pix_fmt;
|
ctx->pix_fmt = encoder.dev_pix_fmt;
|
||||||
|
|
||||||
|
// Create the base hwdevice context
|
||||||
auto buf_or_error = encoder.make_hwdevice_ctx(hwdevice.get());
|
auto buf_or_error = encoder.make_hwdevice_ctx(hwdevice.get());
|
||||||
if(buf_or_error.has_right()) {
|
if(buf_or_error.has_right()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
hwdevice_ctx = std::move(buf_or_error.left());
|
hwdevice_ctx = std::move(buf_or_error.left());
|
||||||
|
|
||||||
|
// If this encoder requires derivation from the base, derive the desired type
|
||||||
|
if(encoder.derived_dev_type != AV_HWDEVICE_TYPE_NONE) {
|
||||||
|
buffer_t derived_hwdevice_ctx;
|
||||||
|
|
||||||
|
// Allow the hwdevice to prepare for this type of context to be derived
|
||||||
|
if(hwdevice->prepare_to_derive_context(encoder.derived_dev_type)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto err = av_hwdevice_ctx_create_derived(&derived_hwdevice_ctx, encoder.derived_dev_type, hwdevice_ctx.get(), 0);
|
||||||
|
if(err) {
|
||||||
|
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||||
|
BOOST_LOG(error) << "Failed to derive device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwdevice_ctx = std::move(derived_hwdevice_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if(hwframe_ctx(ctx, hwdevice.get(), hwdevice_ctx, sw_fmt)) {
|
if(hwframe_ctx(ctx, hwdevice.get(), hwdevice_ctx, sw_fmt)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -1026,7 +1128,18 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
|||||||
auto bitrate = config.bitrate * 1000;
|
auto bitrate = config.bitrate * 1000;
|
||||||
ctx->rc_max_rate = bitrate;
|
ctx->rc_max_rate = bitrate;
|
||||||
ctx->bit_rate = bitrate;
|
ctx->bit_rate = bitrate;
|
||||||
|
|
||||||
|
if(encoder.flags & CBR_WITH_VBR) {
|
||||||
|
// Ensure rc_max_bitrate != bit_rate to force VBR mode
|
||||||
|
ctx->bit_rate--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
ctx->rc_min_rate = bitrate;
|
ctx->rc_min_rate = bitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encoder.flags & RELAXED_COMPLIANCE) {
|
||||||
|
ctx->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
|
||||||
|
}
|
||||||
|
|
||||||
if(!hardware && (ctx->slices > 1 || config.videoFormat != 0)) {
|
if(!hardware && (ctx->slices > 1 || config.videoFormat != 0)) {
|
||||||
// Use a larger rc_buffer_size for software encoding when slices are enabled,
|
// Use a larger rc_buffer_size for software encoding when slices are enabled,
|
||||||
@@ -1211,7 +1324,7 @@ encode_e encode_run_sync(
|
|||||||
encode_session_ctx_queue_t &encode_session_ctx_queue) {
|
encode_session_ctx_queue_t &encode_session_ctx_queue) {
|
||||||
|
|
||||||
const auto &encoder = encoders.front();
|
const auto &encoder = encoders.front();
|
||||||
auto display_names = platf::display_names(map_dev_type(encoder.dev_type));
|
auto display_names = platf::display_names(map_base_dev_type(encoder.base_dev_type));
|
||||||
int display_p = 0;
|
int display_p = 0;
|
||||||
|
|
||||||
if(display_names.empty()) {
|
if(display_names.empty()) {
|
||||||
@@ -1242,7 +1355,7 @@ encode_e encode_run_sync(
|
|||||||
int framerate = synced_session_ctxs.front()->config.framerate;
|
int framerate = synced_session_ctxs.front()->config.framerate;
|
||||||
|
|
||||||
while(encode_session_ctx_queue.running()) {
|
while(encode_session_ctx_queue.running()) {
|
||||||
reset_display(disp, encoder.dev_type, display_names[display_p], framerate);
|
reset_display(disp, encoder.base_dev_type, display_names[display_p], framerate);
|
||||||
if(disp) {
|
if(disp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1502,7 +1615,7 @@ enum validate_flag_e {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &encoder, const config_t &config) {
|
int validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &encoder, const config_t &config) {
|
||||||
reset_display(disp, encoder.dev_type, config::video.output_name, config.framerate);
|
reset_display(disp, encoder.base_dev_type, config::video.output_name, config.framerate);
|
||||||
if(!disp) {
|
if(!disp) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1927,7 +2040,7 @@ 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::mem_type_e map_dev_type(AVHWDeviceType type) {
|
platf::mem_type_e map_base_dev_type(AVHWDeviceType type) {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case AV_HWDEVICE_TYPE_D3D11VA:
|
case AV_HWDEVICE_TYPE_D3D11VA:
|
||||||
return platf::mem_type_e::dxgi;
|
return platf::mem_type_e::dxgi;
|
||||||
|
|||||||
@@ -523,6 +523,7 @@
|
|||||||
<select id="encoder" class="form-select" v-model="config.encoder">
|
<select id="encoder" class="form-select" v-model="config.encoder">
|
||||||
<option value>Autodetect</option>
|
<option value>Autodetect</option>
|
||||||
<option value="nvenc" v-if="platform === 'windows' || platform === 'linux'">NVIDIA NVENC</option>
|
<option value="nvenc" v-if="platform === 'windows' || platform === 'linux'">NVIDIA NVENC</option>
|
||||||
|
<option value="quicksync" v-if="platform === 'windows'">Intel QuickSync</option>
|
||||||
<option value="amdvce" v-if="platform === 'windows'">AMD AMF/VCE</option>
|
<option value="amdvce" v-if="platform === 'windows'">AMD AMF/VCE</option>
|
||||||
<option value="vaapi" v-if="platform === 'linux'">VA-API</option>
|
<option value="vaapi" v-if="platform === 'linux'">VA-API</option>
|
||||||
<option value="videotoolbox" v-if="platform === 'macos'">VideoToolbox</option>
|
<option value="videotoolbox" v-if="platform === 'macos'">VideoToolbox</option>
|
||||||
@@ -695,6 +696,29 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--Intel Encoder Settings-->
|
||||||
|
<div v-if="currentTab === 'qsv'" class="config-page">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="qsv_preset" class="form-label">QuickSync Preset</label>
|
||||||
|
<select id="qsv_preset" class="form-select" v-model="config.qsv_preset">
|
||||||
|
<option value="veryfast">fastest (lowest quality)</option>
|
||||||
|
<option value="faster">faster (lower quality)</option>
|
||||||
|
<option value="fast">fast (low quality)</option>
|
||||||
|
<option value="medium">medium (default)</option>
|
||||||
|
<option value="slow">slow (good quality)</option>
|
||||||
|
<option value="slower">slower (better quality)</option>
|
||||||
|
<option value="slowest">slowest (best quality)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="qsv_coder" class="form-label">QuickSync Coder (H264)</label>
|
||||||
|
<select id="qsv_coder" class="form-select" v-model="config.qsv_coder">
|
||||||
|
<option value="auto">auto -- let ffmpeg decide (default)</option>
|
||||||
|
<option value="cabac">cabac -- context adaptive binary arithmetic coding - higher quality</option>
|
||||||
|
<option value="cavlc">cavlc -- context adaptive variable-length coding - faster decode</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!--AMD Encoder Settings-->
|
<!--AMD Encoder Settings-->
|
||||||
<div v-if="currentTab === 'amd'" class="config-page">
|
<div v-if="currentTab === 'amd'" class="config-page">
|
||||||
<!--Presets-->
|
<!--Presets-->
|
||||||
@@ -820,6 +844,10 @@
|
|||||||
id: "nv",
|
id: "nv",
|
||||||
name: "NVIDIA NVENC Encoder",
|
name: "NVIDIA NVENC Encoder",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "qsv",
|
||||||
|
name: "Intel QuickSync Encoder",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "amd",
|
id: "amd",
|
||||||
name: "AMD AMF Encoder",
|
name: "AMD AMF Encoder",
|
||||||
@@ -855,12 +883,12 @@
|
|||||||
}
|
}
|
||||||
if (this.platform == "linux") {
|
if (this.platform == "linux") {
|
||||||
this.tabs = this.tabs.filter((el) => {
|
this.tabs = this.tabs.filter((el) => {
|
||||||
return el.id !== "amd" && el.id !== "vt";
|
return el.id !== "amd" && el.id !== "qsv" && el.id !== "vt";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.platform == "macos") {
|
if (this.platform == "macos") {
|
||||||
this.tabs = this.tabs.filter((el) => {
|
this.tabs = this.tabs.filter((el) => {
|
||||||
return el.id !== "amd" && el.id !== "nv" && el.id !== "va-api";
|
return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "va-api";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,6 +912,8 @@
|
|||||||
this.config.nv_tune = this.config.nv_tune || "ull";
|
this.config.nv_tune = this.config.nv_tune || "ull";
|
||||||
this.config.nv_coder = this.config.nv_coder || "auto";
|
this.config.nv_coder = this.config.nv_coder || "auto";
|
||||||
this.config.nv_rc = this.config.nv_rc || "cbr";
|
this.config.nv_rc = this.config.nv_rc || "cbr";
|
||||||
|
this.config.qsv_preset = this.config.qsv_preset || "medium";
|
||||||
|
this.config.qsv_coder = this.config.qsv_coder || "auto";
|
||||||
this.config.amd_coder = this.config.amd_coder || "auto"
|
this.config.amd_coder = this.config.amd_coder || "auto"
|
||||||
this.config.amd_quality = this.config.amd_quality || "balanced";
|
this.config.amd_quality = this.config.amd_quality || "balanced";
|
||||||
this.config.amd_rc = this.config.amd_rc || "vbr_latency";
|
this.config.amd_rc = this.config.amd_rc || "vbr_latency";
|
||||||
|
|||||||
Reference in New Issue
Block a user