在編輯視頻的過程當中,時常有場景須要對視頻增添更多個性化的內容,如字幕、水印或者表情等,此時須要對視頻增長一個動畫疊加層,在實現上來說就須要結合 AVFoundation 和 CoreAnimation 一同使用。ide
在 AVFoundation 中使用 CoreAnimation 和在 iOS 其餘地方使用 CoreAnimation 同樣,可是最大的差異在於時間模型。正常使用 CoreAnimation 的時候,時間模型取決於系統主機,可是視頻動畫有其本身的時間線,同時須要支持中止、暫停、回退或快進等效果,因此不能直接用系統主機的時間模型向一個視頻中添加基於時間的動畫。工具
針對這一特性,AVFoundation 在兩個場景下提供了兩個工具實現此效果動畫
不清楚爲何須要兩個類實現同一個效果,這樣作會致使在實現帶有 CoreAnimation 的 layer 時,要保證 layer 的座標與尺寸保持一致會很是麻煩。ui
首先實現一個簡單的 CALayer 來展現一段字符串組成的字幕spa
CGFloat fontSize = 32.0f;
UIFont *font = [UIFont fontWithName:@"GillSans-Bold" size:fontSize];
NSDictionary *attrs = @{NSFontAttributeName : font, NSForegroundColorAttributeName : (id)[UIColor whiteColor].CGColor};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"hello yasic" attributes:attrs];
CGSize textSize = [@"hello yasic" sizeWithAttributes:attrs];
CATextLayer *layer = [CATextLayer layer];
layer.opacity = 0.0;
layer.string = string;
layer.frame = CGRectMake((SCREEN_WIDTH - textSize.width)/2.0, (SCREEN_HEIGHT - textSize.height)/2.0, textSize.width, textSize.height);
layer.backgroundColor = [UIColor clearColor].CGColor;
複製代碼
而後對這個 layer 加上 CoreAnimationcode
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
animation.values = @[@0.0f, @1.0f, @1.0f, @0.0f];
animation.keyTimes = @[@0.0f, @0.3f, @0.7f, @1.0f];
animation.beginTime = CMTimeGetSeconds(CMTimeMake(1, 1));
animation.duration = 5.0f;
animation.removedOnCompletion = NO;
[layer addAnimation:animation forKey:@"opacity"];
複製代碼
這裏必定要設置起始時間,若是要表示影片片頭,不能用 0.0 來賦值 beginTime,由於 CoreAnimation 會將 0.0 的 beginTime 轉爲 CACurrentMediaTime(),因此要用 AVCoreAnimationBeginTimeAtZero 來代替。視頻
另外,須要注意要將 CoreAnimation 的 removedOnCompletion 屬性設置爲 NO,不然在播放過程當中動畫執行一次後就從圖層樹上移除了,以後就不會出現動畫了。ip
CoreAnimation 提供了 CAAnimationGroup 來組合多個動畫,可是書中建議在視頻動畫中避免使用 CAAnimationGroup。ci
最終將這個 layer 加入到 AVSynchronizedLayer 上rem
AVSynchronizedLayer *syncLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:self.avPlayerItem];
[syncLayer addSublayer:[self makeTextLayer]];
syncLayer.frame = CGRectMake(0, 0, 1280, 720);
[self.layer addSublayer:syncLayer];
複製代碼
能夠看到 AVSynchronizedLayer 是與 AVPlayerItem 綁定的,這樣就能實現時間同步了。
導出時也須要建立 CALayer
CALayer *animationLayer = [CALayer layer];
animationLayer.frame = CGRectMake(0, 0, 1280.f, 720.f);
CALayer *videoLayer = [CALayer layer];
videoLayer.frame = CGRectMake(0, 0, 1280.f, 720.f);
[animationLayer addSublayer:videoLayer];
[animationLayer addSublayer:[self makeTextLayer]];
animationLayer.geometryFlipped = YES; // 避免錯位現象
AVVideoCompositionCoreAnimationTool *animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:animationLayer];
複製代碼
獲得 animationTool 後將其綁定到 AVMutableVideoComposition 上,便可用於導出了。
AVMutableVideoComposition *finalVideocomposition = [videoComposition copy];
finalVideocomposition.animationTool = animationTool;
複製代碼