获取视频时钟,首先要获取视频帧的显示时间戳pts;在AVFrame结构体中,有一成员变量为best_effort_timestamp,该变量是 单位为数据流时间基的帧时间戳,官方解释如下:
- frame timestamp estimated using various heuristics, in stream time base
-
- encoding: unused
- decoding: set by libavcodec, read by use
获取到该时间戳后,再将其单位由数据流时间基转换为正常时间;这里的时间基是使用AVRational结构提描述的,即分子—分母式的描述方式;这里使用 av_q2d() 函数对其进行转换,将AVRational描述的时间基转换为正常时间基。
具体流程如下:
- /*pFrameRaw为解码后的YUV数据(AVFrame)*/
- pFrameRaw->pts = pFrameRaw->best_effort_timestamp;
- frame_pts_lf = pFrameRaw->pts * av_q2d(pFormatCtx->streams[video_index]->time_base);
- frame_pts_ld = frame_pts_lf * 1000; //转换为以微妙为单位
外部时钟主要依赖于 av_gettime() 函数,官方解释如下:
Get the current time in microseconds.
通过获取从视频开始播放时的时间作为基准,就能获取到每一帧视频帧播放时,外部时钟实际度过的时间。
知道了视频时钟和外部时钟,如果视频帧时间戳能严格与外部时钟同步,那么视频播放就能按照预定帧率、预定时长去进行。
在每一次视频帧显示前,都计算该视频帧时间戳与外部时钟的差值,通过该差值去动态校准该帧的持续时间,从而使得下一视频帧时间戳与外部时钟的差值越来越小。
根据该系统的实现要求,引入了增量式PID算法来消除视频帧时间戳与外部时钟的差值
获取视频帧时间戳与外部时钟的差值clock_diff,令时钟差值的设定值clock_diff_set为0;
将该差值送入增量式PID控制器中处理得到帧持续时间的增量delay_incre;
与延时基准值delay_basic相加得到实际延时值delay_actual作为当前帧持续时间;
延时过后,重复该操作,当PID控制器参数合理,会使得clock_diff趋向于设定值0;
简略的代码实现如下:
- /*** 解码渲染显示等操作 ***/
- if (time_flag) {
- time_offset = av_gettime();
- time_flag = 0;
- }
- now_time = av_gettime() - time_offset;
- time_diff[0] = time_diff[1];
- time_diff[1] = time_diff[2];
- time_diff[2] = now_time - vc->frame_pts_ld;
- time_incre = kp*(time_diff[2]-time_diff[1]) + ki*time_diff[2] +
- kd*(time_diff[2] - 2*time_diff[1] + time_diff[0]);
- now_delay -= time_incre; //此处的 -=操作 等价于处理clock_diff_set=0
- printf("\r%ld, %lld\t", now_delay, time_diff[2]);
- av_usleep(now_delay);
- /*** 回到第一步重复操作, 不断收敛time_diff为0 ***/
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓