使用 std::chrono::high_resolution_clock 每秒写入帧 30 次

using std::chrono::high_resolution_clock to write a frame 30 times per second

本文关键字:clock chrono std high resolution 使用      更新时间:2023-10-16

我正在使用OpenCV编写视频文件。为了使cv::VideoWriter正常工作,对 write() 函数的调用必须每秒发生 30 次(对于 30fps 视频)。我找到了这段代码,它使用提升库来实现这一点。我想做同样的事情,但在我的程序中使用std::chrono。这是我的实现:

std::chrono::high_resolution_clock::time_point prev = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point current = prev;
long long difference = std::chrono::duration_cast<std::chrono::microseconds>(current-prev).count();
while(recording){
    while (difference < 1000000/30){
        current = std::chrono::high_resolution_clock::now();
        difference = std::chrono::duration_cast<std::chrono::microseconds>(current-prev).count();
    }                   
    theVideoWriter.write(frameToRecord);
    prev = prev + std::chrono::high_resolution_clock::duration(1000000000/30);
    difference = std::chrono::duration_cast<std::chrono::microseconds>(current-prev).count();                  
}
theVideoWriter.release();

我不确定这是否是正确的方法,或者是否有更有效的方法。有什么比将持续时间投射到long long difference更好的吗?

有一个基本的租户来使用 chrono ,它类似于:

如果您使用 count() 和/或您的 chrono代码,那么你就太努力了。

这不是你的错。 真的没有好的chrono教程,这是我的坏处,我最近决定我需要为此做点什么。

在您的情况下,我建议按以下方式重写代码:

首先创建一个表示帧速率周期的持续时间单位:

using frame_period = std::chrono::duration<long long, std::ratio<1, 30>>;

现在当你说frame_period{1},这意味着正好是 1/30 秒。

接下来要注意的是,只要您停留在计时系统中,chrono比较总是准确的。 count()是逃出计时系统的"活板门"。 只有在别无选择的情况下才能逃脱。 所以。。。

auto prev = std::chrono::high_resolution_clock::now();
auto current = pref;
// Just get the difference, and don't worry about the units for now
auto difference = current-prev;
while(recording)
{
    // Find out if the difference is less than one frame period
    // This comparison will do all the conversions for you to get an exact answer
    while (difference < frame_period{1})
    {
        current = std::chrono::high_resolution_clock::now();
        // stay in "native units"...
        difference = current-prev;
    }                   
    theVideoWriter.write(frameToRecord);
    // This is a little tricky...
    // prev + frame_period{1} creates a time_point with a complicated unit
    // Use time_point_cast to convert (via truncation towards zero) back to
    // the "native" duration of high_resolution_clock
    using hr_duration = std::chrono::high_resolution_clock::duration;
    prev = std::chrono::time_point_cast<hr_duration>(prev + frame_period{1});
    // stay in "native units"...
    difference = current-prev;                  
}
theVideoWriter.release();

一旦你得到时间,上面的评论就过于冗长了。 有比上面的代码更多的注释。 但以上只是按您的预期工作,无需"逃脱"计时系统。

更新

如果要初始化difference以便第一次不会执行内部循环,则可以将其初始化为刚超过 frame_period{1} 的值,而不是 0。 为此,此处找到的实用程序派上用场。 具体ceil

// round up
template <class To, class Rep, class Period>
To
ceil(const std::chrono::duration<Rep, Period>& d)
{
    To t = std::chrono::duration_cast<To>(d);
    if (t < d)
        ++t;
    return t;
}

ceilduration_cast 的替代品,当转换不精确时,将四舍五入,而不是截断为零。 现在你可以说:

auto difference = ceil<hr_duration>(frame_period{1});

而且您保证difference >= frame_period{1}. 此外,在实践中已知high_resolution_clock的持续时间为纳秒,因此您可以推断(或测试)difference实际上初始化为 33,333,334ns,即比 1/30 秒大 2/3 纳秒,等于 frame_period{1} ,等于 33,333,333+1/3ns。