一、打时间戳方法
1.1 背景知识
1.1.1 采样频率:
是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。
正常人听觉的频率范围大约在20Hz~20kHz之间.
根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。
常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,
如果采用更高的采样频率,还可以达到DVD的音质.
1.1.2 音频帧
一个AAC音频帧包含以采样率为基准的1024个采样点数据,及其它相关数据。
如: AAC
音频帧的播放时间 = 一个音频帧对应的采样样本的个数 / 采样频率(单位为s)
AAC的一帧是 1024个采样点,
在采样率为44100Hz时(即每秒44100个sample), 所以 根据公式
当前AAC一帧的播放时间是 = 1024* (1000000/44100) = 22.32ms (单位为ms)
再如: MP3
mp3 每帧均为1152个字节, 则:
frame_duration = 1152 * 1000000 / sample_rate
例如:sample_rate = 44100HZ时, 计算出的时长为26.122ms,
这就是经常听到的mp3每帧播放时间固定为26ms的由来。
1.2 固定帧率
1.2.1 视频时间戳:
pts = inc++ *(1000/fps);
其中:
inc , 是视频帧序号,初始值为0,每次打完时间戳inc加1.
1000 , 是1000 毫秒
fps , 视频帧率,换个方式理解的意思是视频采样率,如 25fps 表示 每秒采样25帧;
(1000/fps), 表示每帧,在当前设置的帧率下应该持续显示的毫秒数。如 1000 msec /25 fps = 40 msec;
在ffmpeg中的代码为:
pkt.pts = m_nVideoTimeStamp++ * (1000 * m_VCtx->time_base.num / m_VCtx->time_base.den);
其中:
m_VCtx->time_base.num/m_VCtx->time_base.den, 时针基,
即将1毫秒再切分成多少等份,以提高时钟计算的精度。
如, mpeg-ts是90kHz, FLV是1KHz,FFmpeg为了提高精度,用的是1MHz,
所以ffmpeg中,在接收输入帧时,要将换算成ffmpeg的时钟基表示的时间戳;输出时,换算成输出格式的时针基表示的时间戳。
(1000 * m_VCtx->time_base.num / m_VCtx->time_base.den), 表示以time_base时针基计算的每帧应该持续显示的时间。
以 H264 为例:
视频的播放时间跟帧率有关 frame_duration = 1000/fps
例如:fps = 25.00 ,计算出来的时常为40ms,这就是同行所说的40ms一帧视频数据。
1.2.2 音频时间戳:
pts = inc++ * ( frame_size / sample_rate);
其中:
inc , 是音频帧序号,初始值为0,每次打完时间戳inc加1.
frame_size, 每帧大小,AAC的一帧包含1024个采样点
sample_rate, 采样率,如 8kHz, 11.025kHz, 22.05kHz, 16kHz, 44.1kHz等,表示每秒进行的采样次数,一次一个采样点;
(frame_size / sample_rate), 表示每帧的持续时间,以AAC为例:
AAC一帧的播放时间是= 1024*(1000000微秒/44100)= 23219us = 23.22ms
音频帧率 = 采样率 / 每帧的大小; 如 AAC 在16kHz采样率下:
帧率 = 16 000 / 1024 = 15.625 fps
在ffmpeg中的代码为
pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);
1.3 可变帧率
有很多的采集卡,摄像头,在做采集的时候,明明设置的25FPS,
但实际采集数据回调过来,发现并不是40毫秒的间隔,而是50,60,甚至100不等的时间间隔。
这就给编码后打时间戳带来很大的困难。
在libav里,我们的默认编码参数都是:
ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps;
ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1;
这样在编码后的时间戳以1递增,只适合于固定帧率。
我们来改一下:
ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps * 1000;
ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1* 1000;
这样就把时间戳的scale变成了毫秒,就可以以毫秒为单位进行计算了,如下:
tAvPacket.pts = ((s64)u32TimeStamp * (s64)s32Fps);
其中:
u32TimeStamp, 是从开始记录的时间差值,以毫秒为单位;
s32Fps, 是帧率。
1.3.1 对于MP4
对于音频,mp4文件默认是采样率为tick的,时间戳计算为:
tAvPacket.pts = (AvEncoderAudioInSizeGet(hHandle) * ( (s64)(u32TimeStamp)) /
(AvEncoderAudioInSizeGet(hHandle) * 1000 / ptAvEncoder->ptAvStreamAudio->codec->sample_rate);
其中:
AvEncoderAudioInSizeGet(hHandle), 每次编码器需要的PCM数据长度。
u32TimeStamp, 是从开始记录的时间差值,以毫秒为单位。
ptAvEncoder->ptAvStreamAudio->codec->sample_rate, 是PCM采样率,代表一秒的数据量。
因为乘以了1000,所以也化成了毫秒单位。
对于mp4, 视频直接用绝对时间.
1.3.2 对于RTMP
对于rtmp,音频和视频都是用绝对时间,
视频是毫秒计算,音频也换算成毫秒计算
阅读(5307) | 评论(0) | 转发(0) |