问题
I'm using OpenCV to write a video file. For cv::VideoWriter
to work correctly the call to the write() function has to happen exactly 30 times per second (for a 30fps video).
I found this code which uses the boost library to achieve this. I want to to the same but using std::chrono
in my program. This is my implementation:
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();
I'm not sure if thats the correct way to do this or if there is a more efficient way. Is there anything better than casting the duration to long long difference
?
回答1:
There is a basic tenant to working with chrono
, which goes something like:
If you use
count()
, and/or you have conversion factors in yourchrono
code, then you're trying too hard.
This is not your fault. There really is no good chrono
tutorial and that is my bad, and I've recently decided I need to do something about that.
In your case, I recommend rewriting your code along the lines of the following:
First create a duration unit which represents the period of your frame rate:
using frame_period = std::chrono::duration<long long, std::ratio<1, 30>>;
Now when you say frame_period{1}
, that means exactly 1/30 of a second.
The next thing to note is that chrono
comparisons are always exact, as long as you stay in the chrono system. count()
is a "trap door" for escaping out of the chrono system. Only escape out when you have no other choice. So...
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();
The comments above are overly verbose once you get chrono. There's more comment than code above. But the above just works as you intended, with no need for "escaping out" of the chrono system.
Update
If you would want to initialize difference
such that the inner loop won't be executed the first time, you could initialize it to something just over frame_period{1}
instead of to 0. To do this, the utilities found here come in handy. Specifically 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;
}
ceil
is a replacement for duration_cast
that will round up when the conversion is inexact, as opposed to truncate towards zero. Now you can say:
auto difference = ceil<hr_duration>(frame_period{1});
And you are guaranteed that difference >= frame_period{1}
. Furthermore, it is known in practice that the duration of high_resolution_clock is nanoseconds, thus you can deduce (or test) that difference
is actually initialized to 33,333,334ns, which is 2/3 of a nanosecond greater than 1/30 of a second, which equals frame_period{1}
, which equals 33,333,333+1/3ns.
来源:https://stackoverflow.com/questions/27105564/using-stdchronohigh-resolution-clock-to-write-a-frame-30-times-per-second