Handle legacy UDPVoiceOpus format (type 0x80) inside 1.5 crypto
The server sends audio with type byte 0x80 (legacy UDPVoiceOpus) inside the Mumble 1.5 native UDP crypto envelope. This is the legacy format: [session varint][seq varint][length varint][opus data]. Added handleLegacyUDPVoice to parse this format. Refactored dispatch into dispatchOpus15 + decodeAndDispatch for reuse by both 0x00 protobuf and 0x80 legacy paths.
This commit is contained in:
committed by
Brandon McGinty
parent
e6806a7521
commit
e93437a212
+70
-13
@@ -559,27 +559,37 @@ func (c *Client) HandleUDPPacket15(packet []byte, pktNum uint64) {
|
||||
log.Info("UDP15 #%d: ping response, ignoring", pktNum)
|
||||
return
|
||||
}
|
||||
if msgType != 0x00 {
|
||||
log.Warn("UDP15 #%d: unknown message type 0x%02x", pktNum, msgType)
|
||||
|
||||
// MumbleUDP.Audio protobuf format (1.5 native).
|
||||
if msgType == 0x00 {
|
||||
session, frameNum, opusData, terminator := decodeUDPAudio(plaintext)
|
||||
c.dispatchOpus15(pktNum, session, int64(frameNum), opusData, terminator)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse MumbleUDP.Audio protobuf.
|
||||
session, frameNum, opusData, terminator := decodeUDPAudio(plaintext)
|
||||
// Legacy UDPVoiceOpus format: type byte has bits 5-7 = 4.
|
||||
if (msgType >> 5) == 4 {
|
||||
c.handleLegacyUDPVoice(pktNum, plaintext)
|
||||
return
|
||||
}
|
||||
|
||||
log.Warn("UDP15 #%d: unknown message type 0x%02x", pktNum, msgType)
|
||||
}
|
||||
|
||||
// dispatchOpus15 processes a decoded MumbleUDP.Audio frame and dispatches
|
||||
// the decoded PCM to audio listeners.
|
||||
func (c *Client) dispatchOpus15(pktNum uint64, session uint32, frameNum int64, opusData []byte, terminator bool) {
|
||||
if len(opusData) == 0 && !terminator {
|
||||
log.Info("UDP15 #%d: no opus data (session=%d frame=%d), skipping", pktNum, session, frameNum)
|
||||
return
|
||||
}
|
||||
|
||||
// Find the user.
|
||||
user := c.Users[session]
|
||||
if user == nil {
|
||||
log.Warn("UDP15 #%d: unknown session %d", pktNum, session)
|
||||
return
|
||||
}
|
||||
|
||||
// Get or create decoder.
|
||||
decoder := user.decoder
|
||||
if decoder == nil {
|
||||
codec := c.audioCodec
|
||||
@@ -593,7 +603,6 @@ func (c *Client) HandleUDPPacket15(packet []byte, pktNum uint64) {
|
||||
}
|
||||
|
||||
if terminator && len(opusData) == 0 {
|
||||
// Decoder reset on terminator.
|
||||
decoder.Reset()
|
||||
log.Info("UDP15 #%d: terminator for %s, decoder reset", pktNum, user.Name)
|
||||
return
|
||||
@@ -603,9 +612,59 @@ func (c *Client) HandleUDPPacket15(packet []byte, pktNum uint64) {
|
||||
return
|
||||
}
|
||||
|
||||
// Detect sequence gaps via frame_number.
|
||||
c.decodeAndDispatch(pktNum, user, decoder, frameNum, opusData)
|
||||
}
|
||||
|
||||
// handleLegacyUDPVoice parses the legacy UDPVoice format (type byte 0x80)
|
||||
// inside a 1.5-decrypted payload.
|
||||
func (c *Client) handleLegacyUDPVoice(pktNum uint64, data []byte) {
|
||||
pos := 0
|
||||
|
||||
// Session varint.
|
||||
session, n := varint.Decode(data[pos:])
|
||||
if n <= 0 {
|
||||
log.Warn("UDP15 #%d: legacy session varint decode failed", pktNum)
|
||||
return
|
||||
}
|
||||
pos += n
|
||||
|
||||
// Sequence varint.
|
||||
seq, n := varint.Decode(data[pos:])
|
||||
if n <= 0 {
|
||||
log.Warn("UDP15 #%d: legacy seq varint decode failed", pktNum)
|
||||
return
|
||||
}
|
||||
pos += n
|
||||
|
||||
// Length varint (bit 13 = terminator).
|
||||
length, n := varint.Decode(data[pos:])
|
||||
if n <= 0 {
|
||||
log.Warn("UDP15 #%d: legacy length varint decode failed", pktNum)
|
||||
return
|
||||
}
|
||||
pos += n
|
||||
|
||||
terminator := (length & 0x2000) != 0
|
||||
audioLen := int(length &^ 0x2000)
|
||||
if audioLen > len(data)-pos {
|
||||
log.Warn("UDP15 #%d: legacy audio length %d > remaining %d",
|
||||
pktNum, audioLen, len(data)-pos)
|
||||
return
|
||||
}
|
||||
|
||||
opusData := data[pos : pos+audioLen]
|
||||
|
||||
log.Info("UDP15 #%d: legacy voice session=%d seq=%d opus_len=%d term=%v",
|
||||
pktNum, session, seq, len(opusData), terminator)
|
||||
|
||||
c.dispatchOpus15(pktNum, uint32(session), seq, opusData, terminator)
|
||||
}
|
||||
|
||||
// decodeAndDispatch decodes an Opus frame and dispatches PCM to audio listeners.
|
||||
func (c *Client) decodeAndDispatch(pktNum uint64, user *User, decoder AudioDecoder, frameNum int64, opusData []byte) {
|
||||
// Detect sequence gaps.
|
||||
if user.audioSequenceValid {
|
||||
gap := int64(frameNum) - user.audioSequence
|
||||
gap := frameNum - user.audioSequence
|
||||
if gap > 1 && gap < 100 {
|
||||
log.Info("UDP15 #%d: seq gap for %s: %d -> %d (loss=%d), generating PLC",
|
||||
pktNum, user.Name, user.audioSequence, frameNum, gap-1)
|
||||
@@ -621,10 +680,9 @@ func (c *Client) HandleUDPPacket15(packet []byte, pktNum uint64) {
|
||||
return
|
||||
}
|
||||
}
|
||||
user.audioSequence = int64(frameNum)
|
||||
user.audioSequence = frameNum
|
||||
user.audioSequenceValid = true
|
||||
|
||||
// Decode Opus.
|
||||
pcm, err := decoder.Decode(opusData, AudioMaximumFrameSize)
|
||||
if err != nil {
|
||||
log.Warn("UDP15 #%d: Opus decode failed for %s: %v", pktNum, user.Name, err)
|
||||
@@ -634,12 +692,11 @@ func (c *Client) HandleUDPPacket15(packet []byte, pktNum uint64) {
|
||||
|
||||
log.Info("UDP15 #%d: Opus OK for %s, pcm_samples=%d", pktNum, user.Name, len(pcm))
|
||||
|
||||
// Dispatch.
|
||||
event := AudioPacket{
|
||||
Client: c,
|
||||
Sender: user,
|
||||
Target: &VoiceTarget{ID: 0},
|
||||
Sequence: int64(frameNum),
|
||||
Sequence: frameNum,
|
||||
AudioBuffer: AudioBuffer(pcm),
|
||||
}
|
||||
c.dispatchAudio(user, &event)
|
||||
|
||||
Reference in New Issue
Block a user