想要实现这个功能,可以先了解一下有关录制视频的相关知识:素材库、素材的轨道、合成视频的工程文件等等,具体可以参考
相关术语:
AVAsset:素材库里的素材; AVAssetTrack:素材的轨道; AVMutableComposition:一个用来合成视频的工程文件; AVMutableCompositionTrack:工程文件中的轨道,有音频轨、视频轨等,里面可以插入各种对应的素材; AVMutableVideoCompositionLayerInstruction:视频轨道中的一个视频,可以缩放、旋转等; AVMutableVideoCompositionInstruction:一个视频轨道,包含了这个轨道上的所有视频素材; AVMutableVideoComposition:管理所有视频轨道,可以决定最终视频的尺寸,裁剪需要在这里进行; AVAssetExportSession:配置渲染参数并渲染。复制代码
问题记录:
下面带横线的是错误思路,一开始走进了误区,这种方式并不能解决点击view触发toucherBegan的问题
长按拍摄,利用的是touchesbegan开始录制,touchesEnded结束录制;存在一个小问题就是点击这个view的时候,会触发touchesBegan,然而不会触发touchesEnded。这里使用了一个延时机制去触发touchesBegan事件的触发,代码如下
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event { NSLog(@"开始触摸"); if ([[touches anyObject] view] == self.progressView) { if (!self.capture) { // 表示是否正在录制 double delayInSeconds = 0.5; // 长按0.5s触发录制事件 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self startCapture]; // 开始录制 }); }else { [self startCapture]; } }}复制代码
解决方案就是用长按手势来触发
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(LongPressProgressView:)]; longPress.minimumPressDuration = 0.5; [progressView addGestureRecognizer:longPress];复制代码
- (void)LongPressProgressView:(UILongPressGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { [self startCapture]; }else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { if ([self.mediaCaptureDelegate respondsToSelector:@selector(stopCapture)]) { self.capture = NO; self.bgView.hidden = YES; [self.mediaCaptureDelegate stopCapture]; [self.progressView clearProgress]; } } }复制代码
其次有个可以拖动、缩放、旋转的文本框,点击可以重新编辑,我是封装了一个类FWTextView,具体代码可以从下面的里找到,如图
最关键的一点就是水印的添加,主要功能就是这个么。先说下思路:
首先我们要知道我们能看到的视频实际上是由一个叫做videoLayer负责显示的,和他同级的有个layer叫做animationLayer,我们能够控制的其实就是这个东西,他可以由我们自己创建,他们有一个共同的父类叫做parentLayer。
添加图片水印的代码:
CALayer *imgLayer = [CALayer layer]; imgLayer.contents = (id)img.CGImage; imgLayer.frame = CGRectMake(0, 0, size.width, size.height);复制代码
创建好了图片layer后,就需要创建videoLayer
//把文字和图标都添加到layer CALayer *overlayLayer = [CALayer layer]; [overlayLayer addSublayer:imgLayer]; overlayLayer.frame = CGRectMake(0, 0, size.width, size.height); [overlayLayer setMasksToBounds:YES]; [overlayLayer addSublayer:imgLayer]; CALayer *parentLayer = [CALayer layer]; CALayer *videoLayer = [CALayer layer]; parentLayer.frame = CGRectMake(0, 0, size.width, size.height); parentLayer.backgroundColor = [UIColor redColor].CGColor; videoLayer.frame = CGRectMake(0, 0, size.width, size.height); [parentLayer addSublayer:videoLayer]; [parentLayer addSublayer:overlayLayer]; composition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];复制代码
这里主要是告诉系统我们的layer层时parentLayer,在parentLayer里负责video显示的是我们的videoLayer。想详细了解的可参考
最后就是录制完的视频保存本地,这里使用的不是系统的,ios10开始ALAssetsLibrary被标志为弃用(DEPRECATED),并建议使用Photos framework的PHPhotoLibrary,使用需先引用#import <Photos/Photos.h>
方法1:同步存到系统相册(iOS10系统执行该方法,无法保存成功)
__block NSString *createdAssetID =nil;//唯一标识,可以用于图片资源获取 NSError *error =nil; [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ createdAssetID = [PHAssetChangeRequest creationRequestForAssetFromImage:image].placeholderForCreatedAsset.localIdentifier; } error:&error];复制代码
补充:
上面的方法1在系统是10的真机上出现保存视频失败,更新一下新的方法:
NSString *path = [SNSImageEngine getRealHouseVideoPathWithVideoPath:videoPath]; if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) { UISaveVideoAtPathToSavedPhotosAlbum(path , self, @selector(video:didFinishSavingWithError:contextInfo:), nil); }else { [MBProgressHUD showMessage:@"保存失败"]; }// 视频保存回调- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { if (error == nil) { [MBProgressHUD showMessage:@"已保存至手机"]; }else { [MBProgressHUD showMessage:@"保存失败"]; }}复制代码
上面代码里的path是视频在本地的路径,还有一个回调方法提供。
方法2:存到某个自定义相册
[[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{ PHAssetChangeRequest *changeAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image]; PHAssetCollection *targetCollection = [[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil]lastObject]; PHAssetCollectionChangeRequest *changeCollectionRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:targetCollection]; PHObjectPlaceholder *assetPlaceholder = [changeAssetRequest placeholderForCreatedAsset]; [changeCollectionRequest addAssets:@[assetPlaceholder]]; } completionHandler:^(BOOL success,NSError * _Nullable error) { NSLog(@"finished adding"); }];复制代码
详细了解参考
demo链接:
到此基本结束了,后期有更新的话,会继续更新。
需求更改,要求录制时间加长,大于10s,会出现新的bug。视频超过10s后,声音会丢失。
- 问题所在:AVCaptureMovieFileOutput他有默认的时间限制,默认值是 10 秒
- 更改办法:设置 AVCaptureMovieFileOutput 的 movieFragmentInterval 属性为 kCMTimeInvalid,视频录制就不会受到限制
-(AVCaptureMovieFileOutput *)movieFileOutput{ if (!_movieFileOutput) { _movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; /* 默认的录制视频时间是10秒,如果视频大于10秒必须禁用他,否则录制的视频将会没有声音*/ _movieFileOutput.movieFragmentInterval = kCMTimeInvalid; } return _movieFileOutput;}复制代码