老调重弹之ffmpeg视频时间同步
2016-10-01 00:00
357 查看
之前尝试了对视频数据的解码和简单的显示,解码完一帧图像就显示,然后立即再解码下一帧并显示,相当于一个视频以XN倍的快进播放完。现在尝试对视频图像做时间同步,即按正常的视频速率播放。
解码时简单地延时,延时时间为帧显示的时间,再解码下一帧和显示。
在之前代码基本上,在显示完一帧图片后:
解码还是不断地进行,只是在显示一帧时,计算出当前帧的显示时长,包括延时,在时长过了后,再显示下一帧。
可以通过设置定时器来实现,当定时器超时时,向窗口发消息来显示下一帧。由于一个帧解码后并不立即显示,所以需要一个列表来保存解码后的帧。我们可以再顺便把读packet和解码pakcet分开来,用一个列表来保存读到的packet,解码需要packet时再从这个列表中获取。
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时再从这个列表中获取。
相关文章推荐
- android基于ffmpeg的简单视频播发器 时间同步
- FFMPEG 播放时间计算 AVRational 音视频同步问题分析
- 音视频同步系列文章之------基于时间戳的同步点
- 理解音视频同步-时间戳
- 用FFMPEG SDK进行视频转码压缩时解决音视频不同步问题的方法(转) PTS DTS
- FFMPEG解码流程理解搜集整理及tutorial5的理解,主要是音视频同步
- 音视频同步系列文章之------时间戳与时间尺度(time scale)
- 音视频同步-时间戳
- python+ffmpeg按相等时间分割视频
- 音视频同步-时间戳
- 音视频同步-时间戳
- Ffmpeg和SDL教程(五)如何同步视频
- 音视频同步-时间戳
- 音视频同步-时间戳
- ffmpeg文档5:同步视频
- 利用Ffmpeg获得flv视频缩略图和视频长度时间
- 如何来设置时间戳让音频和视频同步呢?
- 音视频同步-时间戳
- ffmpeg+sdl教程----编写一个简单的播放器5(同步视频到音频)
- 利用Ffmpeg获得flv视频缩略图和视频时间的代码