Cap honored VNC client JPEG quality at 50

This commit is contained in:
Viktor Liu
2026-05-18 14:07:26 +02:00
parent c2fdf62f1f
commit 5543404188
3 changed files with 18 additions and 6 deletions

View File

@@ -16,7 +16,7 @@ import (
// - UTF-8 text format (legacy is Latin-1).
// - Pull-based: a Notify announces "I have new content", the peer fetches
// via Request only when it actually needs the data. Saves bandwidth on
// a high-latency relay path versus pushing every change.
// high-latency transports versus pushing every change.
// - zlib-compressed payloads.
// - Caps negotiation so each side knows the other's per-format max size.
//

View File

@@ -16,7 +16,7 @@ func TestBuildExtClipCaps(t *testing.T) {
require.Len(t, payload, 8, "Caps with one format should be 4 bytes flags + 4 bytes size")
flags := binary.BigEndian.Uint32(payload[0:4])
// noVNC checks individual action bits in our Caps to decide whether to
// Clients check individual action bits in our Caps to decide whether to
// auto-Request on Notify, so all supported actions must be advertised.
assert.NotZero(t, flags&extClipActionCaps, "Caps action bit must be set")
assert.NotZero(t, flags&extClipActionRequest, "Request action bit must be set")

View File

@@ -472,8 +472,11 @@ func newTightStateWithLevels(qualityLevel, compressLevel int) *tightState {
}
// jpegQualityForLevel maps a 0..9 client preference to a JPEG quality value.
// Returns 0 when no preference is set (-1), letting the encoder fall back to
// the area-based tiers.
// Returns 0 when no preference is set (-1), letting the encoder fall back
// to the area-based tiers. The output is capped at jpegQualityClientCap
// so a client asking for the highest quality does not push per-frame JPEG
// byte counts into a regime that overwhelms bandwidth-constrained
// transports. Within the cap the mapping is still linear.
func jpegQualityForLevel(level int) int {
if level < 0 {
return 0
@@ -481,10 +484,19 @@ func jpegQualityForLevel(level int) int {
if level > 9 {
level = 9
}
// 0 -> 30, 9 -> 93. Linear so adjacent steps are perceptually similar.
return 30 + level*7
q := 30 + level*7
if q > jpegQualityClientCap {
q = jpegQualityClientCap
}
return q
}
// jpegQualityClientCap upper-bounds the JPEG quality we honour from the
// client's QualityLevel pseudo-encoding. 50 keeps full-screen JPEGs in
// the same byte range as the area-tiered defaults used when the client
// does not express a preference.
const jpegQualityClientCap = 50
// zlibLevelFor maps a 0..9 client preference to a zlib compression level.
// Level 0 ("no compression") would emit larger output than input on most
// rects, so we floor to BestSpeed (1). -1 (no preference) also picks