Exemplo n.º 1
0
        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;
                }
            }
        }
Exemplo n.º 2
0
        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;
                }
            }
        }