CSCore - Opus Codec funktioniert nicht

  • C#
  • .NET 4.5

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von thefiloe.

    CSCore - Opus Codec funktioniert nicht

    Hi,
    weil ich auf der Suche nach eine Audio Codec war und @Manawyrm den Opus Codec empfohlen hat, habe ich mir diesen nun einmal angeguckt und bin auf einen C# Wrapper gestoßen: github.com/JohnACarruthers/Opus.NET Dieser benutzt als Audio Bibliothek NAudio, welches ich dann durch CSCore ersetzt habe. Soweit, so gut, jedoch funktioniert das alles nicht so, wie gedacht. Hier ist der gekürzte Code, den ich benutze:
    Spoiler anzeigen

    C#-Quellcode

    1. void StartEncoding()
    2. {
    3. _startTime = DateTime.Now;
    4. _bytesSent = 0;
    5. _segmentFrames = 960;
    6. _encoder = OpusEncoder.Create(48000, 1, FragLabs.Audio.Codecs.Opus.Application.Voip);
    7. _encoder.Bitrate = 8192;
    8. _decoder = OpusDecoder.Create(48000, 1);
    9. _bytesPerSegment = _encoder.FrameByteCount(_segmentFrames);
    10. MMDevice captureDevice = null;
    11. using (var deviceEnumerator = new MMDeviceEnumerator())
    12. using (
    13. var deviceCollection = deviceEnumerator.EnumAudioEndpoints(DataFlow.Capture, DeviceState.Active)
    14. )
    15. {
    16. foreach (var device in deviceCollection)
    17. {
    18. if (device.FriendlyName == comboBox1.Text)
    19. {
    20. captureDevice = device;
    21. break;
    22. }
    23. }
    24. }
    25. if (captureDevice == null)
    26. return;
    27. //var waveFormatExtensible = new WaveFormatExtensible(44100, 32, 2, Guid.Parse("00000003-0000-0010-8000-00aa00389b71"));
    28. var waveFormatExtensible = new WaveFormat(48000, 16, 1);
    29. _waveIn = new WasapiCapture(true, AudioClientShareMode.Shared, 100, waveFormatExtensible)
    30. {
    31. Device = captureDevice
    32. };
    33. _waveIn.Initialize();
    34. _waveIn.DataAvailable += _waveIn_DataAvailable1;
    35. _writeableBufferingSource = new WriteableBufferingSource(waveFormatExtensible) {FillWithZeros = true};
    36. _waveOut = new WasapiOut();
    37. _waveOut.Initialize(_writeableBufferingSource);
    38. _waveOut.Play();
    39. _waveIn.Start();
    40. if (_timer == null)
    41. {
    42. _timer = new Timer();
    43. _timer.Interval = 1000;
    44. _timer.Tick += _timer_Tick;
    45. }
    46. _timer.Start();
    47. }
    48. private void _waveIn_DataAvailable1(object sender, DataAvailableEventArgs e)
    49. {
    50. //_writeableBufferingSource.Write(e.Data, e.Offset, e.ByteCount);
    51. //return;
    52. byte[] soundBuffer = new byte[e.ByteCount + _notEncodedBuffer.Length];
    53. for (int i = 0; i < _notEncodedBuffer.Length; i++)
    54. soundBuffer[i] = _notEncodedBuffer[i];
    55. for (int i = 0; i < e.ByteCount; i++)
    56. soundBuffer[i + _notEncodedBuffer.Length] = e.Data[i];
    57. int byteCap = _bytesPerSegment;
    58. int segmentCount = (int)Math.Floor((decimal)soundBuffer.Length / byteCap);
    59. int segmentsEnd = segmentCount * byteCap;
    60. int notEncodedCount = soundBuffer.Length - segmentsEnd;
    61. _notEncodedBuffer = new byte[notEncodedCount];
    62. for (int i = 0; i < notEncodedCount; i++)
    63. {
    64. _notEncodedBuffer[i] = soundBuffer[segmentsEnd + i];
    65. }
    66. for (int i = 0; i < segmentCount; i++)
    67. {
    68. byte[] segment = new byte[byteCap];
    69. for (int j = 0; j < segment.Length; j++)
    70. segment[j] = soundBuffer[i * byteCap + j];
    71. int len;
    72. byte[] buff = _encoder.Encode(segment, segment.Length, out len);
    73. _bytesSent += (ulong)len;
    74. buff = _decoder.Decode(buff, len, out len);
    75. _writeableBufferingSource.Write(buff, 0, len);
    76. }
    77. }


    Wie man sehen kann, ist dort bestimmter Code auskommentiert. Dies ist Testcode, welcher den Codec aushebelt und wodurch es wunderbar funktioniert (zum Test, damit der Fehler nicht irgendwo anders ist).
    Das Problem scheint darin zu liegen, dass ich dem WasapiCapture zwar das richtige WaveFormat gebe (48000 Hz), er jedoch dies nach dem Initialize() aufruf wieder ändert:


    Der WriteableBufferingSource scheint damit jedoch kein Problem zu haben:


    Daran liegt es wohl auch, dass wenn man nur den Encoding Prozess ausklammert (die ersten zwei Zeilen in _waveIn_DataAvailable1 unkommentiert), ohne das WaveFormat zu ändern, sehr unangenehme Töne entstehen (weil WasapiCapture das WaveFormat ablehnt, der WriteableBufferingSource damit jedoch kein Problem hat, wodurch es unterschiedliche WaveFormats werden). Also habe ich mal einen Blick in den Source von CSCore auf Github geworfen und festgestellt, dass wenn der Audio Client das WaveFormat nicht unterstützt, er sich einfach ein anderes sucht: github.com/filoe/cscore/blob/9…dIn/WasapiCapture.cs#L474

    Im Anhang ist ein lauffähiges Beispiel, aus welchem auch der Code oben entstammt. Vielleicht hat ja jemand ne Idee, was man da machen kann. Das Problem ist auch, dass Orpus 44100 Hz nicht unterstützt, wodurch wir nicht einfach dies ändern können.
    Vielleicht hat ja jemand ne Idee :)
    Dateien
    Mfg
    Vincent

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VincentTB“ ()

    Ich war gerade dabei, diesen ​WaveFormatConversionStream zu studieren und für CSCore umzuschreiben, da ist mir aufgefallen, dass in dem funktionieren NAudio Beispiel WaveIn verwendet wird. Ich dachte beim Umschreiben eigentlich, dass Wasapi einfach das neuere ist, aber als ich es dann mit WaveIn ausprobiert habe, hat es geklappt.

    Funktioniert nun alles, in dem Beispiel muss einfach nur WasapiCapture durch WaveIn ersetzt werden. Danke :)
    Mfg
    Vincent

    So, jetzt kann ich antworten:
    WaveIn kann ich nur abraten. Dir geht es um SampleRate, BitsPerSample und Channels richtig?
    Sieh mal im Anhang.
    Dateien
    • opus.zip

      (1,61 MB, 181 mal heruntergeladen, zuletzt: )


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Sehr schön, vielen Dank :)
    Cool, dass es so einfach geht, das zu konvertieren.
    Für alle, die diesen Thread lesen und merken, dass in dem Beispielprojekt von @thefiloe alles funktioniert, aber Opus deaktiviert ist:
    So könnte das DataAvailable-Event dann aussehen: (du hattest dann direkt in den ​WriteableBufferingSource geschrieben)
    Spoiler anzeigen

    C#-Quellcode

    1. private void _waveIn_DataAvailable1(object sender, DataAvailableEventArgs e)
    2. {
    3. int read;
    4. var buffer = new byte[_captureSource.WaveFormat.BytesPerSecond];
    5. while ((read = _captureSource.Read(buffer, 0, buffer.Length)) > 0)
    6. {
    7. byte[] soundBuffer = new byte[read + _notEncodedBuffer.Length];
    8. for (int i = 0; i < _notEncodedBuffer.Length; i++)
    9. soundBuffer[i] = _notEncodedBuffer[i];
    10. for (int i = 0; i < read; i++)
    11. soundBuffer[i + _notEncodedBuffer.Length] = buffer[i];
    12. int byteCap = _bytesPerSegment;
    13. int segmentCount = (int) Math.Floor((decimal) soundBuffer.Length/byteCap);
    14. int segmentsEnd = segmentCount*byteCap;
    15. int notEncodedCount = soundBuffer.Length - segmentsEnd;
    16. _notEncodedBuffer = new byte[notEncodedCount];
    17. for (int i = 0; i < notEncodedCount; i++)
    18. {
    19. _notEncodedBuffer[i] = soundBuffer[segmentsEnd + i];
    20. }
    21. for (int i = 0; i < segmentCount; i++)
    22. {
    23. byte[] segment = new byte[byteCap];
    24. for (int j = 0; j < segment.Length; j++)
    25. segment[j] = soundBuffer[i*byteCap + j];
    26. int len;
    27. byte[] buff = _encoder.Encode(segment, segment.Length, out len);
    28. _bytesSent += (ulong) len;
    29. buff = _decoder.Decode(buff, len, out len);
    30. _writeableBufferingSource.Write(buff, 0, len);
    31. }
    32. }
    33. }



    Vielen Dank
    Mfg
    Vincent

    Logo, kein Problem. Die Woche war nur sehr stressig, konnte nicht wirklich antworten. Wenn man weiß wies geht ists kein Problem :).


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.