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

I am using OpenCV to record a video file. To work correctly, cv::VideoWriter

the write () function call must be executed exactly 30 times per second (for video, 30 frames per second). I found this code which uses the boost library to achieve this goal. I want to do 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 this is the correct way to do it, or if there is a more efficient way. Is there anything better than casting duration long long difference

?

+3


source to share


1 answer


There is a main tenant to work with chrono

, which looks something like this:

If you are using count()

and / or you have conversion factors in yours chrono

, then you are trying too hard.

This is not your fault. There isn't really a good tutorial chrono

, and that's bad, and I recently decided that I needed to do something.

In your case, I recommend rewriting the code in the following lines:

First, create a duration block that represents your frame rate period:

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

      

Now that you speak frame_period{1}

, it means exactly 1/30 of a second.

The next thing to note is that comparisons are chrono

always accurate as long as you stay chronological. count()

is a "trap door" for exiting the chrono system. Just run away when you have no other choice. So that...



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 too complicated once you get the chrono. There are more comments than the above. But the above just works the way you intended, without having to "elude" the chronological system.

Update

If you want to initialize difference

in such a way that the inner loop doesn't execute the first time, you could initialize it with something slightly more frame_period{1}

than 0. To do this, the utilities found here come in handy. In particular 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

, which will round when the conversion is imprecise, as opposed to truncating towards zero. Now you can say:

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

      

And you are guaranteed that difference >= frame_period{1}

. Also, in practice it is known that the duration of high_resolution_clock is nanoseconds, so you can deduce (or check) what difference

actually initializes to 33,333,334 ns, which is 2/3 nanoseconds greater than 1/30 of a second, which is frame_period{1}

equal to 33,333 333 + 1 / 3ns.

+10


source







All Articles