WaveOutWrite 直接来自网络摄像头音频捕获回调
WaveOutWrite direct from Webcam audio capture callback
我正在使用 VFW 和音频捕获回调从网络摄像头捕获音频数据,同时,在同一捕获回调的主体内,使用 waveOutWrite 将采样数据定向到默认 MAPPER。
来自网络摄像头的信号质量为 1 channel/8 bits/11025 samples/sec。
由于 waveOpen 和 FORMAT_QUERY 标志,默认音频设备支持声音格式。
waveWriteOut的return是NOERROR,但我能听到的与我的预期相去甚远。房间里很安静,应该是空虚的白噪音。
请听声音YouTube rec
开始,一个包一个包大小约16K,WAVEHDR结构ok。
然后它逐渐变慢并退出系统未恢复的错误。
这和什么相似?
下面是来自 VFW 的音频 dta 接收器代码,lpWHdr 看起来不错,甚至内部标志触发到 2 = Prepared.. 似乎 VFW 和 WaveAudio 是相互创建的:)
public static void capAudioStreamCallback(UIntPtr hWnd, ref WAVE.WAVEHDR lpWHdr) {
Say(String.Format(DateTime.Now.ToString("mm:ss:fff ") + "Received {0} of audio data", lpWHdr.dwBytesRecorded.ToString()));
Application.DoEvents();
WA.WAVEHDR_FLAGS flag = (WA.WAVEHDR_FLAGS) lpWHdr.dwFlags;
if ((WA.WAVEHDR_FLAGS)lpWHdr.dwFlags != WA.WAVEHDR_FLAGS.WHDR_PREPARED)
CheckWAError("waveOutPrepareHeader", WA.waveOutPrepareHeader(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
CheckWAError("waveOutWrite", WA.waveOutWrite(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
CheckWAError("waveOutUnprepareHeader", WA.waveOutUnprepareHeader(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
return;
}
static void CheckWAError(string Func, WA.MMSYSERR err) {
if (err == WA.MMSYSERR.MMSYSERR_BASE_NOERROR) { Say(Func + " WA Ok"); return; }
IntPtr str = Marshal.AllocHGlobal(200);
string s = "";
WA.waveOutGetErrorText(err, str, 200);
s = Marshal.PtrToStringAnsi(str);
Marshal.FreeHGlobal(str);
Say(Func + " err: " + s);
}
我认为缓冲区没有溢出,因为你可以看到 DateTime 毫秒戳,它每 1400 毫秒滴答一次,采样率 = 11025,缓冲区大小约为 16500 字节 = 看起来不错..
更新:我刚刚将非托管缓冲区复制到托管缓冲区并查看了它的值。看起来像锯齿,甚至是超载的鼻窦。
0 4 0 3 0 32 109 213 255 251 255 243 241 97 0 7 0 2 1 1 0 5 0 然后在大约相同的数字和同一时期再次上下波动。
不完全相同,大致相同 (+/-)。
此外,我可以使用内部 Windows 录音机记录来自该摄像头的信号,我可以看到信号电平在我的声音上下跳跃,所以网络摄像头的麦克风也可以。
我想这可能是 VFW 输入音频信号馈线有问题。
即使它接受了 WAVEFORMATEX 并发回 WAVEHDR,它们都很好......但是缓冲区数据填充了一些其他来源,而不是网络摄像头,尽管 VFW 说它必须来自网络摄像头,因为视频是从同一来源捕获的,它正在工作,我只是添加了一个额外的消息:
SendMessage(camHwnd, WM_CAP_SET_CALLBACK_WAVESTREAM, 0, audioCallback);
我非常确定如果我使用 waveIn 而不是 VFW,它会工作正常..
我稍后会检查它..
但为什么 VFW 的工作方式不如预期?
问题很简单——USB硬件故障。我需要拔掉 USB Camera 并重新插入。
但无论如何,我想分享我的知识。
1)
我们应该使用异步机制来获取和发送音频数据包到播放端。
在第一个缓冲区播放之前,我们必须避免发送新的缓冲区进行播放。该方法称为 - "double" 甚至 "tripple" 缓冲。
使用 VFW,您可以使用 WM_CAP_GET_SEQUENCE_SETUP 消息和 CAPTUREPARAMS 结构轻松组织它。
wNumAudioRequested 参数用于设置将循环使用多少个不同的缓冲区,以将音频数据发送到您的 audioCallback。
默认设置为10,绰绰有余。
2)
检查您的音频信号是否是有效信号的最佳方法是:在您的 WAVESTREAM 回调中,将接收到的带有音频数据的缓冲区中的字节编组到托管的静态字节数组中。
然后,在回调中,使用 Console.Write(array[i] + " ") 输出 50-100 个样本值,并查看这些值是否随着您的声音上下变化。
考虑到零电平位于 WAVEFORMATEX->wBitsPerSample 值的中间,在我的例子中 (8 bits/sample) 值为 125 126 127 128 129。它被接受为静音,无信号,或零-噪音。
一旦你确定你有正确的音频数据,现在你可以进一步实现你的目标。
3)
请记住,当您使用麦克风模式进行录音时,最好关闭本地输出波形设备。您的目标是收集音频数据以进行记录或通过网络发送。不要尝试获取数据并在本地 waveOuit 它们。
有时你的扬声器的延迟值比采样麦克风数据的速度高一点,你会弄乱缓冲区,就像我发生的那样。
然后我就遵循了原则-“录音是当你收集,保存或发送音频数据时,应该在录音后或同时播放,但在端点PC上。
4)
代码待续
我正在使用 VFW 和音频捕获回调从网络摄像头捕获音频数据,同时,在同一捕获回调的主体内,使用 waveOutWrite 将采样数据定向到默认 MAPPER。
来自网络摄像头的信号质量为 1 channel/8 bits/11025 samples/sec。 由于 waveOpen 和 FORMAT_QUERY 标志,默认音频设备支持声音格式。
waveWriteOut的return是NOERROR,但我能听到的与我的预期相去甚远。房间里很安静,应该是空虚的白噪音。
请听声音YouTube rec
开始,一个包一个包大小约16K,WAVEHDR结构ok。 然后它逐渐变慢并退出系统未恢复的错误。
这和什么相似?
下面是来自 VFW 的音频 dta 接收器代码,lpWHdr 看起来不错,甚至内部标志触发到 2 = Prepared.. 似乎 VFW 和 WaveAudio 是相互创建的:)
public static void capAudioStreamCallback(UIntPtr hWnd, ref WAVE.WAVEHDR lpWHdr) {
Say(String.Format(DateTime.Now.ToString("mm:ss:fff ") + "Received {0} of audio data", lpWHdr.dwBytesRecorded.ToString()));
Application.DoEvents();
WA.WAVEHDR_FLAGS flag = (WA.WAVEHDR_FLAGS) lpWHdr.dwFlags;
if ((WA.WAVEHDR_FLAGS)lpWHdr.dwFlags != WA.WAVEHDR_FLAGS.WHDR_PREPARED)
CheckWAError("waveOutPrepareHeader", WA.waveOutPrepareHeader(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
CheckWAError("waveOutWrite", WA.waveOutWrite(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
CheckWAError("waveOutUnprepareHeader", WA.waveOutUnprepareHeader(phwo, lpWHdr, (uint)Marshal.SizeOf(lpWHdr)));
return;
}
static void CheckWAError(string Func, WA.MMSYSERR err) {
if (err == WA.MMSYSERR.MMSYSERR_BASE_NOERROR) { Say(Func + " WA Ok"); return; }
IntPtr str = Marshal.AllocHGlobal(200);
string s = "";
WA.waveOutGetErrorText(err, str, 200);
s = Marshal.PtrToStringAnsi(str);
Marshal.FreeHGlobal(str);
Say(Func + " err: " + s);
}
我认为缓冲区没有溢出,因为你可以看到 DateTime 毫秒戳,它每 1400 毫秒滴答一次,采样率 = 11025,缓冲区大小约为 16500 字节 = 看起来不错..
更新:我刚刚将非托管缓冲区复制到托管缓冲区并查看了它的值。看起来像锯齿,甚至是超载的鼻窦。 0 4 0 3 0 32 109 213 255 251 255 243 241 97 0 7 0 2 1 1 0 5 0 然后在大约相同的数字和同一时期再次上下波动。 不完全相同,大致相同 (+/-)。 此外,我可以使用内部 Windows 录音机记录来自该摄像头的信号,我可以看到信号电平在我的声音上下跳跃,所以网络摄像头的麦克风也可以。 我想这可能是 VFW 输入音频信号馈线有问题。 即使它接受了 WAVEFORMATEX 并发回 WAVEHDR,它们都很好......但是缓冲区数据填充了一些其他来源,而不是网络摄像头,尽管 VFW 说它必须来自网络摄像头,因为视频是从同一来源捕获的,它正在工作,我只是添加了一个额外的消息: SendMessage(camHwnd, WM_CAP_SET_CALLBACK_WAVESTREAM, 0, audioCallback); 我非常确定如果我使用 waveIn 而不是 VFW,它会工作正常.. 我稍后会检查它.. 但为什么 VFW 的工作方式不如预期?
问题很简单——USB硬件故障。我需要拔掉 USB Camera 并重新插入。
但无论如何,我想分享我的知识。
1) 我们应该使用异步机制来获取和发送音频数据包到播放端。 在第一个缓冲区播放之前,我们必须避免发送新的缓冲区进行播放。该方法称为 - "double" 甚至 "tripple" 缓冲。 使用 VFW,您可以使用 WM_CAP_GET_SEQUENCE_SETUP 消息和 CAPTUREPARAMS 结构轻松组织它。 wNumAudioRequested 参数用于设置将循环使用多少个不同的缓冲区,以将音频数据发送到您的 audioCallback。 默认设置为10,绰绰有余。
2) 检查您的音频信号是否是有效信号的最佳方法是:在您的 WAVESTREAM 回调中,将接收到的带有音频数据的缓冲区中的字节编组到托管的静态字节数组中。 然后,在回调中,使用 Console.Write(array[i] + " ") 输出 50-100 个样本值,并查看这些值是否随着您的声音上下变化。 考虑到零电平位于 WAVEFORMATEX->wBitsPerSample 值的中间,在我的例子中 (8 bits/sample) 值为 125 126 127 128 129。它被接受为静音,无信号,或零-噪音。 一旦你确定你有正确的音频数据,现在你可以进一步实现你的目标。
3) 请记住,当您使用麦克风模式进行录音时,最好关闭本地输出波形设备。您的目标是收集音频数据以进行记录或通过网络发送。不要尝试获取数据并在本地 waveOuit 它们。 有时你的扬声器的延迟值比采样麦克风数据的速度高一点,你会弄乱缓冲区,就像我发生的那样。 然后我就遵循了原则-“录音是当你收集,保存或发送音频数据时,应该在录音后或同时播放,但在端点PC上。
4) 代码待续