自助下单地址(拼多多砍价,ks/qq/dy赞等业务):点我进入
本文检查以下函数:
录制视频需要真机测试!
效果图
视频录制
首先我们弹出系统的视频录制界面,由UIImagePickerController控制器实现,但是需要验证用户权限。 只有获得录制视频的权限,我们才能继续。
我们还需要判断 UIImagePickerControllerSourceTypeCamera 是否支持。 比如模拟器不支持。 当然真机不支持我们不知道,但是这样写还是比较稳妥的。 视频录制可以通过videoQuality属性设置录制视频的质量,即码率。 我们还可以通过videoMaximumDuration属性设置来设置录制视频的最大时长,比如这里设置为5分钟。
// 7.0
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted
|| authStatus == AVAuthorizationStatusDenied) {
NSLog(@"摄像头已被禁用,您可在设置应用程序中进行开启");
return;
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.videoQuality = UIImagePickerControllerQualityType640x480; //录像质量
picker.videoMaximumDuration = 5 * 60.0f; // 限制视频录制最多不超过5分钟
picker.mediaTypes = @[(NSString *)kUTTypeMovie];
[self presentViewController:picker animated:YES completion:NULL];
self.shouldAsync = YES;
} else {
NSLog(@"手机不支持摄像");
}
然后实现代理,就可以拿到录制的视频了。
从相机胶卷中选择视频
从相册中选择视频与弹出录制视频的代码类似,只是sourceType不同。 我们还要求先判断权限,用户是否被授权,如果没有,就没办法了。
指定 sourceType 为 UIImagePickerControllerSourceTypeSavedPhotosAlbum 是为了获取相册中保存的媒体。 我们还需要指定mediaTypes,设置为kUTTypeMovie即可。
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted
|| authStatus == AVAuthorizationStatusDenied) {
NSLog(@"摄像头已被禁用,您可在设置应用程序中进行开启");
return;
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
picker.mediaTypes = @[(NSString *)kUTTypeMovie];
[self presentViewController:picker animated:YES completion: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 {
// result
UIImage *image = nil;
// AVAssetImageGenerator
AVAsset *asset = [AVAsset assetWithURL:videoURL];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
// calculate the midpoint time of video
Float64 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 systems
CMTime midpoint = CMTimeMakeWithSeconds(duration / 2.0, 600);
// get the image from
NSError *error = nil;
CMTime actualTime;
// Returns a CFRetained CGImageRef for an asset at or near the specified time.
// So we should mannully release it
CGImageRef centerFrameImage = [imageGenerator copyCGImageAtTime:midpoint
actualTime:&actualTime
error:&error];
if (centerFrameImage != NULL) {
image = [[UIImage alloc] initWithCGImage:centerFrameImage];
// Release the CFRetained image
CGImageRelease(centerFrameImage);
}
return image;
}
异步获取帧图像
异步获取某一帧的图片,与同步相比,只是调用API不同,可以传递多个时间点,然后估计实际时间返回图片,但是返回的图片不需要我们自动释放再次。 有可能获取不到图片,所以需要判断是否是AVAssetImageGeneratorSucceeded,然后进行图片转换:
// 异步获取帧图片,可以一次获取多帧图片
- (void)centerFrameImageWithVideoURL:(NSURL *)videoURL completion:(void (^)(UIImage *image))completion {
// AVAssetImageGenerator
AVAsset *asset = [AVAsset assetWithURL:videoURL];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
// calculate the midpoint time of video
Float64 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 systems
CMTime midpoint = CMTimeMakeWithSeconds(duration / 2.0, 600);
// 异步获取多帧图片
NSValue *midTime = [NSValue valueWithCMTime:midpoint];
[imageGenerator generateCGImagesAsynchronouslyForTimes:@[midTime] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
if (result == AVAssetImageGeneratorSucceeded && image != NULL) {
UIImage *centerFrameImage = [[UIImage alloc] initWithCGImage:image];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(centerFrameImage);
}
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(nil);
}
});
}
}];
}
压缩和导入视频
压缩视频是因为高视频码率生成的视频体积太大。 对于联通设备cf保存的录像怎么上传,内存不宜过大。 如果不支持分片上传到服务器,或者不支持流上传或者文件上传,但是只能支持表单上传,那么一定要限制大小,压缩视频。
就像我们在某平台上传视频一样,到现在还不支持流式上传和文件上传,只支持表单上传,过大会导致视频闪退。 stream upload 表示上传成功,但是后台不识别。 这一次,某平台坏了。 直接带文件上传,通过了,上传进度100%,但还是被当做失败,无奈!
言归正传,压缩导出视频需要通过AVAssetExportSession来实现。 我们需要指定一个preset,判断是否支持这个preset。 只有支持才能使用。
我们这里设置的preset是AVAssetExportPreset640x480,压缩的比较多,需要根据服务器视频上传的支持程度来选择。 然后通过调用异步压缩和导入视频:
- (void)compressVideoWithVideoURL:(NSURL *)videoURL
savedName:(NSString *)savedName
completion:(void (^)(NSString *savedPath))completion {
// Accessing video by URL
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
// Find compatible presets by video asset.
NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset: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 = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPreset640x480];
NSString *doc = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
NSString *folder = [doc stringByAppendingPathComponent:@"HYBVideos"];
BOOL isDir = NO;
BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:folder isDirectory:&isDir];
if (!isExist || (isExist && !isDir)) {
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:folder
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error == nil) {
NSLog(@"目录创建成功");
} else {
NSLog(@"目录创建失败");
}
}
NSString *outPutPath = [folder stringByAppendingPathComponent:savedName];
session.outputURL = [NSURL fileURLWithPath:outPutPath];
// Optimize for network use.
session.shouldOptimizeForNetworkUse = true;
NSArray *supportedTypeArray = session.supportedFileTypes;
if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
session.outputFileType = AVFileTypeMPEG4;
} else if (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.outputURL path]);
}
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(nil);
}
});
}
}];
}
}
解决iOS8上视频录制导致的歪斜bug
iOS8有这样一个bug:弹出录制视频页面,回去后发现整个view是向下连的。 网上可能有很多解决方案,下面只是其中的一种:
[picker dismissViewControllerAnimated:YES completion:^{
// for fixing iOS 8.0 problem that frame changed when open camera to record video.
self.tabBarController.view.frame = [[UIScreen mainScreen] bounds];
[self.tabBarController.view layoutIfNeeded];
}];
提示:记得在选中或取消的代理中调用!
概括
每一个需求,你都可能会遇到坑,但再多的坑也抵不过对技术的执着追求,必须攻克所有的坑。 以前不知道怎么满足视频的需求,别人超越的时候,即使有坑,也不会告诉后来的人坑在哪里,他们往往只记得有坑他们的心。
今天分享给大家cf保存的录像怎么上传,帮助有困难的战友。 这里是一个标志:坑,请不要再跳坑了。 看完这篇文章,你是不是明白了什么? 如果你是做这个需求的,代码可以直接复制使用!
源代码
下载源码,记得star分享一下:
表哥的技术博客:【VideoCaptureDemo】