public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { //Assume threadsafe while (count > 0) { if (_partialFramePos == 0 && count >= OpusConverter.FrameBytes) { //We have enough data and no partial frames. Pass the buffer directly to the encoder int encFrameSize = _encoder.EncodeFrame(buffer, offset, _buffer, 0); await _next.WriteAsync(_buffer, 0, encFrameSize, cancellationToken).ConfigureAwait(false); offset += OpusConverter.FrameBytes; count -= OpusConverter.FrameBytes; } else if (_partialFramePos + count >= OpusConverter.FrameBytes) { //We have enough data to complete a previous partial frame. int partialSize = OpusConverter.FrameBytes - _partialFramePos; Buffer.BlockCopy(buffer, offset, _buffer, _partialFramePos, partialSize); int encFrameSize = _encoder.EncodeFrame(_buffer, 0, _buffer, 0); await _next.WriteAsync(_buffer, 0, encFrameSize, cancellationToken).ConfigureAwait(false); offset += partialSize; count -= partialSize; _partialFramePos = 0; } else { //Not enough data to build a complete frame, store this part for later Buffer.BlockCopy(buffer, offset, _buffer, _partialFramePos, count); _partialFramePos += count; break; } } }
/// <summary> /// Enqueues audio to be sent through the UDP client. /// </summary> /// <param name="voice">PCM audio data.</param> public void SendVoice(byte[] voice) { byte[] opusAudio = new byte[voice.Length + 4]; int encodedLength = mainOpusEncoder.EncodeFrame(voice, 0, opusAudio); byte[] len = BitConverter.GetBytes(encodedLength); opusAudio[voice.Length] = len[0]; opusAudio[voice.Length + 1] = len[1]; opusAudio[voice.Length + 2] = len[2]; opusAudio[voice.Length + 3] = len[3]; voiceToSend.Enqueue(opusAudio); }
public void SendPCMFrame(byte[] data, int count) { if (count != _encoder.FrameSize) throw new InvalidOperationException($"Invalid frame size. Got {count}, expected {_encoder.FrameSize}."); lock (_encoder) { byte[] payload = new byte[4000]; int encodedLength = _encoder.EncodeFrame(data, payload); if (_mode == "xsalsa20_poly1305") { //TODO: Encode } lock (_sendQueue) _sendQueue.Enqueue(new Packet(payload, encodedLength)); } }
public byte[] Encode() { int ms = 20; int channels = 2; int sampleRate = 48000; int blockSize = 48 * 2 * channels * ms; //the size per each frame to encode byte[] buffer = new byte[blockSize]; //a nicely sized pcm buffer to work with. var outFormat = new WaveFormat(sampleRate, 16, channels); if (__filename.EndsWith(".mp3")) { using (var mp3Reader = new Mp3FileReader(__filename)) { using (var resampler = new WaveFormatConversionStream(outFormat, mp3Reader)) { int byteCount; using (BinaryWriter bw = new BinaryWriter(new MemoryStream())) { while ((byteCount = resampler.Read(buffer, 0, blockSize)) > 0) { //now to encode byte[] opusOutput = new byte[buffer.Length]; //extra bytes but that's okay int opusEncoded = encoder.EncodeFrame(buffer, 0, opusOutput); bw.Write((ushort)opusEncoded); bw.Write(opusOutput, 0, opusEncoded); } MemoryStream baseStream = bw.BaseStream as MemoryStream; return(baseStream.ToArray()); } } } } return(null); }
private async Task SendVoiceAsync(CancellationToken cancelToken) { try { while (!cancelToken.IsCancellationRequested && State != ConnectionState.Connected) { await Task.Delay(1).ConfigureAwait(false); } if (cancelToken.IsCancellationRequested) { return; } byte[] frame = new byte[_encoder.FrameSize]; byte[] encodedFrame = new byte[MaxOpusSize]; byte[] voicePacket, pingPacket, nonce = null; uint timestamp = 0; double nextTicks = 0.0, nextPingTicks = 0.0; long ticksPerSeconds = Stopwatch.Frequency; double ticksPerMillisecond = Stopwatch.Frequency / 1000.0; double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength; double spinLockThreshold = 3 * ticksPerMillisecond; uint samplesPerFrame = (uint)_encoder.SamplesPerFrame; Stopwatch sw = Stopwatch.StartNew(); if (_isEncrypted) { nonce = new byte[24]; voicePacket = new byte[MaxOpusSize + 12 + 16]; } else { voicePacket = new byte[MaxOpusSize + 12]; } pingPacket = new byte[8]; int rtpPacketLength = 0; voicePacket[0] = 0x80; //Flags; voicePacket[1] = 0x78; //Payload Type voicePacket[8] = (byte)(_ssrc >> 24); voicePacket[9] = (byte)(_ssrc >> 16); voicePacket[10] = (byte)(_ssrc >> 8); voicePacket[11] = (byte)(_ssrc >> 0); if (_isEncrypted) { Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12); } bool hasFrame = false; while (!cancelToken.IsCancellationRequested) { if (!hasFrame && _sendBuffer.Pop(frame)) { ushort sequence = unchecked (_sequence++); voicePacket[2] = (byte)(sequence >> 8); voicePacket[3] = (byte)(sequence >> 0); voicePacket[4] = (byte)(timestamp >> 24); voicePacket[5] = (byte)(timestamp >> 16); voicePacket[6] = (byte)(timestamp >> 8); voicePacket[7] = (byte)(timestamp >> 0); //Encode int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame); //Encrypt if (_isEncrypted) { Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce int ret = SecretBox.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey); if (ret != 0) { continue; } rtpPacketLength = encodedLength + 12 + 16; } else { Buffer.BlockCopy(encodedFrame, 0, voicePacket, 12, encodedLength); rtpPacketLength = encodedLength + 12; } timestamp = unchecked (timestamp + samplesPerFrame); hasFrame = true; } long currentTicks = sw.ElapsedTicks; double ticksToNextFrame = nextTicks - currentTicks; if (ticksToNextFrame <= 0.0) { if (hasFrame) { try { await _udp.SendAsync(voicePacket, rtpPacketLength, _endpoint).ConfigureAwait(false); } catch (SocketException ex) { Logger.Error("Failed to send UDP packet.", ex); } hasFrame = false; } nextTicks += ticksPerFrame; //Is it time to send out another ping? if (currentTicks > nextPingTicks) { //Increment in LE for (int i = 0; i < 8; i++) { var b = pingPacket[i]; if (b == byte.MaxValue) { pingPacket[i] = 0; } else { pingPacket[i] = (byte)(b + 1); break; } } await _udp.SendAsync(pingPacket, pingPacket.Length, _endpoint).ConfigureAwait(false); nextPingTicks = currentTicks + 5 * ticksPerSeconds; } } else { if (hasFrame) { int time = (int)Math.Floor(ticksToNextFrame / ticksPerMillisecond); if (time > 0) { await Task.Delay(time).ConfigureAwait(false); } } else { await Task.Delay(1).ConfigureAwait(false); //Give as much time to the encrypter as possible } } } } catch (OperationCanceledException) { } catch (InvalidOperationException) { } //Includes ObjectDisposedException }
private void SendVoiceAsync(CancellationToken cancelToken) { try { while (!cancelToken.IsCancellationRequested && _state != (int)WebSocketState.Connected) { Thread.Sleep(1); } if (cancelToken.IsCancellationRequested) { return; } byte[] frame = new byte[_encoder.FrameSize]; byte[] encodedFrame = new byte[MaxOpusSize]; byte[] udpPacket, nonce = null; uint timestamp = 0; double nextTicks = 0.0; double ticksPerMillisecond = Stopwatch.Frequency / 1000.0; double ticksPerFrame = ticksPerMillisecond * _encoder.FrameLength; double spinLockThreshold = 3 * ticksPerMillisecond; uint samplesPerFrame = (uint)_encoder.SamplesPerFrame; Stopwatch sw = Stopwatch.StartNew(); if (_isEncrypted) { nonce = new byte[24]; udpPacket = new byte[MaxOpusSize + 12 + 16]; } else { udpPacket = new byte[MaxOpusSize + 12]; } int rtpPacketLength = 0; udpPacket[0] = 0x80; //Flags; udpPacket[1] = 0x78; //Payload Type udpPacket[8] = (byte)((_ssrc >> 24) & 0xFF); udpPacket[9] = (byte)((_ssrc >> 16) & 0xFF); udpPacket[10] = (byte)((_ssrc >> 8) & 0xFF); udpPacket[11] = (byte)((_ssrc >> 0) & 0xFF); if (_isEncrypted) { Buffer.BlockCopy(udpPacket, 0, nonce, 0, 12); } while (!cancelToken.IsCancellationRequested) { double ticksToNextFrame = nextTicks - sw.ElapsedTicks; if (ticksToNextFrame <= 0.0) { while (sw.ElapsedTicks > nextTicks) { if (_sendBuffer.Pop(frame)) { ushort sequence = unchecked (_sequence++); udpPacket[2] = (byte)((sequence >> 8) & 0xFF); udpPacket[3] = (byte)((sequence >> 0) & 0xFF); udpPacket[4] = (byte)((timestamp >> 24) & 0xFF); udpPacket[5] = (byte)((timestamp >> 16) & 0xFF); udpPacket[6] = (byte)((timestamp >> 8) & 0xFF); udpPacket[7] = (byte)((timestamp >> 0) & 0xFF); //Encode int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame); //Encrypt if (_isEncrypted) { Buffer.BlockCopy(udpPacket, 2, nonce, 2, 6); //Update nonce int ret = Sodium.Encrypt(encodedFrame, encodedLength, udpPacket, 12, nonce, _secretKey); if (ret != 0) { continue; } rtpPacketLength = encodedLength + 12 + 16; } else { Buffer.BlockCopy(encodedFrame, 0, udpPacket, 12, encodedLength); rtpPacketLength = encodedLength + 12; } _udp.Send(udpPacket, rtpPacketLength); } timestamp = unchecked (timestamp + samplesPerFrame); nextTicks += ticksPerFrame; } } //Dont sleep if we need to output audio in the next spinLockThreshold else if (ticksToNextFrame > spinLockThreshold) { int time = (int)Math.Ceiling((ticksToNextFrame - spinLockThreshold) / ticksPerMillisecond); Thread.Sleep(1); } } } catch (OperationCanceledException) { } catch (InvalidOperationException) { } //Includes ObjectDisposedException }
#pragma warning restore 4014 private async Task SendVoiceAsync(CancellationToken cancelToken) { byte[] voiceToEncode; voiceToSend.TryDequeue(out voiceToEncode); if (voiceToEncode != null) { Stopwatch timeToSend = Stopwatch.StartNew(); byte[] rtpPacket = new byte[12 + voiceToEncode.Length]; rtpPacket[0] = (byte)0x80; rtpPacket[1] = (byte)0x78; rtpPacket[8] = (byte)((Params.ssrc >> 24) & 0xFF); rtpPacket[9] = (byte)((Params.ssrc >> 16) & 0xFF); rtpPacket[10] = (byte)((Params.ssrc >> 8) & 0xFF); rtpPacket[11] = (byte)((Params.ssrc >> 0) & 0xFF); byte[] opusAudio = new byte[voiceToEncode.Length]; int encodedLength = mainOpusEncoder.EncodeFrame(voiceToEncode, 0, opusAudio); int dataSent = 0; //actual sending { //sequence big endian rtpPacket[2] = (byte)((___sequence >> 8)); rtpPacket[3] = (byte)((___sequence >> 0) & 0xFF); //timestamp big endian rtpPacket[4] = (byte)((___timestamp >> 24) & 0xFF); rtpPacket[5] = (byte)((___timestamp >> 16) & 0xFF); rtpPacket[6] = (byte)((___timestamp >> 8)); rtpPacket[7] = (byte)((___timestamp >> 0) & 0xFF); if (opusAudio == null) { throw new ArgumentNullException("opusAudio"); } int maxSize = encodedLength; Buffer.BlockCopy(opusAudio, 0, rtpPacket, 12, encodedLength); dataSent = _udp.SendAsync(rtpPacket, encodedLength + 12).Result; ___sequence = unchecked (___sequence++); ___timestamp = unchecked (___timestamp + (uint)(voiceToEncode.Length / 2)); } timeToSend.Stop(); //stop after completely sending //Compensate for however long it took to sent. if (timeToSend.ElapsedMilliseconds > 0) { long timeToWait = (msToSend * TimeSpan.TicksPerMillisecond) - (timeToSend.ElapsedMilliseconds * TimeSpan.TicksPerMillisecond); if (timeToWait > 0) //if it's negative then don't bother waiting { await Task.Delay(new TimeSpan(timeToWait)).ConfigureAwait(false); } } else { await Task.Delay(msToSend).ConfigureAwait(false); } VoiceDebugLogger.LogAsync("Sent " + dataSent + " bytes of Opus audio", MessageLevel.Unecessary); } }
public override void Write(byte[] buffer, int offset, int count) { count = _encoder.EncodeFrame(buffer, offset, count, _buffer, 0); base.Write(_buffer, 0, count); }
#pragma warning restore 4014 private async Task SendVoiceAsync(CancellationToken cancelToken) { byte[] voiceToEncode; //pcm data voiceToSend.TryDequeue(out voiceToEncode); if (voiceToEncode != null) { Stopwatch timeToSend = Stopwatch.StartNew(); byte[] fullVoicePacket = new byte[4000 + 12 + 16]; byte[] nonce = new byte[24]; fullVoicePacket[0] = (byte)0x80; //flags fullVoicePacket[1] = (byte)0x78; //flags fullVoicePacket[8] = (byte)((Params.ssrc >> 24) & 0xFF); //ssrc fullVoicePacket[9] = (byte)((Params.ssrc >> 16) & 0xFF); //ssrc fullVoicePacket[10] = (byte)((Params.ssrc >> 8) & 0xFF); //ssrc fullVoicePacket[11] = (byte)((Params.ssrc >> 0) & 0xFF); //ssrc byte[] opusAudio = new byte[voiceToEncode.Length]; int encodedLength = mainOpusEncoder.EncodeFrame(voiceToEncode, 0, opusAudio); int dataSent = 0; //actual sending { ___sequence = unchecked (___sequence++); //sequence big endian fullVoicePacket[2] = (byte)((___sequence >> 8)); fullVoicePacket[3] = (byte)((___sequence >> 0) & 0xFF); //timestamp big endian fullVoicePacket[4] = (byte)((___timestamp >> 24) & 0xFF); fullVoicePacket[5] = (byte)((___timestamp >> 16) & 0xFF); fullVoicePacket[6] = (byte)((___timestamp >> 8)); fullVoicePacket[7] = (byte)((___timestamp >> 0) & 0xFF); Buffer.BlockCopy(fullVoicePacket, 0, nonce, 0, 12); //copy header into nonce //Buffer.BlockCopy(rtpPacket, 2, nonce, 2, 6); //copy 6 bytes for nonce int returnVal = SecretBox.Encrypt(opusAudio, encodedLength, fullVoicePacket, 12, nonce, __secretKey); if (returnVal != 0) { return; } if (opusAudio == null) { throw new ArgumentNullException("opusAudio"); } int maxSize = encodedLength; int rtpPacketLength = encodedLength + 12 + 16; #if NETFX4_5 //dataSent = _udp.SendAsync(fullVoicePacket, encodedLength + 12 + 16).Result; await _udp.SendAsync(fullVoicePacket, encodedLength + 12 + 16); #else dataSent = _udp.Send(fullVoicePacket, rtpPacketLength); #endif ___timestamp = unchecked (___timestamp + (uint)(voiceToEncode.Length / 2)); } timeToSend.Stop(); //stop after completely sending //Compensate for however long it took to sent. if (timeToSend.ElapsedMilliseconds > 0) { long timeToWait = (msToSend * TimeSpan.TicksPerMillisecond) - (timeToSend.ElapsedMilliseconds * TimeSpan.TicksPerMillisecond); if (timeToWait > 0) //if it's negative then don't bother waiting { #if NETFX4_5 await Task.Delay(new TimeSpan(timeToWait)).ConfigureAwait(false); #else Thread.Sleep(new TimeSpan(timeToWait)); #endif } } else { #if NETFX4_5 await Task.Delay(msToSend).ConfigureAwait(false); #else Thread.Sleep(msToSend); #endif } VoiceDebugLogger.LogAsync("Sent " + dataSent + " bytes of Opus audio", MessageLevel.Unecessary); } }