http://6352513.blog.51cto.com/6342513/1180742
http://blog.csdn.net/happydeer/article/details/206765
http://blog.csdn.net/sidumqz/article/details/53102623
对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在附近的一家游戏厅碰头。你们又不同步了,但在游戏厅将来的某个时间点你们还是会再次同步的。
悟出什么道理了没有?其实,同步是一个动态的过程,是一个有人等待、有人追赶的过程。同步只是暂时的,而不同步才是常态。人们总是在同步的水平线上振荡波动,但不会偏离这条基线太远。
在对音视频重新编码并需要进行同步的场景中,需要遵守几项基本原则(否则音视频就会卡顿,不流畅。以音频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一帧的播放时间是= 1024*1000000/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 个等级, 实在是没必要了.
【交错模式】
数字音频信号存储的方式。数据以连续帧的方式存放,即首先记录帧1的左声道样本和右声道样本,再开始帧2的记录...
【非交错模式】
以电话为例,每秒3000次取样,每个取样是7比特,那么电话的比特率是21000。而CD是每秒44100次取样,两个声道,每个取样是13位PCM编码,所以CD的比特率是44100*2*13=1146600,也就是说CD每秒的数据量大约是144KB,而一张CD的容量是74分等于4440秒,就是639360KB=640MB。
最近研究了FFMPEG的同步技巧,觉得其精妙绝伦,完全不用担心随着时间的推移会发生如上问题!下面简述下FFMPEG下如何进行音视频同步的吧!
FFMPEG有三种同步方式,视频同步音频,音频同步视频,同步到外部时钟!
第三种,同步到外部始终也就是PCR同步和我原来说的那中同步方式,一样!
用的最多的还是,视频同步音频,为什么呢?
废话少说了!用视频同步音频,做法很简单!
1、帧率控制
帧率控制的方法有千万种,最2的方法无非是每解码/显示一帧就进行延时,为了方便我们在进行帧率控制的同时能够理解音视频同步,我在此采用PCR同步的方式来进行帧率控制。网上关于PCR同步的原理讲了一大堆,有些很是难懂,一点儿也不通俗,我这里来给大家把晦涩的理论以最通俗的方式表达出来。还希望大家多多指教!
拿1280x720@30p的视频源来做理解。30P也就是说每秒钟30帧,也就是每一帧需要1/30*1000ms大概也就是每隔33.33ms就必须显示一帧。
要想知道如何正确的进行解码,就必须先了解编码端是如何工作的!
说了这么多,那么我就那个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.030*90K=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
2. 音频时间戳
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;