视频录制,压缩导出,取帧

视频录制

首先,我们弹出系统的视频录制界面,也就是UIImagePickerController控制器来实现,但是我们需要验证用户授权,只有有录制视频的权限,才能继续往下。

我们还需要判断UIImagePickerControllerSourceTypeCamera是否支持,比如模拟器就不支持,当然真机是否有不支持的并不知道,不过更安全的写法是要这么写的。视频录制可以设置录制的视频的质量,也就是分辨率的高低,通过videoQuality属性来设置。我们还可以设置录制视频的最大时长,通过videoMaximumDuration属性设置,比如这里设置为5分钟。

// 7.0AVAuthorizationStatusauthStatus = [AVCaptureDeviceauthorizationStatusForMediaType:AVMediaTypeVideo];if(authStatus ==AVAuthorizationStatusRestricted|| authStatus ==AVAuthorizationStatusDenied) {NSLog(@"摄像头已被禁用,您可在设置应用程序中进行开启");return;}if([UIImagePickerControllerisSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {UIImagePickerController*picker = [[UIImagePickerControlleralloc] init];  picker.delegate=self;  picker.allowsEditing=YES;  picker.sourceType=UIImagePickerControllerSourceTypeCamera;  picker.videoQuality=UIImagePickerControllerQualityType640x480;//录像质量picker.videoMaximumDuration=5*60.0f;// 限制视频录制最多不超过5分钟picker.mediaTypes= @[(NSString*)kUTTypeMovie];  [selfpresentViewController:picker animated:YEScompletion:NULL];self.shouldAsync=YES;}else{NSLog(@"手机不支持摄像");}

然后实现代理,就可以拿到录制的视频了。

从相册选择视频

从相册选择视频与弹出录制视频的代码差不多,只是sourceType不一样而已。我们一样要求先判断权限,用户是否授权,若不允许,就没有办法了。

指定sourceType为UIImagePickerControllerSourceTypeSavedPhotosAlbum就是获取保存到相册中的media。我们还要指定mediaTypes,只需要设置为kUTTypeMovie就可以了。

AVAuthorizationStatusauthStatus = [AVCaptureDeviceauthorizationStatusForMediaType:AVMediaTypeVideo];if(authStatus ==AVAuthorizationStatusRestricted|| authStatus ==AVAuthorizationStatusDenied) {NSLog(@"摄像头已被禁用,您可在设置应用程序中进行开启");return;}if([UIImagePickerControllerisSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {UIImagePickerController*picker = [[UIImagePickerControlleralloc] init];  picker.delegate=self;  picker.allowsEditing=YES;  picker.sourceType=UIImagePickerControllerSourceTypeSavedPhotosAlbum;  picker.mediaTypes= @[(NSString*)kUTTypeMovie];  [selfpresentViewController:picker animated:YEScompletion:NULL];self.shouldAsync=NO;}else{NSLog(@"手机不支持摄像");}

同样,实现代理方法,就可以取到所选择的视频了。

保存视频到相册

写入相册可以通过ALAssetsLibrary类来实现,它提供了写入相册的API,异步写入,完成是要回到主线程更新UI:

NSURL*videoURL = [info objectForKey:UIImagePickerControllerMediaURL];ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];dispatch_async(dispatch_get_global_queue(0,0), ^{// 判断相册是否兼容视频,兼容才能保存到相册if([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {    [library writeVideoAtPathToSavedPhotosAlbum:videoURL completionBlock:^(NSURL*assetURL,NSError*error) {dispatch_async(dispatch_get_main_queue(), ^{// 写入相册if(error ==nil) {NSLog(@"写入相册成功");        }else{NSLog(@"写入相册失败");        }      }    }];  }});

获取视频帧图

同步获取帧图

同步获取中间帧,需要指定哪个时间点的帧,当获取到以后,返回来的图片对象是CFRetained过的,需要外面手动CGImageRelease一下,释放内存。通过AVAsset来访问具体的视频资源,然后通过AVAssetImageGenerator图片生成器来生成某个帧图片:

// Get the video's center frame as video poster image- (UIImage*)frameImageFromVideoURL:(NSURL*)videoURL {// resultUIImage*image =nil;// AVAssetImageGeneratorAVAsset*asset = [AVAssetassetWithURL:videoURL];AVAssetImageGenerator*imageGenerator = [[AVAssetImageGeneratoralloc] initWithAsset:asset];  imageGenerator.appliesPreferredTrackTransform=YES;// calculate the midpoint time of videoFloat64 duration = CMTimeGetSeconds([asset duration]);// 取某个帧的时间,参数一表示哪个时间(秒),参数二表示每秒多少帧// 通常来说,600是一个常用的公共参数,苹果有说明:// 24 frames per second (fps) for film, 30 fps for NTSC (used for TV in North America and// Japan), and 25 fps for PAL (used for TV in Europe).// Using a timescale of 600, you can exactly represent any number of frames in these systemsCMTime midpoint = CMTimeMakeWithSeconds(duration /2.0,600);// get the image fromNSError*error =nil;  CMTime actualTime;// Returns a CFRetained CGImageRef for an asset at or near the specified time.// So we should mannully release itCGImageRefcenterFrameImage = [imageGeneratorcopyCGImageAtTime:midpoint                                                      actualTime:&actualTime                                                            error:&error];if(centerFrameImage !=NULL) {    image = [[UIImagealloc] initWithCGImage:centerFrameImage];// Release the CFRetained imageCGImageRelease(centerFrameImage);  }returnimage;}

异步获取帧图

异步获取某个帧的图片,与同步相比,只是调用API不同,可以传多个时间点,然后计算出实际的时间并返回图片,但是返回的图片不需要我们手动再release了。有可能取不到图片,所以还需要判断是否是AVAssetImageGeneratorSucceeded,是才转换图片:

// 异步获取帧图片,可以一次获取多帧图片- (void)centerFrameImageWithVideoURL:(NSURL*)videoURL completion:(void(^)(UIImage*image))completion {// AVAssetImageGeneratorAVAsset*asset = [AVAssetassetWithURL:videoURL];AVAssetImageGenerator*imageGenerator = [[AVAssetImageGeneratoralloc] initWithAsset:asset];  imageGenerator.appliesPreferredTrackTransform=YES;// calculate the midpoint time of videoFloat64 duration = CMTimeGetSeconds([asset duration]);// 取某个帧的时间,参数一表示哪个时间(秒),参数二表示每秒多少帧// 通常来说,600是一个常用的公共参数,苹果有说明:// 24 frames per second (fps) for film, 30 fps for NTSC (used for TV in North America and// Japan), and 25 fps for PAL (used for TV in Europe).// Using a timescale of 600, you can exactly represent any number of frames in these systemsCMTime midpoint = CMTimeMakeWithSeconds(duration /2.0,600);// 异步获取多帧图片NSValue*midTime = [NSValuevalueWithCMTime:midpoint];  [imageGenerator generateCGImagesAsynchronouslyForTimes:@[midTime] completionHandler:^(CMTime requestedTime,CGImageRef_Nullable image, CMTime actualTime,AVAssetImageGeneratorResultresult,NSError* _Nullable error) {if(result ==AVAssetImageGeneratorSucceeded&& image !=NULL) {UIImage*centerFrameImage = [[UIImagealloc] initWithCGImage:image];dispatch_async(dispatch_get_main_queue(), ^{if(completion) {          completion(centerFrameImage);        }      });    }else{dispatch_async(dispatch_get_main_queue(), ^{if(completion) {          completion(nil);        }      });    }  }];}

压缩并导出视频

压缩视频是因为视频分辨率过高所生成的视频的大小太大了,对于移动设备来说,内存是不能太大的,如果不支持分片上传到服务器,或者不支持流上传、文件上传,而只能支持表单上传,那么必须要限制大小,压缩视频。

就像我们在使用某平台的视频的上传的时候,到现在还没有支持流上传,也不支持文件上传,只支持表单上传,导致视频大一点就会闪退。流上传是上传成功了,但是人家后台不识别,这一次让某平台坑坏了。直接用file上传,也传过去了,上传进度100%了,但是人家那边还是作为失败处理,无奈!

言归正传,压缩、导出视频,需要通过AVAssetExportSession来实现,我们需要指定一个preset,并判断是否支持这个preset,只有支持才能使用。

我们这里设置的preset为AVAssetExportPreset640x480,属于压缩得比较厉害的了,这需要根据服务器视频上传的支持程度而选择的。然后通过调用异步压缩并导出视频:

- (void)compressVideoWithVideoURL:(NSURL*)videoURL                        savedName:(NSString*)savedName                      completion:(void(^)(NSString*savedPath))completion {// Accessing video by URLAVURLAsset*videoAsset = [[AVURLAssetalloc] initWithURL:videoURL options:nil];// Find compatible presets by video asset.NSArray*presets = [AVAssetExportSessionexportPresetsCompatibleWithAsset:videoAsset];// Begin to compress video// Now we just compress to low resolution if it supports// If you need to upload to the server, but server does't support to upload by streaming,// You can compress the resolution to lower. Or you can support more higher resolution.if([presets containsObject:AVAssetExportPreset640x480]) {AVAssetExportSession*session = [[AVAssetExportSessionalloc] initWithAsset:videoAsset  presetName:AVAssetExportPreset640x480];NSString*doc = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];NSString*folder = [doc stringByAppendingPathComponent:@"HYBVideos"];BOOLisDir =NO;BOOLisExist = [[NSFileManagerdefaultManager] fileExistsAtPath:folder isDirectory:&isDir];if(!isExist || (isExist && !isDir)) {NSError*error =nil;      [[NSFileManagerdefaultManager] createDirectoryAtPath:folder                                withIntermediateDirectories:YESattributes:nilerror:&error];if(error ==nil) {NSLog(@"目录创建成功");      }else{NSLog(@"目录创建失败");      }    }NSString*outPutPath = [folder stringByAppendingPathComponent:savedName];    session.outputURL= [NSURLfileURLWithPath:outPutPath];// Optimize for network use.session.shouldOptimizeForNetworkUse=true;NSArray*supportedTypeArray = session.supportedFileTypes;if([supportedTypeArray containsObject:AVFileTypeMPEG4]) {      session.outputFileType=AVFileTypeMPEG4;    }elseif(supportedTypeArray.count==0) {NSLog(@"No supported file types");return;    }else{      session.outputFileType= [supportedTypeArray objectAtIndex:0];    }// Begin to export video to the output path asynchronously.[session exportAsynchronouslyWithCompletionHandler:^{if([session status] ==AVAssetExportSessionStatusCompleted) {dispatch_async(dispatch_get_main_queue(), ^{if(completion) {            completion([session.outputURLpath]);          }        });      }else{dispatch_async(dispatch_get_main_queue(), ^{if(completion) {            completion(nil);          }        });      }    }];  }}

解决iOS8上录视频引起的偏移bug

在iOS8上有这么一样bug:弹出录制视频页面,再回来发现整个view都往下移动了,可能网上有很多解决办法,下面只是其中一种:

[picker dismissViewControllerAnimated:YEScompletion:^{// for fixing iOS 8.0 problem that frame changed when open camera to record video.self.tabBarController.view.frame= [[UIScreenmainScreen] bounds];    [self.tabBarController.viewlayoutIfNeeded];}];

Tip:记得在选择或者取消的代理中都调用!

文/标哥的技术博客(简书作者)

原文链接:http://www.jianshu.com/p/6f23f608048e

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容