Skip to content

Releases: selkies-project/pixelflux

bdf2d46

bdf2d46 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Sep 17:37
bdf2d46

ci: Pin the Rust toolchain action by commit

35204ca

35204ca Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Sep 05:09
35204ca

fix(x11): Count a DRI3 tick that publishes nothing as no frame, so a change landing just after it is published at once

2c4bb53

2c4bb53 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Sep 15:29
2c4bb53

feat(x11): Publish the capture's frame rate for the X server's fake vblank, so a vsynced application presents as fast as it is streamed

976840d

976840d Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Sep 16:21

fix(kde): Offer virtual outputs on the protocol's presence and prove the screen when a display asks for one

40a9d46

40a9d46 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Sep 04:09
40a9d46

docs: Match the README's interpreter range to the wheels a release carries

0dc571a

0dc571a Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Sep 17:36
0dc571a

docs: Note the interpreter range through 3.15 in the agent instructions

2.1.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 15:32
1ebcb0a

PixelFlux 2.1.0

A feature release adding a virtual camera for client webcam uplinks, resolving the software H.264 encoder at build time, and reworking the NVENC and host-capture paths for lower latency, alongside a full license inventory of both build configurations.

Breaking Changes

  • CaptureSettings.output_mode is replaced by CaptureSettings.codec : the integer mode (0 JPEG, 1 H.264) becomes a codec name: "jpeg", "h264" (striped, or full-frame with video_fullframe), or a full-frame video codec "h265", "vp8", "vp9", "av1"; an unknown name raises ValueError at capture start. The Python StripeFrame.data_type is now the codec id (0 JPEG, 1 H.264, 2 VP8, 3 VP9, 4 AV1, 5 H.265), the same ids VirtualCamera.CODEC_* carry
  • Video wire header : the type byte of a 0x04 frame packs the codec id into its high nibble (1 H.264, 2 VP8, 3 VP9, 4 AV1, 5 H.265) and the frame kind into its low nibble (1 key, 2 intra, 0 delta), so a payload names its own codec and a client decodes whatever arrives without out-of-band state. Every encoder frames through one shared header writer
  • ScreenCapture.active_codec() : the codec a capture streams once its pipeline is built, on X11 and Wayland alike, so a caller learns of a demotion the selection ladder made and can tell its clients
  • pixelflux.SOFTWARE_ENCODERS : a dict from codec name to the software encoder the build carries ("x264" or "openh264" for H.264; "x265" or "kvazaar", "libvpx", "svt-av1" for the rest, probed once against the linked FFmpeg, each opened on a frame in a forked child, and absent when it lacks the encoder or the encoder cannot run on the machine); pixelflux.SOFTWARE_FULLCOLOR lists the codecs that encoder takes a 4:4:4 session for, and pixelflux.hardware_fullcolor(encode_node_index, auto_gpu) those the encode node's engine does, from the same probe as hardware_encoders
  • video_min_qp / video_max_qp / video_crf are a quality index on the H.264 QP scale for every codec: each backend maps them onto its own quantizer domain: H.26x takes the index as its QP, while the VP8 quantizer index and the VP9 / AV1 base_q_idx come from tables measured against x264 at the same index (matched on SSIM over a captured desktop, and ramped to the codec's maximum quantizer at the top of the index where those encoders cannot degrade as far as x264), so one video_crf means one quality whichever codec streams; an NVENC AV1 session takes its own curve, matched on SSIM against H.264 on Ada, the metric the generations agree on
  • The recording socket carries the codec's own elementary stream : Annex-B for H.264 and H.265, an OBU temporal-unit stream for AV1, IVF for VP8 and VP9; the MP4 recorder still takes H.264 only and rejects other codecs at start
  • Computer Use server binds loopback by default : PIXELFLUX_CU=<port> and start_computer_use("<port>") now listen on 127.0.0.1,::1 only; comma-separated host:port entries name the addresses to listen on, 0.0.0.0:<port>,[::]:<port> for every interface
  • use_openh264 setting removed : the software H.264 encoder is now chosen at build time, not per capture. The default gpl feature encodes every CPU H.264 session with x264; a PIXELFLUX_ENABLE_GPL=0 build (--no-default-features --features openh264) encodes them with Cisco OpenH264 instead. use_cpu is once again purely the caller's software/hardware choice
  • ScreenCapture.set_clipboard takes (mime, data) entries : the compositor's offer serves one payload per flavour, so a client pasting rich text takes the markup and one pasting into a plain field takes the text the source wrote, instead of both taking whichever flavour was picked for them. The plain-text entry is still advertised under all five conventional target names, and text/html joins the read preference so a copy made inside the session is seen as markup when its source offers it

New Features

  • X11 zero-copy GPU capture (NvFBC) : on an NVIDIA GPU whose session encodes on NVENC, the X11 path no longer goes through shared memory. The NVIDIA X driver composites each frame into a buffer it owns in video memory and hands back a CUDA device pointer, which is registered with the encoder in place and reused for the life of the geometry, so a frame is never read, written or copied by the CPU between the screen and the bitstream — the property the Wayland dmabuf path already had. The session runs in push model, so the driver generates a frame when an application damages the screen rather than on a sampling timer, grabs take a waiting frame without blocking, and a fullscreen unoccluded application may present straight into the capture buffer, bypassing the X server. Measured at 1920x1080 on a Tesla V100 against the XShm path on the same display: 2.51 ms per frame instead of 5.24 ms for H.264 and 3.03 ms instead of 5.69 ms for H.265, with the host CPU under 0.2 ms a frame. It is selected automatically, and a session it cannot serve (a codec NVENC has no engine for, software encoding, a non-NVIDIA encode node, a watermark, or a driver without NvFBC) says so once and streams through XShm as before
  • X11 zero-copy GPU capture (DRI3) : on any X server whose screen lives on the GPU (XLibre's Xvfb with glamor and DRI3, an Xorg on a DRM driver), the server blits the root into dmabufs pixelflux allocated through GBM on the server's own render node, and NVENC or VA-API encodes them in place through the Wayland zero-copy import. The cursor and the watermark are composited by the server through Render, so neither costs a readback. The server's DAMAGE report both says a frame is worth encoding and ends the wait for one, so a change is published as it lands: measured at 1920x1080 and 60 fps on an RTX 5000 Ada, a repaint reaches the encoder 3.8 ms after it is drawn instead of 8.8 ms, for no measurable CPU on either side. Chosen after NvFBC and before XShm, declined with one line saying why, and never a setting.
  • H.265, VP8, VP9 and AV1 : every video codec runs on NVENC (H.264, H.265, AV1), on VA-API (all five, with the low-power entry point some Intel generations require) and on the software encoder the build resolves for it: x265 (or kvazaar in a GPL-free build) for H.265, libvpx for VP8 and VP9, SVT-AV1 for AV1, all reached through the linked FFmpeg (encoders/avcodec.rs, one session type for the VA-API and software paths). One selection ladder (select_frame_encoder) serves X11, Wayland zero-copy and Wayland readback: the hardware backend the encode node's driver names, then the codec's software encoder, then, for a codec with no path at all, the other video codecs the host serves, hardware first, striped H.264 after them and JPEG last, each step with a logged reason, so a codec the machine cannot serve never leaves a black screen. Every backend keeps the same posture: infinite GOP with key frames on request only, no B-frames or lookahead, single-frame latency, parameter sets on every key frame, CBR with a one-and-a-half-frame VBV or a constant quantizer, 4:4:4 where the codec carries it (H.264 and H.265). libvpx VP9 is the one encoder given more than a single tile: one column per 256 pixels of width, the narrowest tile VP9 allows, which takes a third off its encode time for under three percent of bitrate at equal quality
  • NVENC is codec-parameterized : the device's engine list decides which codecs open (AV1 is refused with a message on GPUs before Ada), HEVC and AV1 get the same rate control, VUI and level pinning H.264 had, and AV1 keeps a single tile since tiles cost bitrate and buy no quality
  • VA-API readback converts on the GPU : the Wayland readback path hands NVENC and VA-API the packed rows it holds (BGRA or RGBA) and both convert in hardware, so the encode thread runs no color conversion for a hardware session
  • Virtual Camera : new VirtualCamera / VirtualCameraSettings classes turn a client's webcam uplink into a V4L2 capture device for applications. Encoded frames of any supported codec (H.264, VP8, VP9, AV1, HEVC, MJPEG) are pushed with cam.push(buffer, codec, keyframe=...) and decoded off the GIL on the camera's own thread; a browser sending JPEG is passed through as an MJPEG device with no decode. push() returns immediately and signals KEYFRAME_WANTED when the decoder needs an IDR, and VirtualCamera.shm_layout() reports the ring's byte layout for the interposer ABI test
  • Virtual-camera sinks : the decoded stream is served to the Selkies V4L2 interposer over a shared-memory ring (no privileges), and mirrored to a v4l2loopback output device and a PipeWire Video/Source node where those are available (libpipewire-0.3 is loaded at run time; there is no build-time PipeWire dependency)
  • Cursor delivery cap : cursor_size_cap defaults to 128 on both backends, the most a browser shows as a cursor, so an application's own sprite (a game's 128px pointer) travels whole instead of being downscaled to the theme's size; larger sprites are still downscaled to the cap
  • KWin session screens : a nested KWin session's screens are grown and removed on demand as zkde_screencast_unstable_v1 virtual outputs (add_app_screen, remove_app_screen, and app_screen_control_available, which proves the rung by growing a probe screen and giving it back, since a stock KWin serves the request without registering the output), each held open by its own connection for as long as the screen should exist, and arranged through kde_output_management_v2 (set_app_screen_layout falls through to it where zwlr_output_management_v1 is absent); their scale follows the capture output's, which the host window's preferred fractional scale carries. The KDE output protocols are vendored under protocols/, newer than the copies the wayland-protocols-plasma crate bundles, because KWin sends a mode flags event those copies do not know
  • **OpenH264 striped encoding...
Read more

6a40b64

6a40b64 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Sep 03:07
6a40b64

fix(tegra): Wait for the access unit the engine owes and repeat the last frame on a still screen so it comes out

23bebf5

23bebf5 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Sep 11:10
23bebf5

ci(release): Refuse a tag whose commit is not on main

1ebcb0a

1ebcb0a Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Sep 14:42
1ebcb0a

docs: Write every list of three or more with the serial comma