解决 Sleep 精度不够问题
简介
在处理媒体视频时需要阻塞等待控制间隔,例如监控视频为 25FPS,就需要设置每帧间隔为 40 毫秒,在阻塞等待函数不够精准的情况下,就会出现帧数达不到的情况,可以在代码中提高帧数也就是降低间隔时间给函数留出精度不够的误差,但这似乎不是一个好办法,参考文章 Windows几种sleep精度的测试,结果基于微秒 做的一些测试,根据项目需要选择更适合的方法。
核心代码
测试代码
使用以下代码作为测试,运行时间越趋近于等待函数设置的值,精度越高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <chrono> #include <thread> #include <Windows.h> #include <mmsystem.h> #include <timeapi.h>
using namespace std; using namespace std::this_thread; using namespace chrono;
int main() { int time = 40; for (;;) { steady_clock::time_point start = steady_clock::now();
steady_clock::time_point end = steady_clock::now(); cout << "运行时间:" << duration_cast<nanoseconds>(end - start).count() << endl; } }
|
sleep_for
使用 c++ 11 自带函数设置 sleep 间隔为 40 毫秒。
1 2 3 4 5 6 7 8 9 10 11 12 13
| int main() { int time = 40; for (;;) { steady_clock::time_point start = steady_clock::now();
sleep_for(milliseconds(time));
steady_clock::time_point end = steady_clock::now(); cout << "运行时间:" << duration_cast<nanoseconds>(end - start).count() << endl; } }
|
精度相差 2 - 8 毫秒,对于有精度需求的程序无法使用。
1 2 3 4 5 6 7 8 9 10
| 运行时间:44549900 运行时间:47305000 运行时间:46158900 运行时间:46949000 运行时间:45595100 运行时间:45570100 运行时间:45340200 运行时间:45241500 运行时间:45490600 运行时间:45625900
|
sleep_for + timeBeginPeriod
依旧使用 sleep_for,额外使用 timeBeginPeriod 与 timeEndPeriod 设置系统时钟最小周期为 1 毫秒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
static void wait_sleep_for(int64_t time) { timeBeginPeriod(1); sleep_for(milliseconds(time)); timeEndPeriod(1); }
int main() { int time = 40; for (;;) { steady_clock::time_point start = steady_clock::now();
wait_sleep_for(time);
steady_clock::time_point end = steady_clock::now(); cout << "运行时间:" << duration_cast<nanoseconds>(end - start).count() << endl; } }
|
精度相差 0.1 - 0.9 毫秒,如果读取文件不被察觉,如果是推流的话,会明显看到帧数达不到,25FPS 的设置只能达到 24.6FPS 左右。并且 timeBeginPeriod 为系统全局设置,就算及时关闭也不排除会对其他程序有影响。
1 2 3 4 5 6 7 8 9 10
| 运行时间:40689800 运行时间:40542900 运行时间:40048500 运行时间:40933100 运行时间:40406200 运行时间:40570000 运行时间:40478500 运行时间:40930100 运行时间:40372600 运行时间:40375400
|
基于高精度计时器循环判断实现阻塞等待。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
static void wait_sleep_perform(int64_t time) { LARGE_INTEGER perfCnt, start, now;
QueryPerformanceFrequency(&perfCnt); QueryPerformanceCounter(&start);
do { QueryPerformanceCounter((LARGE_INTEGER*)&now); } while ((now.QuadPart - start.QuadPart) / float(perfCnt.QuadPart) * 1000 * 1000 < time * 1000); }
int main() { int time = 40; for (;;) { steady_clock::time_point start = steady_clock::now();
wait_sleep_perform(time);
steady_clock::time_point end = steady_clock::now(); cout << "运行时间:" << duration_cast<nanoseconds>(end - start).count() << endl; } }
|
精度相差 0.001 - 0.003 毫秒,几乎很完美的实现阻塞等待,但是存在一个问题,就是参考文章中提到的占用 CPU 问题,可能会使用一个 CPU 核心来处理,我这里测试确实有看到 CPU 的占用提升,但是还在可接受的范围内,因为视频大部分都是用 GPU 处理。
1 2 3 4 5 6 7 8 9 10
| 运行时间:40001900 运行时间:40002400 运行时间:40003300 运行时间:40003000 运行时间:40002500 运行时间:40002100 运行时间:40001800 运行时间:40002200 运行时间:40002000 运行时间:40003800
|