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加载即可。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。