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

时间: 2024-08-10 17:01:40

iOS视频录制、压缩导出、取帧的相关文章

Android & IOS视频录制技术方案

屡次想经营一个技术博客,总因为各种理由推脱.这次下定决心开写,不为自我营销,不为扩大社交,只为了督促自己学习.近几个月定时更新两条线,一个是短视频处理技术,一个是<算法导论>笔记,也借机温故知新.精力有限而且学习也不能贪多,所以每周仅各一篇. 警告诸多网站:原创博客,未经本人允许不得转载. 移动端视频录制的技术方案,我所能想到并且尝试过的,有如下几种: 方案一: 用系统开发sdk录制的接口. 弊端: 1):不能更改视频比例,一般都有该手机屏幕分辨率所对应得视频录制分辨率,另外手机系统提供得分辨

iOS 视频录制、压缩、上传

项目中实现功能 视频的录制.压缩.上传 首先调用系统的相机或相册 iOS录制的视频是mov格式的,安卓和PC不支持,因此要转换成MP4,并且要压缩. 获取到视频或者照片,处理的方法 下面两个方法是获取视频文件的大小和时长 下面的方法是压缩视频文件 最后是上传文件,用的系统的NSUrlConnection,还没写完,调试完后续

iOS视频录制裁剪合成

网址链接: 视频裁剪合并:http://blog.sina.com.cn/s/blog_64ea868501018jx3.html 视频之定义裁剪高宽度:http://www.cocoachina.com/bbs/read.php?tid-270444.html 视频自定义录制代码:http://pan.baidu.com/s/1bndP9Sz

iOS视频开发经验

iOS视频开发经验 手机比PC的优势除了便携外,我认为最重要的就是可以快速方便的创作多媒体作品.照片分享,语音输入,视频录制,地理位置.一个成功的手机APP从产品形态上都有这其中的一项或多项,比如instagram,微信.如果把Web2.0的交互体验照搬到手机上就是死路一条. 当智能手机遇上视频就像潘金莲遇上西门庆,各取所需一拍即合,想不发生点事情都难.他们的结晶就是微视频.微视频可以说把手机的视频录制和碎片时间两个特点发挥到了极致,视频相关的APP现在无温不火的原因我认为跟坑爹的运营商有关.虽

iOS视频开发

视频实质: 纯粹的视频(不包括音频)实质上就是一组帧图片,经过视频编码成为视频(video)文件再把音频(audio)文件有些还有字幕文件组装在一起成为我们看到的视频(movie)文件.1秒内出现的图片数就是帧率,图片间隔越小画面就越流畅,所以帧率越高效果就越好,需要的存储空间也就越多. 视频编码: 因为不进行编码的视频数据量非常大,会造成存储和传输上的困难,所以视频文件都需要在录制完成后进行编码.视频编码主要从两个维度压缩数据. 1.单张图像某一区域相邻像素相似,比如一片红色只记录红色色值和区

iOS开发系列--音频播放、录音、视频播放、拍照、视频录制

iOS开发系列--音频播放.录音.视频播放.拍照.视频录制 转载:http://www.cnblogs.com/kenshincui/p/4186022.html#avFoundationCamera --iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操作都提供了多套API.在今天的文章中将会对这些内容进行一一介绍: 音频 音

iOS开发–音频播放、录音、视频播放、拍照、视频录制

概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操作都提供了多套API.在今天的文章中将会对这些内容进行一一介绍: 音频 在iOS中音频播放从形式上可以分为音效播放和音乐播放.前者主要指的是一些短音频播放,通常作为点缀音频,对于这类音频不需要进行进度.循环等控制.后者指的是一些较长的音频,通常是主音频,对于这些音频的播放通常需要进行精确的控制

iOS音频播放、录音、视频播放、拍照、视频录制

随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操作都提供了多套API.在今天的文章中将会对这些内容进行一一介绍: 音频 音效 音乐 音频会话 录音 音频队列服务 视频 MPMoviePlayerController MPMoviePlayerViewController AVPlayer 摄像头 UIImagePickerController拍照

利用FFmpeg玩转Android视频录制与压缩(二)&lt;转&gt;

转载出处:http://blog.csdn.net/mabeijianxi/article/details/72983362 预热 时光荏苒,光阴如梭,离上一次吹牛逼已经过去了两三个月,身边很多人的女票已经分了又合,合了又分,本屌依旧骄傲单身.上一次啊我们大致说了一些简单的FFmpeg命令以及Java层简单的调用方式,然后有很多朋友在github或者csdn上给我留言,很多时候我都选择避而不答,原因是本库以前用的so包是不开源的,我根本改不了里面东西.但是这一次啊我们玩点大的,我重新编译了FFm