• ffmpeg播放时刻与视频文件时间戳对齐(同步)


    问题描述

    当解码较快的时候,并不是解码后立马显示视频帧,这样看着是倍速的效果。如何还原实际的播放速率?

    解决方案

    为了解决在解码后视频播放还原原来每帧播放的时刻点。我们需要在解码较快的情况下对帧显示加一定的时间延时,这个延时策略就是计算出

    延时调整时间 =(当前帧时间戳  -  上一帧时间戳)- (当前机器准显示时间  -  上一帧显示机器时间)
    

    延时调整时间 有可能为负值则丢弃。如果为正值,可根据时长做一定调整,毕竟送帧显示也是耗时操作。

    demo示例:

    1. void dispThread(void *arg)
    2. {
    3. Input *input = (Input *)arg;
    4. static const double interval = 1000000.0 / input->fps;
    5. double q2d = av_q2d(input->tb) * 1000000.0;
    6. int64_t pre_pts = 0;
    7. int64_t frame_pre_pts = AV_NOPTS_VALUE;
    8. AVFrame *pending_frm = NULL;
    9. while (!input->is_req_exit()) {
    10. int64_t cur_pts;
    11. int remaining_time = 10000;
    12. double duration = interval;
    13. AVFrame *frm;
    14. if (pending_frm) {
    15. frm = pending_frm;
    16. } else {
    17. frm = input->PopFrame();
    18. if (!frm) {
    19. msleep(10);
    20. continue;
    21. }
    22. // printf("pop frame pts: %ld\n", frm->pts);
    23. }
    24. static auto delete_func = [](AVFrame * f) {
    25. av_frame_free(&f);
    26. };
    27. cur_pts = av_gettime_relative();
    28. if (frame_pre_pts != AV_NOPTS_VALUE)
    29. duration = (frm->pts - frame_pre_pts) * q2d;
    30. int countdown = (pre_pts == 0) ? 0 : (int)(duration - (cur_pts - pre_pts));
    31. remaining_time = std::min<int>(remaining_time, countdown);
    32. // printf("countdown: %d, remaining_time: %d us\n",
    33. // countdown, remaining_time);
    34. if (input->realtime) {
    35. countdown = 0;
    36. remaining_time = 0;
    37. }
    38. if (countdown <= 0) {
    39. frame_pre_pts = frm->pts;
    40. pre_pts = cur_pts;
    41. if (frm == pending_frm)
    42. pending_frm = NULL;
    43. push_frame(input->join, input->slice_idx,
    44. std::shared_ptr<AVFrame>(frm, delete_func));
    45. } else {
    46. pending_frm = frm;
    47. }
    48. if (remaining_time > 0)
    49. {
    50. printf("countdown: %d, remaining_time: %d us\n",
    51. countdown, remaining_time);
    52. std::this_thread::sleep_for(std::chrono::microseconds(remaining_time));
    53. }
    54. }
    55. if (pending_frm)
    56. av_frame_free(&pending_frm);
    57. }

    知识点1:av_q2d(AVRational a)函数

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

    av_q2d(AVRational);该函数负责把AVRational结构转换成double,通过这个函数可以计算出某一帧在视频中的时间位置

    timestamp(秒) = pts * av_q2d(st->time_base);

    计算视频长度的方法:

    time(秒) = st->duration * av_q2d(st->time_base);

    知识点2:av_gettime_relative();

    该函数是拿到当前的机器时间(系统时间)。

    如果是直播的情况下我们不做延时。

    大致的流程如下:

    取得解码视频帧

    记录当前机器时间

    计算当前准显示时间与上次显示时间差值d1

    计算当前帧时间戳与上次显示时间戳差值d2

    计算延时时间 d2 - d1

    延时时间大于0则进行sleep延时

    并保存当前帧在下一次循环送显

    以上步骤为解决方案。请参考。

  • 相关阅读:
    Toronto Research Chemicals盐酸乙环胺应用说明
    创建对象在Heap堆区中如何分配内存
    进军多项式(三):Chirp Z-Transform
    08_selenium实战——学习平台公开数据批量获取
    【Educoder作业】C&C++函数实训
    JVM内存模型
    NUT UI 虚拟列表高度设置
    【python】Django——连接mysql数据库
    适用于 Windows 的 10 个最佳视频转换器:快速转换高清视频
    java(面向对象)的23种设计模式(10)——模板方法模式
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126589002