Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user