C# opus和NAudio不同步

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

我在游戏中添加了voip,因为Unity的麦克风类在Web_GL中不受支持,而且速度已经很慢,并且会产生浮点而不是字节。现在有人建议我使用编解码器,即Opus,然后我找到了它的包装器和它的使用NAudio的演示,我对它相当满意,它使用了一些额外的循环,移除后也得到了相同的结果,但无论如何,它也给出了4000字节,48k采样率,我将其减少到8k,最大缓冲区大小减少到350。下面是该脚本的代码

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还是游戏