博客同步更新:http://caixindong.leanote.com
前言
因为工作涉及到一些音视频编解码的东西,所以花了点时间学习了音视频在iOS这个平台的相关开发和实现。AVFoundation
是iOS音视频开发的一个利器,它是苹果是对iOS
和MAC OX
这两个平台音视频操作的抽象和封装,而且支持硬编码,通过AVFoundation
,我们可以很快地写出一个播放器、简易的人脸识别应用或者与摄像头相关的一些应用。欲善其事必利其器,《AV Foundation开发秘籍——实践掌握iOS & OS X应用的视听处理技术》这本书就是学习AV Foundation的利器。通过一段时间的阅读,我整理出一份读书笔记,一些关键点我会标注对应页码和参考资料,想学习这方面的读者可以先阅读这份指南,来个快速入门,先在脑中先形成一定的框架,再带着归纳好的知识点去读这本书,哪些需要详读,哪些需要略读,心里都有底,阅读效率至少提高一倍,也能保证对知识的记忆度保持在70%以上。
总概图
阅读指南
数字媒体压缩
色彩二次抽样 P11~12
是通过色彩二次抽样来进行压缩,视频数据使用的是YUV
颜色模式(大部分人认为是RGB
模式),Y
代表亮度,UV
代表颜色。
将一张图片分离亮度通道和色彩通道,发现图片的大部分细节保存在亮度通道,这是因为人眼对亮度的敏感度高于颜色,所以我们可以大幅减少存储在每个像素中的颜色信息,而不至于图片的质量严重受损,这个减少颜色数据的过程就称为色彩二次抽样。
为了维持图片质量,每个像素点都需要各自的亮度值,却不一定需要色度值。
编解码器压缩 P13
指编码器和解码器,使用高级压缩算法对音视频进行压缩编码,还可以将压缩文件解码成合适播放和编辑的文件。
压缩分为无损压缩和有损压缩,常见的zip
和gzip
压缩都是无损压缩。有损压缩的目的是使用psycho-acoustic
或psycho-visual
模式作为一种方法来减少媒体内容中的冗余数据。
视频编解码器 P13~14
H.264视频格式标准,遵循早期MEPG-1
和MEPG-2
标准,通过两个维度缩小视频文件的尺寸:
空间:压缩独立视频帧,也叫帧内压缩。帧内压缩通过消除包含在每个独立帧内的色彩及结构中的冗余信息来进行压缩,它是有损压缩。通过这个过程创建的帧叫作I-frames
,I-frames
也叫关键帧,每个GOP
都正好有一个I-frames
,它尺寸最大,解压也最快
时间:通过以组为单位的视频帧压缩冗余数据,也叫帧间压缩。很多帧组合在一起作为GOP
,对于GOP
所存在的时间维度的冗余可以被消除。GOP
包含三种不同类型帧,除了上面讲到的I-frames
,还有P-frames
和B-frames
。P-frames
又叫预测帧,是基于最近的I-frames
或P-frames
的可预测的图片进行编码得到。B-frames
又叫双向帧,是基于前后的帧信息进行编码得到,几乎不需要存储空间,但解压过程比较耗时间。
音频编解码器 P15
AAC
是H.264
标准相应的音频处理方式,AVFoundation
和Core Audio
支持MP3
数据解码,但不支持对其进行编码。
音频
配置音频
获取音频session(每个app只有一个)->配置(设置分类P22,想更进一步的自定义,可以设置options或mode)->激活
播放音频AVAudioPlayer
建议调用prepareToPlay
方法,他会取得需要的音频硬件并预加载Audio Queue
的缓冲器,
stop
和pause
的区别:在外部用户看来,两者没区别,通过这两种方法停止的音频都会继续播放,它们的区别体现在底层上面,调用stop
会撤销调用prepareToPlay
时所做的设置
pan
修改音道,volume
修改音量,rate
修改音速(先设置enable
)
默认的音频会话配置SoloAmbient
不满足音频播放器的需求,所以需要对它进行配置,配置为Playback
分类,此外还在info.plist
文件配置UIBackgroundModes
添加audio
(P30)
通过监听AVAudioSessionInterruptionNotification
来处理中断事件(P31~33)
通过监听AVAudioSessionRouteChangeNotification
来处理线路变化(例如拔出耳机停止音乐播放,基于内容保密)(P33~35)
录制音频AVAudioRecorder
创建AVAudioRecorder
需要指定存储音频文件的url、录音配置dictionary
录音配置:
AVFormatIDKey
用于定义写入内容的音频格式,指定的音频格式要与存储文件的文件类型兼容(P37)
AVSampleRateKey
用于定义录音的采样率,标识音频信号每一秒的采样数,使用低采样率,文件较小,但音质较低,使用高采用率则反之,一般标准是8000、16000、22050、44100
AVNumberOfChannelsKey
用于定义记录音频内容的通道数,默认值为1,表示使用单声道录制,设置为2表示使用立体声录制
CAF文件是最好的容器格式,他与内容无关并可以保存Core Audio支持的任何音频格式。
建议调用prepareToRecord
,做一些初始化,将录制启动的延时降到最低
stop
和pause
跟AVAudioPlayer
区别不一样,stop会结束当前录音并回调[- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag]
获取声音计量(平均分贝和分值分贝,-160~0dB)时,要设置meteringEnable
为YES,而且每次获取分贝值都需要先调用updateMeter
s才能保证读取的值最新
资源与元数据
参考资料
AVAsset
AVAsset
本身不是媒体资源,它是媒体的容器,它由一个或多个带有描述自身元数据的媒体组成。所有资源抽象化为AVAsset
,它是一个抽象类,不能直接实例化。
AVFoundation
中有一个专门的类 承载多媒体中的tracks:AVAssetTrack
,一般的视频至少有2个track,一个播放声音,一个播放画面,还可以是文本、副标题或隐藏字幕等媒体文件
AVAsset创建时就是对基础URL文件进行处理,这里有一个特殊的设计,就是AVAsset延迟载入资源的属性,只有请求属性的时候才载入,这个有利于快速创建资源,但也造成一个问题,就是访问属性时没有预先加载,这个获取属性的过程是同步的,会阻塞线程,所以一般用异步的方式查询资源的属性。
媒体元数据
AVFoundation中主要媒体格式都可以嵌入描述其内容(例如组织格式)的元数据
Apple环境下主要的媒体格式:QuickTime(mov)、MPEG-4 video(MP4和m4v)、MPEG-4 audio(m4a)、MPEG-Layer III audio(mp3)
QuickTime是跨平台的媒体架构,定义.mov文件的内部结构,它有atoms的数据结构(树状结构)组成,QuickTime文件结构图(P55 图3-2);
MPEG-4是定义MP4文件格式的规范,他直接派生与QuickTime文件格式,也是有atoms这种数据结构组成,文件结构图(P56 图3-3),它存在多种文件扩展名:.mp4、.m4v、.m4p、.m4b(P57)
MP3不使用容器格式,而使用编码音频数据,可选的元数据位于文件开头,因为专利原因,AVFoundation无法支持对MP3数据进行编码,允许读取,但无法写入
使用元数据
AVAsset
和AVAssetTrack
都可以实现查询元数据的功能,有两种元数据的键:common键空间、availableMetadataFormats
,AVMetadataItem
提供读取具体的元数据的接口
视频播放
AVPlayer
AVPlayer
只管理一个单独资源的播放,提供一个子类AVQueuePlayer
来管理一个资源队列,用于播放多个媒体资源或者循环播放
AVPlayerLayer
将播放的内容渲染到图层上,提供几种gravity
用于设置视频显示状态(P87)
AVAsset只包含资源的静态信息,仅用它无法实现播放,所以需要AVPlayerItem
和AVPlayerItemTrack
来构建相应的动态内容
AVAsset
—>AVPlayerItem
(KVO)—>AVPlayer
—>AVPlayerLayer
CMTimer
更可靠的时间展示格式,以分数的形式展示,value是分子,timescale是分母
typedef struct { CMTimeValue value; CMTimeScale timescale; CMTimeFlags flags; CMTimeEpoch epoch; } CMTime CMTimeMake ( int64_t value, //表示 当前视频播放到的第几桢数 int32_t timescale //每秒的帧数 );
时间 = value/timescale
CMTime CMTimeMakeWithSeconds( Float64 seconds, //第几秒的截图,是当前视频播放到的帧数的具体时间 int32_t preferredTimeScale //首选的时间尺度 "每秒的帧数" );
AVKit
提供了AVPlayerViewController
实现播放器的功能,支持iOS 8以上。使用的话直接实例化,并给它的player(AVPlayer)
赋值,添加到视图就可以。
媒体捕获AVCaptureSession
关键类
AVCaptureSession
、AVCaptureDevice
、AVCaptureDeviceInput
、AVCaptureOutput
、AVCaptureConnection
、AVCaptureVideoPreviewLayer
AVCaptureSession
是核心类,用于连接和管理输入和输出的资源
AVCaptureDevice
是物理捕获设备的抽象表示,可以通过[+defaultDeviceWithMediaType]
获取系统默认的捕获设备,传入AVMediaTypeVideo
获取后置摄像头(默认)参考资料
AVCaptureDeviceInput
是捕获会话的输入,AVCaptureDevice
没办法直接添加到session
,需要先将他封装到AVCaptureDeviceInput
才能添加
AVCaptureOutput
是一个抽象基类,用于从捕获会话得到数据寻找输出目的地,框架定义了一些高级拓展类AVCaptureStillImageOutput
(输出为图片)和AVCaptureMovieFileOutput
(输出为视频文件),也定义了一些底层拓展类AVCaptureAudioDataOutput
和AVCaptureVideoDataOutput
,使用它们可以直接访问硬件捕获到的数字样本,可以对音频和视频流进行实时处理
AVCaptureConnection
是输入输出类之间的连接,当session添加input和ouput后会自动生成AVCaptureConnection
,它可让开发者对信号流进行底层控制
AVCaptureVideoPreviewLayer
对捕获的数据进行实时预览,它是CALayer
的子类
媒体捕获大致过程:实例化AVCaptureSession
,设置AVCaptureDevice
和AVCaptureDeviceInput
,session添加input,设置AVCaptureOutput
,session添加output,session启动
高级捕获
视频缩放
设置AVCaptureDevice的videoZoomFactor
可以设置缩放参数,一般通过device的activeFormat.videoMaxZoomFactor
是否大于1来判断设备是否支持缩放,希望平滑缩放可以调用device的[-rampToVideoZoomFactor:(CGFloat)factor withRate:(float)rate]
,放大factor传最大缩放,缩小factor传1,设置缩放时device也要lockForConfiguration
人脸识别
定义一个AVCaptureMetadataOutput
,添加到session上,它输出的是元数据,接着设置元数据的类型metadataObjectTypes
为AVMetadataObjectTypeFace
,那捕获到的元数据都是AVMetadataFaceObject
类型,设置AVCaptureMetadataOutput
的delegate[-setMetadataObjectsDelegate:(id
,一旦有人脸就会通过[AVCaptureMetadataOutputObjectsDelegate -captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection]
回调获取人脸元数据,元数据包含人脸很多信息,例如faceID、范围。
视频处理
参考资料1
参考资料2
通过AVCaptureVideoDataOutput
的回调方法可以获取视频帧数据,每个视频帧都会包装在CMSampleBuffer
中。(未编码)
CMSampleBuffer = CVPixelBuffer(视频帧原始像素数据)+ CMFormatDescription(格式描述信息)+时间信息 + 附加的元数据
读取和写入媒体
关键类
AVAssetReaderOutput
、AVAssetReader
、AVAssetWriterInput
、AVAssetWriter
AVAssetReader
用于从AVAsset
实例读取媒体样本,通过会配置一个或多个AVAssetReaderOutput
,将媒体文件读成CMSampleBuffer,可以配置编码设置(解压)
AVAssetWriter
用于对媒体资源(CMSampleBuffer)进行编码并写入到容器文件,通常会配置一个或多个AVAssetWriterInput
,可以配置编码设置(压缩),会用到一个专门的适配器对象AVAssetWriterInputPixelBufferAdaptor
,这个类会在附加视频样本时提供最优性能,AVAssetWriter
分为实时操作和离线操作(拉模式)