Intel QuickSync support for Windows (#758)

This commit is contained in:
Cameron Gutman
2023-01-14 15:23:49 -06:00
committed by GitHub
parent 5480d3d59d
commit 4fc444b5b3
7 changed files with 322 additions and 37 deletions

View File

@@ -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
amdvce For AMD graphics cards quicksync For Intel graphics cards
software Encoding occurs on the CPU amdvce For AMD graphics cards
======== =========== 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
^^^^^^^^^^^ ^^^^^^^^^^^

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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;
}; };

View File

@@ -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;

View File

@@ -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);
@@ -234,11 +255,13 @@ public:
}; };
enum flag_e { enum flag_e {
DEFAULT = 0x00, DEFAULT = 0x00,
PARALLEL_ENCODING = 0x01, PARALLEL_ENCODING = 0x01,
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;
ctx->rc_min_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;
}
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;

View File

@@ -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";