項目要求根據服務器返回的視頻和秒數阶女,生成該視頻的預覽圖颊糜。
網上一搜關鍵詞 “iOS 視頻 幀” 結果都是:iOS如何獲取視頻的第一幀。
但是如果我不想要第一幀秃踩,要第s秒的第x幀怎么辦衬鱼?
先貼如何獲取第一幀的代碼:
- (UIImage*) getVideoPreViewImage
{
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoPath options:nil];
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
[asset release];
gen.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMakeWithSeconds(0.0, 600);
NSError *error = nil;
CMTime actualTime;
CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
UIImage *img = [[[UIImage alloc] initWithCGImage:image] autorelease];
CGImageRelease(image);
[gen release];
return img;
}
這是很不求甚解的做法,有好多問題都沒有考慮到憔杨。
一般來說鸟赫,如果我們打算求第x秒 看如上代碼,想都不用想的就去把
CMTime time = CMTimeMakeWithSeconds(0.0, 600);
改成想要的時間了消别,但是抛蚤,跑一下就會發(fā)現差強人意。為什么呢寻狂?我們先要說CMTime
是什么東西霉颠。
CMTime 是一個用來描述視頻時間的結構體。
他有兩個構造函數: * CMTimeMake * CMTimeMakeWithSeconds
這兩個的區(qū)別是 * CMTimeMake(a,b) a當前第幾幀, b每秒鐘多少幀.當前播放時間a/b * CMTimeMakeWithSeconds(a,b) a當前時間,b每秒鐘多少幀.
我們引用例子來說明它:
CMTimeMakeWithSeconds
Float64 seconds = 5;
int32_t preferredTimeScale = 600;
CMTime inTime = CMTimeMakeWithSeconds(seconds, preferredTimeScale);
CMTimeShow(inTime);
OUTPUT: {3000/600 = 5.000}
代表當前時間為5s荆虱,視頻一共有3000幀蒿偎,一秒鐘600幀
CMTimeMake
int64_t value = 10000;
int32_t preferredTimeScale = 600;
CMTime inTime = CMTimeMake(value, preferredTimeScale);
CMTimeShow(inTime);
OUTPUT: {10000/600 = 16.667}
代表時間為16.667s, 視頻一共1000幀朽们,每秒600幀
其實,在我們這里诉位,我們關心的只有最后那個總時間骑脱。 換句話說,我們把那個(0, 600)換成(x, 600) 是沒問題的… = =!
requestedTimeTolerance
那么為什么苍糠,效果差了這么多呢叁丧? 我們可以把
CGImageRef image = [gen copyCGImageAtTime:time
actualTime:&actualTime
error:&error];
返回的 actualTime 輸出一下
CMTimeShow(actualTime)
就會發(fā)現時間差的很遠。 這是為什么呢岳瞭。
首先他的 actualTime 使用的 fps * 1000 當每秒的幀率拥娄。 順路普及下fps的獲取方法
float fps = [[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] nominalFrameRate];
然后我們來思考為什么要有 requestTime 和 actualTime 呢? 開始我對這個api 很困惑: 為什么我request的時間 不等于 actual
后來查了一下文檔瞳筏。
當你想要一個時間點的某一幀的時候稚瘾,他會在一個范圍內找,如果有緩存姚炕,或者有在索引內的關鍵幀摊欠,就直接返回,從而優(yōu)化性能柱宦。
這個定義范圍的API就是 requestedTimeToleranceAfter 和 requestedTimeToleranceBefore
如果我們要精確時間些椒,那么只需要
gen.requestedTimeToleranceAfter = kCMTimeZero;
gen.requestedTimeToleranceBefore = kCMTimeZero;
轉自: