音视频知识小结
前言
总结的一些音视频相关的知识,文末总结有传送门。
概念
- 协议层(Protocol Layer):该层处理的数据为符合特定流媒体协议规范的数据,例如http,rtmp,file等;
- 封装层(Format Layer):该层处理的数据为符合特定封装格式规范的数据,例如mkv,mp4,flv,mpegts,avi等;
- 编码层(Codec Layer):该层处理的数据为符合特定编码标准规范的数据,例如h264,h265,mpeg2,mpeg4等;
- 像素层(Pixel Layer):该层处理的数据为符合特定像素格式规范的数据,例如yuv420p,yuv422p,yuv444p,rgb24等;
此分类和图片来自雷神的博客,这里是链接。
很多多媒体数据流需要同时包含音频数据和视频数据,这时通常会加入一些用于音频和视频数据同步的元数据,例如字幕。这三种数据流可能会被不同的程序,进程或者硬件处理,但是当它们传输或者存储的时候,这三种数据通常是被封装在一起的。通常这种封装是通过视频文件格 式来实现的,例如常见的.mpg, .avi, .mov, .mp4, .rm, .ogg or .tta。 这些格式中有些只能使用某些编解码器,而更多可以以容器的方式使用各种编解码器。
视频压缩原理
1、压缩的方向
数字化后的视频信号具有很大的数据冗余,压缩的本质就是去掉这些冗余。
- 空间冗余,视频的背景和整体颜色相近并且平稳变化,可以利用帧内编码进行压缩;(无损)
- 时间冗余,两个视频帧之间具有强相关性,利用运动估计和运动补偿进行帧间压缩;(无损)
- 结构冗余,图像内部存在相似性,通过这种关系可以进行分形编码;
- 编码冗余,出现概率大的颜色编码长度短,概率小的颜色编码长度长;(可变长度编码)
- 视觉冗余,利用人眼对亮度和色度的敏感度不同,在编码时进行数据压缩;(有损压缩)
2、变换
空间域描述的图像相关性不太明显,需要变换到频率域。常用的正交变换有离散傅里叶变换,离散余弦变换等等。数字视频压缩过程中应用广泛的是离散余弦变换。
- 空间域(spatial domain),又称图像空间(image space)。由图像像元组成的空间。在图像空间中以长度(距离)为自变量直接对像元值进行处理称为空间域处理。
- 频率域(spatial frequency domain),以空间频率为自变量描述图像的特征,可以将一幅图像像元值在空间上的变化分解为具有不同振幅、空间频率和相位的简振函数的线性叠加,图像中各种空间频率成分的组成和分布称为空间频谱。这种对图像的空间频率特征进行分解、处理和分析称为频率域处理或波数域处理。
图像处理中的空间域就是像素域,在空间域的处理就是在像素级的处理,如在像素级的图像叠加。通过傅立叶变换后,得到的是图像的频谱。表示图像的能量梯度。
借用雷神blog的三张图来阐述效果:
一个实际8*8图像块
图像块经过DCT变换后的系数
量化后的DCT系数
H.264格式
H.264是音视频知识中,必须深入了解的部分。
先介绍一些基本的概念
-
I帧是关键帧,解码时只需要本帧数据;
-
P帧是参考帧,表示这一帧与前一个关键帧(或P帧)的差别;
-
B帧是双向参考帧,表示本帧与前后帧的差别;(B帧压缩率高,解码复杂,直播中较少用)
-
IDR帧是第一个I帧,为的是和其他I帧区别开,方便控制编码和解码;
IDR会导致DPB(DecodedPictureBuffer 参考帧列表)清空,而I不会。
-
GOP(Group Of Picture)是图像组,是一组连续的画面;(直播实现秒开,关键就是CDN节点缓存GOP,编码器拿到第一个GOP后马上解码播放)
-
帧内压缩:当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息;(帧内压缩一般达不到很高的压缩,跟编码jpeg差不多)
-
帧间压缩:利用相邻帧的相关性提高压缩量,减少冗余;(运动补偿和运动估计是常用的技术)
编码顺序
简单描述下编码的过程,假设我们处理的第一帧是DIR帧:
编码IDR帧
根据scenecut 和 min-keyint的设置,向后移动 min-keyint的距离,开始判定是否为scenechange,直到判定满足,或者到达keyint设置值的距离时候停止。同时记录判定条件不完全满足时候的位置。
编码找到的为指针,亦为IDR帧,GOP形成。
根据2步中得到的判定条件不完全满足的位置,将对应帧按时间顺序用I编码。
找到最头上IDR和离其最近的I帧形成的Sub-GOP(严格意义上说此处并非GOP因为GOP之间不能交换信息,)结合bframes的设定大小,推断P帧出现的位置。具体而言,按时间顺序走每一帧比较该帧用P编码和B编码时的视觉误差和复杂度何者更大,根据某些公式推导出此处应该用何种帧类型,再向后移一帧;如果直到bframes规定的值都未出现P,则强制插入一帧P。这样就决定了每个sub-GOP内P帧的位置。
最后一步,根据I/IDR/P形成的子区间,按时间顺序编码各帧为B帧。
举例:100-120帧这样的一段视频
第一步结束后编码完成 100帧
第二步结束后可能编码完成 120帧和110帧 (IDR帧)同时找出了104,108, 115帧应该编码为I
第四步结束后编码完成104 108 115 帧为I
第五步对 100-104 104- 108 108-110 110 – 115 115-120五个子区间,判断P帧出现的位置并编码有可能判断出102 113 118 为p帧
最后一步在编码之间的部分为B帧
于是解码过程的输出帧顺序其实是
100 110 104 108 102 101 103 105 106 109….
此例来自360文库,但是原文链接已经不能用,欢迎告知出处以备注。
iOS的H.264编解码
使用VideoToolbox硬编码H.264
使用VideoToolbox硬解码H.264
音频压缩原理
数字音频压缩编码在保证信号在听觉方面不产生失真的前提下,对音频数据信号进行尽可能大的压缩。数字音频压缩编码采取去除声音信号中冗余成分的方法来实现。所谓冗余成分指的是音频中不能被人耳感知到的信号,它们对确定声音的音色,音调等信息没有任何的帮助。
冗余信号包含人耳听觉范围外的音频信号以及被掩蔽掉的音频信号等。
人耳听觉的掩蔽效应:当一个强音信号与一个弱音信号同时存在时,弱音信号将被强音信号所掩蔽而听不见,这样弱音信号就可以视为冗余信号而不用传送。
频谱掩蔽效应
一个频率的声音能量小于某个阈值之后,人耳就会听不到,这个阈值称为最小可闻阈。当有另外能量较大的声音出现的时候,该声音频率附近的阈值会提高很多,即所谓的掩蔽效应。
时域掩蔽效应
当强音信号和弱音信号同时出现时,还存在时域掩蔽效应。即两者发生时间很接近的时候,也会发生掩蔽效应。时域掩蔽过程曲线如图所示,分为前掩蔽、同时掩蔽和后掩蔽三部分。
- PCM是编码格式,经过话筒录音后直接得到的未经压缩的数据流;
数据大小=采样频率*采样位数*声道*秒数/8。
采样定理表明采样频率必须大于被采样信号带宽的两倍,另外一种等同的说法是奈奎斯特频率必须大于被采样信号的带宽。如果信号的带宽是 100Hz,那么为了避免混叠现象采样频率必须大于200Hz。换句话说就是采样频率必须至少是信号中最大频率分量频率的两倍,否则就不能从信号采样中恢复原始信号。
- AAC是编解码标准,基于MPEG-2的音频编码技术;
使用AudioToolbox编码AAC
使用AudioToolbox播放AAC
PCM采样率是44100Hz,那么AAC码率可设置64000bps;
如果是16K,可设置为32000bps;
-
MP3是封装格式,所存放数据使用的编码方式称为MPEG1 Layer-3 ;
-
AMR是封装格式,专用于有效地压缩语音频率;
-
WAV是封装格式,里面可以存放多种编码格式的数据,一般是PCM数据;
零碎的知识
FLV封装格式是由一个FLV Header文件头和一个一个的Tag组成的。Tag中包含了音频数据以及视频数据。FLV的结构如下图所示。
FLV(Flash Video)是Adobe公司设计开发的一种流行的流媒体格式,由于其视频文件体积轻巧、封装简单等特点,使其很适合在互联网上进行应用。
RTSP:实时流传输协议,是TCP/IP协议体系中的一个应用层协议;
M4A:.m4a是MPEG-4 音频标准的文件的扩展名,Apple在iTunes以及 iPod中使用“.m4a”以区别MPEG4的视频和音频文件;
音视频同步:时间戳,时间戳即为一帧的采集时间,音视频采取同一个参考时间,给每个帧打上时间戳。
rtmp发送音视频:xcode中编译librtmp库,遵循rtmp协议,将数据发送到指定服务器;
AudioToolbox.framework:提供CoreAudio的中高级别的API服务,处理电话和其他高优先级语音处理而导致的中断和恢复操作等;
AudioUnit.framework:提供DSP数字信号处理相关的插件,包括编解码,混音,音频均衡等;
AVFoundation.framework:提供一个精简的音乐播放类,可以播放所有IOS支持的音频;
OpenAL.framework:提供3D音效播放;
AVFoundation相关
AVAssetExportSession
AVAssetExportSession类可以把AVAsset对应的源文件,转换成预先设置的格式。
exportPresetsCompatibleWithAsset 可以导出可配置格式的列表;
Error相关
AVAssetWriter
AVAssetWriter经常报出错误
AVAssetWriter startSessionAtSourceTime: Cannot call method when status is 0
查看文档,知道status=0表示未知,可知应该是没有初始化成功AVAssetWriter;
typedef NS_ENUM(NSInteger, AVAssetWriterStatus) {
AVAssetWriterStatusUnknown = 0,
AVAssetWriterStatusWriting,
AVAssetWriterStatusCompleted,
AVAssetWriterStatusFailed,
AVAssetWriterStatusCancelled
};
EXC_BAD_ACCESS
mTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
检查sampleBuffer是否为空或者引用是否有retain;
expectsMediaDataInRealTime = YES导致视频断断续续
目前的解释是:
expectsMediaDataInRealTime=YES
时需要interval=20ms左右的音频帧
sdk提供的音频回调是50ms,expectsMediaDataInRealTime=YES的时候会导致音频断断续续(帧数不够);
验证:同样的写法
expectsMediaDataInRealTime=YES
,当音频帧的interval=20ms左右时,音频不卡。
总结
此篇小结并没有讲完自己在研发直播APP过程中,了解的音视频知识。因为在之前的文集中,已经零碎介绍了一部分知识,不想再赘述。
本人也不是专门做音视频开发的,只是在研发过程中,遇到这些知识,不懂的就查,作为知识储备。故而总结的内容,也有不少的部分是来自于其他blog,比如说雷神。
想深入学习音视频开发,还是多到雷神的blog学习,这里是传送门。