// todo 3.2 direct opus streams public int SendPcmFrame(VoiceGateway gw, Span <byte> data, int offset, int count) { var secretKey = gw.SecretKey; if (secretKey.Length == 0) { return((int)SendPcmError.SecretKeyUnavailable); } // encode using opus var encodeOutput = _arrayPool.Rent(LibOpusEncoder.MaxData); try { var encodeOutputLength = Encode(data, encodeOutput); return(SendOpusFrame(gw, encodeOutput, 0, encodeOutputLength)); } finally { _arrayPool.Return(encodeOutput); } }
public int SendOpusFrame(VoiceGateway gw, byte[] data, int offset, int count) { var secretKey = gw.SecretKey; if (secretKey is null) { return((int)SendPcmError.SecretKeyUnavailable); } // form RTP header var headerLength = 1 // version + flags + 1 // payload type + 2 // sequence + 4 // timestamp + 4; // ssrc var header = new byte[headerLength]; header[0] = 0x80; // version + flags header[1] = 0x78; // payload type // get byte values for header data var seqBytes = BitConverter.GetBytes(gw.Sequence); // 2 var nonceBytes = BitConverter.GetBytes(gw.NonceSequence); // 2 var timestampBytes = BitConverter.GetBytes(gw.Timestamp); // 4 var ssrcBytes = BitConverter.GetBytes(gw.Ssrc); // 4 gw.Timestamp += (uint)FrameSizePerChannel; gw.Sequence++; gw.NonceSequence++; if (BitConverter.IsLittleEndian) { Array.Reverse(seqBytes); Array.Reverse(nonceBytes); Array.Reverse(timestampBytes); Array.Reverse(ssrcBytes); } // copy headers Buffer.BlockCopy(seqBytes, 0, header, 2, 2); Buffer.BlockCopy(timestampBytes, 0, header, 4, 4); Buffer.BlockCopy(ssrcBytes, 0, header, 8, 4); //// encryption part //// create a byte array where to store the encrypted data //// it has to be inputLength + crypto_secretbox_MACBYTES (constant with value 16) var encryptedBytes = new byte[count + 16]; //// form nonce with header + 12 empty bytes //var nonce = new byte[24]; //Buffer.BlockCopy(rtpHeader, 0, nonce, 0, rtpHeader.Length); var nonce = new byte[4]; Buffer.BlockCopy(seqBytes, 0, nonce, 2, 2); Sodium.Encrypt(data, 0, count, encryptedBytes, 0, nonce, secretKey); var rtpDataLength = headerLength + encryptedBytes.Length + nonce.Length; var rtpData = _arrayPool.Rent(rtpDataLength); try { //copy headers Buffer.BlockCopy(header, 0, rtpData, 0, header.Length); //copy audio data Buffer.BlockCopy(encryptedBytes, 0, rtpData, header.Length, encryptedBytes.Length); Buffer.BlockCopy(nonce, 0, rtpData, rtpDataLength - 4, 4); gw.SendRtpData(rtpData, rtpDataLength); // todo 3.2 When there's a break in the sent data, // the packet transmission shouldn't simply stop. // Instead, send five frames of silence (0xF8, 0xFF, 0xFE) // before stopping to avoid unintended Opus interpolation // with subsequent transmissions. return(rtpDataLength); } finally { _arrayPool.Return(rtpData); } }