Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -3,10 +3,10 @@
|
||||
* @brief Definitions for FFmpeg Coded Bitstream API.
|
||||
*/
|
||||
extern "C" {
|
||||
#include <cbs/cbs_h264.h>
|
||||
#include <cbs/cbs_h265.h>
|
||||
#include <cbs/h264_levels.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/cbs_h264.h>
|
||||
#include <libavcodec/cbs_h265.h>
|
||||
#include <libavcodec/h264_levels.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +381,10 @@ namespace config {
|
||||
-1,
|
||||
}, // vt
|
||||
|
||||
{
|
||||
false, // strict_rc_buffer
|
||||
}, // vaapi
|
||||
|
||||
{}, // capture
|
||||
{}, // encoder
|
||||
{}, // adapter_name
|
||||
@@ -414,7 +418,7 @@ namespace config {
|
||||
PRIVATE_KEY_FILE,
|
||||
CERTIFICATE_FILE,
|
||||
|
||||
boost::asio::ip::host_name(), // sunshine_name,
|
||||
platf::get_host_name(), // sunshine_name,
|
||||
"sunshine_state.json"s, // file_state
|
||||
{}, // external_ip
|
||||
};
|
||||
@@ -1060,6 +1064,8 @@ namespace config {
|
||||
int_f(vars, "vt_software", video.vt.vt_require_sw, vt::force_software_from_view);
|
||||
int_f(vars, "vt_realtime", video.vt.vt_realtime, vt::rt_from_view);
|
||||
|
||||
bool_f(vars, "vaapi_strict_rc_buffer", video.vaapi.strict_rc_buffer);
|
||||
|
||||
string_f(vars, "capture", video.capture);
|
||||
string_f(vars, "encoder", video.encoder);
|
||||
string_f(vars, "adapter_name", video.adapter_name);
|
||||
|
||||
@@ -74,6 +74,10 @@ namespace config {
|
||||
int vt_coder;
|
||||
} vt;
|
||||
|
||||
struct {
|
||||
bool strict_rc_buffer;
|
||||
} vaapi;
|
||||
|
||||
std::string capture;
|
||||
std::string encoder;
|
||||
std::string adapter_name;
|
||||
|
||||
@@ -341,6 +341,11 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the list of available applications.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getApps(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -354,6 +359,11 @@ namespace confighttp {
|
||||
response->write(content, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the logs from the log file.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getLogs(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -367,6 +377,37 @@ namespace confighttp {
|
||||
response->write(SimpleWeb::StatusCode::success_ok, content, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save an application. If the application already exists, it will be updated, otherwise it will be added.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "name": "<Application Name>",
|
||||
* "output": "<Log Output Path>",
|
||||
* "cmd": "<Command to run the application>",
|
||||
* "index": -1,
|
||||
* "exclude-global-prep-cmd": false,
|
||||
* "elevated": false,
|
||||
* "auto-detach": true,
|
||||
* "wait-all": true,
|
||||
* "exit-timeout": 5,
|
||||
* "prep-cmd": [
|
||||
* {
|
||||
* "do": "<Command to prepare>",
|
||||
* "undo": "<Command to undo preparation>",
|
||||
* "elevated": false
|
||||
* }
|
||||
* ],
|
||||
* "detached": [
|
||||
* "<Detached commands>"
|
||||
* ],
|
||||
* "image-path": "<Full path to the application image. Must be a png file.>",
|
||||
* "uuid": "C3445C24-871A-FD23-0708-615C121B5B78"
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
saveApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -408,6 +449,11 @@ namespace confighttp {
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete an application.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
deleteApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -462,6 +508,18 @@ namespace confighttp {
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Upload a cover image.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "key": "igdb_<game_id>",
|
||||
* "url": "https://images.igdb.com/igdb/image/upload/t_cover_big_2x/<slug>.png",
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
uploadCover(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -522,6 +580,11 @@ namespace confighttp {
|
||||
outputTree.put("path", path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the configuration settings.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -550,6 +613,11 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the locale setting. This endpoint does not require authentication.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getLocale(resp_https_t response, req_https_t request) {
|
||||
// we need to return the locale whether authenticated or not
|
||||
@@ -568,6 +636,19 @@ namespace confighttp {
|
||||
outputTree.put("locale", config::sunshine.locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the configuration settings.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "key": "value"
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @attention{It is recommended to ONLY save the config settings that differ from the default behavior.}
|
||||
*/
|
||||
void
|
||||
saveConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -604,6 +685,11 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restart Sunshine.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
restart(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -643,6 +729,21 @@ namespace confighttp {
|
||||
write_resp.detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update existing credentials.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "currentUsername": "Current Username",
|
||||
* "currentPassword": "Current Password",
|
||||
* "newUsername": "New Username",
|
||||
* "newPassword": "New Password",
|
||||
* "confirmNewPassword": "Confirm New Password"
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
savePassword(resp_https_t response, req_https_t request) {
|
||||
if (!config::sunshine.username.empty() && !authenticate(response, request)) return;
|
||||
@@ -748,6 +849,18 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a pin code to the host. The pin is generated from the Moonlight client during the pairing process.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "pin": "<pin>",
|
||||
* "name": "Friendly Client Name"
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
savePin(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -827,24 +940,6 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unpairAll(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
pt::ptree outputTree;
|
||||
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
nvhttp::erase_all_clients();
|
||||
proc::proc.terminate();
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
|
||||
void
|
||||
updateClient(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -877,6 +972,40 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpair all clients.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
unpairAll(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
|
||||
print_req(request);
|
||||
|
||||
pt::ptree outputTree;
|
||||
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
nvhttp::erase_all_clients();
|
||||
proc::proc.terminate();
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unpair a client.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
* The body for the post request should be JSON serialized in the following format:
|
||||
* @code{.json}
|
||||
* {
|
||||
* "uuid": "<uuid>"
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void
|
||||
unpair(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -998,6 +1127,11 @@ namespace confighttp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the list of paired clients.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
listClients(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
@@ -1020,6 +1154,11 @@ namespace confighttp {
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the currently running application.
|
||||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
closeApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
|
||||
@@ -418,7 +418,7 @@ namespace platf {
|
||||
* @note Implementations may set or modify codec options prior to codec initialization.
|
||||
*/
|
||||
virtual void
|
||||
init_codec_options(AVCodecContext *ctx, AVDictionary *options) {};
|
||||
init_codec_options(AVCodecContext *ctx, AVDictionary **options) {};
|
||||
|
||||
/**
|
||||
* @brief Prepare to derive a context.
|
||||
@@ -861,6 +861,13 @@ namespace platf {
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
init();
|
||||
|
||||
/**
|
||||
* @brief Returns the current computer name in UTF-8.
|
||||
* @return Computer name or a placeholder upon failure.
|
||||
*/
|
||||
std::string
|
||||
get_host_name();
|
||||
|
||||
/**
|
||||
* @brief Gets the supported gamepads for this platform backend.
|
||||
* @details This may be called prior to `platf::input()`!
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// lib includes
|
||||
#include <arpa/inet.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/host_name.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
@@ -324,22 +325,24 @@ get_local_ip_for_gateway() {
|
||||
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
// clang-format off
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > bp::null, bp::std_err > bp::null, bp::limit_handles, ec);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > file, bp::std_err > file, bp::limit_handles, ec);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > bp::null, bp::std_err > bp::null, bp::limit_handles, ec, *group);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > file, bp::std_err > file, bp::limit_handles, ec, *group);
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -878,6 +881,17 @@ get_local_ip_for_gateway() {
|
||||
return std::make_unique<qos_t>(sockfd, reset_options);
|
||||
}
|
||||
|
||||
std::string
|
||||
get_host_name() {
|
||||
try {
|
||||
return boost::asio::ip::host_name();
|
||||
}
|
||||
catch (boost::system::system_error &err) {
|
||||
BOOST_LOG(error) << "Failed to get hostname: "sv << err.what();
|
||||
return "Sunshine"s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace source {
|
||||
enum source_e : std::size_t {
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace platf::publish {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto instance_name = net::mdns_instance_name(boost::asio::ip::host_name());
|
||||
auto instance_name = net::mdns_instance_name(platf::get_host_name());
|
||||
name.reset(avahi::strdup(instance_name.c_str()));
|
||||
|
||||
client.reset(
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <va/va.h>
|
||||
#include <va/va_drm.h>
|
||||
#if !VA_CHECK_VERSION(1, 9, 0)
|
||||
@@ -129,13 +130,173 @@ namespace va {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds a supported VA entrypoint for the given VA profile.
|
||||
* @param profile The profile to match.
|
||||
* @return A valid encoding entrypoint or 0 on failure.
|
||||
*/
|
||||
VAEntrypoint
|
||||
select_va_entrypoint(VAProfile profile) {
|
||||
std::vector<VAEntrypoint> entrypoints(vaMaxNumEntrypoints(va_display));
|
||||
int num_eps;
|
||||
auto status = vaQueryConfigEntrypoints(va_display, profile, entrypoints.data(), &num_eps);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
BOOST_LOG(error) << "Failed to query VA entrypoints: "sv << vaErrorStr(status);
|
||||
return (VAEntrypoint) 0;
|
||||
}
|
||||
entrypoints.resize(num_eps);
|
||||
|
||||
// Sorted in order of descending preference
|
||||
VAEntrypoint ep_preferences[] = {
|
||||
VAEntrypointEncSliceLP,
|
||||
VAEntrypointEncSlice,
|
||||
VAEntrypointEncPicture
|
||||
};
|
||||
for (auto ep_pref : ep_preferences) {
|
||||
if (std::find(entrypoints.begin(), entrypoints.end(), ep_pref) != entrypoints.end()) {
|
||||
return ep_pref;
|
||||
}
|
||||
}
|
||||
|
||||
return (VAEntrypoint) 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if a given VA profile is supported.
|
||||
* @param profile The profile to match.
|
||||
* @return Boolean value indicating if the profile is supported.
|
||||
*/
|
||||
bool
|
||||
is_va_profile_supported(VAProfile profile) {
|
||||
std::vector<VAProfile> profiles(vaMaxNumProfiles(va_display));
|
||||
int num_profs;
|
||||
auto status = vaQueryConfigProfiles(va_display, profiles.data(), &num_profs);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
BOOST_LOG(error) << "Failed to query VA profiles: "sv << vaErrorStr(status);
|
||||
return false;
|
||||
}
|
||||
profiles.resize(num_profs);
|
||||
|
||||
return std::find(profiles.begin(), profiles.end(), profile) != profiles.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines the matching VA profile for the codec configuration.
|
||||
* @param ctx The FFmpeg codec context.
|
||||
* @return The matching VA profile or `VAProfileNone` on failure.
|
||||
*/
|
||||
VAProfile
|
||||
get_va_profile(AVCodecContext *ctx) {
|
||||
if (ctx->codec_id == AV_CODEC_ID_H264) {
|
||||
// There's no VAAPI profile for H.264 4:4:4
|
||||
return VAProfileH264High;
|
||||
}
|
||||
else if (ctx->codec_id == AV_CODEC_ID_HEVC) {
|
||||
switch (ctx->profile) {
|
||||
case FF_PROFILE_HEVC_REXT:
|
||||
switch (av_pix_fmt_desc_get(ctx->sw_pix_fmt)->comp[0].depth) {
|
||||
case 10:
|
||||
return VAProfileHEVCMain444_10;
|
||||
case 8:
|
||||
return VAProfileHEVCMain444;
|
||||
}
|
||||
break;
|
||||
case FF_PROFILE_HEVC_MAIN_10:
|
||||
return VAProfileHEVCMain10;
|
||||
case FF_PROFILE_HEVC_MAIN:
|
||||
return VAProfileHEVCMain;
|
||||
}
|
||||
}
|
||||
else if (ctx->codec_id == AV_CODEC_ID_AV1) {
|
||||
switch (ctx->profile) {
|
||||
case FF_PROFILE_AV1_HIGH:
|
||||
return VAProfileAV1Profile1;
|
||||
case FF_PROFILE_AV1_MAIN:
|
||||
return VAProfileAV1Profile0;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG(error) << "Unknown encoder profile: "sv << ctx->profile;
|
||||
return VAProfileNone;
|
||||
}
|
||||
|
||||
void
|
||||
init_codec_options(AVCodecContext *ctx, AVDictionary *options) override {
|
||||
// Don't set the RC buffer size when using H.264 on Intel GPUs. It causes
|
||||
// major encoding quality degradation.
|
||||
init_codec_options(AVCodecContext *ctx, AVDictionary **options) override {
|
||||
auto va_profile = get_va_profile(ctx);
|
||||
if (va_profile == VAProfileNone || !is_va_profile_supported(va_profile)) {
|
||||
// Don't bother doing anything if the profile isn't supported
|
||||
return;
|
||||
}
|
||||
|
||||
auto va_entrypoint = select_va_entrypoint(va_profile);
|
||||
if (va_entrypoint == 0) {
|
||||
// It's possible that only decoding is supported for this profile
|
||||
return;
|
||||
}
|
||||
|
||||
auto vendor = vaQueryVendorString(va_display);
|
||||
if (ctx->codec_id != AV_CODEC_ID_H264 || (vendor && !strstr(vendor, "Intel"))) {
|
||||
|
||||
if (va_entrypoint == VAEntrypointEncSliceLP) {
|
||||
BOOST_LOG(info) << "Using LP encoding mode"sv;
|
||||
av_dict_set_int(options, "low_power", 1, 0);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Using normal encoding mode"sv;
|
||||
}
|
||||
|
||||
VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
|
||||
auto status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &rc_attr, 1);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
// Stick to the default rate control (CQP)
|
||||
rc_attr.value = 0;
|
||||
}
|
||||
|
||||
VAConfigAttrib slice_attr = { VAConfigAttribEncMaxSlices };
|
||||
status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &slice_attr, 1);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
// Assume only a single slice is supported
|
||||
slice_attr.value = 1;
|
||||
}
|
||||
if (ctx->slices > slice_attr.value) {
|
||||
BOOST_LOG(info) << "Limiting slice count to encoder maximum: "sv << slice_attr.value;
|
||||
ctx->slices = slice_attr.value;
|
||||
}
|
||||
|
||||
// Use VBR with a single frame VBV when the user forces it and for known good cases:
|
||||
// - Intel GPUs
|
||||
// - AV1
|
||||
//
|
||||
// VBR ensures the bitstream isn't full of filler data for bitrate undershoots and
|
||||
// single frame VBV ensures that we don't have large bitrate overshoots (at least
|
||||
// as much as they can be avoided without pre-analysis).
|
||||
//
|
||||
// When we have to resort to the default 1 second VBV for encoding quality reasons,
|
||||
// we stick to CBR in order to avoid encoding huge frames after bitrate undershoots
|
||||
// leave headroom available in the RC window.
|
||||
if (config::video.vaapi.strict_rc_buffer ||
|
||||
(vendor && strstr(vendor, "Intel")) ||
|
||||
ctx->codec_id == AV_CODEC_ID_AV1) {
|
||||
ctx->rc_buffer_size = ctx->bit_rate * ctx->framerate.den / ctx->framerate.num;
|
||||
|
||||
if (rc_attr.value & VA_RC_VBR) {
|
||||
BOOST_LOG(info) << "Using VBR with single frame VBV size"sv;
|
||||
av_dict_set(options, "rc_mode", "VBR", 0);
|
||||
}
|
||||
else if (rc_attr.value & VA_RC_CBR) {
|
||||
BOOST_LOG(info) << "Using CBR with single frame VBV size"sv;
|
||||
av_dict_set(options, "rc_mode", "CBR", 0);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Using CQP with single frame VBV size"sv;
|
||||
av_dict_set_int(options, "qp", config::video.qp, 0);
|
||||
}
|
||||
}
|
||||
else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
|
||||
BOOST_LOG(warning) << "Using CQP rate control"sv;
|
||||
av_dict_set_int(options, "qp", config::video.qp, 0);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Using default rate control"sv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "src/platform/common.h"
|
||||
#include "src/platform/macos/av_img_t.h"
|
||||
#include "src/platform/macos/av_video.h"
|
||||
#include "src/platform/macos/misc.h"
|
||||
#include "src/platform/macos/nv12_zero_device.h"
|
||||
|
||||
#include "src/config.h"
|
||||
@@ -100,6 +101,13 @@ namespace platf {
|
||||
|
||||
int
|
||||
dummy_img(img_t *img) override {
|
||||
if (!platf::is_screen_capture_allowed()) {
|
||||
// If we don't have the screen capture permission, this function will hang
|
||||
// indefinitely without doing anything useful. Exit instead to avoid this.
|
||||
// A non-zero return value indicates failure to the calling function.
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) {
|
||||
auto new_sample_buffer = std::make_shared<av_sample_buf_t>(sampleBuffer);
|
||||
auto new_pixel_buffer = std::make_shared<av_pixel_buf_t>(new_sample_buffer->buf);
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
namespace platf {
|
||||
bool
|
||||
is_screen_capture_allowed();
|
||||
}
|
||||
|
||||
namespace dyn {
|
||||
typedef void (*apiproc)();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/host_name.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
@@ -42,6 +43,16 @@ namespace platf {
|
||||
CGRequestScreenCaptureAccess(void) __attribute__((weak_import));
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
auto screen_capture_allowed = std::atomic<bool> { false };
|
||||
} // namespace
|
||||
|
||||
// Return whether screen capture is allowed for this process.
|
||||
bool
|
||||
is_screen_capture_allowed() {
|
||||
return screen_capture_allowed;
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
// This will generate a warning about CGPreflightScreenCaptureAccess and
|
||||
@@ -68,6 +79,8 @@ namespace platf {
|
||||
return nullptr;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
// Record that we determined that we have the screen capture permission.
|
||||
screen_capture_allowed = true;
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
|
||||
@@ -176,22 +189,24 @@ namespace platf {
|
||||
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
// clang-format off
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > bp::null, bp::std_err > bp::null, bp::limit_handles, ec);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > file, bp::std_err > file, bp::limit_handles, ec);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > bp::null, bp::std_err > bp::null, bp::limit_handles, ec, *group);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group);
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_in < bp::null, bp::std_out > file, bp::std_err > file, bp::limit_handles, ec, *group);
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,6 +545,17 @@ namespace platf {
|
||||
return std::make_unique<qos_t>(sockfd, reset_options);
|
||||
}
|
||||
|
||||
std::string
|
||||
get_host_name() {
|
||||
try {
|
||||
return boost::asio::ip::host_name();
|
||||
}
|
||||
catch (boost::system::system_error &err) {
|
||||
BOOST_LOG(error) << "Failed to get hostname: "sv << err.what();
|
||||
return "Sunshine"s;
|
||||
}
|
||||
}
|
||||
|
||||
class macos_high_precision_timer: public high_precision_timer {
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -1896,6 +1896,16 @@ namespace platf {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_host_name() {
|
||||
WCHAR hostname[256];
|
||||
if (GetHostNameW(hostname, ARRAYSIZE(hostname)) == SOCKET_ERROR) {
|
||||
BOOST_LOG(error) << "GetHostNameW() failed: "sv << WSAGetLastError();
|
||||
return "Sunshine"s;
|
||||
}
|
||||
return to_utf8(hostname);
|
||||
}
|
||||
|
||||
class win32_high_precision_timer: public high_precision_timer {
|
||||
public:
|
||||
win32_high_precision_timer() {
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include <windns.h>
|
||||
#include <winerror.h>
|
||||
|
||||
#include <boost/asio/ip/host_name.hpp>
|
||||
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
@@ -108,7 +106,7 @@ namespace platf::publish {
|
||||
|
||||
std::wstring domain { SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size() };
|
||||
|
||||
auto hostname = boost::asio::ip::host_name();
|
||||
auto hostname = platf::get_host_name();
|
||||
auto name = from_utf8(net::mdns_instance_name(hostname) + '.') + domain;
|
||||
auto host = from_utf8(hostname + ".local");
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ namespace proc {
|
||||
|
||||
// We always call terminate() even if we waited successfully for all processes above.
|
||||
// This ensures the process group state is consistent with the OS in boost.
|
||||
group.terminate();
|
||||
std::error_code ec;
|
||||
group.terminate(ec);
|
||||
group.detach();
|
||||
}
|
||||
|
||||
@@ -462,6 +463,16 @@ namespace proc {
|
||||
|
||||
int
|
||||
proc_t::running() {
|
||||
#ifndef _WIN32
|
||||
// On POSIX OSes, we must periodically wait for our children to avoid
|
||||
// them becoming zombies. This must be synchronized carefully with
|
||||
// calls to bp::wait() and platf::process_group_running() which both
|
||||
// invoke waitpid() under the hood.
|
||||
auto reaper = util::fail_guard([]() {
|
||||
while (waitpid(-1, nullptr, WNOHANG) > 0);
|
||||
});
|
||||
#endif
|
||||
|
||||
if (placebo) {
|
||||
return _app_id;
|
||||
}
|
||||
|
||||
@@ -588,10 +588,6 @@ namespace rtsp_stream {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (all && !ios.stopped()) {
|
||||
ios.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
143
src/video.cpp
143
src/video.cpp
@@ -463,7 +463,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"av1_nvenc"s,
|
||||
},
|
||||
{
|
||||
@@ -473,7 +472,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"hevc_nvenc"s,
|
||||
},
|
||||
{
|
||||
@@ -483,7 +481,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"h264_nvenc"s,
|
||||
},
|
||||
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION | YUV444_SUPPORT // flags
|
||||
@@ -526,7 +523,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"av1_nvenc"s,
|
||||
},
|
||||
{
|
||||
@@ -553,7 +549,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"hevc_nvenc"s,
|
||||
},
|
||||
{
|
||||
@@ -577,7 +572,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"h264_nvenc"s,
|
||||
},
|
||||
PARALLEL_ENCODING
|
||||
@@ -619,7 +613,6 @@ namespace video {
|
||||
{ "profile"s, (int) qsv::profile_av1_e::high },
|
||||
},
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"av1_qsv"s,
|
||||
},
|
||||
{
|
||||
@@ -653,7 +646,6 @@ namespace video {
|
||||
// Fallback options
|
||||
{ "low_power"s, []() { return config::video.qsv.qsv_slow_hevc ? 0 : 1; } },
|
||||
},
|
||||
std::nullopt, // QP rate control fallback
|
||||
"hevc_qsv"s,
|
||||
},
|
||||
{
|
||||
@@ -684,7 +676,6 @@ namespace video {
|
||||
// Fallback options
|
||||
{ "low_power"s, 0 }, // Some old/low-end Intel GPUs don't support low power encoding
|
||||
},
|
||||
std::nullopt, // QP rate control fallback
|
||||
"h264_qsv"s,
|
||||
},
|
||||
PARALLEL_ENCODING | CBR_WITH_VBR | RELAXED_COMPLIANCE | NO_RC_BUF_LIMIT | YUV444_SUPPORT
|
||||
@@ -717,7 +708,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"av1_amf"s,
|
||||
},
|
||||
{
|
||||
@@ -742,7 +732,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"hevc_amf"s,
|
||||
},
|
||||
{
|
||||
@@ -768,7 +757,6 @@ namespace video {
|
||||
// Fallback options
|
||||
{ "usage"s, 2 /* AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY */ }, // Workaround for https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/410
|
||||
},
|
||||
std::nullopt, // QP rate control fallback
|
||||
"h264_amf"s,
|
||||
},
|
||||
PARALLEL_ENCODING
|
||||
@@ -798,13 +786,10 @@ namespace video {
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
|
||||
// QP rate control fallback
|
||||
std::nullopt,
|
||||
|
||||
#ifdef ENABLE_BROKEN_AV1_ENCODER
|
||||
// Due to bugs preventing on-demand IDR frames from working and very poor
|
||||
// real-time encoding performance, we do not enable libsvtav1 by default.
|
||||
// It is only suitable for testing AV1 until the IDR frame issue is fixed.
|
||||
// Due to bugs preventing on-demand IDR frames from working and very poor
|
||||
// real-time encoding performance, we do not enable libsvtav1 by default.
|
||||
// It is only suitable for testing AV1 until the IDR frame issue is fixed.
|
||||
"libsvtav1"s,
|
||||
#else
|
||||
{},
|
||||
@@ -826,7 +811,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"libx265"s,
|
||||
},
|
||||
{
|
||||
@@ -840,7 +824,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt, // QP rate control fallback
|
||||
"libx264"s,
|
||||
},
|
||||
H264_ONLY | PARALLEL_ENCODING | ALWAYS_REPROBE | YUV444_SUPPORT
|
||||
@@ -858,7 +841,6 @@ namespace video {
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "low_power"s, 1 },
|
||||
{ "async_depth"s, 1 },
|
||||
{ "idr_interval"s, std::numeric_limits<int>::max() },
|
||||
},
|
||||
@@ -866,17 +848,12 @@ namespace video {
|
||||
{}, // HDR-specific options
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{
|
||||
// Fallback options
|
||||
{ "low_power"s, 0 }, // Not all VAAPI drivers expose LP entrypoints
|
||||
},
|
||||
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
{}, // Fallback options
|
||||
"av1_vaapi"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "low_power"s, 1 },
|
||||
{ "async_depth"s, 1 },
|
||||
{ "sei"s, 0 },
|
||||
{ "idr_interval"s, std::numeric_limits<int>::max() },
|
||||
@@ -885,17 +862,12 @@ namespace video {
|
||||
{}, // HDR-specific options
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{
|
||||
// Fallback options
|
||||
{ "low_power"s, 0 }, // Not all VAAPI drivers expose LP entrypoints
|
||||
},
|
||||
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
{}, // Fallback options
|
||||
"hevc_vaapi"s,
|
||||
},
|
||||
{
|
||||
// Common options
|
||||
{
|
||||
{ "low_power"s, 1 },
|
||||
{ "async_depth"s, 1 },
|
||||
{ "sei"s, 0 },
|
||||
{ "idr_interval"s, std::numeric_limits<int>::max() },
|
||||
@@ -904,15 +876,11 @@ namespace video {
|
||||
{}, // HDR-specific options
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{
|
||||
// Fallback options
|
||||
{ "low_power"s, 0 }, // Not all VAAPI drivers expose LP entrypoints
|
||||
},
|
||||
std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
{}, // Fallback options
|
||||
"h264_vaapi"s,
|
||||
},
|
||||
// RC buffer size will be set in platform code if supported
|
||||
LIMITED_GOP_SIZE | PARALLEL_ENCODING | SINGLE_SLICE_ONLY | NO_RC_BUF_LIMIT
|
||||
LIMITED_GOP_SIZE | PARALLEL_ENCODING | NO_RC_BUF_LIMIT
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -939,7 +907,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt,
|
||||
"av1_videotoolbox"s,
|
||||
},
|
||||
{
|
||||
@@ -956,7 +923,6 @@ namespace video {
|
||||
{}, // YUV444 SDR-specific options
|
||||
{}, // YUV444 HDR-specific options
|
||||
{}, // Fallback options
|
||||
std::nullopt,
|
||||
"hevc_videotoolbox"s,
|
||||
},
|
||||
{
|
||||
@@ -976,7 +942,6 @@ namespace video {
|
||||
// Fallback options
|
||||
{ "flags"s, "-low_delay" },
|
||||
},
|
||||
std::nullopt,
|
||||
"h264_videotoolbox"s,
|
||||
},
|
||||
DEFAULT
|
||||
@@ -1664,52 +1629,43 @@ namespace video {
|
||||
}
|
||||
}
|
||||
|
||||
if (video_format[encoder_t::CBR]) {
|
||||
auto bitrate = config.bitrate * 1000;
|
||||
ctx->rc_max_rate = bitrate;
|
||||
ctx->bit_rate = bitrate;
|
||||
auto bitrate = config.bitrate * 1000;
|
||||
ctx->rc_max_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;
|
||||
}
|
||||
|
||||
if (encoder.flags & RELAXED_COMPLIANCE) {
|
||||
ctx->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
|
||||
}
|
||||
|
||||
if (!(encoder.flags & NO_RC_BUF_LIMIT)) {
|
||||
if (!hardware && (ctx->slices > 1 || config.videoFormat == 1)) {
|
||||
// Use a larger rc_buffer_size for software encoding when slices are enabled,
|
||||
// because libx264 can severely degrade quality if the buffer is too small.
|
||||
// libx265 encounters this issue more frequently, so always scale the
|
||||
// buffer by 1.5x for software HEVC encoding.
|
||||
ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15);
|
||||
}
|
||||
else {
|
||||
ctx->rc_buffer_size = bitrate / config.framerate;
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (encoder.name == "nvenc" && config::video.nv_legacy.vbv_percentage_increase > 0) {
|
||||
ctx->rc_buffer_size += ctx->rc_buffer_size * config::video.nv_legacy.vbv_percentage_increase / 100;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (video_format.qp) {
|
||||
handle_option(*video_format.qp);
|
||||
if (encoder.flags & CBR_WITH_VBR) {
|
||||
// Ensure rc_max_bitrate != bit_rate to force VBR mode
|
||||
ctx->bit_rate--;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't set video quality: encoder "sv << encoder.name << " doesn't support qp"sv;
|
||||
return nullptr;
|
||||
ctx->rc_min_rate = bitrate;
|
||||
}
|
||||
|
||||
if (encoder.flags & RELAXED_COMPLIANCE) {
|
||||
ctx->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
|
||||
}
|
||||
|
||||
if (!(encoder.flags & NO_RC_BUF_LIMIT)) {
|
||||
if (!hardware && (ctx->slices > 1 || config.videoFormat == 1)) {
|
||||
// Use a larger rc_buffer_size for software encoding when slices are enabled,
|
||||
// because libx264 can severely degrade quality if the buffer is too small.
|
||||
// libx265 encounters this issue more frequently, so always scale the
|
||||
// buffer by 1.5x for software HEVC encoding.
|
||||
ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15);
|
||||
}
|
||||
else {
|
||||
ctx->rc_buffer_size = bitrate / config.framerate;
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (encoder.name == "nvenc" && config::video.nv_legacy.vbv_percentage_increase > 0) {
|
||||
ctx->rc_buffer_size += ctx->rc_buffer_size * config::video.nv_legacy.vbv_percentage_increase / 100;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Allow the encoding device a final opportunity to set/unset or override any options
|
||||
encode_device->init_codec_options(ctx.get(), options);
|
||||
encode_device->init_codec_options(ctx.get(), &options);
|
||||
|
||||
if (auto status = avcodec_open2(ctx.get(), codec, &options)) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
@@ -2432,18 +2388,11 @@ namespace video {
|
||||
return false;
|
||||
}
|
||||
|
||||
retry:
|
||||
// If we're expecting failure, use the autoselect ref config first since that will always succeed
|
||||
// if the encoder is available.
|
||||
auto max_ref_frames_h264 = expect_failure ? -1 : validate_config(disp, encoder, config_max_ref_frames);
|
||||
auto autoselect_h264 = max_ref_frames_h264 >= 0 ? max_ref_frames_h264 : validate_config(disp, encoder, config_autoselect);
|
||||
if (autoselect_h264 < 0) {
|
||||
if (encoder.h264.qp && encoder.h264[encoder_t::CBR]) {
|
||||
// It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
|
||||
encoder.h264.capabilities.set();
|
||||
encoder.h264[encoder_t::CBR] = false;
|
||||
goto retry;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (expect_failure) {
|
||||
@@ -2467,7 +2416,6 @@ namespace video {
|
||||
config_autoselect.videoFormat = 1;
|
||||
|
||||
if (disp->is_codec_supported(encoder.hevc.name, config_autoselect)) {
|
||||
retry_hevc:
|
||||
auto max_ref_frames_hevc = validate_config(disp, encoder, config_max_ref_frames);
|
||||
|
||||
// If H.264 succeeded with max ref frames specified, assume that we can count on
|
||||
@@ -2476,13 +2424,6 @@ namespace video {
|
||||
max_ref_frames_hevc :
|
||||
validate_config(disp, encoder, config_autoselect);
|
||||
|
||||
if (autoselect_hevc < 0 && encoder.hevc.qp && encoder.hevc[encoder_t::CBR]) {
|
||||
// It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
|
||||
encoder.hevc.capabilities.set();
|
||||
encoder.hevc[encoder_t::CBR] = false;
|
||||
goto retry_hevc;
|
||||
}
|
||||
|
||||
for (auto [validate_flag, encoder_flag] : packet_deficiencies) {
|
||||
encoder.hevc[encoder_flag] = (max_ref_frames_hevc & validate_flag && autoselect_hevc & validate_flag);
|
||||
}
|
||||
@@ -2505,7 +2446,6 @@ namespace video {
|
||||
config_autoselect.videoFormat = 2;
|
||||
|
||||
if (disp->is_codec_supported(encoder.av1.name, config_autoselect)) {
|
||||
retry_av1:
|
||||
auto max_ref_frames_av1 = validate_config(disp, encoder, config_max_ref_frames);
|
||||
|
||||
// If H.264 succeeded with max ref frames specified, assume that we can count on
|
||||
@@ -2514,13 +2454,6 @@ namespace video {
|
||||
max_ref_frames_av1 :
|
||||
validate_config(disp, encoder, config_autoselect);
|
||||
|
||||
if (autoselect_av1 < 0 && encoder.av1.qp && encoder.av1[encoder_t::CBR]) {
|
||||
// It's possible the encoder isn't accepting Constant Bit Rate. Turn off CBR and make another attempt
|
||||
encoder.av1.capabilities.set();
|
||||
encoder.av1[encoder_t::CBR] = false;
|
||||
goto retry_av1;
|
||||
}
|
||||
|
||||
for (auto [validate_flag, encoder_flag] : packet_deficiencies) {
|
||||
encoder.av1[encoder_flag] = (max_ref_frames_av1 & validate_flag && autoselect_av1 & validate_flag);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,6 @@ namespace video {
|
||||
enum flag_e {
|
||||
PASSED, ///< Indicates the encoder is supported.
|
||||
REF_FRAMES_RESTRICT, ///< Set maximum reference frames.
|
||||
CBR, ///< Some encoders don't support CBR, if not supported attempt constant quantization parameter instead.
|
||||
DYNAMIC_RANGE, ///< HDR support.
|
||||
YUV444, ///< YUV 4:4:4 support.
|
||||
VUI_PARAMETERS, ///< AMD encoder with VAAPI doesn't add VUI parameters to SPS.
|
||||
@@ -135,7 +134,6 @@ namespace video {
|
||||
switch (flag) {
|
||||
_CONVERT(PASSED);
|
||||
_CONVERT(REF_FRAMES_RESTRICT);
|
||||
_CONVERT(CBR);
|
||||
_CONVERT(DYNAMIC_RANGE);
|
||||
_CONVERT(YUV444);
|
||||
_CONVERT(VUI_PARAMETERS);
|
||||
@@ -167,11 +165,6 @@ namespace video {
|
||||
std::vector<option_t> hdr444_options;
|
||||
std::vector<option_t> fallback_options;
|
||||
|
||||
// QP option to set in the case that CBR/VBR is not supported
|
||||
// by the encoder. If CBR/VBR is guaranteed to be supported,
|
||||
// don't specify this option to avoid wasteful encoder probing.
|
||||
std::optional<option_t> qp;
|
||||
|
||||
std::string name;
|
||||
std::bitset<MAX_FLAGS> capabilities;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user