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

Posted 音视频开发老舅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ffmpeg播放时刻与视频文件时间戳对齐(同步)相关的知识,希望对你有一定的参考价值。

问题描述

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

解决方案

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

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

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

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)函数

本文福利, 免费领取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延时

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

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

与 ffmpeg 的音视频同步

【中文标题】与 ffmpeg 的音视频同步【英文标题】:Audio Video Sync With ffmpeg 【发布时间】:2012-12-16 14:59:52 【问题描述】:

我有一个非常奇怪的视频文件。 它有音频/视频同步问题。视频很快,音频很好。 视频有奇怪的帧速率:26.07 fps。

如何使用 ffmpeg 修复视频流。在视频开始时,同步正常,但在视频流结束时,还剩下大约 5 分钟的音频,并且正在黑屏播放。

这是 ffmpeg -i video.avi 的输出

ffmpeg version git-2011-10-15-1d0afec, Copyright (c) 2000-2011 the FFmpeg developers
  built on Oct 15 2011 14:27:57 with gcc 4.4.5
  configuration: --enable-gpl --enable-libfaac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis --enable-libx264 --enable-nonfree --enable-postproc --enable-version3 --enable-x11grab --enable-libxvid
  libavutil    51. 21. 0 / 51. 21. 0
  libavcodec   53. 20. 1 / 53. 20. 1
  libavformat  53. 16. 0 / 53. 16. 0
  libavdevice  53.  4. 0 / 53.  4. 0
  libavfilter   2. 43. 6 /  2. 43. 6
  libswscale    2.  1. 0 /  2.  1. 0
  libpostproc  51.  2. 0 / 51.  2. 0
[mpeg4 @ 0xa585420] Invalid and inefficient vfw-avi packed B frames detected
Input #0, matroska,webm, from 'video.avi':
  Duration: 02:03:46.01, start: 0.000000, bitrate: 103 kb/s
    Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 704x288 [SAR 1:1 DAR 22:9], 26.07 fps, 25 tbr, 1k tbn, 25 tbc (default)
    Stream #0:1(pol): Audio: ac3, 48000 Hz, 5.1(side), s16, 448 kb/s (default)

【问题讨论】:

【参考方案1】:

首先您可能需要分离音频:

 ffmpeg -i input.avi -vn -ac 2 -ar 48000 -ab 448k -f mp3 audio.mp3

然后您需要将视频文件的整个长度(以秒为单位)除以视频流的实际长度(视频停止并且您可以听到音频的时间)。你应该得到一个略高于 1 的数字(1.12 或类似的数字)。你取这个商,然后:

 ffmpeg -i input.avi -vf "setpts=quotient*PTS" -an output.avi

然后你将视频和音频结合起来。

【讨论】:

以上是关于ffmpeg播放时刻与视频文件时间戳对齐(同步)的主要内容,如果未能解决你的问题,请参考以下文章

音视频同步

与 ffmpeg 的音视频同步

基于FFmpeg的视频播放器之十五:录像

Ffmpeg 实现文件切割

ffmpeg推流中断与画质不清晰的解决办法

FFmpeg学习6:视音频同步