C# opus和NAudio不同步
我在游戏中添加了voip,因为Unity的麦克风类在Web_GL中不受支持,而且速度已经很慢,并且会产生浮点而不是字节。现在有人建议我使用编解码器,即Opus,然后我找到了它的包装器和它的使用NAudio的演示,我对它相当满意,它使用了一些额外的循环,移除后也得到了相同的结果,但无论如何,它也给出了4000字节,48k采样率,我将其减少到8k,最大缓冲区大小减少到350。下面是该脚本的代码C# opus和NAudio不同步,c#,.net,unity3d,naudio,opus,C#,.net,Unity3d,Naudio,Opus,我在游戏中添加了voip,因为Unity的麦克风类在Web_GL中不受支持,而且速度已经很慢,并且会产生浮点而不是字节。现在有人建议我使用编解码器,即Opus,然后我找到了它的包装器和它的使用NAudio的演示,我对它相当满意,它使用了一些额外的循环,移除后也得到了相同的结果,但无论如何,它也给出了4000字节,48k采样率,我将其减少到8k,最大缓冲区大小减少到350。下面是该脚本的代码 private void Start() { //StartEncoding(); Uni
private void Start()
{
//StartEncoding();
UnityEditor.EditorApplication.playmodeStateChanged = PlayModeStateChangedHandler;
}
private void PlayModeStateChangedHandler()
{
if (UnityEditor.EditorApplication.isPaused)
{
StopEncoding();
}
}
public void StartGame()
{
StartEncoding();
}
private void StartEncoding()
{
_client = FindObjectOfType<Client>();
_client.AudioReceivers += UpdateAudioOutput;
_startTime = DateTime.Now;
_bytesSent = 0;
_segmentFrames = 160;
_encoder = OpusEncoder.Create(8000, 1, FragLabs.Audio.Codecs.Opus.Application.Voip);
_encoder.MaxDataBytes = 350;
_encoder.Bitrate = 4000;
_decoder = OpusDecoder.Create(8000, 1);
_decoder.MaxDataBytes = 175;
_bytesPerSegment = _encoder.FrameByteCount(_segmentFrames);
_waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback());
_waveIn.BufferMilliseconds = 50;
_waveIn.DeviceNumber = 0;
_waveIn.DataAvailable += _waveIn_DataAvailable;
_waveIn.WaveFormat = new WaveFormat(8000, 16, 1);
_playBuffer = new BufferedWaveProvider(new WaveFormat(8000, 16, 1));
_playBuffer.DiscardOnBufferOverflow = true;
_waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
_waveOut.DeviceNumber = 0;
_waveOut.Init(_playBuffer);
_waveOut.Play();
_waveIn.StartRecording();
if (_timer == null)
{
_timer = new Timer();
_timer.Interval = 1000;
_timer.Elapsed += _timer_Tick;
}
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
var timeDiff = DateTime.Now - _startTime;
var bytesPerSecond = _bytesSent / timeDiff.TotalSeconds;
}
byte[] _notEncodedBuffer = new byte[0];
private void _waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
byte[] soundBuffer = new byte[e.BytesRecorded + _notEncodedBuffer.Length];
for (int i = 0; i < _notEncodedBuffer.Length; i++)
soundBuffer[i] = _notEncodedBuffer[i];
for (int i = 0; i < e.BytesRecorded; i++)
soundBuffer[i + _notEncodedBuffer.Length] = e.Buffer[i];
int byteCap = _bytesPerSegment;
int segmentCount = (int)Math.Floor((decimal)soundBuffer.Length / byteCap);
int segmentsEnd = segmentCount * byteCap;
int notEncodedCount = soundBuffer.Length - segmentsEnd;
_notEncodedBuffer = new byte[notEncodedCount];
for (int i = 0; i < notEncodedCount; i++)
{
_notEncodedBuffer[i] = soundBuffer[segmentsEnd + i];
}
for (int i = 0; i < segmentCount; i++)
{
byte[] segment = new byte[byteCap];
for (int j = 0; j < segment.Length; j++)
segment[j] = soundBuffer[(i * byteCap) + j];
int len;
byte[] buff = _encoder.Encode(segment, segment.Length, out len);
SendToServer(buff, len);
}
}
public void UpdateAudioOutput(byte[] ba, int len)
{
int outlen = len;
byte[] buff = new byte[len];
buff = _decoder.Decode(ba, outlen, out outlen);
_playBuffer.AddSamples(buff, 0, outlen);
}
private void SendToServer(byte[] EncodedAudio, int Length)
{
print("SENDING AUDIO");
//print("audio length : " + EncodedAudio.Length);
_client.Send(EncodedAudio, Length);
//UpdateAudioOutput(EncodedAudio, Length);
}
private void StopEncoding()
{
_timer.Stop();
_waveIn.StopRecording();
_waveIn.Dispose();
_waveIn = null;
_waveOut.Stop();
_waveOut.Dispose();
_waveOut = null;
_playBuffer = null;
_encoder.Dispose();
_encoder = null;
_decoder.Dispose();
_decoder = null;
}
private void OnApplicationQuit()
{
StopEncoding();
}
private void Start()
{
//StartEncoding();
UnityEditor.EditorApplication.playmodestratechanged=playmodestratechangedhandler;
}
私有void PlayModeStateChangedHandler()
{
if(UnityEditor.EditorApplication.isPaused)
{
停止编码();
}
}
公共无效StartName()
{
StartEncoding();
}
私有void StartEncoding()
{
_client=FindObjectOfType();
_client.AudioReceivers+=UpdateAudioOutput;
_startTime=DateTime.Now;
_bytesSent=0;
_节段帧=160;
_编码器=OpusEncoder.Create(8000,1,FragLabs.Audio.Codecs.Opus.Application.Voip);
_encoder.MaxDataBytes=350;
_编码器,比特率=4000;
_解码器=OpusDecoder.Create(8000,1);
_decoder.MaxDataBytes=175;
_bytesPerSegment=\u编码器.FrameByteCount(\u段帧);
_waveIn=新的waveIn(WaveCallbackInfo.FunctionCallback());
_waveIn.Buffer毫秒=50;
_waveIn.DeviceNumber=0;
_waveIn.DataAvailable+=\u waveIn\u DataAvailable;
_waveIn.WaveFormat=新的波形(8000,16,1);
_playBuffer=新的BufferedWaveProvider(新的波形格式(8000,16,1));
_playBuffer.DiscardOnBufferOverflow=true;
_waveOut=新的waveOut(WaveCallbackInfo.FunctionCallback());
_waveOut.DeviceNumber=0;
_waveOut.Init(_playBuffer);
_waveOut.Play();
_waveIn.StartRecording();
如果(_timer==null)
{
_定时器=新定时器();
_计时器。间隔=1000;
_timer.appead+=\u timer\u Tick;
}
_timer.Start();
}
私有void\u timer\u Tick(对象发送方,事件参数e)
{
var timeDiff=DateTime.Now-\u startTime;
var bytesPerSecond=_bytesSent/timeDiff.TotalSeconds;
}
字节[]_notEncodedBuffer=新字节[0];
私有void\u waveIn\u数据可用(对象发送方,WaveInEventArgs e)
{
byte[]soundBuffer=新字节[e.BytesRecorded+\u notEncodedBuffer.Length];
对于(int i=0;i<\u notEncodedBuffer.Length;i++)
声音缓冲区[i]=\u notEncodedBuffer[i];
for(int i=0;i
现在是tcp发送和接收,它们对于客户端和服务器来说几乎相同
public void Send(byte[] data, int customParamLen = 0)
{
if (!socketReady)
{
return;
}
byte messageType = (1 << 3); // assume that 0000 1000 would be the Message type
byte[] message = data;
byte[] length = BitConverter.GetBytes(message.Length);
byte[] customParam = BitConverter.GetBytes(customParamLen); //length also 4/sizeof(int)
byte[] buffer = new byte[sizeof(int) + message.Length + 1 + customParam.Length];
buffer[0] = messageType;
//Enter length in the buffer
for (int i = 0; i < sizeof(int); i++)
{
buffer[i + 1] = length[i];
}
//Enter data in the buffer
for (int i = 0; i < message.Length; i++)
{
buffer[i + 1 + sizeof(int)] = message[i];
}
//Enter custom Param in the buffer
for (int i = 0; i < sizeof(int); i++)
{
buffer[i + 1 + sizeof(int) + message.Length] = customParam[i];
}
heavyStream.Write(buffer, 0, buffer.Length);
print("Writtin bytes");
}
if (heavyStream.DataAvailable)
{
print("Data Receiving YAY!");
//Get message Type
byte messageType = (byte)heavyStream.ReadByte();
//Get length of the Data
byte[] lengthBuffer = new byte[sizeof(int)];
int recv = heavyStream.Read(lengthBuffer, 0, lengthBuffer.Length);
if (recv == sizeof(int))
{
int messageLen = BitConverter.ToInt32(lengthBuffer, 0);
//Get the Data
byte[] messageBuffer = new byte[messageLen];
recv = heavyStream.Read(messageBuffer, 0, messageBuffer.Length);
if (recv == messageLen)
{
// messageBuffer contains the whole message ...
//Get length paramater needed for opus to decode
byte[] customParamAudioLen = new byte[sizeof(int)];
recv = heavyStream.Read(customParamAudioLen, 0, customParamAudioLen.Length);
if (recv == sizeof(int))
{
AudioReceivers(messageBuffer, BitConverter.ToInt32(customParamAudioLen, 0) - 5);
print("Done! Everything went straight as planned");
}
}
}
public void发送(字节[]数据,int customParamLen=0)
{
如果(!socketReady)
{
返回;
}
byte messageType=(1看起来您只是直接发送音频,接收端没有抖动缓冲区。这意味着如果延迟有任何变化,您将开始听到间隔
你需要做的是在客户端缓冲音频-直到你有一个好的数量,比如说400毫秒,然后开始播放。这给了你一个额外的缓冲时间来解释抖动
这是一种非常幼稚的方法,但可以让您玩一些东西-您可能想看看自适应抖动缓冲区,并可能切换到UDP而不是TCP以获得更好的性能。使用UDP,您将需要处理丢失的数据包、无序等
看看Speex,它有一个抖动缓冲区或Mumble,它使用Speex进行VOIP谢谢,我正在查看链接并在遇到问题时回复,但最重要的是,即使延迟很大,我希望它不会因为糟糕的通话体验而有任何起伏。第二,谢谢你的回答,很抱歉我不能提前回复。嗨,它确实有效ked。收到每个数据后,我将其添加到我的播放缓冲区,然后检查可用数据是否大于400ms,然后播放,否则暂停,等待下一次接收数据。它工作正常,感谢@CiaranFisherGlad!PcTrollGamer!如果您需要更多NAudio和opus的c示例,请查看独立VOIP程序f还是游戏