【问题标题】:using std::chrono::high_resolution_clock to write a frame 30 times per second使用 std::chrono::high_resolution_clock 每秒写入一帧 30 次
【发布时间】:2014-11-24 13:05:41
【问题描述】:

我正在使用 OpenCV 编写视频文件。要使cv::VideoWriter 正常工作,对 write() 函数的调用必须每秒恰好发生 30 次(对于 30fps 视频)。 我发现this 代码使用 boost 库来实现这一点。我也想这样做,但在我的程序中使用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 更好的方法吗?

【问题讨论】:

  • 当你确切地说,你希望你的时间有多准确?

标签: c++ c++11 chrono


【解决方案1】:

有一个基本租户可以与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();

一旦你得到chrono,上面的cmets就过于冗长了。评论比上面的代码多。但以上只是按您的意愿工作,无需“逃离”计时系统。

更新

如果您想初始化 difference 以便第一次不会执行内部循环,您可以将其初始化为 over frame_period{1} 而不是 0。做到这一点,the utilities found here 就派上用场了。具体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 &gt;= frame_period{1}。此外,在实践中已知 high_resolution_clock 的持续时间为纳秒,因此您可以推断(或测试)difference 实际上初始化为 33,333,334ns,即 2/3 纳秒大于 1/30 秒,等于frame_period{1},等于33,333,333+1/3ns。

【讨论】:

  • 谢谢!我知道有比使用 count() 更好的方法。这是有道理的。我唯一的麻烦是循环第一次运行它直接进入第二个while循环,因为current-prev为0。我怎样才能避免这种情况?在我将差异设置为高于 1/30 秒之前。我不确定如何使用auto difference
  • @isADon:根据您的评论更新了我的答案,因为答案不适合这里。
  • 添加到“无原始交易”列表(无原始新/删除/循环/指针/线程,现在 chrono::count()
  • @user2047610: 1) 我坚持使用问题中的循环结构,因为我想让 OP 更熟悉答案,并且因为我相信它可以正常工作,模启动问题(地址为ceil)。 2) 目的是在t0t0+frame{1}t0+frame{2}t0+frame{3} 等处执行循环。这不是:“在每次迭代结束时等待frame{1}”。如果我从头开始编写循环(也许我应该这样做),我会根据时间点而不是持续时间来工作,我认为这样会更清楚。
  • @HowardHinnant 啊是的,我想这取决于您是否希望您的操作发生在某个时间间隔内或 某个时间间隔内(似乎 OP 想要前者,而我想要后者),无论如何感谢您的快速响应。
猜你喜欢
  • 1970-01-01
  • 2017-09-02
  • 2014-03-23
  • 2015-05-14
  • 2017-08-31
  • 2016-09-22
  • 2017-11-25
  • 1970-01-01
  • 2015-06-04
相关资源
最近更新 更多