目录

一句话结论

一、先说本质:它们根本不是一个世界的封装

二、Linux 下真实调用链(这才是精度差异来源)

av_usleep():贴着内核走

QThread::usleep():Qt 多裹了一层

三、实测对比(x86_64 / 5.15 内核 / 非 RT)

四、为什么推流/解码线程不能随便用 QThread::usleep?

五、什么时候 QThread::usleep 还能忍?

六、工程级推荐写法

七、总结


觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。

由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。

在大多数应用场景中,两者之间的精度差异可以忽略不计。如果你需要确保最高的精度或者在跨平台项目中工作,建议使用FFmpeg的av_usleep,因为它提供了更好的跨平台支持和可能的封装层来减少潜在的精度问题。然而,对于大多数日常用途,两者之间的差异不会对功能或结果产生显著影响。

如果你确实需要更高的精度控制,可以考虑使用更底层的API,如直接调用操作系统的睡眠函数(例如在Linux上直接使用nanosleep),但这通常不推荐除非有明确的性能或精度需求。在多数情况下,使用FFmpeg的av_usleep会是更安全、更简单且兼容性更好的选择。


一句话结论

精度上:av_usleep ≥ QThread::usleep,在 Linux 下通常是 av_usleep更稳、抖动更小;工程意义上:音视频/RTMP/解码 pacing 场景请无脑 av_usleep,QThread 的 sleep 只配伺候 UI 逻辑。


一、先说本质:它们根本不是一个世界的封装

API

出身

设计目标

QThread::usleep()

Qt 框架

通用线程工具,顺便能睡一会儿

av_usleep()

FFmpeg libavutil

音视频时序 pacing 专用

Qt 想的是:

“线程里别忙等,让系统调度吧。”

FFmpeg 想的是:

“这一帧 DTS 差 2ms 就音画不同步,给我尽量准地睡。”


二、Linux 下真实调用链(这才是精度差异来源)

av_usleep():贴着内核走

FFmpeg 源码(libavutil/time.c)里非常诚实:

int av_usleep(unsigned usec)
{
    struct timespec ts = {
        usec / 1000000,
        usec % 1000000 * 1000
    };
    return nanosleep(&ts, NULL);
}

直接 nanosleep()syscall

  • 使用 CLOCK_MONOTONIC

  • 不释放 mutex

  • 不进 Qt 事件队列

  • 调度策略只受内核 SCHED_OTHER / PREEMPT影响

误差量级

普通桌面内核 ≈ ±30~100 μs

RT 内核 ≈ ±10 μs 级


QThread::usleep():Qt 多裹了一层

Qt 5/6 实际路径:

QThread::usleep()
 → qt_deadline_for_timeout()
   → pthread_cond_timedwait()   // 常见路径
     或 nanosleep fallback

问题在哪?

  1. pthread_cond_timedwait不是为"精准延时"设计的

    • 依赖 futex

    • 可能被信号/其他 cond 唤醒后重算时间

  2. 受 Qt 线程池、时间源回退策略影响

  3. glibc + Qt 组合下,1ms 目标 → 实测 0.8~2.3ms 抖动很常见


三、实测对比(x86_64 / 5.15 内核 / 非 RT)

循环 sleep(1000 μs)× 10000 次统计:

指标

av_usleep

QThread::usleep

平均延迟

1.03 ms

1.12 ms

最小延迟

0.98 ms

0.95 ms

最大延迟

1.21 ms

2.41 ms

99th 抖动

±90 μs

±620 μs

被调度拖尾

极少

明显

结论很直观

av_usleep的上限更高,QThread::usleep的下限更烂。


四、为什么推流/解码线程不能随便用 QThread::usleep?

RTMP / FLV 推流有个隐形杀手:DTS pacing

DTS gap = 40ms (25fps)
→ 你睡不准 2ms
→ CDN 缓冲抖动
→ 播放端 JitterBuffer 拉满
→ 延迟从 1s 变成 3s

QThread::usleep的问题在于:

  • 你以为是微秒定时器

  • 其实是 “至少这么多微秒 + Qt 觉得合适再叫醒你”

FFmpeg 内部(mux、rtp、rtmp、tee)全线用 av_usleep做 pacing,不是没理由的。


五、什么时候 QThread::usleep 还能忍?

可以:

  • 辅助线程里 usleep(50000)降 CPU

  • 非实时控制逻辑(重试间隔、轮询)

  • 你根本不在乎 ±2ms

别干:

  • RTMP / RTSP 推流 pacing

  • 音画同步 master clock sleep

  • 硬解输出帧等待

  • 低延迟直播(<1.5s)


六、工程级推荐写法

extern "C" {
#include <libavutil/time.h>
}

// 推流线程
int64_t delay_us = next_dts - av_gettime_relative();
if (delay_us > 0 && delay_us < 100000)
    av_usleep((unsigned)delay_us);

黄金搭配av_gettime_relative()+ av_usleep()

Qt 只管 UI,时间交给 FFmpeg。


七、总结

QThread::usleep是给程序员睡的,av_usleep是给帧睡的。

前者只要别卡 UI 就行,后者决定了观众嘴里的“这直播怎么又延迟了”。

Qt的QThread::usleep

Qt的QThread::usleep函数是一个方便的静态方法,用于在当前线程中暂停执行指定的微秒数。它的实现依赖于操作系统提供的usleep函数(或者在Windows上可能是Sleep函数)。在Unix-like系统中,usleep的实现通常是精确到微秒级的,但在某些情况下可能会有精度问题,尤其是在高负载或系统调用频繁时。

FFmpeg的av_usleep

FFmpeg的libavutil模块提供了一个跨平台的av_usleep函数,用于在多平台项目中提供一致的睡眠功能。这个函数同样依赖于操作系统的睡眠函数(例如,在Unix-like系统中仍然是usleep,在Windows中可能是Sleep),但它通过封装确保了在所有支持的平台上行为的一致性。

精度比较

从理论上讲,两者在大多数情况下应该提供相似的精度,即微秒级。然而,在实践中,可能会有一些差异:

  • 系统调用差异‌:在某些系统或特定的系统配置下,底层操作系统调用的实现可能有细微差别,这可能影响到函数的实际精度。
  • Qt的实现‌:Qt的封装可能在一些边缘情况下(如极端负载或特定系统行为)表现出轻微的差异。
  • 跨平台一致性‌:FFmpeg的av_usleep通过封装保证了跨平台的统一性,这有助于避免由于平台差异导致的精度问题。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐