WasapiLoopbackCapture DataAvailable事件读取重复错误数据求助
解决WasapiLoopbackCapture重复读取缓冲区数据的问题
问题背景
使用WasapiLoopbackCapture捕获音频时,DataAvailable事件中读取到的缓冲区数据会出现重复,该现象大概率在GC回收时触发。出现问题后,即使设备无音频播放,也会持续接收错误数据;调试WasapiCapture源码的ReadNextPacket方法时,会看到持续输出packet size: 0 samples。
相关初始化代码:
WasapiCapture = wasapiCapture; // "WasapiCapture" 是局部变量。 WasapiCapture.WaveFormat = new WaveFormat(44100, 16, 2); WasapiCapture.DataAvailable += WaveIn_DataAvailable; WasapiCapture.StartRecording();
WaveIn_DataAvailable事件处理代码(转换逻辑无问题):
private void WaveIn_DataAvailable(object? sender, WaveInEventArgs e) { int bytesPerSamplePerChannel = WasapiCapture.WaveFormat.BitsPerSample / 8; int bytesPerSample = bytesPerSamplePerChannel * WasapiCapture.WaveFormat.Channels; int bufferSampleCount = e.Buffer.Length / bytesPerSample; if (bufferSampleCount >= AudioValuesL.Length) { bufferSampleCount = AudioValuesL.Length; } // 转换为-1到1之间的浮点值 for (int i = 0; i < bufferSampleCount; i++) { AudioValuesL[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample) / 32768d; AudioValuesR[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample + 2) / 32768d; } }
问题根源
- 局部变量被GC回收:代码中
WasapiCapture是局部变量,超出作用域后会被GC标记为可回收对象。实例被回收后,内部音频捕获线程无法正确获取新的音频数据包,只能重复返回旧缓冲区数据。 - 未处理空数据包场景:当
packet size为0时,事件处理逻辑仍会使用旧缓冲区的数据覆盖目标数组,导致数据重复。
解决方案
1. 延长WasapiCapture实例生命周期
将WasapiCapture从局部变量改为类级别的成员变量,避免被GC意外回收:
// 声明为类成员变量 private WasapiCapture wasapiCapture; // 初始化逻辑 wasapiCapture = new WasapiLoopbackCapture(); wasapiCapture.WaveFormat = new WaveFormat(44100, 16, 2); wasapiCapture.DataAvailable += WaveIn_DataAvailable; wasapiCapture.StartRecording();
2. 在事件处理中过滤空数据包
在WaveIn_DataAvailable中先判断是否有有效采样数据,避免重复写入旧数据:
private void WaveIn_DataAvailable(object? sender, WaveInEventArgs e) { var capture = sender as WasapiCapture; if (capture == null) return; int bytesPerSamplePerChannel = capture.WaveFormat.BitsPerSample / 8; int bytesPerSample = bytesPerSamplePerChannel * capture.WaveFormat.Channels; int bufferSampleCount = e.Buffer.Length / bytesPerSample; // 空数据包直接返回,不更新数据 if (bufferSampleCount == 0) return; if (bufferSampleCount >= AudioValuesL.Length) { bufferSampleCount = AudioValuesL.Length; } // 转换为-1到1之间的浮点值 for (int i = 0; i < bufferSampleCount; i++) { AudioValuesL[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample) / 32768d; AudioValuesR[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample + 2) / 32768d; } }
3. 匹配设备默认音频格式
手动指定的WaveFormat可能与系统音频设备格式不兼容,导致捕获异常,可改用设备默认格式:
wasapiCapture.WaveFormat = wasapiCapture.DeviceFormat;
4. 显式释放资源
在停止捕获或程序退出时,显式释放资源,避免残留会话导致异常:
wasapiCapture.StopRecording(); wasapiCapture.Dispose();
内容的提问来源于stack exchange,提问作者FlyingChubbyFish
相关产品推荐
相关产品推荐

