iOS 在线视频生成GIF图功能的方法
在一些视频APP中,都可以看到一个将在线视频转成GIF图的功能。下面就来说说思路以及实现。我们知道本地视频可以生成GIF,那么将在线视频截取成本地视频不就可以了吗?经过比较,腾讯视频App也是这么做的。话不多说,下面开始上代码:
第一步:截取视频
#pragmamark-截取视频 -(void)interceptVideoAndVideoUrl:(NSURL*)videoUrlwithOutPath:(NSString*)outPathoutputFileType:(NSString*)outputFileTyperange:(NSRange)videoRangeintercept:(InterceptBlock)interceptBlock{ _interceptBlock=interceptBlock; //不添加背景音乐 NSURL*audioUrl=nil; //AVURLAsset此类主要用于获取媒体信息,包括视频、声音等 AVURLAsset*audioAsset=[[AVURLAssetalloc]initWithURL:audioUrloptions:nil]; AVURLAsset*videoAsset=[[AVURLAssetalloc]initWithURL:videoUrloptions:nil]; //创建AVMutableComposition对象来添加视频音频资源的AVMutableCompositionTrack AVMutableComposition*mixComposition=[AVMutableCompositioncomposition]; //CMTimeRangeMake(start,duration),start起始时间,duration时长,都是CMTime类型 //CMTimeMake(int64_tvalue,int32_ttimescale),返回CMTime,value视频的一个总帧数,timescale是指每秒视频播放的帧数,视频播放速率,(value/timescale)才是视频实际的秒数时长,timescale一般情况下不改变,截取视频长度通过改变value的值 //CMTimeMakeWithSeconds(Float64seconds,int32_tpreferredTimeScale),返回CMTime,seconds截取时长(单位秒),preferredTimeScale每秒帧数 //开始位置startTime CMTimestartTime=CMTimeMakeWithSeconds(videoRange.location,videoAsset.duration.timescale); //截取长度videoDuration CMTimevideoDuration=CMTimeMakeWithSeconds(videoRange.length,videoAsset.duration.timescale); CMTimeRangevideoTimeRange=CMTimeRangeMake(startTime,videoDuration); //视频采集compositionVideoTrack AVMutableCompositionTrack*compositionVideoTrack=[mixCompositionaddMutableTrackWithMediaType:AVMediaTypeVideopreferredTrackID:kCMPersistentTrackID_Invalid]; //避免数组越界tracksWithMediaType找不到对应的文件时候返回空数组 //TimeRange截取的范围长度 //ofTrack来源 //atTime插放在视频的时间位置 [compositionVideoTrackinsertTimeRange:videoTimeRangeofTrack:([videoAssettracksWithMediaType:AVMediaTypeVideo].count>0)?[videoAssettracksWithMediaType:AVMediaTypeVideo].firstObject:nilatTime:kCMTimeZeroerror:nil]; //视频声音采集(也可不执行这段代码不采集视频音轨,合并后的视频文件将没有视频原来的声音) AVMutableCompositionTrack*compositionVoiceTrack=[mixCompositionaddMutableTrackWithMediaType:AVMediaTypeAudiopreferredTrackID:kCMPersistentTrackID_Invalid]; [compositionVoiceTrackinsertTimeRange:videoTimeRangeofTrack:([videoAssettracksWithMediaType:AVMediaTypeAudio].count>0)?[videoAssettracksWithMediaType:AVMediaTypeAudio].firstObject:nilatTime:kCMTimeZeroerror:nil]; //声音长度截取范围==视频长度 CMTimeRangeaudioTimeRange=CMTimeRangeMake(kCMTimeZero,videoDuration); //音频采集compositionCommentaryTrack AVMutableCompositionTrack*compositionAudioTrack=[mixCompositionaddMutableTrackWithMediaType:AVMediaTypeAudiopreferredTrackID:kCMPersistentTrackID_Invalid]; [compositionAudioTrackinsertTimeRange:audioTimeRangeofTrack:([audioAssettracksWithMediaType:AVMediaTypeAudio].count>0)?[audioAssettracksWithMediaType:AVMediaTypeAudio].firstObject:nilatTime:kCMTimeZeroerror:nil]; //AVAssetExportSession用于合并文件,导出合并后文件,presetName文件的输出类型 AVAssetExportSession*assetExportSession=[[AVAssetExportSessionalloc]initWithAsset:mixCompositionpresetName:AVAssetExportPresetPassthrough]; //混合后的视频输出路径 NSURL*outPutURL=[NSURLfileURLWithPath:outPath]; if([[NSFileManagerdefaultManager]fileExistsAtPath:outPath]) { [[NSFileManagerdefaultManager]removeItemAtPath:outPatherror:nil]; } //输出视频格式 assetExportSession.outputFileType=outputFileType; assetExportSession.outputURL=outPutURL; //输出文件是否网络优化 assetExportSession.shouldOptimizeForNetworkUse=YES; [assetExportSessionexportAsynchronouslyWithCompletionHandler:^{ dispatch_async(dispatch_get_main_queue(),^{ switch(assetExportSession.status){ caseAVAssetExportSessionStatusFailed: if(_interceptBlock){ _interceptBlock(assetExportSession.error,outPutURL); } break; caseAVAssetExportSessionStatusCancelled:{ logdebug(@"ExportStatus:Cancell"); break; } caseAVAssetExportSessionStatusCompleted:{ if(_interceptBlock){ _interceptBlock(nil,outPutURL); } break; } caseAVAssetExportSessionStatusUnknown:{ logdebug(@"ExportStatus:Unknown"); } caseAVAssetExportSessionStatusExporting:{ logdebug(@"ExportStatus:Exporting"); } caseAVAssetExportSessionStatusWaiting:{ logdebug(@"ExportStatus:Wating"); } } }); }]; }
第二步:本地视频生成GIF图
/** 生成GIF图片 @paramvideoURL视频的路径URL @paramloopCount播放次数 @paramtime每帧的时间间隔默认0.25s @paramimagePath存放GIF图片的文件路径 @paramcompleteBlock完成的回调 */ #pragmamark--制作GIF -(void)createGIFfromURL:(NSURL*)videoURLloopCount:(int)loopCountdelayTime:(CGFloat)timegifImagePath:(NSString*)imagePathcomplete:(CompleteBlock)completeBlock{ _completeBlock=completeBlock; floatdelayTime=time?:0.25; //Createpropertiesdictionaries NSDictionary*fileProperties=[selffilePropertiesWithLoopCount:loopCount]; NSDictionary*frameProperties=[selfframePropertiesWithDelayTime:delayTime]; AVURLAsset*asset=[AVURLAssetassetWithURL:videoURL]; floatvideoWidth=[[[assettracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0]naturalSize].width; floatvideoHeight=[[[assettracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0]naturalSize].height; GIFSizeoptimalSize=GIFSizeMedium; if(videoWidth>=1200||videoHeight>=1200) optimalSize=GIFSizeVeryLow; elseif(videoWidth>=800||videoHeight>=800) optimalSize=GIFSizeLow; elseif(videoWidth>=400||videoHeight>=400) optimalSize=GIFSizeMedium; elseif(videoWidth<400||videoHeight<400) optimalSize=GIFSizeHigh; //Getthelengthofthevideoinseconds floatvideoLength=(float)asset.duration.value/asset.duration.timescale; intframesPerSecond=4; intframeCount=videoLength*framesPerSecond; //Howfaralongthevideotrackwewanttomove,inseconds. floatincrement=(float)videoLength/frameCount; //Addframestothebuffer NSMutableArray*timePoints=[NSMutableArrayarray]; for(intcurrentFrame=0;currentFrame经过上面两步,就可以生成本地的视频和GIF图了,存储在沙盒即可。贴上两步所用到的方法:
#pragmamark-Basemethods -(NSURL*)createGIFforTimePoints:(NSArray*)timePointsfromURL:(NSURL*)urlfileProperties:(NSDictionary*)filePropertiesframeProperties:(NSDictionary*)framePropertiesgifImagePath:(NSString*)imagePathframeCount:(int)frameCountgifSize:(GIFSize)gifSize{ NSURL*fileURL=[NSURLfileURLWithPath:imagePath]; if(fileURL==nil) returnnil; CGImageDestinationRefdestination=CGImageDestinationCreateWithURL((__bridgeCFURLRef)fileURL,kUTTypeGIF,frameCount,NULL); CGImageDestinationSetProperties(destination,(CFDictionaryRef)fileProperties); AVURLAsset*asset=[AVURLAssetURLAssetWithURL:urloptions:nil]; AVAssetImageGenerator*generator=[AVAssetImageGeneratorassetImageGeneratorWithAsset:asset]; generator.appliesPreferredTrackTransform=YES; CMTimetol=CMTimeMakeWithSeconds([tolerancefloatValue],[timeIntervalintValue]); generator.requestedTimeToleranceBefore=tol; generator.requestedTimeToleranceAfter=tol; NSError*error=nil; CGImageRefpreviousImageRefCopy=nil; for(NSValue*timeintimePoints){ CGImageRefimageRef; #ifTARGET_OS_IPHONE||TARGET_IPHONE_SIMULATOR imageRef=(float)gifSize/10!=1?createImageWithScale([generatorcopyCGImageAtTime:[timeCMTimeValue]actualTime:nilerror:&error],(float)gifSize/10):[generatorcopyCGImageAtTime:[timeCMTimeValue]actualTime:nilerror:&error]; #elifTARGET_OS_MAC imageRef=[generatorcopyCGImageAtTime:[timeCMTimeValue]actualTime:nilerror:&error]; #endif if(error){ _error=error; logdebug(@"Errorcopyingimage:%@",error); returnnil; } if(imageRef){ CGImageRelease(previousImageRefCopy); previousImageRefCopy=CGImageCreateCopy(imageRef); }elseif(previousImageRefCopy){ imageRef=CGImageCreateCopy(previousImageRefCopy); }else{ _error=[NSErrorerrorWithDomain:NSStringFromClass([selfclass])code:0userInfo:@{NSLocalizedDescriptionKey:@"Errorcopyingimageandnopreviousframestoduplicate"}]; logdebug(@"Errorcopyingimageandnopreviousframestoduplicate"); returnnil; } CGImageDestinationAddImage(destination,imageRef,(CFDictionaryRef)frameProperties); CGImageRelease(imageRef); } CGImageRelease(previousImageRefCopy); //FinalizetheGIF if(!CGImageDestinationFinalize(destination)){ _error=error; logdebug(@"FailedtofinalizeGIFdestination:%@",error); if(destination!=nil){ CFRelease(destination); } returnnil; } CFRelease(destination); returnfileURL; } #pragmamark-Helpers CGImageRefcreateImageWithScale(CGImageRefimageRef,floatscale){ #ifTARGET_OS_IPHONE||TARGET_IPHONE_SIMULATOR CGSizenewSize=CGSizeMake(CGImageGetWidth(imageRef)*scale,CGImageGetHeight(imageRef)*scale); CGRectnewRect=CGRectIntegral(CGRectMake(0,0,newSize.width,newSize.height)); UIGraphicsBeginImageContextWithOptions(newSize,NO,0); CGContextRefcontext=UIGraphicsGetCurrentContext(); if(!context){ returnnil; } //Setthequalityleveltousewhenrescaling CGContextSetInterpolationQuality(context,kCGInterpolationHigh); CGAffineTransformflipVertical=CGAffineTransformMake(1,0,0,-1,0,newSize.height); CGContextConcatCTM(context,flipVertical); //Drawintothecontext;thisscalestheimage CGContextDrawImage(context,newRect,imageRef); //Releaseoldimage CFRelease(imageRef); //GettheresizedimagefromthecontextandaUIImage imageRef=CGBitmapContextCreateImage(context); UIGraphicsEndImageContext(); #endif returnimageRef; } #pragmamark-Properties -(NSDictionary*)filePropertiesWithLoopCount:(int)loopCount{ return@{(NSString*)kCGImagePropertyGIFDictionary: @{(NSString*)kCGImagePropertyGIFLoopCount:@(loopCount)} }; } -(NSDictionary*)framePropertiesWithDelayTime:(float)delayTime{ return@{(NSString*)kCGImagePropertyGIFDictionary: @{(NSString*)kCGImagePropertyGIFDelayTime:@(delayTime)}, (NSString*)kCGImagePropertyColorModel:(NSString*)kCGImagePropertyColorModelRGB }; }最后,截取的本地视频可用AVPlayer播放,生成的GIF图则用UIWebView或者WKWebView又或者YYImage加载即可。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。