【iOS】视频的自定义拍摄与压缩
2018-03-29 本文已影响204人
取名有丶难
前言
本篇还是将之前研究过用过的知识做一个梳理与总结。开发过程总体来说是一个快速解决功能需求的过程,在有限的时间内将侧重点放在实现具体功能上,而在此过程中可能对于遇到的问题并没有时间去做进一步的思考的总结,于是就有了事后的总结。
运行效果
话不多说,有图有真相
运行效果.gif基本结构
很简单的结构,如图所示
结构.png
过程
拍摄
既然是自定义视频拍摄,那么当然用到的是AVFoundation这个框架的东西,需要用到框架提供的几个类包含有
AVCaptureSession
AVCaptureDevice
AVCaptureVideoPreviewLayer
AVCaptureDeviceInput
AVCaptureConnection
AVCaptureVideoDataOutput
AVCaptureAudioDataOutput
AVAssetWriter
AVAssetWriterInput
其实就是session会话管理器,这是个中枢管理类,然后有摄像头设备的抽象类,视频流数据的输入输出类和连接类,预览的Layer类,最后就是写视频文件的相关类。其实这一部分已经有很多人总结过了,用法什么的也有很多人写了相关内容,我就不多说了。具体的用法可看这位老哥的总结:传送门在此
压缩
事实上拍摄和压缩是同步进行的,如果拍完再压缩那就没什么意义了。说白了压缩就是对视频写入的数据在写入之前先处理一下,由于写入是子线程中的异步任务也不会阻塞UI。直接上代码:
//设置写入视频属性,在这里进行压缩
- (void)setUpWriterSettings
{
NSError *error = nil;
self.assetWriter = [AVAssetWriter assetWriterWithURL:self.writeVideoUrl fileType:AVFileTypeMPEG4 error:&error];
//写入视频大小
NSInteger numPixels = self.videoSize.width * self.videoSize.height;
//每像素比特
CGFloat bitsPerPixel = 6.0;
NSInteger bitsPerSecond = numPixels * bitsPerPixel;
// 码率和帧率设置
NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
AVVideoExpectedSourceFrameRateKey : @(30),
AVVideoMaxKeyFrameIntervalKey : @(30),
AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel };
//视频属性
self.videoCompressionSettings = @{ AVVideoCodecKey : AVVideoCodecTypeH264,
AVVideoScalingModeKey : AVVideoScalingModeResizeAspectFill,
AVVideoWidthKey : @(self.videoSize.width*2),
AVVideoHeightKey : @(self.videoSize.height*2),
AVVideoCompressionPropertiesKey : compressionProperties };
_assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:self.videoCompressionSettings];
//expectsMediaDataInRealTime 必须设为yes,需要从capture session 实时获取数据
_assetWriterVideoInput.expectsMediaDataInRealTime = YES;
_assetWriterVideoInput.transform = CGAffineTransformMakeTranslation(M_PI_2, M_PI_2);
_assetWriterVideoInput.transform = CGAffineTransformScale(_assetWriterVideoInput.transform, -1, 1);
// 音频设置
self.audioCompressionSettings = @{ AVEncoderBitRatePerChannelKey : @(28000),
AVFormatIDKey : @(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey : @(1),
AVSampleRateKey : @(22050) };
_assetWriterAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioCompressionSettings];
_assetWriterAudioInput.expectsMediaDataInRealTime = YES;
if ([_assetWriter canAddInput:_assetWriterVideoInput]) {
[_assetWriter addInput:_assetWriterVideoInput];
}else {
NSLog(@"AssetWriter error");
[self resetAsset];
}
if ([_assetWriter canAddInput:_assetWriterAudioInput]) {
[_assetWriter addInput:_assetWriterAudioInput];
}else {
NSLog(@"AssetWriter error");
[self resetAsset];
}
}
在上面的代码中对视频质量相关的属性(帧率,码率,像素)进行设置,这些属性的组合设置既要保持视频的流畅,在清晰度上也要有一定的保证,毕竟不能压缩的太过分,如果都最后出来的视频都看不清了那就有点扯蛋了。因此在这些属性的值的设置上需要一定的考究,以便在同等压缩率下保持相对最好的清晰度。上面的相关参数设置之后在 AVCaptureSessionPresetHigh 高质量录制下会有10倍左右的压缩倍率,而且清晰度还保留的相当不错。如果大家还有更高效率的参数设置方案也欢迎一起学习讨论。
一些小坑
由于这个功能会动用手机的硬件设备,在初始化录制管理器的时候会实例化很多个的AVFoundation提供的相关类的对象,这个过程据我测试会有一个等待的时间,根据手机性能的不同最低也会有将近一秒的等待。因此建议将这些类的实例化放在异步进行,不要阻塞UI,这样用户体验也会好一点,我在demo中也是这样做的。
//在异步加载录制管理器对象,然后刷新视图
dispatch_async(dispatch_get_main_queue(), ^{
_videoRecord = [[CWVideoRecord alloc]initWithPreset:AVCaptureSessionPresetHigh writePath:NSTemporaryDirectory()];
_videoRecord.delegate = self;
//展示预览层
[_videoRecord displayRecordLayer:_videoView];
//刷新视图
[_videoView reloadRecord:CWRecordReady];
});
事实上系统的相机也应该是这个做法,系统相机点开时会先进入控制器视图,此时会在黑屏状态下等待一段时间才会出来预览界面。
在视频写入的过程中全程都是在子线程中执行,此时要注意在写入和完成写入的代码上加上线程锁,否则会崩到怀疑人生,另外在写入开启时
[self.assetWriter startWriting];