e82e7853d1
Three changes to make the stream survive constrained links: Adaptive quality: the sender pipeline now tracks the PLI rate from the receiver. Every 5 seconds it evaluates: >0.5 PLI/s means the link is saturated (the receiver is dropping frames), so the CRF increases by 2 (lower quality, fewer bits) and the encoder restarts with a keyframe. <0.1 PLI/s means the link is stable, so the CRF decreases by 1 (better quality) and the encoder probes upward. Clamped to [user CRF, user CRF + 10] so quality never degrades below what the link can handle, and never exceeds what the user asked for. The adaptation is logged to stderr for visibility. Frame rate capping (--fps N): throttles the capture loop to N frames per second (0 = no cap; monitor rate). At 15fps instead of 60fps, the bandwidth requirement drops 4x at the same quality level. Desktop content is still smooth at 15-20fps. Tighter VBV: one frame period of buffer instead of two. A two-frame buffer lets a keyframe spike to twice the target rate in one burst, which overflows any constrained hop (Wi-Fi hotspot, slow switch) and cascades into PLI storms. One frame period keeps bursts within what the link can absorb in real time.
78 lines
2.2 KiB
C++
78 lines
2.2 KiB
C++
#pragma once
|
|
|
|
#include "screencast/capture/capture.h"
|
|
#include "screencast/codec/decoder.h"
|
|
#include "screencast/codec/encoder.h"
|
|
#include "screencast/network/transport.h"
|
|
#include "screencast/render/renderer.h"
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
namespace sc {
|
|
|
|
struct SenderPipelineConfig {
|
|
CaptureTarget capture_target;
|
|
EncoderConfig encoder;
|
|
Endpoint local_rtp_endpoint;
|
|
std::optional<Endpoint> signaling_server;
|
|
// Where encoded RTP packets are sent. Defaults to the local loopback so
|
|
// a sender and receiver on one machine work without any configuration.
|
|
Endpoint peer_rtp_endpoint{"127.0.0.1", 5004};
|
|
// Session identity from signaling; written to the sender state file for
|
|
// status widgets (waybar) and one-click restarts.
|
|
std::string session_id;
|
|
// The receiver's display resolution (0 = unknown). The sender downscales
|
|
// to this before encoding so bitrate is not spent on pixels the display
|
|
// cannot show.
|
|
int max_encode_width = 0;
|
|
int max_encode_height = 0;
|
|
// Cap the encoding frame rate (0 = no cap; use the capture rate).
|
|
// Lowering the frame rate halves the bandwidth at the same quality
|
|
// level — useful on constrained links.
|
|
int max_frame_rate = 0;
|
|
};
|
|
|
|
struct ReceiverPipelineConfig {
|
|
DecoderConfig decoder;
|
|
Endpoint local_rtp_endpoint;
|
|
std::optional<Endpoint> peer_signaling;
|
|
RendererConfig renderer;
|
|
// TCP port the signaling server listens on (offers arrive here).
|
|
std::uint16_t signaling_port = 5005;
|
|
};
|
|
|
|
// Sender pipeline: capture → encode → packetize → RTP/UDP.
|
|
class SenderPipeline {
|
|
public:
|
|
explicit SenderPipeline(SenderPipelineConfig config);
|
|
~SenderPipeline();
|
|
|
|
bool start();
|
|
void stop();
|
|
|
|
// Ask the sender to encode its next frame as a keyframe. Thread-safe;
|
|
// used by the PLI feedback path.
|
|
void request_keyframe();
|
|
|
|
private:
|
|
class Impl;
|
|
std::unique_ptr<Impl> impl_;
|
|
};
|
|
|
|
// Receiver pipeline: RTP/UDP → depacketize → decode → render.
|
|
class ReceiverPipeline {
|
|
public:
|
|
explicit ReceiverPipeline(ReceiverPipelineConfig config);
|
|
~ReceiverPipeline();
|
|
|
|
bool start();
|
|
void stop();
|
|
|
|
private:
|
|
class Impl;
|
|
std::unique_ptr<Impl> impl_;
|
|
};
|
|
|
|
} // namespace sc
|