25 Commits

Author SHA1 Message Date
SamAcctX c75a2f60b5 server: sanitised session ids, chat length limit, websocket read limit, leaky frame queues (#712)
* server: generated video pipelines keep at most one raw frame queued

GetPipeline used plain `queue` elements, which buffer up to 1s / 200
buffers. When the encoder cannot keep up, frames pile up there and the
stream (and every reaction to input) falls behind by up to that much.
The queues now hold only the newest frame (leaky=downstream): a slow
encoder drops frames instead of adding latency.

Measured input-to-display latency (Alt+F2/Escape opens/closes a dialog,
time to the first changed received frame; xfce image built with ./build,
1920x1080, default VP8 pipeline, 30 trials per run, runs interleaved):

                  before p50/p90 ms     after p50/p90 ms
  no CPU limit    268/303, 188/225      183/217, 157/226
  --cpus=1        270/422, 362/673      155/237, 200/428

fps unchanged without a limit (25), slightly higher with one (22.5 ->
23.5-24). Pipelines given as raw gst strings are unchanged.

Tests: TestVideoConfigGetPipelineQueues; client e2e on the built image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* server: session ids no longer carry the login name verbatim

The multiuser and noauth providers built the session id as
"<username>-<token>". The v3 API addresses sessions and members by id
in the URL path (DELETE /api/sessions/{id}, POST /api/members/{id},
POST /api/room/control/give/{id}), so a login name like
"x/../../logout?" produced an id that sent an admin's unencoded
request to another endpoint, and even when the client encodes it, an
id with "/" never matches the chi route: such a member could not be
kicked, muted or banned.

utils.SafeID maps a name to letters and digits of any script plus
".", "_" and "-": every run of other characters becomes one "-",
leading and trailing "-" and "." are trimmed, the result is cut to 64
characters, and an empty result becomes "user". Nothing with a meaning
in a URL path survives; letters outside ASCII are kept so names such as
"Müller" stay recognisable (clients percent-encode ids in paths). Both
providers now use SafeID(username) + "-" + token; the profile name
keeps the name as typed, so nothing changes on screen.

The file and object providers use the username itself as the member
id. Members there are created explicitly by an admin, so
POST /api/members now rejects a name SafeID would change with a 400
instead of silently altering it. The oauth provider derives ids from
the IdP subject ("oauth:" + sub) and is unchanged.

Tests: TestSafeID (pkg/utils).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: chat plugin limits message length (chat.max_length)

The chat plugin accepted text of any size, over the websocket
(chat/message) and over REST (POST /api/chat), and broadcast it to
every session. The web client caps its input box at 512 characters,
but any client can send more, and one large message stalls every
viewer's renderer.

New option chat.max_length (default 512, counted in characters with
utf8.RuneCountInString, not bytes; 0 disables the limit). Both paths
go through one check: REST answers 400 with the length and the limit,
the websocket handler logs at warn level and drops the message, as it
already does for a sender without permission. The default never
rejects what the web client can type: its maxlength counts UTF-16
units, which is never fewer than runes.

Docs: plugins.md and release notes. help.txt/help.json list no plugin
options (no chat.*, filetransfer.*, openinapp.* entries), so nothing
was added there.

Tests: TestCheckLength (internal/plugins/chat).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: limit the size of websocket frames a client may send

There was no read limit on the websocket, so a client could push frames
of any size; they were read and decoded in full before a handler (such
as the chat length check) could reject them. Frames above 1 MiB now end
the connection, as gorilla's SetReadLimit does. The largest legitimate
messages, an SDP answer and pasted clipboard text, stay far below that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: video RTP timestamps follow the time between captured frames

ximagesrc gives a frame it captures late a buffer duration of only the
time left until the next frame slot ("Frame duration is from now until
the next expected capture time"), and frames dropped before the encoder
leave gaps. pion advances the RTP clock by Sample.Duration, so whenever
the encoder falls behind, the stream's clock runs slower than real
time. Browsers grow their jitter buffer to make up for it and lip sync
drifts. This happens with any queue in front of the encoder.

Each video sample's duration is now the gap since the previous sample's
PTS. The gap is known only when the next frame arrives, so it is
applied one frame late; the clock itself stays right. The sink also
counts frames in neko_capture_streamsink_frames: a rate below the
configured fps means the encoder falls behind.

Measured on the firefox image built with ./build, 1080p25 VP8, the
container limited to 0.5 CPU: RTP clock 0.2-0.9x real time before,
1.00x after; browser video jitter buffer 0.24-1.9 s before, 60-220 ms
after; audio up to 0.4 s (default queue) or 0.74 s (one-frame leaky
queue) ahead of the video before, within -70..+150 ms after.

Tests: TestSampleDuration; go test -race ./internal/capture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:09:43 +02:00
a f4d632df2c feat: create capture subscription api to support more transports (#699)
* feat: introduce encoded media subscriptions

* chore: preserve sample field comments

* perf: reduce encoded sample dispatch copies

* docs: define encoded sample buffer ownership

* fix: avoid pipeline recreation errors

Reuse pipelines started by a new subscription during resize.

* fix: synchronize stream bitrate state

Protect bitrate updates, reads, and resets with one mutex.

* simplify

* group stream sink fields

* fix: remove unnecessary blank line in removeSubscription method

* remove defensive check

* remove noop

---------

Co-authored-by: Miroslav Šedivý <sedivy.miro@gmail.com>
2026-09-24 10:58:39 +02:00
Miroslav Šedivý 3f3ae10fb9 feat: implement user email restrictions for OAuth login 2026-09-24 00:17:58 +02:00
@刘羡鱼 dae23a90b5 feat(auth): add OIDC-only member authentication (#686)
* feat(auth): add OIDC member authentication

* docs(auth): document OIDC member login

* fix(auth): address OAuth review feedback

* refactor(auth): move OAuth flow into member provider
2026-09-24 00:15:46 +02:00
Abdellatif Anaflous 347418c3e7 fix(drop): free CString allocations and GFile refs in drag uri handling (#700)
Co-authored-by: hktitof <hktitof@users.noreply.github.com>
2026-09-10 13:31:35 +02:00
Ruben Vereecken dafda5bd5c XInput keyboard: dispatch via XTestFakeDeviceKeyEvent (#642)
GDK3 (used by Firefox/Camoufox) selects XI2 for the seat keyboard at
startup and ignores core-protocol KeyPress events. XTestFakeKeyEvent
produces core events, so typed characters from the Neko viewer silently
never reach Firefox.

Fix: discover the "Virtual core XTEST keyboard" XInput1 device at
startup, cache it, and dispatch key events via XTestFakeDeviceKeyEvent
which goes through the XI path. Falls back to the old core XTest path
if the device isn't found.

Requires -lXi (libxi-dev at build, libxi6 at runtime).
2026-08-04 15:44:18 +02:00
Budovi dfc41f73d6 Add support for H265 encoded streams (#648)
* Add H265 support

Adds H265 as a selectable video codec with software (x265enc), VAAPI
(vah265enc), and NVENC (nvh265enc) pipeline paths in both the legacy
pipeline builder and the RTPCodec definition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Replace legacy vaapih264enc with vah264enc

The gstreamer-vaapi plugin suite (vaapih264enc) is superseded by the va
plugin (vah264enc) in gst-plugins-bad. Switch to it and update the
parameters accordingly: CBR instead of VBR for lower-latency streaming,
key-int-max instead of keyframe-period, and target-usage instead of
quality-level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Unify keyframe interval to 60 frames for NVENC H264/H265 encoder paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-04 15:16:33 +02:00
Miroslav Šedivý 827944349b fix: reduce audio jitter buffer latency (#673)
* fix: reduce audio jitter buffer latency

- Release listenersMu before dispatching samples to listeners
- Buffer Track.sample channel (size 2) with non-blocking WriteSample
- Buffer pipeline.sample channel (size 4) to unblock GStreamer thread
- Add leaky=downstream max-size-buffers=5 to audio queue to prevent burst accumulation
- Set sync=false on appsink to deliver buffers immediately without clock wait

These changes break the backpressure chain that caused the GStreamer
streaming thread to stall. When the Go scheduler has a hiccup, buffered
frames were delivered in a burst, causing the browser audio jitter buffer
to grow to ~10 seconds over time.

Fixes: #598

* fix: log message when dropping samples due to full track channel
2026-08-04 14:59:11 +02:00
Miroslav Šedivý db8452416b refactor: replace custom array search utility with slices.Contains for improved readability and performance. 2026-08-04 12:49:38 +02:00
Miroslav Šedivý e6239d6a51 feat: update video encoding to use nvautogpuh264enc for NVIDIA driver 590+ support. (#667) 2026-07-31 14:28:19 +02:00
Miroslav Šedivý 2c124c506e Upgrade go dependencies (#644)
* update pion WebRTC from v3 to v4.

* update mapstructure dependency to use go-viper version.

* update go-chi dependency to v5.
2026-04-25 15:20:43 +02:00
Miroslav Šedivý 51bbad7650 rename package to github name. 2025-03-26 23:51:19 +01:00
Miroslav Šedivý bb6fe698ad update docs. 2025-02-16 00:24:37 +01:00
Miroslav Šedivý 3082d3241b implement client heartbeat #460. 2024-12-30 13:41:22 +01:00
Miroslav Šedivý bbefb393f8 fix uint64 and cursors on 32bit systems. 2024-09-08 15:47:42 +02:00
Miroslav Šedivý d42165a5fd fix structs alignment. 2024-09-08 15:01:31 +02:00
Miroslav Šedivý 373e9970f9 add legacy simulcast stream that shows pointer. 2024-09-07 21:13:40 +02:00
Miroslav Šedivý 4f401ac2b6 implement session stats, fixes last (admin|user) left at won't work for any provider that removed users after logout. 2024-09-07 18:55:27 +02:00
Miroslav Šedivý 15e805c9b2 rename package. 2024-09-06 23:26:29 +02:00
Miroslav Šedivý 8ce16e2280 fix PluginSettings. 2024-09-06 22:59:23 +02:00
Miroslav Šedivý 15591287cf fix control give for users. 2024-09-06 22:59:04 +02:00
Miroslav Šedivý 9ea9c1c208 add legacy webrtc handler. 2024-09-06 22:59:04 +02:00
Miroslav Šedivý 6d72a795ef fix broadcast typo. 2024-09-06 22:58:25 +02:00
Miroslav Šedivý 0ee11b661a add AV1 codec. 2024-09-06 22:57:34 +02:00
Miroslav Šedivý cfb423b13d move server to server directory. 2024-09-06 22:56:24 +02:00