想做一个微信朋友圈上传视频时视频裁剪编辑类似的功能,拖动视频和拖动裁剪范围,本来以为做起来很简单,但是实际操作中在优化上面还有很多改进。
一、视频封面截取
进入界面之后,首先要生成区域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效果演示
版权属于:东哥笔记 - DongGe.org
本文链接:http://dongge.org/blog/575.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!
1 条评论
ss