音频压缩
对声音进行采样、量化过程被称为脉冲编码调制(Pulse Code Modulation),直接脉冲编码调制(Linear Pulse Code Modulation)产生的数据称为Linear PCM数据,LPCM数据是最原始的音频数据完全无损,但是他的体积非常大,比如一个44.1kHz,16bit,双音道的音频文件,每分钟的数据量为44.1*16*2*60kbit=10.3M。一个普通5分钟的音乐就得50M,这个数据量对普通用户来说是过于庞大的。
为了解决这个问题,诞生了一系列的压缩算法,其中主要分为有无损压缩(ALAC、APE、FLAC)和有损压缩(MP3、AAC、OGG、WMA)两种。其具体的压缩算法对应到AVFoundation的CoreAudioTypes.h
中有:
CF_ENUM(AudioFormatID) { kAudioFormatLinearPCM = 'lpcm', kAudioFormatAC3 = 'ac-3', kAudioFormat60958AC3 = 'cac3', kAudioFormatAppleIMA4 = 'ima4', kAudioFormatMPEG4AAC = 'aac ', kAudioFormatMPEG4CELP = 'celp', kAudioFormatMPEG4HVXC = 'hvxc', kAudioFormatMPEG4TwinVQ = 'twvq', kAudioFormatMACE3 = 'MAC3', kAudioFormatMACE6 = 'MAC6', kAudioFormatULaw = 'ulaw', kAudioFormatALaw = 'alaw', kAudioFormatQDesign = 'QDMC', kAudioFormatQDesign2 = 'QDM2', kAudioFormatQUALCOMM = 'Qclp', kAudioFormatMPEGLayer1 = '.mp1', kAudioFormatMPEGLayer2 = '.mp2', kAudioFormatMPEGLayer3 = '.mp3', kAudioFormatTimeCode = 'time', kAudioFormatMIDIStream = 'midi', kAudioFormatParameterValueStream = 'apvs', kAudioFormatAppleLossless = 'alac', kAudioFormatMPEG4AAC_HE = 'aach', kAudioFormatMPEG4AAC_LD = 'aacl', kAudioFormatMPEG4AAC_ELD = 'aace', kAudioFormatMPEG4AAC_ELD_SBR = 'aacf', kAudioFormatMPEG4AAC_ELD_V2 = 'aacg', kAudioFormatMPEG4AAC_HE_V2 = 'aacp', kAudioFormatMPEG4AAC_Spatial = 'aacs', kAudioFormatAMR = 'samr', kAudioFormatAMR_WB = 'sawb', kAudioFormatAudible = 'AUDB', kAudioFormatiLBC = 'ilbc', kAudioFormatDVIIntelIMA = 0x6D730011, kAudioFormatMicrosoftGSM = 0x6D730031, kAudioFormatAES3 = 'aes3', kAudioFormatEnhancedAC3 = 'ec-3'};
经过大量的研究表明,人的眼睛对亮度非常敏感,但是对色彩信息比较迟钝。色彩二次抽样是指在原有的图片样本中,二次抽样,减少对色彩信息的抽样,而达到减小图片体积(样本数减小),又不怎么影响图片质量(眼睛对色彩不敏感)的效果。
视频数据使用的是YCbCr的颜色模型,YCbCr也称为YUV。其中Y表示亮度分量,Cb表示蓝色分量,Cr表示红色分量。
常用的YCbCr有4:4:4、4:2:2、4:2:0、4:1:1。
4:4:4 亮度和色彩信息的比值为1:1,所有色彩信息都会被抽样,也就是全彩。
4:2:2 亮度和色彩信息的比值为2:1,只有1/2的色彩信息被抽样
4:2:0和4:1:1 亮度和色彩信息比值为4:1,只有1/4的色彩信息被抽样
对应到AVFoundation的CVImageBuffer.h
中有:
CV_EXPORT const CFStringRef CV_NONNULL kCVImageBufferChromaSubsamplingKey __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_4_0); // CFString/CFNumber with one of the following valuesCV_EXPORT const CFStringRef CV_NONNULL kCVImageBufferChromaSubsampling_420 __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_4_0); CV_EXPORT const CFStringRef CV_NONNULL kCVImageBufferChromaSubsampling_422 __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_4_0); CV_EXPORT const CFStringRef CV_NONNULL kCVImageBufferChromaSubsampling_411 __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_4_0);
一些特别专业的相机可能会用到4:4:4参数捕捉图片,大部分情况下会用4:2:2参数,普通的摄像头(如:iPhone摄像头)一般都是4:2:0、4:1:1
跟音频文件相比,视频文件体积更大。我们平时使用的视频文件都是经过压缩处理的。大部分音视频都使用编解码器(codec)来进行压缩和解压,iOS中使用的主要编解码器标准为H.264,H.264对应AVFoundation的AVVideoSettings.h
中:
AVF_EXPORT NSString *const AVVideoCodecKey /* NSString (CMVideoCodecType) */ NS_AVAILABLE(10_7, 4_0); AVF_EXPORT NSString *const AVVideoCodecH264 /* @"avc1" */ NS_AVAILABLE(10_7, 4_0);
H.264通过空间和时间2个维度来压缩体积:
空间:空间上的压缩独立于视频帧,也称为帧内压缩。主要通过压缩图片的方式(如色彩二次抽样等)来减小体积,帧内压缩一般为有损压缩
时间:时间上的压缩也称为帧间压缩。视频是由连续的帧(图片)组成。一段视频中(很多张图片)有很多不变的冗余元素,通过减小这些冗余元素来达到压缩的目的,这就是帧间压缩,帧间压缩一般为无损压缩
H.264有3种profile,用于确定编码过程中帧间压缩使用的算法:
BaseLine:这个标准提供了最低效的压缩,经过这个标准压缩后的文件体积任然比较大,但是这种算法计算强度最小
Main:这个标准的计算强度比BaseLine大,但是能达到更高的压缩效果
High:这个标准能达到最高质量的压缩效果,但他的压缩算法最复杂
H.264除了profile还有一个等级(Level)标准,Level是对自身特性的一些描述(码率,分辨率,fps等),Level越高,视频的码率、分辨率、fps越高。
对应AVfoundation的AVVideoSettings.h
中:
AVF_EXPORT NSString *const AVVideoProfileLevelKey /* NSString, H.264 only, one of: */ NS_AVAILABLE(10_8, 4_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Baseline30 /* Baseline Profile Level 3.0 */ NS_AVAILABLE(10_8, 4_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Baseline31 /* Baseline Profile Level 3.1 */ NS_AVAILABLE(10_8, 4_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Baseline41 /* Baseline Profile Level 4.1 */ NS_AVAILABLE(10_8, 5_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264BaselineAutoLevel /* Baseline Profile Auto Level */ NS_AVAILABLE(10_9, 7_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Main30 /* Main Profile Level 3.0 */ NS_AVAILABLE(10_8, 4_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Main31 /* Main Profile Level 3.1 */ NS_AVAILABLE(10_8, 4_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Main32 /* Main Profile Level 3.2 */ NS_AVAILABLE(10_8, 5_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264Main41 /* Main Profile Level 4.1 */ NS_AVAILABLE(10_8, 5_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264MainAutoLevel /* Main Profile Auto Level */ NS_AVAILABLE(10_9, 7_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264High40 /* High Profile Level 4.0 */ NS_AVAILABLE(10_9, 6_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264High41 /* High Profile Level 4.1 */ NS_AVAILABLE(10_9, 6_0); AVF_EXPORT NSString *const AVVideoProfileLevelH264HighAutoLevel /* High Profile Auto Level */ NS_AVAILABLE(10_9, 7_0);
一些移动设备(手机、游戏机、PMP)由于性能有限,不支持全部高级视频压缩特性和高分辨率图像,只支持基础压缩特性和分辨率低一些的图像。
码率是指单位时间内传输的数据位数。视频体积=视频码率*时间
,由此可见固定长度的视频,码率是决定大小的唯一因素。视频码率决定着视频的压缩效果,也决定了视频是质量。码率越高,质量越好,体积越大。码率越低,体积越小,视频质量也越差。
编码时,根据码率可以分为固定码率(CBR)编码和可变码率(VBR)编码。
固定码率:指编码器输出的码率一直为一个固定值,这种编码方式计算量小,编码速度快,但是编码效果不怎么好。对于画面变化大的视频片段,由于码率限制,导致视频非常模糊,然而对于画面变化小的片段,却会造成码率浪费。
可变码率:指编码器的输出码率可以根据编码器的输入源信号的复杂度自适应的调整,这种方式编码效果比较好
码率对应于AVFoundation中AVVideoSettings.h
:
AVF_EXPORT NSString *const AVVideoAverageBitRateKey /* NSNumber (bits per second, H.264 only) */ NS_AVAILABLE(10_7, 4_0);
AVFoundation是一个可以用来使用和创建基于时间的视听媒体的框架,它提供了一个能使用基于时间的视听数据的详细级别的Objective-C接口。例如:您可以用它来检查,创建,编辑或是重新编码媒体文件。也可以从设备中获取输入流,在视频实时播放时操作和回放。下图时AVFoundation在IOS中的架构。
AVFoundation在IOS中的位置
AVFoundation在OS X中的位置
在使用时,应尽量使用最高级别的抽象的控件:
1.如果只是想播放视频,应该使用AVKit框架。
2.在IOS中,如果只是想录制视频,可以使用UIKit框架(UIImagePickerController).
但是,值得注意的是,一些用在AV Foundation-including的基于时间数据结构的原始数据结构和带有未公开的媒体数据描述的部分是声明在Core Media框架的。
1.Using Assets [自己理解为:数据的来源]
这个资源可以来自自己的ipod媒体库或图片也可以时文件
创建一个Assets
SRUL *url = //后跟一个网址 如电影文件等资源 AVURLAsset *ansset = [[AVURLSset alloc] initwithURL:url options:nil];
2.获得一个视频的图像
使用AVASsetImageGenerator类来实现
用来生成图像序列
3.Playback
我们在播放视频时可以使用AVPlayer和AVQueuePlayer播放,AVPlayer是AVQueuePlayer的父类
a.先创建一个路径
b.可以使用AVPlayerItem加载路径
c.使用AVPlayer播放文件
当然我们还可以控制它的播放速度
使用rate属性它是一个介于0.0--1.0之间的数
我们也可以播放多个项目 :
NSArray *items = // 设置一个播放的组合 AVQueuePlayer *queueplayer = [[AVQueuePlayer alloc]initwithItems:items]; //然后使用AVPlayerItem AVPlayerItem *anItem = // get a player item //使用canInsertItem:afterItem 测试
4.Media capture
我们可以配置预设图片的质量和分辨率
AVCaptureSessionPresetHigh High 最高的录制质量,每台设备不同
AVCaptureSessionPresetMedium Medium 基于无线分享的,实际值可能会改变
AVCaptureSessionPresetLow LOW 基于3g分享的
AVCaptureSessionPreset640x480 640x480 VGA
AVCaptureSessionPreset1280x720 1280x720 720p HD
AVCaptureSessionPresetPhoto Photo 完整的照片分辨率,不支持视频输出
判断一个设备是否适用 :
AVCaptreSessuion *session = [[AVCaptureSession alloc]init]; if([session canSetSessionPreset:AVCaptureSessionPrese 1280x720]){ session.sessionPreset = AVCaptureSessionPreset 1280x720; }else{ // Handle the failure. }
可以在 [session beginConfigration], [session commit configuration]中配置重新添加你想要适用的设备以及删除以前的设备等操作 (详解在6)。
5.当我们不知道设备的一些特性时我们可以使用以下代码查找相应的设备
NSArray *devices = [AVCaptureDevice devices]; for(AVCaptureDevice *device in device){ NSLog("Device name %@",[devic localizedName]); //当然还可以判断设备的位置 if([device hasMediaType:AVMediaTypeVideo]){ if([device postion] == AVCaptureDevicePostionBack){ NSLog(@"Device postion :back"); }else{ NSLog(@"Device postion :front"); } } }
下面的demo说明如何找到视频输入设备 :
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; NSMutableArray *torchDevices = [[NSMutableArray alloc]init]; for(AVCaptureDevice *device in devices){ if([device hasTorch]&&[device supportsAVCaptureSessionPreset:AVCaptureSessionPreset 640x480]){ [torchDevices addObject:device]; } }
6设备间切换
AVCaptureSession *session = //一个设备session [session beginConfiguration]; [session removeInput:frontFacingCameraDeviceInput]; [session AddInput:backFacikngCameraDeviceInput]; [session commitConfiguration];
7 配置AVCaptureDeviceInput
AVCaptureSession *captureSession = <#Get a capture session#>; AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>; // 检查是否适用 if ([captureSession canAddInput:captureDeviceInput]) { // 适用则添加 [captureSession addInput:captureDeviceInput]; } else { // Handle the failure. }
8 配置AVCaptureOutput
输出的类型:
a.AVCaptureMovieFileOutput 输出一个电影文件
b.AVCaptureVideoDataOutput 输出处理视频帧被捕获
c.AVCaptureAudioDataOutput 输出音频数据被捕获
d.AVCaptureStillImageOutput 捕获元数据
AVCaptureSession *captureSession = <#Get a capture session#>; AVCaptureMovieFileOutput *movieInput = <#Create and configure a movie output#>; if ([captureSession canAddOutput:movieInput]) { [captureSession addOutput:movieInput]; } else { // Handle the failure. }
9 保存到一个电影文件
AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; CMTime maxDuration = <#Create a CMTime to represent the maximum duration#>; aMovieFileOutput.maxRecordedDuration = maxDuration; aMovieFileOutput.minFreeDiskSpaceLimit = <#An appropriate minimum given the quality of the movie format and the duration#>;
10 录音设备
使用AVCaptureFileOutputRecordingDelegate代理而且必须实现方法:
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: method.
11 像素和编码格式
iphone 3G iphone 3GS iphone 4
yuvs,2vuy,BGRA,jpeg 420f,420v,BGRA,jpeg 420f, 420v, BGRA, jpeg
12 静态图像捕捉
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil]; [stillImageOutput setOutputSettings:outputSettings];
13 填充模式
支持使用videoGravity 设置三种模式:
● AVLayerVideoGravityResizeAspect:保留长宽比,未填充部分会有黑边
● AVLayerVideoGravityResizeAspectFill:保留长宽比,填充所有的区域
● AVLayerVideoGravityResize:拉伸填满所有的空间
设备之间切换:
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position { NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for ( AVCaptureDevice *device in devices ) { if ( device.position == position ) return device; return nil; } - (void)swapFrontAndBackCameras { // 确保session已经在使用了NSArray *inputs = self.session.inputs; for ( AVCaptureDeviceInput *input in inputs ) { AVCaptureDevice *device = input.device; if ( [device hasMediaType:AVMediaTypeVideo] ) { AVCaptureDevicePosition position = device.position; AVCaptureDevice *newCamera = nil; AVCaptureDeviceInput *newInput = nil; if (position == AVCaptureDevicePositionFront) {newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack]; } else {newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront]; } newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil]; // beginConfiguration 确保更改未被立即使用 [self.session beginConfiguration]; [self.session removeInput:input]; [self.session addInput:newInput]; // 更改立即实现 [self.session commitConfiguration]; break; } } }
初步了解了AVFoundation框架,那么我们一般用来做什么呢?
一个方向是可以用它来扫描二维码。参考文档:使用系统原生代码处理QRCode,想要看懂参考中的代码,不得不了解些AVFoundation的使用啊。
1.session
AVFoundation是基于session(会话)概念的。 一个session用于控制数据从input设备到output设备的流向。
声明一个session:
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session允许定义音频视频录制的质量。
[session setSessionPreset:AVCaptureSessionPresetLow];
2.capture device
定义好session后,就该定义session所使用的设备了。(使用AVMediaTypeVideo 来支持视频和图片)
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
3.capture device input
有了capture device, 然后就获取其input capture device, 并将该input device加到session上。
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];if ( [session canAddInput:deviceInput] ) [session addInput:deviceInput];
4.preview
在定义output device之前,我们可以先使用preview layer来显示一下camera buffer中的内容。这也将是相机的“取景器”。
AVCaptureVideoPreviewLayer可以用来快速呈现相机(摄像头)所收集到的原始数据。
我们使用第一步中定义的session来创建preview layer, 并将其添加到main view layer上。
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];CALayer *rootLayer = [[self view] layer]; [rootLayer setMasksToBounds:YES]; [previewLayer setFrame:CGRectMake(-70, 0, rootLayer.bounds.size.height, rootLayer.bounds.size.height)]; [rootLayer insertSublayer:previewLayer atIndex:0];
5.start Run
最后需要start the session.
[session startRunning];