FFmpeg 播放时间计算
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg 播放时间计算相关的知识,希望对你有一定的参考价值。
参考技术A 概念通过 pAVFormatCtx->duration 即可得到总时长,但是再 FFmpeg 中 duration 是以 AV_TIME_BASE 为单位,所以转换为秒的计算为:
其中 AV_TIME_BASE 是FFmpeg 中的内部计时单位(时间基),FFmpeg 中的所有时间都是于它为一个单位,内部定义为
可见FFmpeg 内部时间单位为微秒 (μs), 即AV_TIME_BASE 单位是1us。
根据PTS(显示时间戳)来计算一帧在整个视频或音频中的时间位置,PTS的时间单位为AVRational,即AV_TIME_BASE的倒数,av_q2d()是FFmpeg 提供把AVRatioal结构转换成double的函数。av_q2d(time_base)=每个刻度是多少秒。
AVRational 这个结构标识一个分数,结构定义:
实际上 time_base 的意思就是时间的刻度:
如(1,25),那么时间刻度就是1/25
如(1,9000),那么时间刻度就是1/90000
pts的值就是占多少个时间刻度(占多少个格子)。它的单位不是秒,而是时间刻度。只有pts加上time_base两者同时在一起,才能表达出时间是多少。
假设某通道的音频信号是采样率为8kHz,位宽为16bit,20ms一帧,双通道,则一帧音频数据的大小为:
int size = 8000 x 16bit x 0.02s x 2 = 5120 bit = 640 byte
公式:时间 = PCM实际数据大小 / 每秒理论PCM大小;
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 播放时间计算的主要内容,如果未能解决你的问题,请参考以下文章