Files
screen_cast/src/app/cli.cpp
T
fegger 36d086af4e perf(codec): CRF rate control, longer GOP, faster preset, screen tuning
Four encoder quality improvements, all sender-side:

- CRF rate control (default 22, --crf to override): targets a
  constant visual quality level instead of a fixed bitrate. Static
  desktop content uses 300-800 kbps (vs. forced 4000+), and the saved
  bits go to sharp text and clean motion when they appear. The VBV
  max rate (the --bitrate value, now a cap rather than a target)
  bounds bursts so the receiver's UDP buffers stay safe. Round-trip
  test bitrate dropped from 1390 kb/s to 47 kb/s on synthetic frames
  — the encoder uses only what it needs.

- 5-second GOP (was 1 second): 80% fewer keyframe bits freed for
  detail frames. Screen content changes incrementally, not
  wholesale; PLI feedback recovers from loss in one frame time
  regardless of GOP length.

- faster preset (was veryfast): better sub-pixel estimation and
  RDO on more decisions. The desktop handles it trivially at 1080p.

- Screen-content x264 tuning: aq-mode=2 (auto-variance AQ moves
  bits away from flat areas toward text edges) and psy-rd=1.5
  (preserves texture sharpness).

Combined with the earlier veryfast upgrade and sender-side
downscaling, this is roughly 2x the perceived quality at the same
average bandwidth compared to the original ultrafast ABR encoder.

meson test 5/5 in both configurations, valgrind clean.
2026-09-09 12:12:36 +02:00

166 lines
5.3 KiB
C++

#include "screencast/app/cli.h"
#include <charconv>
#include <cstdio>
#include <string_view>
namespace sc {
namespace {
void print_usage() {
std::fputs(
"usage: screencast --send [--target monitor|window] [--peer HOST[:PORT]] [--bitrate MAX_KBPS] [--crf 0-51]\n"
" screencast --receive [--port PORT] [--signaling-port PORT] [--fullscreen] [--swdecode]\n"
" screencast --discover [--timeout SECONDS]\n"
" screencast waybar [--toggle] # for waybar widgets\n"
"\n"
"--send without --peer discovers a receiver on the LAN and requires\n"
"that exactly one is found.\n",
stderr);
}
bool parse_int(std::string_view text, int& value) {
const auto [pointer, error] = std::from_chars(text.data(), text.data() + text.size(), value);
return error == std::errc{} && pointer == text.data() + text.size();
}
bool next_argument(int argc, const char* const argv[], int& index, std::string_view& value) {
if (index + 1 >= argc) {
print_usage();
return false;
}
value = argv[++index];
return true;
}
} // namespace
std::optional<Command> parse_cli(int argc, const char* const argv[]) {
enum class Mode {
None,
Send,
Receive,
Discover,
Waybar,
};
Mode mode = Mode::None;
SendCommand send;
ReceiveCommand receive;
DiscoverCommand discover;
WaybarCommand waybar;
for (int index = 1; index < argc; ++index) {
const std::string_view argument = argv[index];
if (argument == "--send") {
if (mode != Mode::None) {
print_usage();
return std::nullopt;
}
mode = Mode::Send;
} else if (argument == "--receive") {
if (mode != Mode::None) {
print_usage();
return std::nullopt;
}
mode = Mode::Receive;
} else if (argument == "--discover") {
if (mode != Mode::None) {
print_usage();
return std::nullopt;
}
mode = Mode::Discover;
} else if (argument == "waybar") {
if (mode != Mode::None) {
print_usage();
return std::nullopt;
}
mode = Mode::Waybar;
} else if (argument == "--toggle") {
waybar.toggle = true;
} else if (argument == "--target") {
std::string_view value;
if (!next_argument(argc, argv, index, value)) {
return std::nullopt;
}
if (value != "monitor" && value != "window") {
print_usage();
return std::nullopt;
}
send.target = value;
} else if (argument == "--peer") {
std::string_view value;
if (!next_argument(argc, argv, index, value)) {
return std::nullopt;
}
if (mode == Mode::Receive) {
receive.peer_address = value;
} else {
send.peer_address = value;
}
} else if (argument == "--bitrate") {
std::string_view value;
if (!next_argument(argc, argv, index, value) || !parse_int(value, send.bitrate_kbps) ||
send.bitrate_kbps <= 0) {
print_usage();
return std::nullopt;
}
} else if (argument == "--crf") {
std::string_view value;
if (!next_argument(argc, argv, index, value) || !parse_int(value, send.crf) || send.crf < 0 ||
send.crf > 51) {
print_usage();
return std::nullopt;
}
} else if (argument == "--port") {
std::string_view value;
int port = 0;
if (!next_argument(argc, argv, index, value) || !parse_int(value, port) || port <= 0 || port > 65535) {
print_usage();
return std::nullopt;
}
receive.local_rtp_port = port;
} else if (argument == "--signaling-port") {
std::string_view value;
int port = 0;
if (!next_argument(argc, argv, index, value) || !parse_int(value, port) || port <= 0 || port > 65535) {
print_usage();
return std::nullopt;
}
receive.signaling_port = port;
} else if (argument == "--fullscreen") {
receive.fullscreen = true;
} else if (argument == "--swdecode") {
receive.software_decode = true;
} else if (argument == "--timeout") {
std::string_view value;
if (!next_argument(argc, argv, index, value) || !parse_int(value, discover.timeout_seconds) ||
discover.timeout_seconds <= 0) {
print_usage();
return std::nullopt;
}
} else {
print_usage();
return std::nullopt;
}
}
if (mode == Mode::Send) {
return Command{std::move(send)};
}
if (mode == Mode::Receive) {
return Command{std::move(receive)};
}
if (mode == Mode::Discover) {
return Command{std::move(discover)};
}
if (mode == Mode::Waybar) {
return Command{std::move(waybar)};
}
print_usage();
return std::nullopt;
}
} // namespace sc