async Task ReceiveLoop() { ArraySegment <byte> buffer = new ArraySegment <byte>(new byte[socket.ReceiveBufferSize]); while (socket.Connected) { try { int read = await socket.ReceiveAsync(buffer, SocketFlags.None) .ConfigureAwait(false); if (read == 70 && discoveringIP) { HandleIPDiscoveryPacket(buffer.Array); // For now, the receive loop is only needed for discovering the IP. // To save from some unneeded calculations, we can end the loop here. // TODO: remove when voice data receiving support is added. break; } } catch (ObjectDisposedException) { // Socket was disposed while receiving break; } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.TimedOut) { // Since we have the receive timeout set to a finite value, // if the bot is deafened, or has no one in the voice channel with it, // it will time out on receiving, but this is okay. continue; } else if (ex.SocketErrorCode == SocketError.Interrupted || ex.SocketErrorCode == SocketError.Shutdown) { // Socket was shutdown, just end loop. break; } else { // Unexpected error log.LogError($"[ReceiveLoop] Unexpected socket error: code = {ex.SocketErrorCode}, error = {ex}"); OnClosedPrematurely?.Invoke(this, EventArgs.Empty); break; } } catch (Exception ex) { // Unexpected error log.LogError($"[ReceiveLoop] Unexpected error: {ex}"); OnClosedPrematurely?.Invoke(this, EventArgs.Empty); break; } } }
void SendLoop() { const int TICKS_PER_MS = 1; const int MAX_OPUS_SIZE = 4000; byte[] frame = new byte[encoder.FrameSize]; byte[] encodedFrame = new byte[MAX_OPUS_SIZE]; ushort sequence = 0; uint timestamp = 0; int ticksPerFrame = TICKS_PER_MS * encoder.FrameLength; uint samplesPerFrame = (uint)encoder.SamplesPerFrame; byte[] nonce = new byte[24]; byte[] voicePacket = new byte[MAX_OPUS_SIZE + 12 + 16]; int rtpPacketLength = 0; // Setup RTP packet header voicePacket[0] = 0x80; // Packet Type voicePacket[1] = 0x78; // Packet Version voicePacket[8] = (byte)(Ssrc >> 24); // ssrc voicePacket[9] = (byte)(Ssrc >> 16); voicePacket[10] = (byte)(Ssrc >> 8); voicePacket[11] = (byte)(Ssrc >> 0); // Copy RTP packet header into nonce Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12); int nextTicks = Environment.TickCount; // Begin send loop bool hasFrame = false; while (socket.Connected) { try { // If we don't have a frame to send and we have a frame buffered if (!hasFrame && sendBuffer.Count > 0) { // Read frame from buffer sendBuffer.Read(frame, 0, frame.Length); // Set sequence number in RTP packet voicePacket[2] = (byte)(sequence >> 8); voicePacket[3] = (byte)(sequence >> 0); // Set timestamp in RTP packet voicePacket[4] = (byte)(timestamp >> 24); voicePacket[5] = (byte)(timestamp >> 16); voicePacket[6] = (byte)(timestamp >> 8); voicePacket[7] = (byte)(timestamp >> 0); // Increase the sequence number, use unchecked because wrapping is valid unchecked { sequence++; }; // Encode the frame int encodedLength = encoder.EncodeFrame(frame, 0, encodedFrame); // Update the separately stored nonce from RTP packet Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); // Encrypt the frame int encryptStatus = LibSodium.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, secretKey); if (encryptStatus == 0) { // Update timestamp timestamp = unchecked (timestamp + samplesPerFrame); rtpPacketLength = encodedLength + 12 + 16; hasFrame = true; } else { // Failed to encrypt log.LogError($"[SendLoop] Failed to encrypt RTP packet. encryptStatus = {encryptStatus}"); } } int currentTicks = Environment.TickCount; int ticksToNextFrame = nextTicks - currentTicks; // Is it time to send the next frame? if (ticksToNextFrame <= 0) { if (IsPaused) { // If we are paused, do nothing Thread.Sleep(100); } // If we have a frame to send else if (hasFrame) { hasFrame = false; try { // Send the frame across UDP Send(voicePacket, 0, rtpPacketLength); } catch (ObjectDisposedException) { // Socket was disposed while sending break; } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.Interrupted || ex.SocketErrorCode == SocketError.Shutdown) { // Socket was shutdown while sending break; } else { // Unexpected error log.LogError($"[SendLoop] Unexpected socket error: code = {ex.SocketErrorCode}, error = {ex}"); OnClosedPrematurely?.Invoke(this, EventArgs.Empty); break; } } } // Calculate the time for next frame nextTicks += ticksPerFrame; } else { // Nothing to do, so sleep Thread.Sleep(1); } } catch (Exception ex) { // Unexpected error log.LogError($"[SendLoop] Unexpected error: {ex}"); OnClosedPrematurely?.Invoke(this, EventArgs.Empty); break; } } }