时间戳 音视频同步

http://6352513.blog.51cto.com/6342513/1180742

http://blog.csdn.net/happydeer/article/details/206765

http://blog.csdn.net/sidumqz/article/details/53102623

一、time_base
AVStream的time_base的单位是秒。每种格式的time_base的值不一样,根据采样来计算,比如mpeg的pts、dts都是以90kHz来采样的,所以采样间隔就是1/900000秒。

AVCodecContext的time_base单位同样为秒,不过精度没有AVStream->time_base高,大小为1/framerate。

AVPacket下的pts和dts以AVStream->time_base为单位(数值比较大),时间间隔就是AVStream->time_base。

AVFrame里面的pkt_pts和pkt_dts是拷贝自AVPacket,同样以AVStream->time_base为单位;而pts是为输出(显示)准备的,以AVCodecContex->time_base为单位。

输入流InputStream下的pts和dts以AV_TIME_BASE为单位(微秒),至于为什么要转化为微秒,可能是为了避免使用浮点数。

输出流OutputStream涉及音视频同步,结构和InputStream不同,暂时只作记录,不分析。

二、各个time_base之前的转换
ffmpeg提供av_rescale_q函数用于time_base之间转换,av_rescale_q(a,b,c)作用相当于执行a*b/c,通过设置b,c的值,可以很方便的实现time_base之间转换。

** 例如 **

InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
{
pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
}
AVPacket(AVStream->time_base)到InputStream(AV_TIME_BASE)
static int process_input_packet(InputStream *ist, const AVPacket *pkt)
{
if (pkt->dts != AV_NOPTS_VALUE)
{
ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
}
}
三、其他
AVFrame->pts和AVPacket->pts、AVPacket->dts的值,在解码/编码后,会经历短暂的time_base不匹配的情况:

解码后,decoded_frame->pts的值使用AVStream->time_base为单位,后在AVFilter里面转换成以AVCodecContext->time_base为单位。
编码后,pkt.pts、pkt.dts使用AVCodecContext->time_base为单位,后通过调用"av_packet_rescale_ts"转换为AVStream->time_base为单位。

对pts、dts、duration的处理主要集中在两大函数里面
1、process_input()读入数据并处理,放到滤镜里面
2、reap_filters()从滤镜读出数据,处理后写入文件

媒体内容在播放时,最令人头痛的就是音视频不同步。从技术上来说,解决音视频同步问题的最佳方案就是时间戳:

首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);

生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间);

在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块的开始时间大于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间;如果数据块的开始时间小于当前参考时钟上的时间,则“尽快”播放这块数据或者索性将这块数据“丢弃”,以使播放进度追上参考时钟)。
在这里插入图片描述
图2.8 解决音视频同步问题的时间戳方案

可见,避免音视频不同步现象有两个关键——

一是在生成数据流时要打上正确的时间戳。如果数据块上打的时间戳本身就有问题,那么播放时再怎么调整也于事无补。

如图2.8,视频流内容是从0s开始的,假设10s时有人开始说话,要求配上音频流,那么音频流的起始时间应该是10s,如果时间戳从0s或其它时间开始打,则这个混合的音视频流在时间同步上本身就出了问题。

打时间戳时,视频流和音频流都是参考参考时钟的时间,而数据流之间不会发生参考关系;也就是说,视频流和音频流是通过一个中立的第三方(也就是参考时钟)来实现同步的。

第二个关键的地方,就是在播放时基于时间戳对数据流的控制,也就是对数据块早到或晚到采取不同的处理方法。

图2.8中,参考时钟时间在0-10s内播放视频流内容过程中,即使收到了音频流数据块也不能立即播放它,而必须等到参考时钟的时间达到10s之后才可以,否则就会引起音视频不同步问题。

基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,有时候是不够的。

如果想要更加主动并且有效地调节播放性能,需要引入一个反馈机制,也就是要将当前数据流速度太快或太慢的状态反馈给“源”,让源去放慢或加快数据流的速度。

熟悉DirectShow的读者一定知道,DirectShow中的质量控制(Quality Control)就是这么一个反馈机制。DirectShow对于音视频同步的解决方案是相当出色的。但WMF SDK在播放时只负责将ASF数据流读出并解码,而并不负责音视频内容的最终呈现,所以它也缺少这样的一个反馈机制。

为了更好地理解基于时间戳的音视频同步方案,下面举一个生活中的例子。假设你和你的一个朋友约好了今天18:00在沪上广场见面,然后一起吃饭,再去打游戏。实际上,这个18:00就是你和你朋友保持同步的一个时间点。结果你17:50就到了沪上广场,那么你必须等你的朋友。10分钟过后,你的朋友还没有到,这时他打来电话说有事耽搁了,要晚一点才能到。你没办法,因为你已经在旁边的餐厅预订了位置,如果不马上赶过去,预订就会被取消,于是你告诉你的朋友直接到餐厅碰头吧,要他加快点。于是在餐厅将来的某个时间点就成为你和你朋友的又一个同步点。虽然具体时间不定(要看你朋友赶过来的速度),但这样努力的方向是对的,你和你朋友肯定能在餐厅见到面。结果呢?你朋友终于在18:30赶过来了,你们最终“同步”了。吃完饭19:30了,你临时有事要处理一下,于是跟你朋友再约好了20:00在附近的一家游戏厅碰头。你们又不同步了,但在游戏厅将来的某个时间点你们还是会再次同步的。

悟出什么道理了没有?其实,同步是一个动态的过程,是一个有人等待、有人追赶的过程。同步只是暂时的,而不同步才是常态。人们总是在同步的水平线上振荡波动,但不会偏离这条基线太远。

====================================http://www.cnblogs.com/NerdWill/p/6744432.html
在对音视频重新编码并需要进行同步的场景中,需要遵守几项基本原则(否则音视频就会卡顿,不流畅。以音频aac编码频率44.1k,视频h264编码25帧帧率为例):

  1. 保证输入端的音视频帧到达间隔基本精确。音频aac每帧时长是23.2ms(1000*1024/44100),视频每帧时长是40ms(1000/25)。所以,用于编码的原始音频samples的到达频率(或从buffer中获取的频率)应该为441 samples/per channel/ per 10ms(每个样本假设16bits,即882字节/通道/10ms,如果原始音频采样率不是44.1k,编码前需要重新采样);原始视频帧到达频率(或从buffer中获取的频率)应该为1帧/per 40ms(视频帧率可能需要重新采样)。如果输出的音视频流不流畅,可先检查输入端音视频流的输入间隔情况。

2.保证输出端的音视频流时间戳使用同一参考系。比如音视频流都使用当前系统时间作为同一时间参考系,但音视频流可以有不同的系统时间起始点。比如音频流先开始于1492764087000 ms,视频稍后700ms开始于1492764087700ms。比如rtmp里面使用32位时间戳,则音视频流只能使用相对时间戳,接上例,音频时间戳增长到700ms的时候视频流才从0开始,表示视频流是从音频流的700ms处开始的,这样才能达到同步的效果。 另外一种时间戳方案是每个音视频帧按固定间隔增长,比如音视频时间戳都从0开始,音频每个aac帧增加23.2ms,每个视频帧增长40ms。正常情况下,音视频流是同时从0开始按相应各自间隔发送帧的,但也有视频流晚于音频流或音频流晚于视频流的情况。这种情况需要做时间戳同步,稍晚的流起始时间要根据超前的流的时间来设置。

3.保证交叉输出时音视频间隔基本精确。这里的输出端就完全等同于一个硬件编码器,只有保证交叉输出的音视频帧间隔稳定,才能保证播放端的流畅。比如rtmp,每个aac音频帧输出间隔应该在23ms左右,每个视频帧输出间隔应该在40ms左右,而且音视频帧是交叉输出。换句话说,每23ms要发送一个aac音频帧,每40ms发送一个视频帧(可以使用两个单独的线程来分别发送音视频流)。如果排除了上面的两个问题还是不能流畅播放,可以检查这个环节是否正常。

总之,重新编码并同步的这个环节,必须建立在数学测量的基础上。有错误或补充的地方,欢迎指出。

================

http://www.cnblogs.com/my_life/articles/6944054.html

视频中帧就是一个图片采样。音频中一帧一般包含多个样本,如AAC格式会包含1024个样本。

http://blog.sina.com.cn/s/blog_6b87c7eb010182hs.html

http://www.jianshu.com/p/030288800a61

采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。

正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质。
在这里插入图片描述

音频
在数字音频领域,常用的采样率有:

8,000 Hz - 电话所用采样率, 对于人的说话已经足够
11,025 Hz
22,050 Hz - 无线电广播所用采样率
32,000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率
44,100 Hz - 音频CD, 也常用于MPEG-1 音频(VCD, SVCD, MP3)所用采样率
47,250 Hz - Nippon Columbia (Denon)开发的世界上第一个商用 PCM 录音机所用采样率
48,000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采样率
50,000 Hz - 二十世纪七十年代后期出现的3M 和Soundstream 开发的第一款商用数字录音机所用采样率
50,400 Hz - 三菱 X-80 数字录音机所用所用采样率
96,000 或者 192,000 Hz - DVD-Audio、一些 LPCM DVD 音轨、BD-ROM(蓝光盘)音轨、和 HD-DVD (高清晰度 DVD)音轨所用所用采样率
2.8224 MHz - SACD、索尼 和 飞利浦 联合开发的称为Direct Stream Digital的1位sigma-delta modulation 过程所用采样率。
采样频率定义了每秒从连续信号中提取并组成离散信号的采样个数,采样频率的倒数是采样周期或者叫作采样时间,它是采样之间的时间间隔

44100已是CD音质, 超过48000或96000的采样对人耳已经没有意义。这和电影的每秒 24 帧图片的道理差不多。

AAC一帧可以解析出的音频时长
一个AAC原始帧包含一段时间内1024个采样及相关数据
音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)
一帧 1024个 sample。采样率 Samplerate 44100KHz,每秒44100个sample, 所以 根据公式 音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率
当前AAC一帧的播放时间是= 10241000000/44100= 22.32ms(单位为ms)
(一个aac帧是由1024个样本组成的,一秒内aac的采样次数是44.1k次,所以一个aac帧的时长是1
1024/44.1k, 单位为秒)
对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

MP3一帧可以解析出的音频时长
mp3 每帧均为1152个字节 (样本), 则:
frame_duration = 1152 * 1000000 / sample_rate
sample_rate = 44100HZ时, 计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来。
H264 视频
视频的一个帧的播放时间跟帧率有关:

frame_duration = 1000/帧率(fps)

例如:fps = 25.00 ,计算出来的时常为40ms,这就是同行所说的40ms一帧视频数据。

================

http://blog.csdn.net/aoshilang2249/article/details/38469051

http://blog.csdn.net/ownWell/article/details/8114121

【采样位数】
在这里插入图片描述
即采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。

每个采样数据记录的是振幅, 采样精度取决于采样位数的大小:
1 字节(也就是8bit) 只能记录 256 个数, 也就是只能将振幅划分成 256 个等级;
2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了;
4 字节(也就是32bit) 能把振幅细分到 4294967296 个等级, 实在是没必要了.

【交错模式】
http://www.cnblogs.com/my_life/articles/6841859.html
数字音频信号存储的方式。数据以连续帧的方式存放,即首先记录帧1的左声道样本和右声道样本,再开始帧2的记录…
【非交错模式】
首先记录的是一个周期内所有帧的左声道样本,再记录所有右声道样本。

==================
http://blog.sina.com.cn/s/blog_864ca0b60101p7zn.html
音频中经常会提到两个概念:采样率、比特率(码率)。实际上当我们在播放MP3时,这两个信息有些播放器会显示:例如:采样率44.1KHz,比特率192Kbps。
采样率是指每秒从连续信号中提取并组成离散信号的采样个数,用赫兹(Hz)来表示。
1、编码前比特率
比特率是指经过编码后的音频数据每秒钟需要用多少个比特来表示,但是这里的编码需要注意不是指MP3,WMA等编码概念,是指模拟信号到数字信号的编码即(PCM数据),与它们密切相关的是表示每个PCM样点的bit位数。比如8bit,16bit
以网上的例子来说:
以电话为例,每秒3000次取样,每个取样是7比特,那么电话的比特率是21000。而CD是每秒44100次取样,两个声道,每个取样是13位PCM编码,所以CD的比特率是44100213=1146600,也就是说CD每秒的数据量大约是144KB,而一张CD的容量是74分等于4440秒,就是639360KB=640MB。

通过上面的原理说明,就很容易解释,为什么有些音频文件采样率一样,但是比特率不一样,或者是比特率一样,采样率不一样的问题。当然,在实际的应用中,为了同一输出,可能会将解码后的PCM数据进行上采样或者下采样统一到48K,最后输出。由于这个限制,对于采样文件本身为96K的高保真文件就无法有效播放。需要更加高端的播放硬件。
2、编码后比特率
比特率是指经过编码后的音频数据每秒钟需要用多少个比特来表示,但是这里的编码需要注意是指MP3,WMA等编码概念,不是指模拟信号到数字信号的编码即(PCM数据),与它们密切相关的是表示每个PCM样点的bit位数。比如8bit,16bit
编码的时候设置比特率(wav —>aac—>wav),例如为64Kbps,也就是说音乐播放时每秒读取的编码文件的大小是64KBytes,因此假如一个12m的音乐片段,它的文件大小应该是64*12 /8=96Kb,以上数据都是指AAC文件的大小。至于aac编码得到的文件wav大小,它与aac压缩说法的压缩比有关。
aac—>wav的原理,aac 文件中需要包含信道个数信息,采样率信息,帧大小。解码的时候是一帧一帧的解码得到480或者是512PCM数据。做仿真时存放到wav文件中。如果是实时播放,则需要考虑DAC的采样率问题,一般它的采样率是一定的,我们的平台是按48K输出。即每秒钟48000个PCM数据输入到 DAC处处理。这里需要注意区分比特率概念。如果是做仿真保存到wav文件,则可以看到相应的信息.

=====================================
http://developer.t-firefly.com/thread-5731-1-1.html
http://www.cnblogs.com/my_life/articles/6274692.html

PCR(Program Clock
Reference)同步在非硬件精确时钟源的情况还是谨慎使用,我起初采用PCR同步就会出现,随着时间的推移,软件模拟的时钟不精确会导致视频出现延时或者音视频不同步。
最近研究了FFMPEG的同步技巧,觉得其精妙绝伦,完全不用担心随着时间的推移会发生如上问题!下面简述下FFMPEG下如何进行音视频同步的吧!
FFMPEG有三种同步方式,视频同步音频,音频同步视频,同步到外部时钟!
第三种,同步到外部始终也就是PCR同步和我原来说的那中同步方式,一样! 用的最多的还是,视频同步音频,为什么呢?
音频的采样率是固定的,若音频稍有卡顿,都会很明显的听出来,反则视频则不如此,虽然表面上说的是30P(每秒30帧),不一定每一帧的间隔就必须精确到33.33ms(所以每帧间隔大约33.33ms),
因为人肉眼是观察不出来的,所以视频的帧率可以是动态的,并不是严格标准的! 废话少说了!用视频同步音频,做法很简单!
首先,音频线程只管自己独立解码播放。视频线程在显示之前只需要检测视频PTS是否大于音频PTS,若大,则等待音频PTS>=视频PTS,若小,则直接播放,小太多则可以直接丢弃(跳帧)。做法就如此简单!但前提是你编码器一定要打好正确的PTS,若没有则只有自己伪造PTS了!

音视频同步和帧率控制其实是一个东西。我们先不管音视频同步是什么,我们先来看看如何进行帧率控制。明白了帧率控制,音视频同步那些都一通百通。一些基本的音视频术语我就不介绍了,大家自己百度吧!
1、帧率控制
帧率控制的方法有千万种,最2的方法无非是每解码/显示一帧就进行延时,为了方便我们在进行帧率控制的同时能够理解音视频同步,我在此采用PCR同步的方式来进行帧率控制。网上关于PCR同步的原理讲了一大堆,有些很是难懂,一点儿也不通俗,我这里来给大家把晦涩的理论以最通俗的方式表达出来。还希望大家多多指教!

拿1280x720@30p的视频源来做理解。30P也就是说每秒钟30帧,也就是每一帧需要1/30*1000ms大概也就是每隔33.33ms就必须显示一帧。

要想知道如何正确的进行解码,就必须先了解编码端是如何工作的!
一般编码器会以27MHZ的时钟来进行编码,这些都不重要,重要的就是,编码器一般默认会每隔30ms会发送一次PCR信息,这里的PCR信息就很重要了,
他是我们在解码端解码进行帧率控制的时间基点,同时也是我们以后在进行同步校准的校准基点。

说了这么多,那么我就那个PCR信息来给大家分析分析。PCR信息是33bit组成的一个INT64_T的数据,从解复用器里面出来我们可以得到一个很庞大的数字,看这个我们当然看不太懂!但是如果知道这个数字如何生成的那就好理解多了!

PCR信息说白了就是给视频的时间戳信息,比如一部电影是从 (00:01:23:033)时 : 分 : 秒 : 毫秒 开始,那么这个时间基点生成的PCR信息就是 (((00*60+1)*60+23.033)90K)%2^33。90K为27M,300分频的结果。刚刚说了PCR会每30ms更新一次,那么PCR每次递增的数值就为0.03090K=2700,这和PTS的值原理是相同的,这里先提一下,其实这个增量也不重要。我们需要的知识第一个PCR值就OK,但是如果考虑到后期校准,还是要用到以后的PCR值的。这里先不管校准的问题!

说了PCR,还有个值是我们需要的,那就是PTS。其实对于硬解码器来说DTS信息我们根本就不需要管他,我们只需要一帧一帧的把数据送进去,顺便把每一帧的PTS信息送进去,解码器送出来的就是排列好了PTS信息的帧了,其他解码器不知道至少RK3288是这样的。大家可以试着把解复用后的每一帧的PTS打印出来,你会发现在解复用后一般是这样排列的9000 3000 6000 18000 12000 15000…这种是AVC编码的使用的是预测编码决定的,先不管他,你只管这样把没一帧依次送入解码器,解码器解码输出后自然就排列成3000 6000 9000 12000 15000 18000这才是我们需要的PTS!至于什么是PTS,实际上和PCR原理差不多,但是有个关键的地方PTS的增量值可不是默认的30ms了,他是由视频的帧率来决定的!说道重要的地方了哈!根据上面PCR的原理,如果是30p的视频那么每一帧就是1/30这么多的增量,再乘90K=3000。

说透了,我们这里就是利用这个PTS值来进行同步顺便进行帧率控制!
关键的地方来了!
如果视频流现在来了,我们先获取到第一个PCR值为1230000,我们现在马上在解码器端重建一个90K的时钟!这就是关键所在,至于如何重建90K的时钟,说白了就是开一个定时器,定时时间为1/90K(11.11us),每隔11.11us我们就把PCR计数值+1,同时这时候解码器也在工作,试想一下,如果是30P的视频,也就是33.33ms显示一次,那么当过了33.33ms后,PCR的数值加到好多了呢?没错就是33.33ms/11.11us=3000,这个增量不是和PTS的增量一摸一样!这时候你只需要在解码线程里判断当前帧的PTS是不是和这个PCR相等,如果相等就显示,如果PCR大可以丢弃当前帧,也就是说的跳帧,如果PCR小说明解码快了,这个时候就可以等待定时器线程到PCR==PTS。
这样就很巧妙的解决了帧率控制的问题了!同理,音视频同步也可以这样!你可以让音频的PTS去和PCR对比!其实大多数情况下都是以视频同步音频,音频解码不用管它,直接解码播放就OK了,你只需要进行帧率控制就OK了!同时注意随着时间的推移有可能出现延时,那么这个时候就需要重新来获取PCR来更新定时器线程里面的PCR基值了!

http://www.cnblogs.com/NerdWill/p/6744432.html

在对音视频重新编码并需要进行同步的场景中,需要遵守几项基本原则(否则音视频就会卡顿,不流畅。以音频aac编码频率44.1k,视频h264编码25帧帧率为例):

  1. 保证输入端的音视频帧到达间隔基本精确。音频aac每帧时长是23.2ms(1000*1024/44100),视频每帧时长是40ms(1000/25)。所以,用于编码的原始音频samples的到达频率(或从buffer中获取的频率)应该为441 samples/per channel/ per 10ms(每个样本假设16bits,即882字节/通道/10ms,如果原始音频采样率不是44.1k,编码前需要重新采样);原始视频帧到达频率(或从buffer中获取的频率)应该为1帧/per 40ms(视频帧率可能需要重新采样)。如果输出的音视频流不流畅,可先检查输入端音视频流的输入间隔情况。

2.保证输出端的音视频流时间戳使用同一参考系。比如音视频流都使用当前系统时间作为同一时间参考系,但音视频流可以有不同的系统时间起始点。比如音频流先开始于1492764087000 ms,视频稍后700ms开始于1492764087700ms。比如rtmp里面使用32位时间戳,则音视频流只能使用相对时间戳,接上例,音频时间戳增长到700ms的时候视频流才从0开始,表示视频流是从音频流的700ms处开始的,这样才能达到同步的效果。 另外一种时间戳方案是每个音视频帧按固定间隔增长,比如音视频时间戳都从0开始,音频每个aac帧增加23.2ms,每个视频帧增长40ms。正常情况下,音视频流是同时从0开始按相应各自间隔发送帧的,但也有视频流晚于音频流或音频流晚于视频流的情况。这种情况需要做时间戳同步,稍晚的流起始时间要根据超前的流的时间来设置。

3.保证交叉输出时音视频间隔基本精确。这里的输出端就完全等同于一个硬件编码器,只有保证交叉输出的音视频帧间隔稳定,才能保证播放端的流畅。比如rtmp,每个aac音频帧输出间隔应该在23ms左右,每个视频帧输出间隔应该在40ms左右,而且音视频帧是交叉输出。换句话说,每23ms要发送一个aac音频帧,每40ms发送一个视频帧(可以使用两个单独的线程来分别发送音视频流)。如果排除了上面的两个问题还是不能流畅播放,可以检查这个环节是否正常。

总之,重新编码并同步的这个环节,必须建立在数学测量的基础上。有错误或补充的地方,欢迎指出。

======================

http://www.xuebuyuan.com/1400936.html

视频、音频打时间戳的方法

http://blog.csdn.net/wfqxx/article/details/5497138

AVCodecContext *m_VCtx;

一 固定帧率

  1. 视频时间戳

    pts = inc++ *(1000/fps); 其中inc是一个静态的,初始值为0,每次打完时间戳inc加1.

    在ffmpeg,中的代码为

    pkt.pts= m_nVideoTimeStamp++ * (m_VCtx->time_base.num * 1000 / m_VCtx->time_base.den);

注:见AVCodecContext 的代码注释:

对于固定帧率, timebase == 1/framerate

framerate = fps

  1. 音频时间戳

    pts = inc++ * (frame_size * 1000 / sample_rate)

    在ffmpeg中的代码为

    pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

注: frame_size: Number of samples per channel in an audio frame. 每个音频帧的 sample 个数.

采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。

正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质

对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

背景知识:

(一个AAC原始帧包含一段时间内1024个采样及相关数据)

(一个MP3原始帧包含一段时间内1152个采样及相关数据)

分析:

1 AAC

音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)

一帧 1024个 sample。采样率 Samplerate 44100KHz,每秒44100个sample, 所以 根据公式 音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率

当前AAC一帧的播放时间是= 1024*1000000/44100= 22.32ms(单位为ms)

2 MP3

mp3 每帧均为1152个字节, 则:

frame_duration = 1152 * 1000000 / sample_rate

例如:sample_rate = 44100HZ时, 计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来。

二 可变帧率

有很多的采集卡,摄像头,在做采集的时候,明明设置的25FPS,但实际采集数据回调过来,发现并不是40毫秒(1s=1000ms; 1000ms / 25 = 40 ms)的间隔,而是50,60,甚至100不等的时间间隔。这就给编码后打时间戳带来很大的困难。

在libav里,我们的默认编码参数都是:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps; //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是帧率。

对于音频,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,所以也化成了毫秒单位。

===示例AVInputFormat mio=

音频基本信息初始化(read_header):

st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
st->codecpar->sample_rate = 48000;
st->codecpar->channels = 2;
avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE); /* 64 bits pts in us */
// to assume AAC encode
md->a_frame_duration = AV_TIME_BASE * 1024 / st->codecpar->sample_rate;   //每个音频帧的时长

 
给音频帧打时间戳(read_packet):

int64_t ats = 0;

av_init_packet(&pkt);
pkt.pts = (ats += a_frame_duration);    //计算方式其实跟上面    pts = inc++ * (frame_size * 1000 / sample_rate)一样
pkt.dts = pkt.pts;
pkt.data = (uint8_t *)abuf;
pkt.size = rtval;
pkt.stream_index = audio_index;
 

====视频基本信息的初始化==

st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->width = vhcnt;
st->codecpar->height = vvcnt;
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
st->codecpar->format = AV_PIX_FMT_UYVY422;
st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y');
st->time_base = av_make_q(1, 25);                //25: 帧率
st->avg_frame_rate = av_inv_q(st->time_base);
md->v_frame_duration = av_q2d(st->time_base)  * AV_TIME_BASE;   //每个视频帧的时长
avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE); /* 64 bits pts in use */
 

给视频帧打时间戳(read_packet):

int64_t vts = 0;

av_init_packet(&pkt);
pkt.pts = (vts += v_frame_duration);
pkt.dts = pkt.pts;
pkt.data = (uint8_t *)vbuf;
pkt.size = rtval;
pkt.stream_index = video_index;

Guess you like

Origin blog.csdn.net/liulina603/article/details/108003112