From 9fa8d1d4277b6346baa34961f869f17d5ae32e3e Mon Sep 17 00:00:00 2001 From: Dan <31395415+cakedan@users.noreply.github.com> Date: Fri, 15 Jun 2018 16:51:34 -0400 Subject: [PATCH] added more debug logs --- disco/voice/client.py | 35 ++++++++++++++++++++++------------- disco/voice/udp.py | 40 +++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/disco/voice/client.py b/disco/voice/client.py index 79e3cdd..08f53e9 100644 --- a/disco/voice/client.py +++ b/disco/voice/client.py @@ -103,7 +103,6 @@ class VoiceClient(LoggingClass): self._heartbeat_task = None # SSRCs - self.audio_ssrcs = {} def __repr__(self): @@ -128,9 +127,15 @@ class VoiceClient(LoggingClass): self.send(VoiceOPCode.HEARTBEAT, time.time()) gevent.sleep(interval / 1000) - def set_speaking(self, value, delay=0): + def set_speaking(self, voice=False, soundshare=False, delay=0): + value = SpeakingCodes.NONE.value + if voice: + value |= SpeakingCodes.VOICE.value + if soundshare: + value |= SpeakingCodes.SOUNDSHARE.value + self.send(VoiceOPCode.SPEAKING, { - 'speaking': int(value), + 'speaking': value, 'delay': delay, 'ssrc': self.ssrc, }) @@ -157,6 +162,9 @@ class VoiceClient(LoggingClass): self.video_codec = data['video_codec'] self.transport_id = data['media_session_id'] + # Set the UDP's RTP Audio Header's Payload Type + self.udp.set_audio_codec(data['audio_codec']) + def on_voice_hello(self, data): self.log.info('[%s] Recieved Voice HELLO payload, starting heartbeater', self) self._heartbeat_task = gevent.spawn(self._heartbeat, data['heartbeat_interval']) @@ -188,16 +196,14 @@ class VoiceClient(LoggingClass): codecs = [] - for i in range(len(AudioCodecs)): - codec = AudioCodecs[i] - payload_type = PayloadTypes.get(codec) - if payload_type: - codecs.append({ - 'name': codec, - 'type': 'audio', - 'priority': (i + 1) * 1000, - 'payload_type': payload_type.value - }) + # Sending discord our available codecs and rtp payload type for it + for idx, codec in enumerate(AudioCodecs): + codecs.append({ + 'name': codec, + 'type': 'audio', + 'priority': (idx + 1) * 1000, + 'payload_type': PayloadTypes.get(codec).value, + }) self.log.debug('[%s] IP discovery completed (ip = %s, port = %s), sending SELECT_PROTOCOL', self, ip, port) self.send(VoiceOPCode.SELECT_PROTOCOL, { @@ -227,6 +233,9 @@ class VoiceClient(LoggingClass): self.video_codec = sdp['video_codec'] self.transport_id = sdp['media_session_id'] + # Set the UDP's RTP Audio Header's Payload Type + self.udp.set_audio_codec(sdp['audio_codec']) + # Create a secret box for encryption/decryption self.udp.setup_encryption(bytes(bytearray(sdp['secret_key']))) diff --git a/disco/voice/udp.py b/disco/voice/udp.py index bcf50fe..840f4c1 100644 --- a/disco/voice/udp.py +++ b/disco/voice/udp.py @@ -20,7 +20,8 @@ PayloadTypes = Enum(OPUS=0x78) MAX_UINT32 = 4294967295 MAX_SEQUENCE = 65535 -RTP_HEADER_ONE_BYTE = (0xBE, 0xDE) +RTP_HEADER_VERSION = 0x80 # Only RTP Version is set here (value of 2 << 6) +RTP_EXTENSION_ONE_BYTE = (0xBE, 0xDE) RTPHeader = namedtuple('RTPHeader', [ @@ -65,10 +66,17 @@ class UDPVoiceClient(LoggingClass): self._run_task = None self._secret_box = None - # Buffer used for encoding/sending frames - self._header = bytearray(12) - self._header[0] = 2 << 6 # Only RTP Version set in the first byte of the header, 0x80 - self._header[1] = PayloadTypes.OPUS.value + # RTP Header + self._rtp_audio_header = bytearray(12) + self._rtp_audio_header[0] = RTP_HEADER_VERSION + + def set_audio_codec(self, codec): + ptype = PayloadTypes.get(codec) + if ptype: + self._rtp_audio_header[1] = ptype.value + self.log.debug('[%s] Set UDP\'s Audio Codec to %s, RTP payload type %s', self.vc, ptype.name, ptype.value) + else: + raise Exception('The voice codec, {}, isn\'t supported.'.format(codec)) def increment_timestamp(self, by): self.timestamp += by @@ -83,9 +91,9 @@ class UDPVoiceClient(LoggingClass): frame = bytearray(frame) # Pack the rtc header into our buffer - struct.pack_into('>H', self._header, 2, sequence or self.sequence) - struct.pack_into('>I', self._header, 4, timestamp or self.timestamp) - struct.pack_into('>i', self._header, 8, self.vc.ssrc) + struct.pack_into('>H', self._rtp_audio_header, 2, sequence or self.sequence) + struct.pack_into('>I', self._rtp_audio_header, 4, timestamp or self.timestamp) + struct.pack_into('>i', self._rtp_audio_header, 8, self.vc.ssrc) if self.vc.mode == 'xsalsa20_poly1305_lite': # Use an incrementing number as a nonce, only first 4 bytes of the nonce is padded on @@ -103,20 +111,20 @@ class UDPVoiceClient(LoggingClass): elif self.vc.mode == 'xsalsa20_poly1305': # Nonce is the header nonce = bytearray(24) - nonce[:12] = self._header + nonce[:12] = self._rtp_audio_header nonce_padding = None else: raise Exception('The voice mode, {}, isn\'t supported.'.format(self.vc.mode)) # Encrypt the payload with the nonce - raw = self._secret_box.encrypt(bytes(frame), bytes(nonce)).ciphertext + payload = self._secret_box.encrypt(bytes(frame), bytes(nonce)).ciphertext # Pad the payload with the nonce, if applicable if nonce_padding: - raw += nonce_padding + payload += nonce_padding # Send the header (sans nonce padding) plus the payload - self.send(self._header + raw) + self.send(self._rtp_audio_header + payload) # Increment our sequence counter self.sequence += 1 @@ -133,6 +141,7 @@ class UDPVoiceClient(LoggingClass): # Data cannot be less than the bare minimum, just ignore if len(data) <= 12: + self.log.debug('[%s] [VoiceData] Received voice data under 13 bytes', self.vc) continue first, second, sequence, timestamp, ssrc = struct.unpack_from('>BBHII', data) @@ -151,12 +160,14 @@ class UDPVoiceClient(LoggingClass): # Check if rtp version is 2 if rtp.version != 2: + self.log.debug('[%s] [VoiceData] Received an invalid RTP packet version, %s', self.vc, rtp.version) continue payload_type = PayloadTypes.get(rtp.payload_type) # Unsupported payload type received if not payload_type: + self.log.debug('[%s] [VoiceData] Received unsupported payload type, %s', self.vc, rtp.payload_type) continue nonce = bytearray(24) @@ -169,11 +180,13 @@ class UDPVoiceClient(LoggingClass): elif self.vc.mode == 'xsalsa20_poly1305': nonce[:12] = data[:12] else: + self.log.debug('[%s] [VoiceData] Unsupported Encryption Mode, %s', self.vc, self.vc.mode) continue try: data = self._secret_box.decrypt(bytes(data[12:]), bytes(nonce)) except Exception: + self.log.debug('[%s] [VoiceData] Failed to decode data from ssrc %s', self.vc, rtp.ssrc) continue # RFC3550 Section 5.1 (Padding) @@ -184,7 +197,7 @@ class UDPVoiceClient(LoggingClass): if rtp.extension: # RFC5285 Section 4.2: One-Byte Header rtp_extension_header = struct.unpack_from('>BB', data) - if rtp_extension_header == RTP_HEADER_ONE_BYTE: + if rtp_extension_header == RTP_EXTENSION_ONE_BYTE: data = data[2:] fields_amount, = struct.unpack_from('>H', data) @@ -217,6 +230,7 @@ class UDPVoiceClient(LoggingClass): # RFC3550 Section 5.3: Profile-Specific Modifications to the RTP Header # clients send it sometimes, definitely on fresh connects to a server, dunno what to do here if rtp.marker: + self.log.debug('[%s] [VoiceData] Received RTP data with the marker set, skipping', self.vc) continue payload = VoiceData(