您的位置:首页 > 其它

老调重弹之ffmpeg视频时间同步

2016-10-01 00:00 357 查看
之前尝试了对视频数据的解码和简单的显示,解码完一帧图像就显示,然后立即再解码下一帧并显示,相当于一个视频以XN倍的快进播放完。现在尝试对视频图像做时间同步,即按正常的视频速率播放。

DTS和PTS

首先有两个概念:DTS和PTS。一个是解码时间,一个是显示时间。对视频的编码,并不是一个帧就包含该帧的所有数据。简单地理解就是,很多时候,前后两帧视频图像之间的差别并不大,若两个帧用用完整的图像数据进行编码,相当于浪费了空间。因此,有些帧的数据只是对前一帧的的差值,这个帧需要参考前一帧的数据才能正确地解码出图像。然后,有些格式的帧,不仅要参考前面的帧,还要参考后面的帧,这样,在这一帧解码时,需要后面的帧先解码。这样就有了解码时间和显示时间。

ffmpeg中与时间有关的结构

AVPacket
AVFrame
AVCodecContext
AVStream
中与时间相关的成员

AVPacket
:

int64_t pts;
int64_t dts;
int64_t duration;
int64_t pos;

pts
dts
duration
都是以
AVStream->time_base
为单位。对
pts
dts
,如果文件是没有存储这个时间的话,其值将是
AV_NOPTS_VALUE
。对
duration
,如果不知道的话,其值会是
0

pos
是packet在流中的位置,以字节计数,如果不知道的话,其值为
-1


AVFrame


int64_t pts;
int64_t best_effort_timestamp;
int repeat_pict;
int64_t pkt_pts;
int64_t pkt_dts;
int64_t pkt_pos;
int64_t pkt_duration;
int pkt_size;
int nb_samples;
int64_t smaple_rate;
uint64_t channel_layout;
int channels;

best_effort_timestamp
用多种方式估算出的帧的时间戳,以stream中的time base为单位。应该使用
av_frame_get_best_effort_timestamp(frame)
函数来获取该值。
repeat_pict
: 指示这个图像必须延时多长,extra_delay = repeat_pict / (2*fps)
pkt_pts
pkt_dts
是从相应的AVPacket中复制来。
sample_rate
音频数据的采样率。
nb_samples
音频数据每个通道的采样数。
channel_layout
音频数据的通道布局。
channels
音频数据的通道数。使用
av_frame_get_channles(frame)
来获取该值。

AVCodecContext


AVRational time_base;
AVRational framerate;


time_base
:显示帧的时间戳的基本单位(秒)。若fps固定,其值就是1/framerate,且时间戳的值每次加1。解码时这个值的使用被废弃,而应使用framerate。
framerate
:{0,1} when unkonwn.

AVStream


AVRational time_base;
int64_t start_time;
int64_t duration;
int64_t nb_frames;

time_base
:显示帧的时间戳的基本单位(秒)。

视频图像显示的时间同步

简单地,有两种方法:

解码时简单地延时,延时时间为帧显示的时间,再解码下一帧和显示。
在之前代码基本上,在显示完一帧图片后:

frame_delay = av_q2d(pcodecContext->time_base);
frame_delay += pframe->repeat_pict * (frame_delay * 0.5);
av_usleep(frame_delay * 1000 * 1000);


解码还是不断地进行,只是在显示一帧时,计算出当前帧的显示时长,包括延时,在时长过了后,再显示下一帧。
可以通过设置定时器来实现,当定时器超时时,向窗口发消息来显示下一帧。由于一个帧解码后并不立即显示,所以需要一个列表来保存解码后的帧。我们可以再顺便把读packet和解码pakcet分开来,用一个列表来保存读到的packet,解码需要packet时再从这个列表中获取。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ffmpeg video