2013-08-13 2 views
2

Хорошо, поэтому я делаю программное обеспечение для голосового чата. Я использую для этого NAudio, отличную библиотеку.Голосовой чат, увеличенная задержка (буфер) - Можно ли это решить?

Но у меня проблема. Буфер может повышаться, когда что-то происходит. Я предполагаю, что это из примера, когда ОС загружает что-то, и приложение Voice Chat помещается на «hold» в течение секунды. За это время он добавляет данные в буфер, заставляя текущие данные задерживаться.

И поскольку приемник играет в одинаковом темпе все время, он всегда будет задерживаться.

Теперь у меня есть «решение» для этого, чтобы очистить буфер, когда он достигает определенной длины. Хотя это совсем не идеально, и это скорее хитрость, чем решение.

Теперь введите код. Сначала я инициализирую то, что я использую.

 private NAudio.Wave.WaveInEvent SendStream = new WaveInEvent(); 
     private NAudio.Wave.AsioOut Aut; 
     private NAudio.Wave.WaveFormat waveformat = new WaveFormat(48000, 16, 2); 
     private WasapiLoopbackCapture Waloop = new WasapiLoopbackCapture(); 
     private NAudio.Wave.BufferedWaveProvider waveProvider; 

    waveProvider = new NAudio.Wave.BufferedWaveProvider(waveformat); 
       waveProvider.DiscardOnBufferOverflow = true; 
       SendStream.WaveFormat = waveformat; 

waveformat используется только для того, чтобы я не переписывал его все время. DiscardOnBufferOverflow используется, поэтому, если я устанавливаю определенную длину в буфере, например 20 мс. Он отбросит все выше, иначе он вернет исключение. Я думаю, однако, что он ничего не делает, если я не задал длину, он, вероятно, бесконечен при дефолте.

И не более того, SendStream - это WaveInEvent, то есть он будет работать на BackgroundThread, когда я использую DataAvailable. Waloop почти то же самое, за исключением петли. waveprovider используется в принимающей части для воспроизведения звука. Waveformat - это хорошо, waveformat, it's importat, чтобы установить его и иметь все то же самое, по крайней мере, в моем приложении.

Ниже представлена ​​принимающая сторона. Как вы можете, он помещает данные в массив байтов, а затем воспроизводит их. ничего странного.

byte[] byteData = udpClient.Receive(ref remoteEP); 
    waveProvider.AddSamples(byteData, 0, byteData.Length); 

Это передающая/записывающая часть.

private void Sendv2() 
    { 
     try 
     { 
      if (connect == true) 
      { 
       if (AudioDevice == "Wasapi Loopback") 
       { 
        SendStream.StopRecording(); 
        Waloop.StartRecording(); 
       } 
       else 
       { 
        Waloop.StopRecording(); 
        SendStream.StartRecording(); 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      MessageBox.Show(e.Message); 
     } 
    } 

void Sending(object sender, NAudio.Wave.WaveInEventArgs e) 
     { 
      if (connect == true && MuteMic.Checked == false) 
      { 
       udpClient.Send(e.Buffer, e.BytesRecorded, otherPartyIP.Address.ToString(), 1500); 
      } 

     } 
void SendWaloop(object sender, NAudio.Wave.WaveInEventArgs e) 
    { 

      byte[] newArray16Bit = new byte[e.BytesRecorded/2]; 
      short two; 
      float value; 
      for (int i = 0, j = 0; i < e.BytesRecorded; i += 4, j += 2) 
      { 
       value = (BitConverter.ToSingle(e.Buffer, i)); 
       two = (short)(value * short.MaxValue); 

       newArray16Bit[j] = (byte)(two & 0xFF); 
       newArray16Bit[j + 1] = (byte)((two >> 8) & 0xFF); 
      } 
      if (connect == true && MuteMic.Checked == false) 
      { 
       udpClient.Send(newArray16Bit, newArray16Bit.Length, otherPartyIP.Address.ToString(), 1500); 
      } 

     } 

Waloop - это Loopback, поэтому он проходит через другой «канал», но здесь это не очень важно.

Очень просто, когда доступны данные (когда они записываются), и если соединение истинно и т. Д., Оно просто отправит буфер.

Так же, как часть приемника, но наоборот.

Теперь, как я в настоящее время решить эту проблему, как это:

if (waveProvider.BufferedDuration.Milliseconds > 40) 
      { 

       waveProvider.ClearBuffer(); 
       TimesBufferClear++; 
      } 

Так я очистка буфера, если подмигнули выше 40мс (это в таймер на 600мс интервала). (TimesBufferClear ++; именно так я могу отслеживать время, когда оно было очищено)

Теперь, к сожалению, я понятия не имею, как предотвратить увеличение буфера и настройку его на принудительное состояние (20 мс и т. Д.). просто приведет к тому, что воспроизведение будет хуже и хуже, чем выше буфер, так как он не останавливается, он просто игнорирует часть выше, чем я думаю.

Вот создание g устройств ввода.Он немного отличается от ASIO и Wasapi в моей реализации, но он в значительной степени работает одинаково, только реальная разница заключается в том, что я говорю о том, что ASIO включен или выключен, как вы можете видеть в коде, в конце я добавляю Событие DataAvailable для обоих SendStream (любой вход, микрофон и т. Д.) И Waloop (звук Loopback, который воспроизводится).

private void CheckAsio() 
    { 

     if (NAudio.Wave.AsioOut.isSupported()) 
     { 

      Aut = new NAudio.Wave.AsioOut(); 
      ASIO.Text += "\nSupported: " + Aut.DriverName; 
      ASIO.ForeColor = System.Drawing.Color.Green; 
      Aut.Init(waveProvider); 
      Aut.Play(); 
      SendStream.NumberOfBuffers = 2; 
      SendStream.BufferMilliseconds = 10; 
     } 
     else 
     { 
      AsioSettings.Enabled = false; 
      ASIO.Text += "\n Not Supported: Wasapi used"; 
      ASIO.ForeColor = System.Drawing.Color.DarkGray; 
      Wasout = new WasapiOut(AudioClientShareMode.Shared, 0); 
      Wasout.Init(waveProvider); 
      Wasout.Play(); 
      SendStream.NumberOfBuffers = 2; 
      SendStream.BufferMilliseconds = 9; 
     } 
     SendStream.DataAvailable += Sending; 
     Waloop.DataAvailable += SendWaloop; 

    } 

Я не уверен, что это даже можно решить. Но поскольку я не вижу других программ голосового чата, у меня есть предположение, что должно быть что-то, что можно сделать.

ответ

1

Способ, которым, как представляется, обрабатывается в большинстве приложений, является отправка блоков данных определенной скоростью (в выборках/сек) и удаление блоков данных, превышающих эту скорость. Если отправитель ограничен ресурсами и не может поддерживать скорость, поток будет иметь звуковые промежутки. Это случалось в звуковых вызовах по коммутируемому соединению, когда скорость передачи была заблокирована выше, чем может быть связано с сетевым соединением, или когда код кодекса занимал слишком много времени.

Но из-за шума вещей буферизация и пропуски являются симптомами, а не причинами. Корень проблемы заключается в том, что ваш процесс откладывается на другие операции. Вы можете решить эту проблему, выполнив более высокий приоритет процесса и/или потока. Чем выше приоритет, тем меньше прерываний, которые вы будете иметь, что уменьшит вероятность очереди данных для обработки.

В .NET вы можете довольно просто повысить приоритет процесса и/или потока. Для приоритета процесса:

using System.Diagnostics; 

... 

Process.GetCurrentProcess().PriorityClass = PriorityClass.Highest; 

Или для потока:

using System.Threading; 

... 

Thread.CurrentThread.Priority = ThreadPriority.Highest; 

Это не является полным решением, так как операционная система будет по-прежнему ворует временные срезы из приложения при различных обстоятельствах, но в несколько -CPU/core с большим количеством памяти вы должны получить неплохой снимок в стабильной среде записи.

Разумеется, нет надежных методов, и всегда есть тот медленный компьютер, который вас испортит, поэтому вы должны позволить системе отбрасывать лишние образцы, когда это необходимо. Следите за тем, сколько данных вы отправляете и когда он начинает резервное копирование, отбрасывайте что-либо по максимальным образцам/сек. Таким образом, ваш сервер (или клиент) не будет буферизировать увеличивающиеся объемы данных и отставать дальше и дальше в реальном времени.

Один из вариантов - отпечатать каждый отправляемый пакет, чтобы клиент мог выбрать, когда начинать отбрасывать данные, чтобы догнать их. Лучше потерять несколько миллисекунд выходных данных здесь и там, чем дрейфовать все дальше и дальше от синхронизации.

+1

К сожалению, у меня уже есть это, и, как правило, я выполняю свои потоки с наивысшим приоритетом. И это ничего не меняет: похоже, это что-то другое, потому что оно складывается независимо от того, что. Довольно сложно и сложно отслеживать. – Zerowalker

+0

Нечетный. Даже система низкого уровня должна справиться с такими проблемами без проблем. Это проблема сети? Профилированы ли вы, чтобы узнать, сколько времени ваш код выполняет для каждой фазы? Вы пытались отделить код записи от другого сетевого кода? – Corey

+0

Да, но я думаю, что это ошибка в фактическом «Waveprovider», как если бы я записывал данные на Клиенте и на сервере (как файл), оба будут одинаковыми, никаких сбоев, которые могут случиться, когда я слушаю в чат Live. Значение Waveprovider должно быть причиной этого как-то, так как оно может быть в 10 мс все время, тогда оно «взломается» и может стать 80 мс, не уверен, что происходит, но сохраненное, что так же жидкое, как всегда, и сохраненное данные и данные в Waveprovider должны быть одинаковыми; S – Zerowalker

Смежные вопросы