author: hjjdebug
date: 2025年 07月 05日 星期六 18:20:45 CST
descrip: 视频播放中时钟的概念及音视频同步概念
文章目录
- 1.前言: 视频播放:
- 1. 固定延时时间
- 2. 根据frame的duration来延时.
- 3. 根据frame的PTS 来播放
- 3.1. 时钟是什么?
- 3.2. 时钟的用途.
- 2.音视频同步:
1.前言: 视频播放:
一帧一帧的把画面渲染出来就构成了视频播放.
从上一帧到这一帧的时间间隔是多少呢?
1. 固定延时时间
我们可以通过帧率计算每一帧的延时时间
每一帧和每一帧之间延时固定长度来播放.
架构大概是这样的.
double duration=(double)1/framerate;
for(;;)
{getframe(ctx,frame);delay(duration);display(frame);
}
这样每个画面看起来是连续的,但是播放时间却是不准确的.
2. 根据frame的duration来延时.
每个frame 都有duration属性值,视频一般是3600个时间单位,40ms时间,也有例外.
架构大概是这样的.
for(;;)
{getframe(ctx,frame);delay(frame.duration);display(frame);
}
缺点. 只考虑了每一帧和每一帧之间理论上的时间间隔,
但是获取到该帧也是要花时间的,
播放时间还是不准确.没有按frame 的时间戳来播放.
3. 根据frame的PTS 来播放
让每一次播放时间与PTS 时间戳对齐. PTS 让我们啥时候播放,我们就啥时候播放.
当拿到一个frame时,根据frame.pts,判断需要等待多少时间后再播放
架构大概是这样的.
for(;;)
{getframe(ctx,frame);double duration=compute_target_delay(video_clock,frame.pts); //这个duration 是动态的.根据pts计算的.delay(duration);display(frame);
}
那根据pts 计算duration的过程是怎样的呢?
关键就是这个时钟video_clock. 它必需要知道某个时间点对应的PTS值是多少,
这样给一个新的pts, 就能够计算出期望的时间点.
拿期望的时间点减掉当前时间戳,就能得到延时时间duration
所以计算过程是这样的.
typedef struct _Clock
{long pts_ref; //参考pts时间double sys_time_ref; //对应该pts的系统时间
}Clockdouble compute_target_delay(Clock *clk, long pts)
{long delta_pts = pts - clk->pts_ref;double time = delta_pts * AVRational(1,90000); //时间戳pts一般是90Kdouble expect_time=sys_time_ref+time;double current_time=av_gettime_relative()/1000000.0;double delay=expect_time-current_time; //期望播出时间减去当前时间就是应该delay的时间.return delay;
}
是的,也许你发现了问题,参考时间和参考pts又如何获得呢?
这个简单,你可以在获取第一帧数据时,设定pts为pts_ref, 那时的系统时间为sys_time_ref.
而且在播放过程中,当你发现不正常时,随时设置参考帧pts及对应的参考时间.
这叫时钟同步,可能对应某个set_clock函数, 下面给个示例.
程序代码:
for(;;)
{getframe(ctx,frame);double duration=compute_target_delay(vid_clk,frame.pts); //这个duration 是动态的.根据pts计算的.if(duration > 0){delay(duration)}else if(duration < -0.1) //超前有点离谱,我们矫正一下时钟{set_clock(vid_clk,frame.pts); //设置时钟同步点}display(frame);
}
//时钟同步
void set_clock(Clock *vid_clk,long pts)
{vid_clk. pts_ref = pts;vid_clk.sys_time_ref = av_gettime_relative()/1000000.0;
}
3.1. 时钟是什么?
时钟就是某个frame的PTS, 对应着播放的系统时钟时间是多少.
这是时钟的主要概念.
有个这个基础,我们可以以后根据frame的pts, 计算出它期望的播出时间.
在此基础上再衍生出其它成员只是为了完善其它的功能而已.
1.1 时钟对时:
把PTS与系统时间关联上叫时钟对时, 通过调用 set_clock(Clock *clk,LONG pts) 来完成
3.2. 时钟的用途.
给定一个frame, 可以拿到它的pts, 通过与时钟比较,可以知道它应该在什么时间播放.
从而也知道,它此时应该等待多少时间才可以播放.
2.音视频同步:
音频和视频解码/渲染是独立线程,它们各有自己的时间戳PTS, 因而可以对应不同的时钟.
即音频对应音频时钟. audclk
视频对应视频时钟. vidclk
如果我们拿音频时钟去调教视频播放,这就实现了音视频同步,具体是怎样操作的呢?
视频时钟与主时钟本身可能就有偏差,这个需要考虑在内.
diff = get_clock(&is->vidclk) - get_master_clock(is); //这里的master_clock 可能是音频时钟,视频时钟或外部时钟
我们现在在谈视频同步到音频,当然这里要把主时钟理解为音频时钟.
根据视频计算的delay值,用音频进一步矫正,需要一种算法,可能是简单的delay+diff,
也可能在极端条件下进行过激调整2delay+diff, delay+2diff 等等, 可以观察一下哪种效果更好.
这就是编程灵活的地方了. 算法以简洁,使用,有效为基础.
有了这些概念,我们再来读ffplay6, 就好很多了.