You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }
}

问题根源

  1. 局部变量被GC回收:代码中WasapiCapture是局部变量,超出作用域后会被GC标记为可回收对象。实例被回收后,内部音频捕获线程无法正确获取新的音频数据包,只能重复返回旧缓冲区数据。
  2. 未处理空数据包场景:当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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 04:56:31