首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OSX和Windows 10上的低延迟同步输出

OSX和Windows 10上的低延迟同步输出
EN

Stack Overflow用户
提问于 2017-06-01 18:00:27
回答 1查看 214关注 0票数 0

我正在尝试通过高速USB 2输出同步数据(以编程方式生成),延迟非常低。理想情况下约为1-2毫秒。在Windows上我用的是WinUsb,在OSX上我用的是IOKit。

我想到了两种方法。我在想哪一个最好。

1帧传输

WinUsb在其允许的范围内是相当严格的,并且要求每个等时传输是整数个帧(1帧=1毫秒)。因此,为了最大限度地减少延迟,在循环中使用每个帧的传输,如下所示:

代码语言:javascript
复制
for (;;)
{
    // Submit a 1-frame transfer ASAP.
    WinUsb_WriteIsochPipeAsap(..., &overlapped[i]);

    // Wait for the transfer from 2 frames ago to complete, for timing purposes. This
    // keeps the loop in sync with the USB frames.
    WinUsb_GetOverlappedResult(..., &overlapped[i-2], block=true);
}

这工作得相当好,并且提供了2毫秒的延迟。在OSX上,我可以做类似的事情,尽管它要复杂得多。这是代码的要点-完整的代码太长了,不能在这里发布:

代码语言:javascript
复制
uint64_t frame = ...->GetBusFrameNumber(...) + 1;
for (;;)
{
    // Submit at the next available frame.
    for (a few attempts)
    {
        kr = ...->LowLatencyWriteIsochPipeAsync(...
                                            frame, // Start on this frame.
                                            &transfer[i]); // Callback
        if (kr == kIOReturnIsoTooOld)
            frame++; // Try the next frame.
        else if (kr == kIOReturnSuccess)
            break;
        else
            abort();
    }

    // Above, I pass a callback with a reference to a condition_variable. When
    // the transfer completes the condition_variable is triggered and wakes this up:
    transfer[i-5].waitForResult();

    // I have to wait for 5 frames ago on OSX, otherwise it skips frames.
}

同样,这种工作方式的延迟约为3.5毫秒。但它并不是超级可靠的。

与内核竞争

OSX的低延迟等时函数允许你提交长时间的传输(例如64帧),然后定期(每毫秒最大更新一次)更新帧列表,该列表表明内核在读取写缓冲区时必须到哪里。

我认为这个想法是你以某种方式每N毫秒(或微秒)唤醒一次,读取帧列表,计算出你需要写入的位置并这样做。我还没有为此编写代码,但我不完全确定如何继续,也没有我能找到的示例。

当帧列表更新时,它似乎不提供回调,所以我想你必须使用自己的timer - CFRunLoopTimerCreate()并从该回调中读取帧列表?

另外,我想知道WinUsb是否允许类似的事情,因为它还强制您注册一个缓冲区,以便内核和用户空间可以同时访问它。但是,我找不到任何明确说明可以在内核读取缓冲区时写入缓冲区的示例。您是否打算在常规回调中使用WinUsb_GetCurrentFrameNumber来计算内核在传输中的位置?

这将需要在Windows上获得一个常规的回调,这似乎有点棘手。我见过的唯一方法是使用multimedia timers,它的最小周期为1毫秒(除非您使用未记录的(NtSetTimerResolution?)。

所以我的问题是:我是否可以改进“1帧传输”的方法,或者我应该切换到1 kHz回调,试图与内核竞争。示例代码非常感谢!

EN

回答 1

Stack Overflow用户

发布于 2017-06-03 21:04:15

(评论太长了,所以…)

我只能解决OS方面的问题。问题的这一部分:

我认为

的想法是,你以某种方式每N毫秒(或微秒)唤醒一次,读取帧列表,找出需要写入的位置并执行此操作。我还没有为此编写代码,但我不完全确定如何继续,也没有我能找到的示例。

当帧列表更新时,它似乎没有提供回调,所以我想您必须使用自己的timer - CFRunLoopTimerCreate()并从该回调中读取帧列表?

让我为你想做的事抓狂。您的数据来自哪里,延迟很关键,但数据源还没有在数据准备就绪时通知您?

这样做的想法是,您的数据是从某个源流式传输的,一旦有任何数据可用,您就可以将所有可用的数据写入用户/内核共享数据缓冲区中的适当位置。

因此,也许你可以更详细地解释一下你想要做的事情,我也许能帮上忙。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44304453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档