Tags: nakagami/grdp
Tags
rdpgfx: fix AVC444 colour shift by forcing limited-range BT.709 in NV… …12 Y-cache VideoToolbox on macOS outputs kCVPixelFormatType_420YpCbCr8BiPlanarFullRange (color_range=2), so nv12.FullRange was propagated to avc444YPlane.fullRange=true. This caused combineAVC444v2BGRA to apply the BT.709 full-range coefficients (256·y + 403·v, etc.) on LC=2 chroma-upgrade frames. SDL2's Metal renderer, however, auto-selects limited-range BT.709 for HD resolutions (>1000×600) via SDL_GetYUVConversionModeForResolution. The result was a visible colour shift whenever the display alternated between LC=0/1 NV12 frames (SDL2 limited BT.709) and LC=2 BGRA overlay frames (full-range BT.709): e.g. a blue pixel at Y=98, Cb=210 rendered as (95,77,255) via SDL2 but (98,82,250) via LC=2. Fix: always set avc444YPlane.fullRange=false in updateAVC444YCacheFromNV12. Both SDL2 and combineAVC444v2BGRA now apply limited-range BT.709 coefficients to the same VideoToolbox-supplied Y and UV values, making the B2/B3 pixels (shared stream-1 chroma) identical between the two rendering paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rdpgfx: skip no-IDR soft reset when in SW fallback mode After a VideoToolbox stall, ForceRefresh is already sent multiple times during the stall period before the SW fallback is created. Allowing a no-IDR soft reset in SW fallback mode just adds another keyframeWaitTimeoutSWFallback seconds of freeze for no benefit — the Windows server consistently does not deliver an IDR in response to ForceRefresh in post-stall scenarios. Skip straight to reconnect when usingSWFallback is true. For pure SW sessions (no prior HW stall) the existing one-retry budget is kept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix isAuxChromaBlank: require majority of samples to be non-zero Previously, a single sampled position with Cb ≥ 8 was enough to declare the LC=2 aux frame 'has real chroma data'. Windows Server's stream2 IDR frame can contain isolated codec-initialisation artefacts (e.g. Cb=9 at a few scattered pixels) while the rest of the frame is still zero. That one stray pixel fooled the check into combining a mostly-zero chroma frame with real luma, producing bright-green blocks at connection startup. Switch from 'any non-zero sample → not blank' to 'majority of sampled positions must exceed threshold → not blank'. The 6×2 = 12 sampled values now all need at least 7 to be ≥ 8 before the frame is accepted. A real desktop always has consistent non-zero Cb/Cr; the artefact case passes only 1–2 samples, so it is correctly treated as still blank. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rdpgfx: degrade LC=2 gracefully when stream2 IDR never arrives When the server sends stream2 P-frames in LC=0 packets but never sends a stream2 IDR (even after repeated ForceRefresh keyframe requests), the previous code would reconnect after auxDecoderMaxIDRRetries attempts. Since the server behaviour is consistent, reconnecting reproduces the same failure loop. Add lc2PermanentlyDegraded flag. After exhausting retries with lc2EverDecoded=false, set the flag and degrade silently to LC=0-only instead of reconnecting. decodeAVC444LC2 skips LC=2 frames without arming the renegotiation timer, stopping the endless cycle. If the server later delivers a stream2 IDR, primeAuxDecoder clears the flag and recovers the aux decoder. RESET_GRAPHICS also clears the flag for a clean slate on the next AVC444 sequence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
h264: add V4L2 M2M hardware decoder probe (Raspberry Pi / Linux SoC) The av_hwdevice_iterate_types loop does not expose h264_v4l2m2m since it is a standalone FFmpeg codec rather than an hwdevice-backed decoder. Probe it explicitly after the hwdevice loop so that Linux SoCs (e.g. Raspberry Pi 4/5, Rockchip, Allwinner) can use their kernel V4L2 M2M H.264 hardware decoder when FFmpeg is compiled with V4L2 M2M support. h264_v4l2m2m outputs NV12 frames directly in CPU-accessible memory, so no av_hwframe_transfer_data step is needed. The existing NV12 handling path in convertFrame() processes these frames correctly. Changes: - Add grdp_find_v4l2m2m() C helper wrapping avcodec_find_decoder_by_name - Add #cgo nocallback grdp_find_v4l2m2m directive - In newH264DecoderInternal: after hwdevice loop, try h264_v4l2m2m; on success replace the codec context, set useHW=true, hwNeedsZeroCheck=false, and skip the duplicate avcodec_open2 call via the new alreadyOpened flag - On macOS or FFmpeg builds without V4L2, grdp_find_v4l2m2m returns nil and the probe is a no-op Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix green screen and improve VT stall detection - plugin/rdpgfx/avc.go: Add isAuxChromaBlank() to detect uninitialised AVC444v2 stream2 chroma (Cb=0/Cr=0). Windows Server sends near-zero chroma in the stream2 IDR and only updates changed regions; combining zero chroma with any luma produces BGRA(0,135,0,255) — a bright green frame. Drop the frame until real chroma data arrives. - plugin/rdpgfx/h264_ffmpeg.go: Add mid-session null-frame stall detection (avcHWMidSessionNullFrameLimit=150). Previously only the early session window (hwSentCount<50) used null-frame counting; mid- session stalls waited for the 7-second CGo-safe timeout. 150 null frames ≈ 5 s at 30 fps — safely above normal GOP-boundary noise (≤25) but 2 s before the safety valve, reducing observed freeze duration from ~17 s to ~11 s. - example/gxui.go: Remove debug-level slog handler from main(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rdpgfx: prime aux decoder before zero-fill drop check When the main HW decoder (VideoToolbox) returns a zero-filled IOSurface, decodeAVC444 and decodeAVC444WithI420 return early before reaching the primeAuxDecoder call at the end of each function. This silently discards any stream2 IDR embedded in the dropped LC=0 frame, which can prevent the aux decoder (h264dec2) from being created at the right time and causes a full-screen green tint on the next LC=2 frame. Move the primeAuxDecoder call to before the frame.Dropped check in both functions. stream2 is parsed independently of the VideoToolbox pipeline, so its IDR data is always valid even when the main frame is zero-filled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PreviousNext