Implement input only mode w/ double framerate for virtual display
This commit is contained in:
@@ -132,6 +132,13 @@ namespace audio {
|
|||||||
void
|
void
|
||||||
capture(safe::mail_t mail, config_t config, void *channel_data) {
|
capture(safe::mail_t mail, config_t config, void *channel_data) {
|
||||||
auto shutdown_event = mail->event<bool>(mail::shutdown);
|
auto shutdown_event = mail->event<bool>(mail::shutdown);
|
||||||
|
|
||||||
|
if (config.input_only) {
|
||||||
|
BOOST_LOG(info) << "Input only session, audio will not be captured."sv;
|
||||||
|
shutdown_event->view();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||||
if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) {
|
if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) {
|
||||||
apply_surround_params(stream, config.customStreamParams);
|
apply_surround_params(stream, config.customStreamParams);
|
||||||
@@ -148,6 +155,7 @@ namespace audio {
|
|||||||
// Wait for shutdown to be signalled if we fail init.
|
// Wait for shutdown to be signalled if we fail init.
|
||||||
// This allows streaming to continue without audio.
|
// This allows streaming to continue without audio.
|
||||||
shutdown_event->view();
|
shutdown_event->view();
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto &control = ref->control;
|
auto &control = ref->control;
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ namespace audio {
|
|||||||
int channels;
|
int channels;
|
||||||
int mask;
|
int mask;
|
||||||
|
|
||||||
|
bool input_only;
|
||||||
|
|
||||||
stream_params_t customStreamParams;
|
stream_params_t customStreamParams;
|
||||||
|
|
||||||
std::bitset<MAX_FLAGS> flags;
|
std::bitset<MAX_FLAGS> flags;
|
||||||
|
|||||||
@@ -418,6 +418,7 @@ namespace config {
|
|||||||
video_t video {
|
video_t video {
|
||||||
false, // headless_mode
|
false, // headless_mode
|
||||||
true, // limit_framerate
|
true, // limit_framerate
|
||||||
|
false, // double_framerate
|
||||||
28, // qp
|
28, // qp
|
||||||
|
|
||||||
0, // hevc_mode
|
0, // hevc_mode
|
||||||
@@ -543,6 +544,7 @@ namespace config {
|
|||||||
true, // always send scancodes
|
true, // always send scancodes
|
||||||
true, // high resolution scrolling
|
true, // high resolution scrolling
|
||||||
true, // native pen/touch support
|
true, // native pen/touch support
|
||||||
|
false, // enable input only mode
|
||||||
};
|
};
|
||||||
|
|
||||||
sunshine_t sunshine {
|
sunshine_t sunshine {
|
||||||
@@ -1091,6 +1093,7 @@ namespace config {
|
|||||||
|
|
||||||
bool_f(vars, "headless_mode", video.headless_mode);
|
bool_f(vars, "headless_mode", video.headless_mode);
|
||||||
bool_f(vars, "limit_framerate", video.limit_framerate);
|
bool_f(vars, "limit_framerate", video.limit_framerate);
|
||||||
|
bool_f(vars, "double_framerate", video.double_framerate);
|
||||||
int_f(vars, "qp", video.qp);
|
int_f(vars, "qp", video.qp);
|
||||||
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
|
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
|
||||||
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });
|
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });
|
||||||
@@ -1263,6 +1266,7 @@ namespace config {
|
|||||||
|
|
||||||
bool_f(vars, "high_resolution_scrolling", input.high_resolution_scrolling);
|
bool_f(vars, "high_resolution_scrolling", input.high_resolution_scrolling);
|
||||||
bool_f(vars, "native_pen_touch", input.native_pen_touch);
|
bool_f(vars, "native_pen_touch", input.native_pen_touch);
|
||||||
|
bool_f(vars, "enable_input_only_mode", input.enable_input_only_mode);
|
||||||
|
|
||||||
bool_f(vars, "hide_tray_controls", sunshine.hide_tray_controls);
|
bool_f(vars, "hide_tray_controls", sunshine.hide_tray_controls);
|
||||||
bool_f(vars, "enable_pairing", sunshine.enable_pairing);
|
bool_f(vars, "enable_pairing", sunshine.enable_pairing);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace config {
|
|||||||
struct video_t {
|
struct video_t {
|
||||||
bool headless_mode;
|
bool headless_mode;
|
||||||
bool limit_framerate;
|
bool limit_framerate;
|
||||||
|
bool double_framerate;
|
||||||
// ffmpeg params
|
// ffmpeg params
|
||||||
int qp; // higher == more compression and less quality
|
int qp; // higher == more compression and less quality
|
||||||
|
|
||||||
@@ -198,6 +199,8 @@ namespace config {
|
|||||||
|
|
||||||
bool high_resolution_scrolling;
|
bool high_resolution_scrolling;
|
||||||
bool native_pen_touch;
|
bool native_pen_touch;
|
||||||
|
|
||||||
|
bool enable_input_only_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace flag {
|
namespace flag {
|
||||||
|
|||||||
@@ -1163,7 +1163,7 @@ namespace confighttp {
|
|||||||
|
|
||||||
BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv;
|
BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv;
|
||||||
|
|
||||||
auto launch_session = nvhttp::make_launch_session(true, appid, args, &named_cert);
|
auto launch_session = nvhttp::make_launch_session(true, false, appid, args, &named_cert);
|
||||||
auto err = proc::proc.execute(appid, app, launch_session);
|
auto err = proc::proc.execute(appid, app, launch_session);
|
||||||
if (err) {
|
if (err) {
|
||||||
bad_request(response, request, err == 503
|
bad_request(response, request, err == 503
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ namespace nvhttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<rtsp_stream::launch_session_t>
|
std::shared_ptr<rtsp_stream::launch_session_t>
|
||||||
make_launch_session(bool host_audio, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p) {
|
make_launch_session(bool host_audio, bool input_only, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p) {
|
||||||
auto launch_session = std::make_shared<rtsp_stream::launch_session_t>();
|
auto launch_session = std::make_shared<rtsp_stream::launch_session_t>();
|
||||||
|
|
||||||
launch_session->id = ++session_id_counter;
|
launch_session->id = ++session_id_counter;
|
||||||
@@ -420,6 +420,8 @@ namespace nvhttp {
|
|||||||
launch_session->client_do_cmds = named_cert_p->do_cmds;
|
launch_session->client_do_cmds = named_cert_p->do_cmds;
|
||||||
launch_session->client_undo_cmds = named_cert_p->undo_cmds;
|
launch_session->client_undo_cmds = named_cert_p->undo_cmds;
|
||||||
|
|
||||||
|
launch_session->input_only = input_only;
|
||||||
|
|
||||||
return launch_session;
|
return launch_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,7 +962,10 @@ namespace nvhttp {
|
|||||||
tree.put("root.PairStatus", pair_status);
|
tree.put("root.PairStatus", pair_status);
|
||||||
|
|
||||||
if constexpr (std::is_same_v<SunshineHTTPS, T>) {
|
if constexpr (std::is_same_v<SunshineHTTPS, T>) {
|
||||||
auto current_appid = proc::proc.running();
|
int current_appid = 0;
|
||||||
|
if (!config::input.enable_input_only_mode || rtsp_stream::session_count() == 0) {
|
||||||
|
current_appid = proc::proc.running();
|
||||||
|
}
|
||||||
tree.put("root.currentgame", current_appid);
|
tree.put("root.currentgame", current_appid);
|
||||||
tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE");
|
tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE");
|
||||||
} else {
|
} else {
|
||||||
@@ -1046,7 +1051,14 @@ namespace nvhttp {
|
|||||||
|
|
||||||
auto named_cert_p = get_verified_cert(request);
|
auto named_cert_p = get_verified_cert(request);
|
||||||
if (!!(named_cert_p->perm & PERM::_all_actions)) {
|
if (!!(named_cert_p->perm & PERM::_all_actions)) {
|
||||||
|
auto current_appid = proc::proc.running();
|
||||||
|
auto input_only_id_int = util::from_view(proc::input_only_app_id);
|
||||||
|
auto should_hide_inactive_apps = config::input.enable_input_only_mode && rtsp_stream::session_count() != 0 && current_appid != input_only_id_int;
|
||||||
for (auto &app : proc::proc.get_apps()) {
|
for (auto &app : proc::proc.get_apps()) {
|
||||||
|
auto appid = util::from_view(app.id);
|
||||||
|
if (should_hide_inactive_apps && appid != current_appid && appid != input_only_id_int) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
pt::ptree app_node;
|
pt::ptree app_node;
|
||||||
|
|
||||||
app_node.put("IsHdrSupported"s, video::active_hevc_mode == 3 ? 1 : 0);
|
app_node.put("IsHdrSupported"s, video::active_hevc_mode == 3 ? 1 : 0);
|
||||||
@@ -1086,8 +1098,22 @@ namespace nvhttp {
|
|||||||
response->close_connection_after_response = true;
|
response->close_connection_after_response = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto args = request->parse_query_string();
|
||||||
|
|
||||||
|
auto appid_str = get_arg(args, "appid");
|
||||||
|
auto appid = util::from_view(appid_str);
|
||||||
|
auto current_appid = proc::proc.running();
|
||||||
|
bool is_input_only = config::input.enable_input_only_mode && appid_str == proc::input_only_app_id;
|
||||||
|
|
||||||
auto named_cert_p = get_verified_cert(request);
|
auto named_cert_p = get_verified_cert(request);
|
||||||
if (!(named_cert_p->perm & PERM::launch)) {
|
auto perm = PERM::launch;
|
||||||
|
|
||||||
|
// If we have already launched an app, we should allow clients with view permission to join the input only or current app's session.
|
||||||
|
if (current_appid > 0 && (is_input_only || appid == current_appid)) {
|
||||||
|
perm = PERM::_allow_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(named_cert_p->perm & perm)) {
|
||||||
BOOST_LOG(debug) << "Permission LaunchApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
|
BOOST_LOG(debug) << "Permission LaunchApp denied for [" << named_cert_p->name << "] (" << (uint32_t)named_cert_p->perm << ")";
|
||||||
|
|
||||||
tree.put("root.resume", 0);
|
tree.put("root.resume", 0);
|
||||||
@@ -1096,8 +1122,6 @@ namespace nvhttp {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = request->parse_query_string();
|
|
||||||
if (
|
if (
|
||||||
args.find("rikey"s) == std::end(args) ||
|
args.find("rikey"s) == std::end(args) ||
|
||||||
args.find("rikeyid"s) == std::end(args) ||
|
args.find("rikeyid"s) == std::end(args) ||
|
||||||
@@ -1110,20 +1134,18 @@ namespace nvhttp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current_appid = proc::proc.running();
|
if (!is_input_only) {
|
||||||
if (current_appid > 0) {
|
if (current_appid > 0 && current_appid != util::from_view(proc::input_only_app_id) && appid != current_appid) {
|
||||||
tree.put("root.resume", 0);
|
tree.put("root.resume", 0);
|
||||||
tree.put("root.<xmlattr>.status_code", 400);
|
tree.put("root.<xmlattr>.status_code", 400);
|
||||||
tree.put("root.<xmlattr>.status_message", "An app is already running on this host");
|
tree.put("root.<xmlattr>.status_message", "An app is already running on this host");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto appid_str = get_arg(args, "appid");
|
|
||||||
auto appid = util::from_view(appid_str);
|
|
||||||
|
|
||||||
host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
|
host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
|
||||||
auto launch_session = make_launch_session(host_audio, appid, args, named_cert_p);
|
auto launch_session = make_launch_session(host_audio, is_input_only, appid, args, named_cert_p);
|
||||||
|
|
||||||
auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address());
|
auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address());
|
||||||
if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) {
|
if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) {
|
||||||
@@ -1136,7 +1158,18 @@ namespace nvhttp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appid > 0) {
|
if (is_input_only) {
|
||||||
|
BOOST_LOG(info) << "Launching input only session..."sv;
|
||||||
|
|
||||||
|
// Still probe encoders once, if input only session is launched first
|
||||||
|
// But we're ignoring if it's successful or not
|
||||||
|
if (rtsp_stream::session_count() == 0 && !proc::proc.virtual_display) {
|
||||||
|
video::probe_encoders();
|
||||||
|
if (current_appid == 0) {
|
||||||
|
proc::proc.launch_input_only();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (appid > 0 && appid != current_appid) {
|
||||||
const auto& apps = proc::proc.get_apps();
|
const auto& apps = proc::proc.get_apps();
|
||||||
auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str](const auto _app) {
|
auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str](const auto _app) {
|
||||||
return _app.id == appid_str;
|
return _app.id == appid_str;
|
||||||
@@ -1229,7 +1262,7 @@ namespace nvhttp {
|
|||||||
if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) {
|
if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) {
|
||||||
host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
|
host_audio = util::from_view(get_arg(args, "localAudioPlayMode"));
|
||||||
}
|
}
|
||||||
auto launch_session = make_launch_session(host_audio, 0, args, named_cert_p);
|
auto launch_session = make_launch_session(host_audio, false, 0, args, named_cert_p);
|
||||||
|
|
||||||
if (!proc::proc.allow_client_commands) {
|
if (!proc::proc.allow_client_commands) {
|
||||||
launch_session->client_do_cmds.clear();
|
launch_session->client_do_cmds.clear();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace nvhttp {
|
|||||||
extract_command_entries(const boost::property_tree::ptree& pt, const std::string& key);
|
extract_command_entries(const boost::property_tree::ptree& pt, const std::string& key);
|
||||||
|
|
||||||
std::shared_ptr<rtsp_stream::launch_session_t>
|
std::shared_ptr<rtsp_stream::launch_session_t>
|
||||||
make_launch_session(bool host_audio, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p);
|
make_launch_session(bool host_audio, bool input_only, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Setup the nvhttp server.
|
* @brief Setup the nvhttp server.
|
||||||
|
|||||||
126
src/process.cpp
126
src/process.cpp
@@ -49,6 +49,8 @@ namespace proc {
|
|||||||
|
|
||||||
proc_t proc;
|
proc_t proc;
|
||||||
|
|
||||||
|
std::string input_only_app_id;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
VDISPLAY::DRIVER_STATUS vDisplayDriverStatus = VDISPLAY::DRIVER_STATUS::UNKNOWN;
|
VDISPLAY::DRIVER_STATUS vDisplayDriverStatus = VDISPLAY::DRIVER_STATUS::UNKNOWN;
|
||||||
|
|
||||||
@@ -158,10 +160,21 @@ namespace proc {
|
|||||||
return cmd_path.parent_path();
|
return cmd_path.parent_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
proc_t::launch_input_only() {
|
||||||
|
_app_id = util::from_view(input_only_app_id);
|
||||||
|
placebo = true;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
proc_t::execute(int app_id, const ctx_t& app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session) {
|
proc_t::execute(int app_id, const ctx_t& app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session) {
|
||||||
// Ensure starting from a clean slate
|
if (_app_id == util::from_view(input_only_app_id)) {
|
||||||
terminate();
|
terminate();
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
} else {
|
||||||
|
// Ensure starting from a clean slate
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
_app = app;
|
_app = app;
|
||||||
_app_id = app_id;
|
_app_id = app_id;
|
||||||
@@ -241,12 +254,18 @@ namespace proc {
|
|||||||
|
|
||||||
memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID));
|
memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID));
|
||||||
|
|
||||||
|
int target_fps = launch_session->fps ? launch_session->fps : 60;
|
||||||
|
|
||||||
|
if (config::video.double_framerate) {
|
||||||
|
target_fps *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
|
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
|
||||||
device_uuid_str.c_str(),
|
device_uuid_str.c_str(),
|
||||||
device_name.c_str(),
|
device_name.c_str(),
|
||||||
render_width,
|
render_width,
|
||||||
render_height,
|
render_height,
|
||||||
launch_session->fps ? launch_session->fps : 60,
|
target_fps,
|
||||||
launch_session->display_guid
|
launch_session->display_guid
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -291,7 +310,7 @@ namespace proc {
|
|||||||
// encoder matches the active GPU (which could have changed
|
// encoder matches the active GPU (which could have changed
|
||||||
// due to hotplugging, driver crash, primary monitor change,
|
// due to hotplugging, driver crash, primary monitor change,
|
||||||
// or any number of other factors).
|
// or any number of other factors).
|
||||||
if (video::probe_encoders()) {
|
if (rtsp_stream::session_count() == 0 && video::probe_encoders()) {
|
||||||
return 503;
|
return 503;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,6 +920,73 @@ namespace proc {
|
|||||||
std::set<std::string> ids;
|
std::set<std::string> ids;
|
||||||
std::vector<proc::ctx_t> apps;
|
std::vector<proc::ctx_t> apps;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
// Input Only entry
|
||||||
|
if (config::input.enable_input_only_mode) {
|
||||||
|
proc::ctx_t ctx;
|
||||||
|
// ctx.uuid = ""; // We're not using uuid for this special entry
|
||||||
|
ctx.name = "Input Only";
|
||||||
|
ctx.image_path = parse_env_val(this_env, "input_only.png");
|
||||||
|
ctx.virtual_display = false;
|
||||||
|
ctx.scale_factor = 100;
|
||||||
|
ctx.use_app_identity = false;
|
||||||
|
ctx.per_client_app_identity = false;
|
||||||
|
ctx.allow_client_commands = false;
|
||||||
|
|
||||||
|
ctx.elevated = false;
|
||||||
|
ctx.auto_detach = true;
|
||||||
|
ctx.wait_all = true;
|
||||||
|
ctx.exit_timeout = 5s;
|
||||||
|
|
||||||
|
auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++);
|
||||||
|
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
||||||
|
// Avoid using index to generate id if possible
|
||||||
|
ctx.id = std::get<0>(possible_ids);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Fallback to include index on collision
|
||||||
|
ctx.id = std::get<1>(possible_ids);
|
||||||
|
}
|
||||||
|
ids.insert(ctx.id);
|
||||||
|
|
||||||
|
input_only_app_id = ctx.id;
|
||||||
|
|
||||||
|
apps.emplace_back(std::move(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Virtual Display entry
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
|
||||||
|
proc::ctx_t ctx;
|
||||||
|
// ctx.uuid = ""; // We're not using uuid for this special entry
|
||||||
|
ctx.name = "Virtual Display";
|
||||||
|
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
|
||||||
|
ctx.virtual_display = true;
|
||||||
|
ctx.scale_factor = 100;
|
||||||
|
ctx.use_app_identity = false;
|
||||||
|
ctx.per_client_app_identity = false;
|
||||||
|
ctx.allow_client_commands = false;
|
||||||
|
|
||||||
|
ctx.elevated = false;
|
||||||
|
ctx.auto_detach = true;
|
||||||
|
ctx.wait_all = true;
|
||||||
|
ctx.exit_timeout = 5s;
|
||||||
|
|
||||||
|
auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++);
|
||||||
|
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
||||||
|
// Avoid using index to generate id if possible
|
||||||
|
ctx.id = std::get<0>(possible_ids);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Fallback to include index on collision
|
||||||
|
ctx.id = std::get<1>(possible_ids);
|
||||||
|
}
|
||||||
|
ids.insert(ctx.id);
|
||||||
|
|
||||||
|
apps.emplace_back(std::move(ctx));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
for (auto &[_, app_node] : apps_node) {
|
for (auto &[_, app_node] : apps_node) {
|
||||||
proc::ctx_t ctx;
|
proc::ctx_t ctx;
|
||||||
|
|
||||||
@@ -1032,38 +1118,6 @@ namespace proc {
|
|||||||
apps.emplace_back(std::move(ctx));
|
apps.emplace_back(std::move(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
|
|
||||||
proc::ctx_t ctx;
|
|
||||||
// ctx.uuid = ""; // We're not using uuid for this special entry
|
|
||||||
ctx.name = "Virtual Display";
|
|
||||||
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
|
|
||||||
ctx.virtual_display = true;
|
|
||||||
ctx.scale_factor = 100;
|
|
||||||
ctx.use_app_identity = false;
|
|
||||||
ctx.per_client_app_identity = false;
|
|
||||||
ctx.allow_client_commands = false;
|
|
||||||
|
|
||||||
ctx.elevated = false;
|
|
||||||
ctx.auto_detach = true;
|
|
||||||
ctx.wait_all = true;
|
|
||||||
ctx.exit_timeout = 5s;
|
|
||||||
|
|
||||||
auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++);
|
|
||||||
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
|
||||||
// Avoid using index to generate id if possible
|
|
||||||
ctx.id = std::get<0>(possible_ids);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Fallback to include index on collision
|
|
||||||
ctx.id = std::get<1>(possible_ids);
|
|
||||||
}
|
|
||||||
ids.insert(ctx.id);
|
|
||||||
|
|
||||||
apps.emplace_back(std::move(ctx));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return proc::proc_t {
|
return proc::proc_t {
|
||||||
std::move(this_env), std::move(apps)
|
std::move(this_env), std::move(apps)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ namespace proc {
|
|||||||
_env(std::move(env)),
|
_env(std::move(env)),
|
||||||
_apps(std::move(apps)) {}
|
_apps(std::move(apps)) {}
|
||||||
|
|
||||||
|
void
|
||||||
|
launch_input_only();
|
||||||
|
|
||||||
int
|
int
|
||||||
execute(int app_id, const ctx_t& _app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session);
|
execute(int app_id, const ctx_t& _app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session);
|
||||||
|
|
||||||
@@ -178,4 +181,5 @@ namespace proc {
|
|||||||
terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout);
|
terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout);
|
||||||
|
|
||||||
extern proc_t proc;
|
extern proc_t proc;
|
||||||
|
extern std::string input_only_app_id;
|
||||||
} // namespace proc
|
} // namespace proc
|
||||||
|
|||||||
@@ -1056,6 +1056,8 @@ namespace rtsp_stream {
|
|||||||
config.monitor.encodingFramerate = config.monitor.framerate;
|
config.monitor.encodingFramerate = config.monitor.framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.monitor.input_only = session.input_only;
|
||||||
|
|
||||||
configuredBitrateKbps = util::from_view(args.at("x-ml-video.configuredBitrateKbps"sv));
|
configuredBitrateKbps = util::from_view(args.at("x-ml-video.configuredBitrateKbps"sv));
|
||||||
}
|
}
|
||||||
catch (std::out_of_range &) {
|
catch (std::out_of_range &) {
|
||||||
@@ -1099,6 +1101,8 @@ namespace rtsp_stream {
|
|||||||
config.audio.flags[audio::config_t::CUSTOM_SURROUND_PARAMS] = valid;
|
config.audio.flags[audio::config_t::CUSTOM_SURROUND_PARAMS] = valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.audio.input_only = session.input_only;
|
||||||
|
|
||||||
// If the client sent a configured bitrate, we will choose the actual bitrate ourselves
|
// If the client sent a configured bitrate, we will choose the actual bitrate ourselves
|
||||||
// by using FEC percentage and audio quality settings. If the calculated bitrate ends up
|
// by using FEC percentage and audio quality settings. If the calculated bitrate ends up
|
||||||
// too low, we'll allow it to exceed the limits rather than reducing the encoding bitrate
|
// too low, we'll allow it to exceed the limits rather than reducing the encoding bitrate
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace rtsp_stream {
|
|||||||
std::string unique_id;
|
std::string unique_id;
|
||||||
crypto::PERM perm;
|
crypto::PERM perm;
|
||||||
|
|
||||||
|
bool input_only;
|
||||||
bool host_audio;
|
bool host_audio;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|||||||
@@ -1847,6 +1847,20 @@ namespace video {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.input_only) {
|
||||||
|
BOOST_LOG(info) << "Input only session, video will not be captured."sv;
|
||||||
|
|
||||||
|
// Encode the dummy img only once
|
||||||
|
if (encode(frame_nr++, *session, packets, channel_data, std::chrono::steady_clock::now())) {
|
||||||
|
BOOST_LOG(error) << "Could not encode dummy video packet"sv;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown_event->view();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point last_frame_timestamp;
|
std::chrono::steady_clock::time_point last_frame_timestamp;
|
||||||
std::chrono::steady_clock::time_point last_encoded_timestamp = std::chrono::steady_clock::now();
|
std::chrono::steady_clock::time_point last_encoded_timestamp = std::chrono::steady_clock::now();
|
||||||
bool stop_encoding = false;
|
bool stop_encoding = false;
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ namespace video {
|
|||||||
int chromaSamplingType; // 0 - 4:2:0, 1 - 4:4:4
|
int chromaSamplingType; // 0 - 4:2:0, 1 - 4:4:4
|
||||||
|
|
||||||
int enableIntraRefresh; // 0 - disabled, 1 - enabled
|
int enableIntraRefresh; // 0 - disabled, 1 - enabled
|
||||||
|
|
||||||
|
bool input_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
platf::mem_type_e
|
platf::mem_type_e
|
||||||
|
|||||||
Reference in New Issue
Block a user