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

博客围绕FFmpeg解码后视频播放速率问题展开。当解码快时,视频播放会有倍速效果,为还原实际播放速率,需在解码快时对帧显示加时间延时。介绍了av_q2d和av_gettime_relative函数的用法,还给出大致流程,如计算时间差值、判断延时等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

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

解决方案

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

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

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

demo示例:

void dispThread(void *arg)
{
    Input *input = (Input *)arg;
    static const double interval = 1000000.0 / input->fps;
    double q2d = av_q2d(input->tb) * 1000000.0;
    int64_t pre_pts = 0;
    int64_t frame_pre_pts = AV_NOPTS_VALUE;
    AVFrame *pending_frm = NULL;

    while (!input->is_req_exit()) {
        int64_t cur_pts;
        int remaining_time = 10000;
        double duration = interval;
        AVFrame *frm;

        if (pending_frm) {
            frm = pending_frm;
        } else {
            frm = input->PopFrame();
            if (!frm) {
                msleep(10);
                continue;
            }
            // printf("pop frame pts: %ld\n", frm->pts);
        }
        static auto delete_func = [](AVFrame * f) {
            av_frame_free(&f);
        };

        cur_pts = av_gettime_relative();
        if (frame_pre_pts != AV_NOPTS_VALUE)
            duration = (frm->pts - frame_pre_pts) * q2d;

        int countdown = (pre_pts == 0) ? 0 : (int)(duration - (cur_pts - pre_pts));
        remaining_time = std::min<int>(remaining_time, countdown);

        // printf("countdown: %d, remaining_time: %d us\n",
        //         countdown, remaining_time);
        if (input->realtime) {
            countdown = 0;
            remaining_time = 0;
        }
        if (countdown <= 0) {
            frame_pre_pts = frm->pts;
            pre_pts = cur_pts;
            if (frm == pending_frm)
                pending_frm = NULL;
            push_frame(input->join, input->slice_idx,
                       std::shared_ptr<AVFrame>(frm, delete_func));
        } else {
            pending_frm = frm;
        }

        if (remaining_time > 0)
        {
            printf("countdown: %d, remaining_time: %d us\n",
                 countdown, remaining_time);
            std::this_thread::sleep_for(std::chrono::microseconds(remaining_time));
        }
    }
    if (pending_frm)
        av_frame_free(&pending_frm);
}
  • 知识点1:av_q2d(AVRational a)函数
    av_q2d(AVRational);该函数负责把AVRational结构转换成double,通过这个函数可以计算出某一帧在视频中的时间位置
    timestamp(秒) = pts * av_q2d(st->time_base);
    计算视频长度的方法:
    time(秒) = st->duration * av_q2d(st->time_base);
  • 知识点2:av_gettime_relative();
    该函数是拿到当前的机器时间(系统时间)。

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

  1. 取得解码视频帧
  2. 记录当前机器时间
  3. 计算当前准显示时间与上次显示时间差值d1
  4. 计算当前帧时间戳与上次显示时间戳差值d2
  5. 计算延时时间 d2 - d1
  6. 延时时间大于0则进行sleep延时
  7. 并保存当前帧在下一次循环送显

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值