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

视频录制

首先,我们弹出系统的视频录制界面,也就是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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容