iOS视频录制、压缩导出、取帧
概述
花了点时间研究了一下常用的视频获取、录制、压缩、取帧图功能,分享给大家了!相信阅读完本篇文章,会对你有很大的帮助的!
本篇文章研究几下以个功能:
需要真机测试,才能录制视频!
效果图

视频录制
首先,我们弹出系统的视频录制界面,也就是UIImagePickerController控制器来实现,但是我们需要验证用户授权,只有有录制视频的权限,才能继续往下。
我们还需要判断UIImagePickerControllerSourceTypeCamera是否支持,比如模拟器就不支持,当然真机是否有不 支持的并不知道,不过更安全的写法是要这么写的。视频录制可以设置录制的视频的质量,也就是分辨率的高低,通过videoQuality属性来设置。我们 还可以设置录制视频的最大时长,通过videoMaximumDuration属性设置,比如这里设置为5分钟。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 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就是获取保存到相册中的media。我们还要指定mediaTypes,只需要设置为kUTTypeMovie就可以了。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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图片生成器来生成某个帧图片:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
// 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不同,可以传多个时间点,然后计算出实际的时间并返回图片,但是返回的图片不需要我们手动再 release了。有可能取不到图片,所以还需要判断是否是AVAssetImageGeneratorSucceeded,是才转换图片:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// 异步获取帧图片,可以一次获取多帧图片
- (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);
}
});
}
}];
}
|
压缩并导出视频
压缩视频是因为视频分辨率过高所生成的视频的大小太大了,对于移动设备来说,内存是不能太大的,如果不支持分片上传到服务器,或者不支持流上传、文件上传,而只能支持表单上传,那么必须要限制大小,压缩视频。
就像我们在使用某平台的视频的上传的时候,到现在还没有支持流上传,也不支持文件上传,只支持表单上传,导致视频大一点就会闪退。流上传是上传成功 了,但是人家后台不识别,这一次让某平台坑坏了。直接用file上传,也传过去了,上传进度100%了,但是人家那边还是作为失败处理,无奈!
言归正传,压缩、导出视频,需要通过AVAssetExportSession来实现,我们需要指定一个preset,并判断是否支持这个preset,只有支持才能使用。
我们这里设置的preset为AVAssetExportPreset640x480,属于压缩得比较厉害的了,这需要根据服务器视频上传的支持程度而选择的。然后通过调用异步压缩并导出视频:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
- (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都往下移动了,可能网上有很多解决办法,下面只是其中一种:
|
1
2
3
4
5
6
7
|
[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];
}];
|
Tip:记得在选择或者取消的代理中都调用!
小结
做每种需求,都可能会遇到坑,不过再多的坑也抵不过一颗对技术执着追求的心,必定荡平一切的坑。以前也没有怎么弄过视频类的需求,而别人超过的路,即使有坑也不会告诉后来的人坑在哪里,往往只是心里记着有个坑就算了。
今天给大家分享出来,是帮助有困难的同志们,这里立了一个牌:坑,请大家不要再跳到坑里了。看完本篇文章,是否有所了解了呢?如果您正在做这方面的需求,代码完全可以直接Copy过去使用哦!
源代码
下载源代码,记得star一下,分享出去:
标哥的技术博客:【VideoCaptureDemo】
iOS视频录制、压缩导出、取帧的更多相关文章
- iOS视频录制、压缩导出、取帧等http://www.jianshu.com/p/6f23f608048e
原文网址请参考:http://www.jianshu.com/p/6f23f608048e
- iOS 视频录制、压缩、上传
项目中实现功能 视频的录制.压缩.上传 首先调用系统的相机或相册 iOS录制的视频是mov格式的,安卓和PC不支持,因此要转换成MP4,并且要压缩. 获取到视频或者照片,处理的方法 下面两个方法是获取 ...
- iOS 视频选择压缩
//原理,还是调用UIImagePickerController控制器,设置Type为视频 #import "ViewController.h" #import <AVFou ...
- iOS视频录制裁剪合成
网址链接: 视频裁剪合并:http://blog.sina.com.cn/s/blog_64ea868501018jx3.html 视频之定义裁剪高宽度:http://www.cocoachina.c ...
- ios 视频/图片压缩
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...
- iOS视频录制,裁剪(输出指定大小)
弄了几天,终于搞懂一点了,博客后面再写,先附上demo,等研究清楚再写 demo地址:http://pan.baidu.com/s/1o6w2CMu
- iOS视频开发经验
iOS视频开发经验 手机比PC的优势除了便携外,我认为最重要的就是可以快速方便的创作多媒体作品.照片分享,语音输入,视频录制,地理位置.一个成功的手机APP从产品形态上都有这其中的一项或多项,比如in ...
- iOS - 视频开发
视频实质: 纯粹的视频(不包括音频)实质上就是一组帧图片,经过视频编码成为视频(video)文件再把音频(audio)文件有些还有字幕文件组装在一起成为我们看到的视频(movie)文件.1秒内出现的图 ...
- iOS开发系列--音频播放、录音、视频播放、拍照、视频录制
--iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制, ...
随机推荐
- 你必须知道的.NET
作者博客地址:http://www.cnblogs.com/anytao/archive/2008/04/09/anytao_insidenet_center.html 第1章 OO大智慧 1.1对象 ...
- 修改UI中的值,无反应解决办法
var targetObj = $("<input name='mydate' class='easyui-datebox'>").appendTo("#id ...
- 22 高级SQL特性
1.约束 为正确地进行关系数据库设计,需要一种方法来保证只在表中插入合法的数据.例如,如果Orders表存储订单信息,OrderItems表存储订单详细内容,应该保证Orderitems中引用的任何订 ...
- JS对Json对象Distinct
Json对象去重 今日有一个需求如下: 从数据库中取出数据源转化成json字符串绑定到隐藏域中,取出的json字符串如下: string data="[{"CompanyName& ...
- 【quartz】 各数据库创建脚本
QRTZ_CALENDARS 以 Blob 类型存储 Quartz 的 Calendar 信息 QRTZ_CRON_TRIGGERS 存储 Cron Trigger,包括 Cron表达式和时区信息 ...
- win2008修改最大远程桌面连接数
win2008修改最大远程桌面连接数 运行——gredit.msc——管理模板——windows组件——远程桌面服务——远程桌面回话主机——连接——限制连接的数量——修改为999999
- (ACM)C++ STL 训练(第一天)
因为老师说ACM考的是纯C++,所以打算抛弃VS的VC++不用了,针对纯C++的编译器有Intel Compiler(不过要钱),MinGw(个人用的),当然还有微软的VC++ 编译器,IDE你们可以 ...
- Valid format values for declare-styleable/attr tags[转]
http://chaosinmotion.com/blog/?p=179 reference string color dimension boolean integer float fraction ...
- Ubuntu下安装配置zsh和oh my zsh
zsh优势:自动补全功能强大和很高的可配置性 1.查看当前系统装了哪些shell cat /etc/shells 2.当前正在运行的是哪个版本的shell echo $SHELL 3.安装 ...
- WCF 之 DataContract
在客户端与服务端之间传递的自定义数据类型,格式如下: [DataContract] public class User :IExtensibleDataObject { [DataMember] pu ...