mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-10 16:14:36 +00:00
Reprobe encoders each time streaming begins
Available encoders can change due to driver updates, GPU hotplugging, primary monitor changes, etc.
This commit is contained in:
parent
44f89de33b
commit
6467e10def
@ -356,8 +356,8 @@ main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
reed_solomon_init();
|
reed_solomon_init();
|
||||||
auto input_deinit_guard = input::init();
|
auto input_deinit_guard = input::init();
|
||||||
if (video::init()) {
|
if (video::probe_encoders()) {
|
||||||
BOOST_LOG(error) << "Video failed to initialize"sv;
|
BOOST_LOG(error) << "Video failed to find working encoder"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http::init()) {
|
if (http::init()) {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "rtsp.h"
|
#include "rtsp.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "uuid.h"
|
#include "uuid.h"
|
||||||
|
#include "video.h"
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
namespace nvhttp {
|
namespace nvhttp {
|
||||||
@ -762,6 +763,19 @@ namespace nvhttp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Probe encoders again before streaming to ensure our chosen
|
||||||
|
// encoder matches the active GPU (which could have changed
|
||||||
|
// due to hotplugging, driver crash, primary monitor change,
|
||||||
|
// or any number of other factors).
|
||||||
|
if (rtsp_stream::session_count() == 0) {
|
||||||
|
if (video::probe_encoders()) {
|
||||||
|
tree.put("root.<xmlattr>.status_code", 503);
|
||||||
|
tree.put("root.gamesession", 0);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (appid > 0) {
|
if (appid > 0) {
|
||||||
auto err = proc::proc.execute(appid);
|
auto err = proc::proc.execute(appid);
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -820,6 +834,19 @@ namespace nvhttp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Probe encoders again before streaming to ensure our chosen
|
||||||
|
// encoder matches the active GPU (which could have changed
|
||||||
|
// due to hotplugging, driver crash, primary monitor change,
|
||||||
|
// or any number of other factors).
|
||||||
|
if (rtsp_stream::session_count() == 0) {
|
||||||
|
if (video::probe_encoders()) {
|
||||||
|
tree.put("root.resume", 0);
|
||||||
|
tree.put("root.<xmlattr>.status_code", 503);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rtsp_stream::launch_session_raise(make_launch_session(host_audio, args));
|
rtsp_stream::launch_session_raise(make_launch_session(host_audio, args));
|
||||||
|
|
||||||
tree.put("root.<xmlattr>.status_code", 200);
|
tree.put("root.<xmlattr>.status_code", 200);
|
||||||
|
@ -710,23 +710,25 @@ namespace video {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static std::vector<encoder_t> encoders {
|
static const std::vector<encoder_t *> encoders {
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
nvenc,
|
&nvenc,
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
quicksync,
|
&quicksync,
|
||||||
amdvce,
|
&amdvce,
|
||||||
#endif
|
#endif
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
vaapi,
|
&vaapi,
|
||||||
#endif
|
#endif
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
videotoolbox,
|
&videotoolbox,
|
||||||
#endif
|
#endif
|
||||||
software
|
&software
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static encoder_t *chosen_encoder;
|
||||||
|
|
||||||
void
|
void
|
||||||
reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type, const std::string &display_name, const config_t &config) {
|
reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type, const std::string &display_name, const config_t &config) {
|
||||||
// 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
|
||||||
@ -1446,7 +1448,7 @@ namespace video {
|
|||||||
encode_run_sync(
|
encode_run_sync(
|
||||||
std::vector<std::unique_ptr<sync_session_ctx_t>> &synced_session_ctxs,
|
std::vector<std::unique_ptr<sync_session_ctx_t>> &synced_session_ctxs,
|
||||||
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 = *chosen_encoder;
|
||||||
auto display_names = platf::display_names(map_base_dev_type(encoder.base_dev_type));
|
auto display_names = platf::display_names(map_base_dev_type(encoder.base_dev_type));
|
||||||
int display_p = 0;
|
int display_p = 0;
|
||||||
|
|
||||||
@ -1673,7 +1675,7 @@ namespace video {
|
|||||||
display = ref->display_wp->lock();
|
display = ref->display_wp->lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &encoder = encoders.front();
|
auto &encoder = *chosen_encoder;
|
||||||
auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
||||||
auto hwdevice = display->make_hwdevice(pix_fmt);
|
auto hwdevice = display->make_hwdevice(pix_fmt);
|
||||||
if (!hwdevice) {
|
if (!hwdevice) {
|
||||||
@ -1709,7 +1711,7 @@ namespace video {
|
|||||||
auto idr_events = mail->event<bool>(mail::idr);
|
auto idr_events = mail->event<bool>(mail::idr);
|
||||||
|
|
||||||
idr_events->raise(true);
|
idr_events->raise(true);
|
||||||
if (encoders.front().flags & PARALLEL_ENCODING) {
|
if (chosen_encoder->flags & PARALLEL_ENCODING) {
|
||||||
capture_async(std::move(mail), config, channel_data);
|
capture_async(std::move(mail), config, channel_data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1927,86 +1929,95 @@ namespace video {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is called once at startup and each time a stream is launched to
|
||||||
|
ensure the best encoder is selected. Encoder availablility can change
|
||||||
|
at runtime due to all sorts of things from driver updates to eGPUs.
|
||||||
|
|
||||||
|
This is only safe to call when there is no client actively streaming.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
init() {
|
probe_encoders() {
|
||||||
bool encoder_found = false;
|
auto encoder_list = encoders;
|
||||||
|
|
||||||
|
// Reset encoder selection
|
||||||
|
chosen_encoder = nullptr;
|
||||||
|
|
||||||
if (!config::video.encoder.empty()) {
|
if (!config::video.encoder.empty()) {
|
||||||
// If there is a specific encoder specified, use it if it passes validation
|
// If there is a specific encoder specified, use it if it passes validation
|
||||||
KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), {
|
KITTY_WHILE_LOOP(auto pos = std::begin(encoder_list), pos != std::end(encoder_list), {
|
||||||
auto encoder = *pos;
|
auto encoder = *pos;
|
||||||
|
|
||||||
if (encoder.name == config::video.encoder) {
|
if (encoder->name == config::video.encoder) {
|
||||||
// Remove the encoder from the list entirely if it fails validation
|
// Remove the encoder from the list entirely if it fails validation
|
||||||
if (!validate_encoder(encoder)) {
|
if (!validate_encoder(*encoder)) {
|
||||||
pos = encoders.erase(pos);
|
pos = encoder_list.erase(pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we can't satisfy both the encoder and HDR requirement, prefer the encoder over HDR support
|
// If we can't satisfy both the encoder and HDR requirement, prefer the encoder over HDR support
|
||||||
if (config::video.hevc_mode == 3 && !encoder.hevc[encoder_t::DYNAMIC_RANGE]) {
|
if (config::video.hevc_mode == 3 && !encoder->hevc[encoder_t::DYNAMIC_RANGE]) {
|
||||||
BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support HDR on this system"sv;
|
BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support HDR on this system"sv;
|
||||||
config::video.hevc_mode = 0;
|
config::video.hevc_mode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoders.clear();
|
chosen_encoder = encoder;
|
||||||
encoders.emplace_back(encoder);
|
|
||||||
encoder_found = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos++;
|
pos++;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!encoder_found) {
|
if (chosen_encoder == nullptr) {
|
||||||
BOOST_LOG(error) << "Couldn't find any working encoder matching ["sv << config::video.encoder << ']';
|
BOOST_LOG(error) << "Couldn't find any working encoder matching ["sv << config::video.encoder << ']';
|
||||||
config::video.encoder.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv;
|
BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv;
|
||||||
|
|
||||||
// If we haven't found an encoder yet, but we want one with HDR support, search for that now.
|
// If we haven't found an encoder yet, but we want one with HDR support, search for that now.
|
||||||
if (!encoder_found && config::video.hevc_mode == 3) {
|
if (chosen_encoder == nullptr && config::video.hevc_mode == 3) {
|
||||||
KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), {
|
KITTY_WHILE_LOOP(auto pos = std::begin(encoder_list), pos != std::end(encoder_list), {
|
||||||
auto encoder = *pos;
|
auto encoder = *pos;
|
||||||
|
|
||||||
// Remove the encoder from the list entirely if it fails validation
|
// Remove the encoder from the list entirely if it fails validation
|
||||||
if (!validate_encoder(encoder)) {
|
if (!validate_encoder(*encoder)) {
|
||||||
pos = encoders.erase(pos);
|
pos = encoder_list.erase(pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip it if it doesn't support HDR
|
// Skip it if it doesn't support HDR
|
||||||
if (!encoder.hevc[encoder_t::DYNAMIC_RANGE]) {
|
if (!encoder->hevc[encoder_t::DYNAMIC_RANGE]) {
|
||||||
pos++;
|
pos++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoders.clear();
|
chosen_encoder = encoder;
|
||||||
encoders.emplace_back(encoder);
|
|
||||||
encoder_found = true;
|
|
||||||
break;
|
break;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!encoder_found) {
|
if (chosen_encoder == nullptr) {
|
||||||
BOOST_LOG(error) << "Couldn't find any working HDR-capable encoder"sv;
|
BOOST_LOG(error) << "Couldn't find any working HDR-capable encoder"sv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no encoder was specified or the specified encoder was unusable, keep trying
|
// If no encoder was specified or the specified encoder was unusable, keep trying
|
||||||
// the remaining encoders until we find one that passes validation.
|
// the remaining encoders until we find one that passes validation.
|
||||||
if (!encoder_found) {
|
if (chosen_encoder == nullptr) {
|
||||||
KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), {
|
KITTY_WHILE_LOOP(auto pos = std::begin(encoder_list), pos != std::end(encoder_list), {
|
||||||
if (!validate_encoder(*pos)) {
|
auto encoder = *pos;
|
||||||
pos = encoders.erase(pos);
|
|
||||||
|
if (!validate_encoder(*encoder)) {
|
||||||
|
pos = encoder_list.erase(pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chosen_encoder = encoder;
|
||||||
break;
|
break;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encoders.empty()) {
|
if (chosen_encoder == nullptr) {
|
||||||
BOOST_LOG(fatal) << "Couldn't find any working encoder"sv;
|
BOOST_LOG(fatal) << "Couldn't find any working encoder"sv;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -2015,7 +2026,7 @@ namespace video {
|
|||||||
BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant. //"sv;
|
BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant. //"sv;
|
||||||
BOOST_LOG(info);
|
BOOST_LOG(info);
|
||||||
|
|
||||||
auto &encoder = encoders.front();
|
auto &encoder = *chosen_encoder;
|
||||||
|
|
||||||
BOOST_LOG(debug) << "------ h264 ------"sv;
|
BOOST_LOG(debug) << "------ h264 ------"sv;
|
||||||
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
||||||
@ -2147,7 +2158,7 @@ namespace video {
|
|||||||
|
|
||||||
int
|
int
|
||||||
start_capture_async(capture_thread_async_ctx_t &capture_thread_ctx) {
|
start_capture_async(capture_thread_async_ctx_t &capture_thread_ctx) {
|
||||||
capture_thread_ctx.encoder_p = &encoders.front();
|
capture_thread_ctx.encoder_p = chosen_encoder;
|
||||||
capture_thread_ctx.reinit_event.reset();
|
capture_thread_ctx.reinit_event.reset();
|
||||||
|
|
||||||
capture_thread_ctx.capture_ctx_queue = std::make_shared<safe::queue_t<capture_ctx_t>>(30);
|
capture_thread_ctx.capture_ctx_queue = std::make_shared<safe::queue_t<capture_ctx_t>>(30);
|
||||||
|
@ -97,7 +97,7 @@ namespace video {
|
|||||||
void *channel_data);
|
void *channel_data);
|
||||||
|
|
||||||
int
|
int
|
||||||
init();
|
probe_encoders();
|
||||||
} // namespace video
|
} // namespace video
|
||||||
|
|
||||||
#endif // SUNSHINE_VIDEO_H
|
#endif // SUNSHINE_VIDEO_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user