api: notifications for video stream (#884)
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "capture_pump.h"
|
#include "capture_pump.h"
|
||||||
#include "hooks/graphics/graphics.h"
|
#include "hooks/graphics/graphics.h"
|
||||||
|
#include "overlay/notifications.h"
|
||||||
#include "stream_format.h"
|
#include "stream_format.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
@@ -178,6 +179,13 @@ namespace api {
|
|||||||
// carry it too, or the client sees an opaque failure instead of the status.
|
// carry it too, or the client sees an opaque failure instead of the status.
|
||||||
constexpr const char *cors_header = "Access-Control-Allow-Origin: *\r\n";
|
constexpr const char *cors_header = "Access-Control-Allow-Origin: *\r\n";
|
||||||
|
|
||||||
|
// the port is unauthenticated, so a scanner hammering a busy/missing screen could
|
||||||
|
// otherwise flood the overlay; throttle failure toasts per distinct cause. kept under
|
||||||
|
// a second so it only swallows that, not a legitimate reconnect - substream itself
|
||||||
|
// switches screens with a 300ms gap, and only backs off to a full second once a
|
||||||
|
// retry has actually failed
|
||||||
|
constexpr double notification_throttle_seconds = 0.5;
|
||||||
|
|
||||||
void send_error(SOCKET socket, const char *status) {
|
void send_error(SOCKET socket, const char *status) {
|
||||||
const std::string response =
|
const std::string response =
|
||||||
std::string("HTTP/1.0 ") + status + "\r\n"
|
std::string("HTTP/1.0 ") + status + "\r\n"
|
||||||
@@ -334,6 +342,11 @@ namespace api {
|
|||||||
|
|
||||||
if (slot < 0) {
|
if (slot < 0) {
|
||||||
log_warning("api::stream", "client limit of {} hit", client_limit);
|
log_warning("api::stream", "client limit of {} hit", client_limit);
|
||||||
|
overlay::notifications::add_throttled(
|
||||||
|
overlay::notifications::Severity::Warning,
|
||||||
|
"api::stream.client_limit",
|
||||||
|
notification_throttle_seconds,
|
||||||
|
fmt::format("Video stream refused: client limit reached ({})", address));
|
||||||
send_error(client, "503 Service Unavailable");
|
send_error(client, "503 Service Unavailable");
|
||||||
closesocket(client);
|
closesocket(client);
|
||||||
continue;
|
continue;
|
||||||
@@ -410,16 +423,32 @@ namespace api {
|
|||||||
if (!streamable(screen)) {
|
if (!streamable(screen)) {
|
||||||
log_warning("api::stream",
|
log_warning("api::stream",
|
||||||
"screen {} is not available, refusing {}", screen, address);
|
"screen {} is not available, refusing {}", screen, address);
|
||||||
|
overlay::notifications::add_throttled(
|
||||||
|
overlay::notifications::Severity::Warning,
|
||||||
|
fmt::format("api::stream.screen_unavailable.{}", screen),
|
||||||
|
notification_throttle_seconds,
|
||||||
|
fmt::format("Video stream refused: screen {} not available ({})",
|
||||||
|
screen, address));
|
||||||
send_error(socket, "404 Not Found");
|
send_error(socket, "404 Not Found");
|
||||||
} else if (!capture_pump::claim_screen(screen)) {
|
} else if (!capture_pump::claim_screen(screen)) {
|
||||||
log_warning("api::stream",
|
log_warning("api::stream",
|
||||||
"screen {} is already being streamed, refusing {}",
|
"screen {} is already being streamed, refusing {}",
|
||||||
screen, address);
|
screen, address);
|
||||||
|
overlay::notifications::add_throttled(
|
||||||
|
overlay::notifications::Severity::Warning,
|
||||||
|
fmt::format("api::stream.screen_claimed.{}", screen),
|
||||||
|
notification_throttle_seconds,
|
||||||
|
fmt::format("Video stream refused: screen {} already streaming ({})",
|
||||||
|
screen, address));
|
||||||
send_error(socket, "503 Service Unavailable");
|
send_error(socket, "503 Service Unavailable");
|
||||||
} else {
|
} else {
|
||||||
log_info("api::stream",
|
log_info("api::stream",
|
||||||
"client connected: {} ({}, screen={}, fps={}, quality={})",
|
"client connected: {} ({}, screen={}, fps={}, quality={})",
|
||||||
address, request.path, screen, fps, quality);
|
address, request.path, screen, fps, quality);
|
||||||
|
overlay::notifications::add(
|
||||||
|
overlay::notifications::Severity::Success,
|
||||||
|
fmt::format("Video stream client connected ({}, screen {})",
|
||||||
|
address, screen));
|
||||||
|
|
||||||
const std::string header =
|
const std::string header =
|
||||||
"HTTP/1.0 200 OK\r\n"
|
"HTTP/1.0 200 OK\r\n"
|
||||||
@@ -460,6 +489,9 @@ namespace api {
|
|||||||
|
|
||||||
capture_pump::release_screen(screen);
|
capture_pump::release_screen(screen);
|
||||||
log_info("api::stream", "client disconnected: {}", address);
|
log_info("api::stream", "client disconnected: {}", address);
|
||||||
|
overlay::notifications::add(
|
||||||
|
overlay::notifications::Severity::Info,
|
||||||
|
fmt::format("Video stream client disconnected ({})", address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,15 @@ namespace api {
|
|||||||
private:
|
private:
|
||||||
ClientState *state = nullptr;
|
ClientState *state = nullptr;
|
||||||
|
|
||||||
|
// headsocket doesn't expose the peer address on its own client API, but the
|
||||||
|
// sockaddr_in captured at accept time is sitting right there in the impl
|
||||||
|
std::string remote_address() const {
|
||||||
|
char address_data[INET_ADDRSTRLEN] {};
|
||||||
|
inet_ntop(AF_INET, &this->_p->conn.impl()->from.sin_addr,
|
||||||
|
address_data, INET_ADDRSTRLEN);
|
||||||
|
return std::string(address_data);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool async_received_data(const data_block &db, uint8_t *ptr, size_t length) override;
|
bool async_received_data(const data_block &db, uint8_t *ptr, size_t length) override;
|
||||||
|
|
||||||
@@ -120,19 +129,21 @@ namespace api {
|
|||||||
srv->websocket->controller->init_state(state);
|
srv->websocket->controller->init_state(state);
|
||||||
|
|
||||||
// log connection
|
// log connection
|
||||||
log_info("api::websocket", "client connected");
|
const auto address = this->remote_address();
|
||||||
|
log_info("api::websocket", "client connected: {}", address);
|
||||||
overlay::notifications::add(
|
overlay::notifications::add(
|
||||||
overlay::notifications::Severity::Success,
|
overlay::notifications::Severity::Success,
|
||||||
"API websocket client connected");
|
fmt::format("API websocket client connected ({})", address));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::on_disconnect() {
|
void WebSocketClient::on_disconnect() {
|
||||||
|
|
||||||
// log disconnection
|
// log disconnection
|
||||||
log_info("api::websocket", "client disconnected");
|
const auto address = this->remote_address();
|
||||||
|
log_info("api::websocket", "client disconnected: {}", address);
|
||||||
overlay::notifications::add(
|
overlay::notifications::add(
|
||||||
overlay::notifications::Severity::Info,
|
overlay::notifications::Severity::Info,
|
||||||
"API websocket client disconnected");
|
fmt::format("API websocket client disconnected ({})", address));
|
||||||
|
|
||||||
// get pointer to server
|
// get pointer to server
|
||||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||||
|
|||||||
Vendored
+4
-1
@@ -1450,7 +1450,10 @@ void basic_tcp_server::accept_thread()
|
|||||||
while (_p->isRunning)
|
while (_p->isRunning)
|
||||||
{
|
{
|
||||||
detail::connection_impl conn_impl;
|
detail::connection_impl conn_impl;
|
||||||
conn_impl.socket = ::accept(_p->serverSocket, reinterpret_cast<struct sockaddr *>(&conn_impl.from), nullptr);
|
// addrlen must be a valid in/out pointer or the OS leaves conn_impl.from untouched,
|
||||||
|
// so the peer address silently reads back as 0.0.0.0
|
||||||
|
int from_len = sizeof(conn_impl.from);
|
||||||
|
conn_impl.socket = ::accept(_p->serverSocket, reinterpret_cast<struct sockaddr *>(&conn_impl.from), &from_len);
|
||||||
conn_impl.id = _p->nextClientID++;
|
conn_impl.id = _p->nextClientID++;
|
||||||
|
|
||||||
if (!_p->nextClientID)
|
if (!_p->nextClientID)
|
||||||
|
|||||||
@@ -172,7 +172,9 @@ namespace overlay::notifications {
|
|||||||
// small gutter past the accent bar, then wrapped text
|
// small gutter past the accent bar, then wrapped text
|
||||||
ImGui::Dummy(ImVec2(apply_scaling(2.0f), 0.f));
|
ImGui::Dummy(ImVec2(apply_scaling(2.0f), 0.f));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::PushTextWrapPos(win_pos.x + win_size.x - apply_scaling(TOAST_PAD_X));
|
// wrap pos is in window-local space, not screen space - adding win_pos.x here
|
||||||
|
// pushed the boundary far past the window's own width, so it never wrapped
|
||||||
|
ImGui::PushTextWrapPos(win_size.x - apply_scaling(TOAST_PAD_X));
|
||||||
ImGui::TextUnformatted(n.text.c_str());
|
ImGui::TextUnformatted(n.text.c_str());
|
||||||
ImGui::PopTextWrapPos();
|
ImGui::PopTextWrapPos();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user