• 视频播放器——外部时钟源同步


    获取视频时钟

    • 获取视频时钟,首先要获取视频帧的显示时间戳pts;在AVFrame结构体中,有一成员变量为best_effort_timestamp,该变量是 单位为数据流时间基的帧时间戳,官方解释如下:

      1. frame timestamp estimated using various heuristics, in stream time base
      2. encoding: unused
      3. decoding: set by libavcodec, read by use

    获取到该时间戳后,再将其单位由数据流时间基转换为正常时间;这里的时间基是使用AVRational结构提描述的,即分子—分母式的描述方式;这里使用 av_q2d() 函数对其进行转换,将AVRational描述的时间基转换为正常时间基。

    具体流程如下:

    1. /*pFrameRaw为解码后的YUV数据(AVFrame)*/
    2. pFrameRaw->pts = pFrameRaw->best_effort_timestamp;
    3. frame_pts_lf = pFrameRaw->pts * av_q2d(pFormatCtx->streams[video_index]->time_base);
    4. frame_pts_ld = frame_pts_lf * 1000; //转换为以微妙为单位

    获取外部时钟

    外部时钟主要依赖于 av_gettime() 函数,官方解释如下:

    Get the current time in microseconds.

    通过获取从视频开始播放时的时间作为基准,就能获取到每一帧视频帧播放时,外部时钟实际度过的时间。

    修正帧持续时间

    知道了视频时钟和外部时钟,如果视频帧时间戳能严格与外部时钟同步,那么视频播放就能按照预定帧率、预定时长去进行。

    在每一次视频帧显示前,都计算该视频帧时间戳与外部时钟的差值,通过该差值去动态校准该帧的持续时间,从而使得下一视频帧时间戳与外部时钟的差值越来越小。

    根据该系统的实现要求,引入了增量式PID算法来消除视频帧时间戳与外部时钟的差值

    获取视频帧时间戳与外部时钟的差值clock_diff,令时钟差值的设定值clock_diff_set为0;

    将该差值送入增量式PID控制器中处理得到帧持续时间的增量delay_incre;

    与延时基准值delay_basic相加得到实际延时值delay_actual作为当前帧持续时间;

    延时过后,重复该操作,当PID控制器参数合理,会使得clock_diff趋向于设定值0;

    简略的代码实现如下:

    1. /*** 解码渲染显示等操作 ***/
    2. if (time_flag) {
    3. time_offset = av_gettime();
    4. time_flag = 0;
    5. }
    6. now_time = av_gettime() - time_offset;
    7. time_diff[0] = time_diff[1];
    8. time_diff[1] = time_diff[2];
    9. time_diff[2] = now_time - vc->frame_pts_ld;
    10. time_incre = kp*(time_diff[2]-time_diff[1]) + ki*time_diff[2] +
    11. kd*(time_diff[2] - 2*time_diff[1] + time_diff[0]);
    12. now_delay -= time_incre; //此处的 -=操作 等价于处理clock_diff_set=0
    13. printf("\r%ld, %lld\t", now_delay, time_diff[2]);
    14. av_usleep(now_delay);
    15. /*** 回到第一步重复操作, 不断收敛time_diff为0 ***/

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 相关阅读:
    (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
    v.$message不弹框的问题
    【js闭包】
    南昌云宸网络发展有限公司-小分类客户可自选
    Linux命令(105)之readlink
    探索C++在软件开发中的应用
    Spark源码(启动ApplicationMaster和Driver线程)-第二期
    Allwinner T3 汽车级处理器为工业级 SoM 提供动力
    ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)
    Spring中@Autowired注解装配流程
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126541960