前言
最近要求做一個(gè)類似抖音將短視頻生成動(dòng)圖在列表進(jìn)行展示的需求,生成動(dòng)圖最大的好處是用戶在列表能夠最直觀地預(yù)覽到短視頻的大致內(nèi)容跨算,雖然這個(gè)是個(gè)用戶體驗(yàn)的加分項(xiàng)哺呜,但是如果我們不能處理好圖片占用空間及清晰度問(wèn)題蜘澜,也會(huì)帶來(lái)副作用。
那么俭识,我們?cè)撊绾螜?quán)衡呢慨削?
Gif or Webp?
要想使用動(dòng)圖并且圖片足夠小套媚,當(dāng)然是用Webp了缚态,圖1是gif和webp的對(duì)比,可見(jiàn)webp節(jié)省了不少空間玫芦!這里有篇介紹Webp的經(jīng)典文章,有興趣的朋友可以了解下:濃縮的精華本辐!從零開(kāi)始帶你認(rèn)識(shí)最新的圖片格式WEBP桥帆。
截取視頻幀
截取視頻一幀關(guān)鍵代碼如下:
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = YES;
//下面兩個(gè)值設(shè)為0表示精確取幀,否則系統(tǒng)會(huì)有優(yōu)化取出來(lái)的幀時(shí)間間隔不對(duì)等
generator.requestedTimeToleranceAfter = kCMTimeZero;
generator.requestedTimeToleranceBefore = kCMTimeZero;
生成Webp
這里推薦一個(gè)功能強(qiáng)大的iOS 圖像框架:YYImage慎皱。
使用YYImageEncoder可以很方便的生成gif或webp動(dòng)圖,實(shí)例代碼:
YYImageEncoder *gifEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP];
gifEncoder.loopCount=0;
gifEncoder.quality=0.8;
[gifEncoder addImage:img duration:0.1];
[gifEncoder encodeToFile:filePath];
至此老虫,2個(gè)關(guān)鍵技術(shù)點(diǎn)講完了,不妨試試截取幾幀生成一個(gè)webp試試手......
當(dāng)你驚喜的發(fā)現(xiàn)圖片已經(jīng)生成的同時(shí)茫多,也會(huì)驚訝的發(fā)現(xiàn)圖片占用空間依然很大啊祈匙。
繼續(xù)優(yōu)化
要想盡可能的優(yōu)化圖片空間,只有從兩個(gè)方面入手:
1、盡可能減少圖片幀數(shù)
2夺欲、盡可能壓縮圖片
針對(duì)第一點(diǎn)跪帝,以及參考抖音的效果,我的方案如下:
總共截取9幀圖片些阅,前5幀從視頻的0.5秒開(kāi)始伞剑,每間隔0.1秒截取一幀;然后倒序再截取4幀市埋,從而形成倒序播放的效果黎泣。
針對(duì)第二點(diǎn),首先對(duì)圖片大小按比例進(jìn)行裁剪腰素,以最大邊長(zhǎng)不超過(guò)480為依據(jù)進(jìn)行等比壓縮聘裁,然后設(shè)置0.8的有損壓縮。
最終方案:
- (void)saveToWebpByVideoPath:(NSURL*)videoUrl webpFilePath:(NSString*)webpFilePath{
? ? YYImageEncoder *gifEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP];
? ? gifEncoder.loopCount=0;
? ? gifEncoder.quality=0.8;
? ? AVURLAsset*asset = [[AVURLAssetalloc]initWithURL:videoUrloptions:nil];
? ? int64_t scale = asset.duration.timescale;
? ? AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
? ? generator.appliesPreferredTrackTransform = YES;
? ? //下面兩個(gè)值設(shè)為0表示精確取幀弓千,否則系統(tǒng)會(huì)有優(yōu)化取出來(lái)的幀時(shí)間間隔不對(duì)等
? ? generator.requestedTimeToleranceAfter = kCMTimeZero;
? ? generator.requestedTimeToleranceBefore = kCMTimeZero;
? ? for(inti =0; i <=4; i++) {
? ? ? ? CGFloatstarttime = i*0.1+0.5;
? ? ? ? CMTimetime =CMTimeMakeWithSeconds(starttime, (int)scale);
? ? ? ? NSError*error =nil;
? ? ? ? CMTimeactualTime;
? ? ? ? CGImageRefimage = [generatorcopyCGImageAtTime:timeactualTime:&actualTimeerror:&error];
? ? ? ? UIImage* img = [UIImageimageWithCGImage:image];
? ? ? ? img = [selfresizeToMaxHeight:480img:img];
? ? ? ? [gifEncoderaddImage:imgduration:0.1];
? ? ? ? CGImageRelease(image);
? ? }
? ? for(inti=3; i>=0; i--) {
? ? ? ? CGFloatstarttime = i*0.1+0.5;
? ? ? ? CMTimetime =CMTimeMakeWithSeconds(starttime, (int)scale);
? ? ? ? NSError*error =nil;
? ? ? ? CMTimeactualTime;
? ? ? ? CGImageRefimage = [generatorcopyCGImageAtTime:timeactualTime:&actualTimeerror:&error];
? ? ? ? UIImage* img = [UIImageimageWithCGImage:image];
? ? ? ? img = [selfresizeToMaxHeight:480img:img];
? ? ? ? [gifEncoderaddImage:imgduration:0.1];
? ? ? ? CGImageRelease(image);
? ? }
? ? [gifEncoderencodeToFile:webpFilePath];
? ? NSLog(@"生成webp成功!");
}
- (UIImage*)resizeToMaxHeight:(CGFloat)height img:(UIImage*)img{
? ? if(img.size.width
? ? ? ? if(img.size.height>height) {
? ? ? ? ? ? CGSizenewSize =CGSizeMake(height*1.0*img.size.width/img.size.height, height);
? ? ? ? ? ? img = [imgyy_imageByResizeToSize:newSize contentMode:UIViewContentModeScaleToFill];
? ? ? ? }
? ? }
? ? else{
? ? ? ? if(img.size.width>height) {
? ? ? ? ? ? CGSizenewSize =CGSizeMake(height,img.size.height*height*1.0/img.size.width);
? ? ? ? ? ? img = [imgyy_imageByResizeToSize:newSize contentMode:UIViewContentModeScaleToFill];
? ? ? ? }
? ? }
? ? returnimg;
}