nvenc: new config page
This commit is contained in:
133
src/config.cpp
133
src/config.cpp
@@ -24,6 +24,11 @@
|
|||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
// For NVENC legacy constants
|
||||||
|
#include <ffnvcodec/nvEncodeAPI.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
@@ -35,96 +40,16 @@ using namespace std::literals;
|
|||||||
namespace config {
|
namespace config {
|
||||||
|
|
||||||
namespace nv {
|
namespace nv {
|
||||||
#ifdef __APPLE__
|
|
||||||
// values accurate as of 27/12/2022, but aren't strictly necessary for MacOS build
|
|
||||||
#define NV_ENC_TUNING_INFO_HIGH_QUALITY 1
|
|
||||||
#define NV_ENC_TUNING_INFO_LOW_LATENCY 2
|
|
||||||
#define NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY 3
|
|
||||||
#define NV_ENC_TUNING_INFO_LOSSLESS 4
|
|
||||||
#define NV_ENC_PARAMS_RC_CONSTQP 0x0
|
|
||||||
#define NV_ENC_PARAMS_RC_VBR 0x1
|
|
||||||
#define NV_ENC_PARAMS_RC_CBR 0x2
|
|
||||||
#define NV_ENC_H264_ENTROPY_CODING_MODE_CABAC 1
|
|
||||||
#define NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC 2
|
|
||||||
#else
|
|
||||||
#include <ffnvcodec/nvEncodeAPI.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum preset_e : int {
|
nvenc::nvenc_two_pass
|
||||||
p1 = 12, // PRESET_P1, // must be kept in sync with <libavcodec/nvenc.h>
|
twopass_from_view(const std::string_view &preset) {
|
||||||
p2, // PRESET_P2,
|
if (preset == "disabled") return nvenc::nvenc_two_pass::disabled;
|
||||||
p3, // PRESET_P3,
|
if (preset == "quarter_res") return nvenc::nvenc_two_pass::quarter_resolution;
|
||||||
p4, // PRESET_P4,
|
if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution;
|
||||||
p5, // PRESET_P5,
|
BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset;
|
||||||
p6, // PRESET_P6,
|
return nvenc::nvenc_two_pass::quarter_resolution;
|
||||||
p7 // PRESET_P7
|
|
||||||
};
|
|
||||||
|
|
||||||
enum tune_e : int {
|
|
||||||
hq = NV_ENC_TUNING_INFO_HIGH_QUALITY,
|
|
||||||
ll = NV_ENC_TUNING_INFO_LOW_LATENCY,
|
|
||||||
ull = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY,
|
|
||||||
lossless = NV_ENC_TUNING_INFO_LOSSLESS
|
|
||||||
};
|
|
||||||
|
|
||||||
enum rc_e : int {
|
|
||||||
constqp = NV_ENC_PARAMS_RC_CONSTQP, /**< Constant QP mode */
|
|
||||||
vbr = NV_ENC_PARAMS_RC_VBR, /**< Variable bitrate mode */
|
|
||||||
cbr = NV_ENC_PARAMS_RC_CBR /**< Constant bitrate mode */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum coder_e : int {
|
|
||||||
_auto = 0,
|
|
||||||
cabac = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC,
|
|
||||||
cavlc = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::optional<preset_e>
|
|
||||||
preset_from_view(const std::string_view &preset) {
|
|
||||||
#define _CONVERT_(x) \
|
|
||||||
if (preset == #x##sv) return x
|
|
||||||
_CONVERT_(p1);
|
|
||||||
_CONVERT_(p2);
|
|
||||||
_CONVERT_(p3);
|
|
||||||
_CONVERT_(p4);
|
|
||||||
_CONVERT_(p5);
|
|
||||||
_CONVERT_(p6);
|
|
||||||
_CONVERT_(p7);
|
|
||||||
#undef _CONVERT_
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<tune_e>
|
|
||||||
tune_from_view(const std::string_view &tune) {
|
|
||||||
#define _CONVERT_(x) \
|
|
||||||
if (tune == #x##sv) return x
|
|
||||||
_CONVERT_(hq);
|
|
||||||
_CONVERT_(ll);
|
|
||||||
_CONVERT_(ull);
|
|
||||||
_CONVERT_(lossless);
|
|
||||||
#undef _CONVERT_
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<rc_e>
|
|
||||||
rc_from_view(const std::string_view &rc) {
|
|
||||||
#define _CONVERT_(x) \
|
|
||||||
if (rc == #x##sv) return x
|
|
||||||
_CONVERT_(constqp);
|
|
||||||
_CONVERT_(vbr);
|
|
||||||
_CONVERT_(cbr);
|
|
||||||
#undef _CONVERT_
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
coder_from_view(const std::string_view &coder) {
|
|
||||||
if (coder == "auto"sv) return _auto;
|
|
||||||
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
|
|
||||||
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} // namespace nv
|
} // namespace nv
|
||||||
|
|
||||||
namespace amd {
|
namespace amd {
|
||||||
@@ -400,12 +325,8 @@ namespace config {
|
|||||||
11, // superfast
|
11, // superfast
|
||||||
}, // software
|
}, // software
|
||||||
|
|
||||||
{
|
{}, // nv
|
||||||
nv::p4, // preset
|
{}, // nv_legacy
|
||||||
nv::ull, // tune
|
|
||||||
nv::cbr, // rc
|
|
||||||
nv::_auto // coder
|
|
||||||
}, // nv
|
|
||||||
|
|
||||||
{
|
{
|
||||||
qsv::medium, // preset
|
qsv::medium, // preset
|
||||||
@@ -640,6 +561,16 @@ namespace config {
|
|||||||
vars.erase(it);
|
vars.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
void
|
||||||
|
generic_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, T &input, F &&f) {
|
||||||
|
std::string tmp;
|
||||||
|
string_f(vars, name, tmp);
|
||||||
|
if (!tmp.empty()) {
|
||||||
|
input = f(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
string_restricted_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input, const std::vector<std::string_view> &allowed_vals) {
|
string_restricted_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input, const std::vector<std::string_view> &allowed_vals) {
|
||||||
std::string temp;
|
std::string temp;
|
||||||
@@ -989,10 +920,18 @@ namespace config {
|
|||||||
video.sw.svtav1_preset = sw::svtav1_preset_from_view(video.sw.sw_preset);
|
video.sw.svtav1_preset = sw::svtav1_preset_from_view(video.sw.sw_preset);
|
||||||
}
|
}
|
||||||
string_f(vars, "sw_tune", video.sw.sw_tune);
|
string_f(vars, "sw_tune", video.sw.sw_tune);
|
||||||
int_f(vars, "nv_preset", video.nv.nv_preset, nv::preset_from_view);
|
|
||||||
int_f(vars, "nv_tune", video.nv.nv_tune, nv::tune_from_view);
|
int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 });
|
||||||
int_f(vars, "nv_rc", video.nv.nv_rc, nv::rc_from_view);
|
generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view);
|
||||||
int_f(vars, "nv_coder", video.nv.nv_coder, nv::coder_from_view);
|
bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc);
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
video.nv_legacy.preset = video.nv.quality_preset + 11;
|
||||||
|
video.nv_legacy.multipass = video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION :
|
||||||
|
video.nv.two_pass == nvenc::nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION :
|
||||||
|
NV_ENC_MULTI_PASS_DISABLED;
|
||||||
|
video.nv_legacy.h264_coder = video.nv.h264_cavlc ? NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC : NV_ENC_H264_ENTROPY_CODING_MODE_CABAC;
|
||||||
|
#endif
|
||||||
|
|
||||||
int_f(vars, "qsv_preset", video.qsv.qsv_preset, qsv::preset_from_view);
|
int_f(vars, "qsv_preset", video.qsv.qsv_preset, qsv::preset_from_view);
|
||||||
int_f(vars, "qsv_coder", video.qsv.qsv_cavlc, qsv::coder_from_view);
|
int_f(vars, "qsv_coder", video.qsv.qsv_cavlc, qsv::coder_from_view);
|
||||||
|
|||||||
13
src/config.h
13
src/config.h
@@ -11,6 +11,8 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "nvenc/nvenc_config.h"
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
struct video_t {
|
struct video_t {
|
||||||
// ffmpeg params
|
// ffmpeg params
|
||||||
@@ -26,12 +28,13 @@ namespace config {
|
|||||||
std::optional<int> svtav1_preset;
|
std::optional<int> svtav1_preset;
|
||||||
} sw;
|
} sw;
|
||||||
|
|
||||||
|
nvenc::nvenc_config nv;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::optional<int> nv_preset;
|
int preset;
|
||||||
std::optional<int> nv_tune;
|
int multipass;
|
||||||
std::optional<int> nv_rc;
|
int h264_coder;
|
||||||
int nv_coder;
|
} nv_legacy;
|
||||||
} nv;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::optional<int> qsv_preset;
|
std::optional<int> qsv_preset;
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ namespace nvenc {
|
|||||||
|
|
||||||
struct nvenc_config {
|
struct nvenc_config {
|
||||||
// Quality preset from 1 to 7, higher is slower
|
// Quality preset from 1 to 7, higher is slower
|
||||||
unsigned quality_preset = 1;
|
int quality_preset = 1;
|
||||||
|
|
||||||
// Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores
|
// Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores
|
||||||
nvenc_two_pass two_pass = nvenc_two_pass::disabled;
|
nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution;
|
||||||
|
|
||||||
// Improves fades compression, uses CUDA cores
|
// Improves fades compression, uses CUDA cores
|
||||||
bool weighted_prediction = false;
|
bool weighted_prediction = false;
|
||||||
|
|||||||
@@ -874,12 +874,8 @@ namespace platf::dxgi {
|
|||||||
init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override {
|
init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override {
|
||||||
if (!nvenc_d3d) return false;
|
if (!nvenc_d3d) return false;
|
||||||
|
|
||||||
nvenc::nvenc_config nvenc_config;
|
|
||||||
nvenc_config.quality_preset = config::video.nv.nv_preset ? (*config::video.nv.nv_preset - 11) : 1;
|
|
||||||
nvenc_config.h264_cavlc = (config::video.nv.nv_coder == NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC);
|
|
||||||
|
|
||||||
auto nvenc_colorspace = nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace);
|
auto nvenc_colorspace = nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace);
|
||||||
if (!nvenc_d3d->create_encoder(nvenc_config, client_config, nvenc_colorspace, buffer_format)) return false;
|
if (!nvenc_d3d->create_encoder(config::video.nv, client_config, nvenc_colorspace, buffer_format)) return false;
|
||||||
|
|
||||||
base.apply_colorspace(colorspace);
|
base.apply_colorspace(colorspace);
|
||||||
return base.init_output(nvenc_d3d->get_input_texture(), client_config.width, client_config.height) == 0;
|
return base.init_output(nvenc_d3d->get_input_texture(), client_config.width, client_config.height) == 0;
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ namespace video {
|
|||||||
},
|
},
|
||||||
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION // flags
|
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION // flags
|
||||||
};
|
};
|
||||||
#else
|
#elif !defined(__APPLE__)
|
||||||
static encoder_t nvenc {
|
static encoder_t nvenc {
|
||||||
"nvenc"sv,
|
"nvenc"sv,
|
||||||
std::make_unique<encoder_platform_formats_avcodec>(
|
std::make_unique<encoder_platform_formats_avcodec>(
|
||||||
@@ -625,9 +625,10 @@ namespace video {
|
|||||||
{ "delay"s, 0 },
|
{ "delay"s, 0 },
|
||||||
{ "forced-idr"s, 1 },
|
{ "forced-idr"s, 1 },
|
||||||
{ "zerolatency"s, 1 },
|
{ "zerolatency"s, 1 },
|
||||||
{ "preset"s, &config::video.nv.nv_preset },
|
{ "preset"s, &config::video.nv_legacy.preset },
|
||||||
{ "tune"s, &config::video.nv.nv_tune },
|
{ "tune"s, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY },
|
||||||
{ "rc"s, &config::video.nv.nv_rc },
|
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
|
||||||
|
{ "multipass"s, &config::video.nv_legacy.multipass },
|
||||||
},
|
},
|
||||||
// SDR-specific options
|
// SDR-specific options
|
||||||
{},
|
{},
|
||||||
@@ -642,9 +643,10 @@ namespace video {
|
|||||||
{ "delay"s, 0 },
|
{ "delay"s, 0 },
|
||||||
{ "forced-idr"s, 1 },
|
{ "forced-idr"s, 1 },
|
||||||
{ "zerolatency"s, 1 },
|
{ "zerolatency"s, 1 },
|
||||||
{ "preset"s, &config::video.nv.nv_preset },
|
{ "preset"s, &config::video.nv_legacy.preset },
|
||||||
{ "tune"s, &config::video.nv.nv_tune },
|
{ "tune"s, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY },
|
||||||
{ "rc"s, &config::video.nv.nv_rc },
|
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
|
||||||
|
{ "multipass"s, &config::video.nv_legacy.multipass },
|
||||||
},
|
},
|
||||||
// SDR-specific options
|
// SDR-specific options
|
||||||
{
|
{
|
||||||
@@ -662,10 +664,11 @@ namespace video {
|
|||||||
{ "delay"s, 0 },
|
{ "delay"s, 0 },
|
||||||
{ "forced-idr"s, 1 },
|
{ "forced-idr"s, 1 },
|
||||||
{ "zerolatency"s, 1 },
|
{ "zerolatency"s, 1 },
|
||||||
{ "preset"s, &config::video.nv.nv_preset },
|
{ "preset"s, &config::video.nv_legacy.preset },
|
||||||
{ "tune"s, &config::video.nv.nv_tune },
|
{ "tune"s, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY },
|
||||||
{ "rc"s, &config::video.nv.nv_rc },
|
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
|
||||||
{ "coder"s, &config::video.nv.nv_coder },
|
{ "coder"s, &config::video.nv_legacy.h264_coder },
|
||||||
|
{ "multipass"s, &config::video.nv_legacy.multipass },
|
||||||
},
|
},
|
||||||
// SDR-specific options
|
// SDR-specific options
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -872,41 +872,60 @@
|
|||||||
<div v-if="currentTab === 'nv'" class="config-page">
|
<div v-if="currentTab === 'nv'" class="config-page">
|
||||||
<!--NVENC SETTINGS-->
|
<!--NVENC SETTINGS-->
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="nv_preset" class="form-label">NVENC Preset</label>
|
<label for="nvenc_preset" class="form-label">Performance preset</label>
|
||||||
<select id="nv_preset" class="form-select" v-model="config.nv_preset">
|
<select id="nvenc_preset" class="form-select" v-model="config.nvenc_preset">
|
||||||
<option value="p1">p1 -- fastest (lowest quality)</option>
|
<option value="1">P1 (fastest, default)</option>
|
||||||
<option value="p2">p2 -- faster (lower quality)</option>
|
<option value="2">P2</option>
|
||||||
<option value="p3">p3 -- fast (low quality)</option>
|
<option value="3">P3</option>
|
||||||
<option value="p4">p4 -- medium (default)</option>
|
<option value="4">P4</option>
|
||||||
<option value="p5">p5 -- slow (good quality)</option>
|
<option value="5">P5</option>
|
||||||
<option value="p6">p6 -- slower (better quality)</option>
|
<option value="6">P6</option>
|
||||||
<option value="p7">p7 -- slowest (best quality)</option>
|
<option value="7">P7 (slowest)</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div class="form-text">Higher numbers improve compression (quality at given bitrate) at the cost of
|
||||||
|
<strong>increased encoding latency</strong>.<br>
|
||||||
|
Recommended to change only when limited by network or decoder, otherwise similar effect can be accomplished by
|
||||||
|
increasing bitrate.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="nv_tune" class="form-label">NVENC Tune</label>
|
<label for="nvenc_twopass" class="form-label">Two-pass mode</label>
|
||||||
<select id="nv_tune" class="form-select" v-model="config.nv_tune">
|
<select id="nvenc_twopass" class="form-select" v-model="config.nvenc_twopass">
|
||||||
<option value="hq">hq -- high quality</option>
|
<option value="disabled">Disabled (fastest, not recommended)</option>
|
||||||
<option value="ll">ll -- low latency</option>
|
<option value="quarter_res">Quarter resolution (faster, default)</option>
|
||||||
<option value="ull">ull -- ultra low latency (default)</option>
|
<option value="full_res">Full resolution (slower)</option>
|
||||||
<option value="lossless">lossless -- lossless</option>
|
|
||||||
</select>
|
</select>
|
||||||
|
<div class="form-text">Adds preliminary encoding pass.<br>
|
||||||
|
This allows to detect more motion vectors, better distribute bitrate across the frame and more strictly adhere to
|
||||||
|
bitrate limits.<br>
|
||||||
|
Disabling it is not recommended since this can lead to occasional bitrate overshoot and subsequent packet loss.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="accordion">
|
||||||
<label for="nv_rc" class="form-label">NVENC Rate Control</label>
|
<div class="accordion-item">
|
||||||
<select id="nv_rc" class="form-select" v-model="config.nv_rc">
|
<h2 class="accordion-header">
|
||||||
<option value="constqp">constqp -- constant qp mode</option>
|
<button class="accordion-button" type="button" data-bs-toggle="collapse"
|
||||||
<option value="vbr">vbr -- variable bitrate</option>
|
data-bs-target="#panelsStayOpen-collapseOne">
|
||||||
<option value="cbr">cbr -- constant bitrate (default)</option>
|
Miscellaneous options
|
||||||
</select>
|
</button>
|
||||||
</div>
|
</h2>
|
||||||
<div class="mb-3">
|
<div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse show"
|
||||||
<label for="nv_coder" class="form-label">NVENC Coder (H264)</label>
|
aria-labelledby="panelsStayOpen-headingOne">
|
||||||
<select id="nv_coder" class="form-select" v-model="config.nv_coder">
|
<div class="accordion-body">
|
||||||
<option value="auto">auto -- let ffmpeg decide (default)</option>
|
<div>
|
||||||
<option value="cabac">cabac -- context adaptive binary arithmetic coding - higher quality</option>
|
<label for="nvenc_h264_cavlc" class="form-label">Prefer CAVLC over CABAC in H.264</label>
|
||||||
<option value="cavlc">cavlc -- context adaptive variable-length coding - faster decode</option>
|
<select id="nvenc_h264_cavlc" class="form-select" v-model="config.nvenc_h264_cavlc">
|
||||||
</select>
|
<option value="disabled">Disabled (default)</option>
|
||||||
|
<option value="enabled">Enabled</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Simpler form of entropy coding.<br>
|
||||||
|
CAVLC needs around 10% more bitrate for same quality.<br>
|
||||||
|
Only relevant for really old decoding devices.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--Intel Encoder Settings-->
|
<!--Intel Encoder Settings-->
|
||||||
@@ -1061,10 +1080,9 @@
|
|||||||
"keyboard": "enabled",
|
"keyboard": "enabled",
|
||||||
"min_log_level": 2,
|
"min_log_level": 2,
|
||||||
"mouse": "enabled",
|
"mouse": "enabled",
|
||||||
"nv_coder": "auto",
|
"nvenc_preset": "1",
|
||||||
"nv_preset": "p4",
|
"nvenc_twopass": "quarter_res",
|
||||||
"nv_rc": "cbr",
|
"nvenc_h264_cavlc": "disabled",
|
||||||
"nv_tune": "ull",
|
|
||||||
"origin_pin_allowed": "pc",
|
"origin_pin_allowed": "pc",
|
||||||
"origin_web_ui_allowed": "lan",
|
"origin_web_ui_allowed": "lan",
|
||||||
"qsv_coder": "auto",
|
"qsv_coder": "auto",
|
||||||
|
|||||||
Reference in New Issue
Block a user