如何消除 DirectShow 过滤器链中 1 秒的延迟? (使用Delphi和DSPACK)
我有一个 Delphi 6 Pro 应用程序,它使用 DSPACK 组件库将音频从系统的首选音频输入设备发送到 Skype。我正在使用 TSampleGrabber 组件进入 Filter Graph 链,然后将音频缓冲区发送到 Skype。问题是我每秒只能收到一次音频。换句话说,TSampleGrabber 实例的 OnBuffer() 事件每秒仅触发一次,Buffer 参数中包含一整秒的数据。我需要知道如何修改我的过滤器图链,以便它以比每秒一次更快的间隔从输入设备获取数据。如果可能的话,我希望每 50 毫秒或至少每 100 毫秒执行一次。
我的 Filter Graph 链由一个 TFilter 组成,该 TFilter 映射到顶部系统的首选音频输入设备。我将该滤波器的输出引脚附加到分配给 TFilter 的“WAV Dest”的输入引脚,这样我就可以获得 PCM WAV 格式的样本。然后,我将“WAV Dest”过滤器的输出引脚连接到 TSampleGrabber 实例的输入引脚。我需要更改什么才能使 TSampleGrabber OnBuffer() 事件以更快的间隔触发?
更新:根据 Roman R 的回答,我能够实现下面展示的解决方案。一张纸条。他的链接引导我看到以下博客文章,该文章对解决方案很有帮助:
http://sid6581.wordpress.com/2006/10/09/minimizing-audio-capture-latency-in-directshow/
// Variable declaration for output pin to manipulate.
var
intfCapturePin: IPin;
...............
// Put this code after you have initialized your audio capture device
// TFilter instance *and* set it's wave audio format. My variable for
// this is FFiltAudCap. I believe you need to set the buffer size before
// connecting up the pins of the Filters. The media type was
// retrieved earlier (theMediaType) when I initialized the audio
// input device Filter so you will need to do similarly.
// Get a reference to the desired output pin for the audio capture device.
with FFiltAudCap as IBaseFilter do
CheckDSError(findPin(StringToOleStr('Capture'), intfCapturePin));
if not Assigned(intfCapturePin) then
raise Exception.Create('Unable to find the audio input device''s Capture output pin.');
// Set the capture device buffer to 50 ms worth of audio data to
// reduce latency. NOTE: This will fail if the device does not
// support the latency you desire so make sure you watch out for that.
setBufferLatency(intfCapturePin as IAMBufferNegotiation, 50, theMediaType);
..................
// The setBufferLatency() procedure.
procedure setBufferLatency(
// A buffer negotiation interface pointer.
intfBufNegotiate: IAMBufferNegotiation;
// The desired latency in milliseconds.
bufLatencyMS: WORD;
// The media type the audio stream is set to.
theMediaType: TMediaType);
var
allocProp: _AllocatorProperties;
wfex: TWaveFormatEx;
begin
if not Assigned(intfBufNegotiate) then
raise Exception.Create('The buffer negotiation interface object is unassigned.');
// Calculate the number of bytes per second using the wave
// format belonging to the given Media Type.
wfex := getWaveFormat(theMediaType);
if wfex.nAvgBytesPerSec = 0 then
raise Exception.Create('The average bytes per second value for the given Media Type is 0.');
allocProp.cbAlign := -1; // -1 means "no preference".
// Calculate the size of the buffer needed to get the desired
// latency in milliseconds given the average bytes per second
// of the Media Type's audio format.
allocProp.cbBuffer := Trunc(wfex.nAvgBytesPerSec * (bufLatencyMS / 1000));
allocProp.cbPrefix := -1;
allocProp.cBuffers := -1;
// Try to set the buffer size to the desired.
CheckDSError(intfBufNegotiate.SuggestAllocatorProperties(allocProp));
end;
I have a Delphi 6 Pro app that uses the DSPACK component library to send audio to Skype from the system's preferred audio input device. I am using a TSampleGrabber component to tap into the Filter Graph chain and then send the audio buffers to Skype. The problem is that I am only getting audio once a second. In other words, the OnBuffer() event for the TSampleGrabber instance only fires once a second with a full second's worth of data in the Buffer parameter. I need to know how to modify my Filter Graph chain so it grabs data from the input device at a faster interval than once a second. If possible, I'd like to do it as fast as every 50 ms or at least every 100ms.
My Filter Graph chain consists of a TFilter that is mapped to the system's preferred audio input device at the top. I attach the output pins of that filter to the input pins of a 'WAV Dest' assigned TFilter so I can get the samples in PCM WAV format. I then attach the output pins of the 'WAV Dest' filter to the input pins of the TSampleGrabber instance. What do I need to change to get the TSampleGrabber OnBuffer() event to fire at a faster interval?
UPDATE: Based on Roman R's answer I was able to implement a solution that I am showing below. One note. His link led me to the following blog post that was helpful in solution:
http://sid6581.wordpress.com/2006/10/09/minimizing-audio-capture-latency-in-directshow/
// Variable declaration for output pin to manipulate.
var
intfCapturePin: IPin;
...............
// Put this code after you have initialized your audio capture device
// TFilter instance *and* set it's wave audio format. My variable for
// this is FFiltAudCap. I believe you need to set the buffer size before
// connecting up the pins of the Filters. The media type was
// retrieved earlier (theMediaType) when I initialized the audio
// input device Filter so you will need to do similarly.
// Get a reference to the desired output pin for the audio capture device.
with FFiltAudCap as IBaseFilter do
CheckDSError(findPin(StringToOleStr('Capture'), intfCapturePin));
if not Assigned(intfCapturePin) then
raise Exception.Create('Unable to find the audio input device''s Capture output pin.');
// Set the capture device buffer to 50 ms worth of audio data to
// reduce latency. NOTE: This will fail if the device does not
// support the latency you desire so make sure you watch out for that.
setBufferLatency(intfCapturePin as IAMBufferNegotiation, 50, theMediaType);
..................
// The setBufferLatency() procedure.
procedure setBufferLatency(
// A buffer negotiation interface pointer.
intfBufNegotiate: IAMBufferNegotiation;
// The desired latency in milliseconds.
bufLatencyMS: WORD;
// The media type the audio stream is set to.
theMediaType: TMediaType);
var
allocProp: _AllocatorProperties;
wfex: TWaveFormatEx;
begin
if not Assigned(intfBufNegotiate) then
raise Exception.Create('The buffer negotiation interface object is unassigned.');
// Calculate the number of bytes per second using the wave
// format belonging to the given Media Type.
wfex := getWaveFormat(theMediaType);
if wfex.nAvgBytesPerSec = 0 then
raise Exception.Create('The average bytes per second value for the given Media Type is 0.');
allocProp.cbAlign := -1; // -1 means "no preference".
// Calculate the size of the buffer needed to get the desired
// latency in milliseconds given the average bytes per second
// of the Media Type's audio format.
allocProp.cbBuffer := Trunc(wfex.nAvgBytesPerSec * (bufLatencyMS / 1000));
allocProp.cbPrefix := -1;
allocProp.cBuffers := -1;
// Try to set the buffer size to the desired.
CheckDSError(intfBufNegotiate.SuggestAllocatorProperties(allocProp));
end;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想您需要微调音频捕获过滤器以捕获所需大小的缓冲区,即足够短以使整体延迟变小。
音频捕获过滤器在输出引脚上公开
IAMBufferNegotiation
接口,并且SuggestAllocatorProperties
允许您指定缓冲区配置。有关详细信息,请参阅:配置 Windows Media 音频编码器 DMO减少延迟。
I suppose you need to fine tune Audio Capture filter to capture in buffers of the size you want, i.e. short enough to make overall latency small.
Audio capture filters expose
IAMBufferNegotiation
interface on output pins andSuggestAllocatorProperties
lets you specify buffer configuration.See for more info: Configuring Windows Media Audio Encoder DMO to reduce delay.