想做一个微信朋友圈上传视频时视频裁剪编辑类似的功能,拖动视频和拖动裁剪范围,本来以为做起来很简单,但是实际操作中在优化上面还有很多改进。

0E81D8D7B33A85C731B2F4A100E73742.png

一、视频封面截取

进入界面之后,首先要生成区域2的视频的缩略图

//截图 - (UIImage*)getVideoPreViewImageFromVideoPath:(NSString*)videoPath withAtTime:(float)atTime { if (!videoPath) { return nil; } AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:videoPath] options:nil]; if ([asset tracksWithMediaType:AVMediaTypeVideo].count == 0) { return nil; } AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset]; gen.appliesPreferredTrackTransform = YES; gen.requestedTimeToleranceAfter = kCMTimeZero; gen.requestedTimeToleranceBefore = kCMTimeZero; CMTime time = CMTimeMakeWithSeconds(atTime, 600); NSError *error = nil; CMTime actualTime; CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error]; UIImage *img = [[UIImage alloc] initWithCGImage:image]; UIGraphicsBeginImageContext(CGSizeMake([[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].width, [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].height));//asset.naturalSize.width, asset.naturalSize.height) [img drawInRect:CGRectMake(0, 0, [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].width, [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize].height)]; UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGImageRelease(image); return scaledImage; }

因为我们裁剪视频的时间范围是6s到10s,为了铺满下面的整个scrollview,同时保持每张图片的宽度一致,所以设置了固定的图片宽度

//图片宽 float imgWidth = self.maxWidth/10.0; for (int i = 0; i< self.coverImgs.count; i++) { UIImageView *imageView = [[UIImageView alloc] initWithImage:[self.coverImgs objectAtIndex:i]]; [imageView setFrame:CGRectMake(i*imgWidth, 0, imgWidth, 50)]; [self.scrollView addSubview:imageView]; } [self.scrollView setContentSize:CGSizeMake(imgWidth*self.coverImgs.count, 50)];

为了铺满scrollview,又兼顾上传时长视频和短视频两种区别,所以在裁剪图片的时机上面有区别

- (void)getCoverImgs { NSMutableArray *imageArrays = [NSMutableArray array]; self.videoDuration = [self durationWithVideo:self.videoUrlStr]; //大于11s if (self.videoDuration>11.0) { //每隔1s截取一张图片 for (int i = 0; i < self.videoDuration-1; i++) { UIImage *image = [self getVideoPreViewImageFromVideoPath:self.videoUrlStr withAtTime:i+0.1]; [imageArrays addObject:image]; } } else{ //截取11张 for (int i = 0; i < 11; i++) { UIImage *image = [self getVideoPreViewImageFromVideoPath:self.videoUrlStr withAtTime:self.videoDuration*i/12.0+0.1]; [imageArrays addObject:image]; } } self.coverImgs = [NSArray arrayWithArray:imageArrays]; self.cover = [imageArrays objectAtIndex:0]; }

这样就保证了不管长视频还是短视频都可以铺满自己的时长范围,超过10s时可以滚动,不超过10s就以自己的长度为准。

二、视频裁剪起点

在区域1中,根据视频的长宽比,以一边铺满,一边滚动为准,在滚动的时候,计算偏移量来取视频裁剪的起点

- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.tag == kCoverImageScrollTag) { self.clipPoint = CGPointMake(scrollView.contentOffset.x*720.0/kScreenWidth, scrollView.contentOffset.y*720.0/kScreenWidth); } }

三、左右拖动

在区域2中,通过左右两边的拖动,主要就是调用AVPlayer的seekToTime来实现,其中AVPlayer的seekToTime有两种,这里采取精确的方式

- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^)(BOOL finished))completionHandler

如果不需要滚动范围的精确,推荐使用下面这种不精确的方式更省内部计算量

- (void)seekToTime:(CMTime)time;

如果直接调用这些方式,会发现如果是比较大的视频的话,在拖动的时候会出现卡顿的现象,所以需要使用多线程来进行优化

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ [self.player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) { if (finished) { [self.player pause]; self.playBtn.hidden = NO; } }]; }); });

这样才会避免拖动卡顿现象。

四、底部滚动

在区域2中,如果是视频长度大于10s,就需要滚动选取,而滚动时需要记录偏移量,从而避免在滚动之后,又点击左右按钮造成时间错误。

在开始滚动的额时候,计算偏移量并且减去之前的偏移过的量才可以,在结束滚动,结束拖拽的时候记录当前的偏移量,这样才能保证数据的正确性。通过每个点对应多长时间,来计算滚动的偏移量对应了多长的时间起点。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.tag == kCoverImageScrollTag) { self.clipPoint = CGPointMake(scrollView.contentOffset.x*720.0/kScreenWidth, scrollView.contentOffset.y*720.0/kScreenWidth); } else if (scrollView.tag == kClipTimeScrollTag){ if (scrollView.contentOffset.x>=0) { CGFloat addTime = scrollView.contentOffset.x*self.timeScale; self.tempStartTime = self.startTime + addTime - self.contentOffsetX*self.timeScale; self.tempEndTime = self.endTime + addTime - self.contentOffsetX*self.timeScale; CMTime time = CMTimeMakeWithSeconds(self.tempStartTime, self.m_ftp); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ [self.player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) { if (finished) { [self.player pause]; self.playBtn.hidden = NO; } }]; }); }); } } NSLog(@"offset:%@", NSStringFromCGPoint(scrollView.contentOffset)); NSLog(@"%f",self.startTime); } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { if (scrollView.tag == kClipTimeScrollTag){ self.contentOffsetX = scrollView.contentOffset.x; self.startTime = self.tempStartTime; self.endTime = self.tempEndTime; } } - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { if (scrollView.tag == kClipTimeScrollTag){ self.contentOffsetX = scrollView.contentOffset.x; self.startTime = self.tempStartTime; self.endTime = self.tempEndTime; } } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (scrollView.tag == kClipTimeScrollTag){ self.contentOffsetX = scrollView.contentOffset.x; self.startTime = self.tempStartTime; self.endTime = self.tempEndTime; } }

五、优化

之前没有进行过优化,会出现数据不太正确的问题,操作不太流畅,所以最好做下以下优化

5.1、拖动时使用多线程

就是上面说的,在seekToTime的时候,使用多线程去更新UI,这样才不至于卡顿的现象。

5.2、导入视频压缩

demo中是为了演示,在实际使用中,在导入视频的时候,需要进行压缩之后再编辑,这样处理速度会更快,对宽带的压力也比较小

5.3、计算拖动位置时间

拖动的时间是最不好掌握的,所以需要计算好拖动起点和终点的坐标,在demo中,两边拖动的浮标是在两侧的,如图标出的红色那样布局的,所以在计算右侧的时候,需要减去图片的宽度。

Demo下载

GitHub下载:https://github.com/DamonHu/HudongBlogDemo/tree/master/VideoClipDrag

Gitosc下载:https://git.oschina.net/DamonHoo/HudongBlogDemo/tree/master/VideoClipDrag

Demo效果演示


☟☟可点击下方广告支持一下☟☟

最后修改:2017 年 07 月 18 日
请我喝杯可乐,请随意打赏: ☞已打赏列表