From 955da396c7ce089d30a288ec34da661965600b7f Mon Sep 17 00:00:00 2001 From: asepharyana Date: Wed, 2 Sep 2026 19:22:01 +0700 Subject: [PATCH] =?UTF-8?q?debug(stream-watch):=20add=20per-packet=20decry?= =?UTF-8?q?pt=20diagnostics=20=E2=80=94=20why=20VIDEO-PKT=20fires=20but=20?= =?UTF-8?q?no=20mp4=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice-recording/streamWatchReceiver.ts | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/services/discord-gateway/src/modules/voice-recording/streamWatchReceiver.ts b/services/discord-gateway/src/modules/voice-recording/streamWatchReceiver.ts index 132bbe9b..48a1292b 100644 --- a/services/discord-gateway/src/modules/voice-recording/streamWatchReceiver.ts +++ b/services/discord-gateway/src/modules/voice-recording/streamWatchReceiver.ts @@ -43,6 +43,7 @@ import { createChildLogger } from "@/shared/logger/index"; import { H264Depacketizer, muxToMp4 } from "./videoReceiver.js"; const logger = createChildLogger("stream-watch"); +const _log = createChildLogger("stream-watch-decrypt"); // Faithful ports of @discordjs/voice receive constants (dist/index.mjs). const AUTH_TAG_LENGTH = 16; @@ -464,10 +465,20 @@ function decryptVideoPacket( let packet: Buffer; try { packet = legacyDecryptLayer(buffer, encryptionMode, nonceBuffer, secretKey); - } catch { + } catch (e) { + _log.debug( + { userId, err: String(e), msgLen: buffer.length }, + "legacyDecryptLayer threw", + ); return null; // per-packet legacy decrypt failures are transient } - if (!packet || packet.length === 0) return null; + if (!packet || packet.length === 0) { + _log.debug( + { userId, msgLen: buffer.length }, + "legacyDecryptLayer returned empty", + ); + return null; + } // RTP padding (P bit) — strip trailing padding bytes (see parsePacket). const hasPadding = buffer[0] !== 0 && Boolean(buffer[0] & 32); @@ -499,7 +510,12 @@ function decryptVideoPacket( packet, ); if (decrypted && decrypted.length > 0) return decrypted; - } catch { + _log.debug( + { userId, decryptedLen: decrypted?.length, pktLen: packet.length }, + "DAVE VIDEO decrypt returned empty/falsy", + ); + } catch (e) { + _log.debug({ userId, err: String(e) }, "DAVE VIDEO decrypt threw"); // fall through to AUDIO attempt } try { @@ -509,10 +525,23 @@ function decryptVideoPacket( packet, ); if (decryptedAudio && decryptedAudio.length > 0) return decryptedAudio; - } catch { + _log.debug( + { userId, decryptedLen: decryptedAudio?.length, pktLen: packet.length }, + "DAVE AUDIO decrypt returned empty/falsy", + ); + } catch (e) { + _log.debug({ userId, err: String(e) }, "DAVE AUDIO decrypt threw"); // fall through to passthrough } // Passthrough: assume unencrypted above AES layer (or decryptor unavailable). + _log.debug( + { + userId, + passthroughLen: packet.length, + firstBytes: packet.subarray(0, 8).toString("hex"), + }, + "VIDEO passthrough (decrypt failed for all modes)", + ); return packet.length > 0 ? packet : null; } @@ -595,7 +624,15 @@ function handleUdpMessage( uid, ); if (!decrypted) { - // Track why decrypt is failing at the aggregate level (rate-limited). + // Log why — no other visibility into per-packet decrypt failures. + _log.debug( + { + videoCount: stat?.video, + daveAttached: !!net.state.dave?.session, + msgLen: msg.length, + }, + "decryptVideoPacket returned NULL — packet dropped", + ); return; }