AVFoundation框架解析(二十三) —— 向視頻層添加疊加層和動(dòng)畫(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.12.14 星期六

前言

AVFoundation框架是ios中很重要的框架来吩,所有與視頻音頻相關(guān)的軟硬件控制都在這個(gè)框架里面,接下來這幾篇就主要對(duì)這個(gè)框架進(jìn)行介紹和講解蔽莱。感興趣的可以看我上幾篇弟疆。
1. AVFoundation框架解析(一)—— 基本概覽
2. AVFoundation框架解析(二)—— 實(shí)現(xiàn)視頻預(yù)覽錄制保存到相冊(cè)
3. AVFoundation框架解析(三)—— 幾個(gè)關(guān)鍵問題之關(guān)于框架的深度概括
4. AVFoundation框架解析(四)—— 幾個(gè)關(guān)鍵問題之AVFoundation探索(一)
5. AVFoundation框架解析(五)—— 幾個(gè)關(guān)鍵問題之AVFoundation探索(二)
6. AVFoundation框架解析(六)—— 視頻音頻的合成(一)
7. AVFoundation框架解析(七)—— 視頻組合和音頻混合調(diào)試
8. AVFoundation框架解析(八)—— 優(yōu)化用戶的播放體驗(yàn)
9. AVFoundation框架解析(九)—— AVFoundation的變化(一)
10. AVFoundation框架解析(十)—— AVFoundation的變化(二)
11. AVFoundation框架解析(十一)—— AVFoundation的變化(三)
12. AVFoundation框架解析(十二)—— AVFoundation的變化(四)
13. AVFoundation框架解析(十三)—— 構(gòu)建基本播放應(yīng)用程序
14. AVFoundation框架解析(十四)—— VAssetWriter和AVAssetReader的Timecode支持(一)
15. AVFoundation框架解析(十五)—— VAssetWriter和AVAssetReader的Timecode支持(二)
16. AVFoundation框架解析(十六)—— 一個(gè)簡(jiǎn)單示例之播放、錄制以及混合視頻(一)
17. AVFoundation框架解析(十七)—— 一個(gè)簡(jiǎn)單示例之播放盗冷、錄制以及混合視頻之源碼及效果展示(二)
18. AVFoundation框架解析(十八)—— AVAudioEngine之基本概覽(一)
19. AVFoundation框架解析(十九)—— AVAudioEngine之詳細(xì)說明和一個(gè)簡(jiǎn)單示例(二)
20. AVFoundation框架解析(二十)—— AVAudioEngine之詳細(xì)說明和一個(gè)簡(jiǎn)單示例源碼(三)
21. AVFoundation框架解析(二十一)—— 一個(gè)簡(jiǎn)單的視頻流預(yù)覽和播放示例之解析(一)
22. AVFoundation框架解析(二十二)—— 一個(gè)簡(jiǎn)單的視頻流預(yù)覽和播放示例之源碼(二)

開始

首先看下主要內(nèi)容:

在本AVFoundation教程中怠苔,您將學(xué)習(xí)如何使用AVVideoCompositionCoreAnimationTool向視頻添加疊加層(overlays)和動(dòng)畫,該工具可將CALayers與視頻結(jié)合在一起以添加背景和疊加層仪糖。

接著柑司,看下寫作環(huán)境

Swift 5, iOS 13, Xcode 11

如果您要制作相機(jī)應(yīng)用,則可以在視頻中添加疊加層和動(dòng)畫锅劝,從而從中受益帜羊。 無論您添加當(dāng)前日期,位置名稱鸠天,天氣還是有趣的GIF讼育,您的用戶都將能夠自定義視頻。

您可以使用AVFoundation(蘋果公司用于處理音頻和視頻內(nèi)容的框架)來完成所有這些工作。 您可以將AVFoundation視為一個(gè)程序化的視頻和音頻編輯器奶段,它使您可以組成視頻和音頻軌道(tracks)饥瓷,然后向其添加漂亮的疊加層。

在此AVFoundation教程中痹籍,您將學(xué)習(xí)如何:

  • 為您的視頻添加自定義邊框呢铆。
  • 在視頻中添加文字和圖像。
  • 對(duì)視頻疊加層進(jìn)行動(dòng)畫處理蹲缠。
  • 將疊加的視頻導(dǎo)出到文件中棺克。

要充分利用本教程,您需要熟悉iOS開發(fā)线定。 熟悉Core Animation也會(huì)很有用娜谊。 不過請(qǐng)放心,本教程將逐步解釋所有內(nèi)容斤讥。

打開入門項(xiàng)目纱皆,該項(xiàng)目的名稱為Cubica,代表“自定義生日賀卡”芭商。 您將制作一個(gè)應(yīng)用程序派草,使您可以錄制視頻并添加疊加層和邊框,以將其轉(zhuǎn)變?yōu)闉榕笥讯ㄖ频纳召R卡铛楣。

在Xcode中打開begin項(xiàng)目近迁。 您可以使用模擬器或設(shè)備,但是請(qǐng)記住簸州,本教程需要視頻钳踊。 確保將視頻文件從Mac拖放到模擬器中。

開始項(xiàng)目已經(jīng)有一個(gè)屏幕勿侯,您可以在其中輸入朋友的名字并選擇一個(gè)視頻來添加疊加層。 這一切都發(fā)生在PickerViewController.swift中缴罗。

用戶選擇視頻后助琐,應(yīng)用程序會(huì)將其發(fā)送到VideoEditor.swift丈钙。 當(dāng)前怎爵,該文件只有幾個(gè)幫助程序方法和一個(gè)名為makeBirthdayCard(fromVideoAt:forName:onComplete :)的方法终畅。 您將編輯此方法寇僧,以將疊加層添加到視頻中角撞。

一旦應(yīng)用將疊加層添加到視頻中压汪,該方法就會(huì)調(diào)用完成處理程序(completion handler)丽蝎,并將視頻URL發(fā)送到PlayerViewController.swift飒房。 該視圖控制器播放視頻文件呻拌,并允許您將文件導(dǎo)出到照片庫葱轩。


Composing a Video

在向視頻添加任何疊加之前,您需要進(jìn)行一些設(shè)置。您要做的就是根據(jù)現(xiàn)有視頻創(chuàng)建一個(gè)新的視頻文件靴拱,并添加背景和疊加層垃喊。

首先,您將創(chuàng)建一個(gè)新的AVFoundation composition袜炕。您可以將composition視為程序化視頻編輯器本谜。該composition包含不同類型的音軌,例如音頻和視頻音軌偎窘,并管理它們?cè)谝曨l時(shí)間線上的開始或結(jié)束時(shí)間乌助。

創(chuàng)建空composition后,您將在composition中添加兩條軌道陌知,一條軌道用于視頻他托,一條軌道用于音頻。對(duì)于音軌纵诞,您只需復(fù)制現(xiàn)有視頻的音頻上祈。要?jiǎng)?chuàng)建視頻,您將使用AVVideoCompositionCoreAnimationTool浙芙,該類可讓您將現(xiàn)有視頻與Core Animation圖層結(jié)合在一起登刺。

composition的視頻和音頻都包含在合成中之后,您將使用AVAssetExportSessioncomposition導(dǎo)出到視頻文件中嗡呼。

別擔(dān)心纸俭,它并不像聽起來那樣令人生畏!第一步是創(chuàng)建composition南窗。

1. Creating a Composition

打開VideoEditor.swift揍很。 示例項(xiàng)目的內(nèi)容位于makeBirthdayCard(fromVideoAt:forName:onComplete :)中。 當(dāng)前万伤,此方法僅使用現(xiàn)有視頻調(diào)用完成處理程序(completion handler)窒悔。 用以下內(nèi)容替換onComplete(videoURL)行:

let asset = AVURLAsset(url: videoURL)
let composition = AVMutableComposition()

創(chuàng)建一個(gè)AVAset,其中包含有關(guān)所提供視頻的所有必需信息和數(shù)據(jù)敌买。 此外简珠,創(chuàng)建一個(gè)空的構(gòu)圖。 您將用疊加的視頻填充此composition虹钮。

接下來聋庵,將軌道添加到composition中,并通過向該方法添加以下代碼芙粱,從資源中獲取視頻軌道:

guard
  let compositionTrack = composition.addMutableTrack(
    withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid),
  let assetTrack = asset.tracks(withMediaType: .video).first
  else {
    print("Something is wrong with the asset.")
    onComplete(nil)
    return
}

您可以通過使用.video媒體類型調(diào)用addMutableTrack來添加新的視頻軌道祭玉。 如果您將不使用ID,則為軌道ID傳遞無效的ID常數(shù)春畔。 您還可以通過捕獲資產(chǎn)中的第一個(gè)也是唯一一個(gè)視頻軌道來從資產(chǎn)中捕獲視頻脱货。 如果您無法執(zhí)行這兩個(gè)操作中的任何一個(gè)岛都,請(qǐng)打印出錯(cuò)誤并使用nil調(diào)用完成處理程序。

現(xiàn)在蹭劈,在makeBirthdayCard(fromVideoAt:forName:onComplete :)的末尾添加以下代碼疗绣,以將資產(chǎn)中的視頻軌道插入到合成的視頻軌道內(nèi):

do {
  // 1
  let timeRange = CMTimeRange(start: .zero, duration: asset.duration)
  // 2
  try compositionTrack.insertTimeRange(timeRange, of: assetTrack, at: .zero)
  
  // 3
  if let audioAssetTrack = asset.tracks(withMediaType: .audio).first,
    let compositionAudioTrack = composition.addMutableTrack(
      withMediaType: .audio, 
      preferredTrackID: kCMPersistentTrackID_Invalid) {
    try compositionAudioTrack.insertTimeRange(
      timeRange, 
      of: audioAssetTrack, 
      at: .zero)
  }
} catch {
  // 4
  print(error)
  onComplete(nil)
  return
}

上面的代碼是這樣的:

  • 1) CMTimeRange指定視頻內(nèi)的時(shí)間范圍。 在這種情況下铺韧,您要從頭到尾添加視頻多矮,因此您可以設(shè)置從零到視頻持續(xù)時(shí)間的范圍。
  • 2) 一旦有了時(shí)間范圍哈打,就可以將資產(chǎn)中的整個(gè)視頻插入到合成的視頻軌道中塔逃。
  • 3) 如果資源還包含音頻軌道,請(qǐng)執(zhí)行與音頻軌道相同的操作料仗。 首先湾盗,將新的音軌添加到您的composition中,然后將資產(chǎn)的音頻插入音軌中立轧。
  • 4) 如果出現(xiàn)錯(cuò)誤格粪,請(qǐng)打印該錯(cuò)誤并使用nil調(diào)用完成處理程序。

2. Setting Up the Composition

接下來氛改,通過在方法末尾添加以下代碼來考慮composition的大小和方向:

compositionTrack.preferredTransform = assetTrack.preferredTransform
let videoInfo = orientation(from: assetTrack.preferredTransform)

let videoSize: CGSize
if videoInfo.isPortrait {
  videoSize = CGSize(
    width: assetTrack.naturalSize.height,
    height: assetTrack.naturalSize.width)
} else {
  videoSize = assetTrack.naturalSize
}

首先帐萎,請(qǐng)確保composition和資源的首選變換相同。 入門項(xiàng)目包括orientation(from:)胜卤,它返回視頻的方向(縱向還是橫向)疆导。 如果方向是縱向,則在檢查視頻尺寸時(shí)需要反轉(zhuǎn)寬度和高度葛躏。 否則澈段,您可以使用原始尺寸。

現(xiàn)在舰攒,您已經(jīng)創(chuàng)建了一個(gè)新的composition败富,其中包括原始文件中的視頻和音頻。 接下來摩窃,您將設(shè)置圖層兽叮,以確保可以向composition中的視頻添加背景和疊加層偶芍。

3. Core Animation – The Star of the Show

您的背景和疊加層均為CALayersCALayer是稱為Core Animation的框架的主要類德玫。

Core Animation位于應(yīng)用程序中每個(gè)視圖的后面匪蟀,負(fù)責(zé)繪制內(nèi)容并為其設(shè)置動(dòng)畫。 由于CALayer支持所有視圖宰僧,因此手機(jī)屏幕上繪制的所有內(nèi)容都是一個(gè)圖層材彪。

顧名思義,您可以在其他圖層的下方或上方繪制圖層,使其非常適合在視頻中添加背景或疊加層段化。 您可以通過AVFoundation合適的類AVVideoCompositionCoreAnimationTool在視頻中利用此功能嘁捷。 這是compositionCore Animation之間的橋梁,可讓您創(chuàng)建將CALayers應(yīng)用于視頻composition的新視頻显熏。

首先雄嚣,您需要三個(gè)不同的層。 一層是背景喘蟆,繪制在視頻后面缓升。 第二層繪制視頻的幀。 第三層是在視頻層頂部繪制的覆蓋層蕴轨。

4. Layering the Cake

通過將以下代碼添加到makeBirthdayCard(fromVideoAt:forName:onComplete :)的末尾來創(chuàng)建三層:

let backgroundLayer = CALayer()
backgroundLayer.frame = CGRect(origin: .zero, size: videoSize)
let videoLayer = CALayer()
videoLayer.frame = CGRect(origin: .zero, size: videoSize)
let overlayLayer = CALayer()
overlayLayer.frame = CGRect(origin: .zero, size: videoSize)

每層將具有相同的frame港谊,跨越整個(gè)視頻。

接下來橙弱,通過將所有這些層添加到方法中歧寺,將所有這些層組裝為單個(gè)父層:

let outputLayer = CALayer()
outputLayer.frame = CGRect(origin: .zero, size: videoSize)
outputLayer.addSublayer(backgroundLayer)
outputLayer.addSublayer(videoLayer)
outputLayer.addSublayer(overlayLayer)

在這里,您將創(chuàng)建一個(gè)新圖層棘脐,它將成為最終composition的圖層斜筐。 首先添加背景層,然后添加視頻層荆残,最后添加疊加層奴艾。 添加這些層的順序很重要。 在這里内斯,您將視頻布置在疊加層后面蕴潦,將背景布置在視頻后面。

現(xiàn)在俘闯,您可能會(huì)急于構(gòu)建項(xiàng)目并查看結(jié)果潭苞。 不幸的是,您還沒有視頻要播放真朗!

您已經(jīng)設(shè)置好圖層此疹,但是仍然必須使用AVFoundation從這些圖層導(dǎo)出視頻。


Exporting the Video

現(xiàn)在遮婶,您已經(jīng)擁有了所有的圖層蝗碎,是時(shí)候使用AVVideoCompositionCoreAnimationTool將它們組合成美味的視頻合成蛋糕了!

1. Creating a Video Composition

將以下內(nèi)容添加到方法中:

let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = videoSize
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(
  postProcessingAsVideoLayer: videoLayer, 
  in: outputLayer)

首先旗扑,創(chuàng)建一個(gè)新的AVMutableVideoComposition蹦骑。 您之前創(chuàng)建的compositionAVComposition,它可以保存視頻臀防,音頻和其他類型的音軌眠菇。 另一方面边败,您僅使用AVVideoComposition來合成視頻軌道。 在這種情況下捎废,您只需要一個(gè)視頻軌道笑窜。

接下來,將渲染視頻的尺寸設(shè)置為等于原始視頻的尺寸登疗。 frameDuration確定幀持續(xù)多長時(shí)間排截。 通過傳遞值為1且時(shí)間標(biāo)度(timescale)30CMTime,可以將幀持續(xù)時(shí)間設(shè)置為1/30秒谜叹,從而產(chǎn)生具有每秒30幀速率的視頻匾寝。

視頻合成(composition)的最后一點(diǎn)是添加動(dòng)畫工具。 此工具將組裝的輸出層和視頻層帶入視頻層荷腊,并將視頻軌道渲染到視頻層中艳悔。

接下來,添加以下代碼以將視頻軌道添加到視頻composition中:

let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(
  start: .zero, 
  duration: composition.duration)
videoComposition.instructions = [instruction]
let layerInstruction = compositionLayerInstruction(
  for: compositionTrack, 
  assetTrack: assetTrack)
instruction.layerInstructions = [layerInstruction]

視頻composition使用指令集來確定隨時(shí)顯示在視頻中的內(nèi)容女仰。 在這種情況下猜年,您只需要一條指令就可以在整個(gè)composition過程中顯示組合的視頻。

每個(gè)指令可以具有自己的分層指令疾忍,這些分層指令確定如何分層不同的視頻軌道乔外。 入門項(xiàng)目包括一個(gè)有用的方法,稱為compositionLayerInstruction一罩,它為您的視頻返回正確的指令杨幼。 這些說明告訴視頻進(jìn)行縮放和旋轉(zhuǎn)以匹配原始視頻的尺寸和方向。 如果沒有此指示聂渊,豎屏視頻將顯示為橫屏差购,并導(dǎo)致怪異的視頻拉伸。

現(xiàn)在汉嗽,您擁有了所有必需的片段:一個(gè)包含原始視頻和音頻的新合成欲逃,以及一個(gè)將視頻渲染到您的多層蛋糕中的視頻合成。 您將在導(dǎo)出會(huì)話(export session)中合并這些片段饼暑。

2. Using an Export Session

創(chuàng)建導(dǎo)出會(huì)話(export session)以將視頻渲染到文件:

guard let export = AVAssetExportSession(
  asset: composition, 
  presetName: AVAssetExportPresetHighestQuality) 
  else {
    print("Cannot create export session.")
    onComplete(nil)
    return
}

您可以通過傳遞其組成composition并使用最高質(zhì)量視頻的預(yù)設(shè)來創(chuàng)建新的AVAssetExportSession稳析。 最優(yōu)質(zhì)的生日賀卡,最優(yōu)質(zhì)的朋友弓叛。

現(xiàn)在彰居,創(chuàng)建一個(gè)文件路徑并設(shè)置導(dǎo)出會(huì)話:

let videoName = UUID().uuidString
let exportURL = URL(fileURLWithPath: NSTemporaryDirectory())
  .appendingPathComponent(videoName)
  .appendingPathExtension("mov")

export.videoComposition = videoComposition
export.outputFileType = .mov
export.outputURL = exportURL

在這里,您可以使用UUID獲得唯一的隨機(jī)字符串撰筷,并將其用作文件名陈惰。 您現(xiàn)在將文件保存到臨時(shí)目錄中; 用戶可以稍后決定將其存儲(chǔ)在照片庫中闭专。

然后奴潘,確保導(dǎo)出會(huì)話知道文件的URL和擴(kuò)展名,并為其提供您之前創(chuàng)建的視頻組成以呈現(xiàn)視頻影钉。

仍在makeBirthdayCard(fromVideoAt:forName:onComplete :)中画髓,通過添加以下內(nèi)容來啟動(dòng)導(dǎo)出:

export.exportAsynchronously {
  DispatchQueue.main.async {
    switch export.status {
    case .completed:
      onComplete(exportURL)
    default:
      print("Something went wrong during export.")
      print(export.error ?? "unknown error")
      onComplete(nil)
      break
    }
  }
}

開始導(dǎo)出時(shí),最終將通過在后臺(tái)線程上調(diào)用完成處理程序來完成導(dǎo)出平委。 確保調(diào)度到主線程奈虾,然后檢查導(dǎo)出結(jié)果。 如果成功完成廉赔,請(qǐng)使用導(dǎo)出文件的URL調(diào)用完成處理程序(completion handler)肉微。 如果出了點(diǎn)問題,您就會(huì)知道:打印出錯(cuò)誤和帶有nilcompletion蜡塌。

現(xiàn)在碉纳,終于到了構(gòu)建和運(yùn)行項(xiàng)目的時(shí)候了。 選擇一個(gè)視頻并輸入一個(gè)名稱馏艾,然后在您的設(shè)備稍等之后…您會(huì)看到與以前相同的結(jié)果劳曹。

是的,您完成所有這些工作都是為了獲得完全相同的結(jié)果琅摩。 只是不一樣铁孵。 背景和疊加層正在渲染,只是它們目前完全透明房资,因?yàn)槟€沒有添加任何內(nèi)容蜕劝。 所以開始添加?xùn)|西!


Adding a Background

現(xiàn)在轰异,您已經(jīng)設(shè)置了用于制作視頻的應(yīng)用程序岖沛,就可以開始為視頻添加色彩,使其看起來像一張生日賀卡溉浙。

在視頻下方繪制背景層烫止。 當(dāng)前,視頻與背景一樣大戳稽,因此您首先需要裁剪視頻以使背景從下方顯示馆蠕。 背景層將充當(dāng)視頻的邊框。 然后惊奇,您將圖像添加到背景圖層互躬,以使邊框充滿紙屑。

makeBirthdayCard(fromVideoAt:forName:onComplete :)中颂郎,在創(chuàng)建三層的下面吼渡,在設(shè)置overlayLayerframe的行的下面,添加以下代碼:

backgroundLayer.backgroundColor = UIColor(named: "rw-green")?.cgColor
videoLayer.frame = CGRect(
  x: 20,
  y: 20, 
  width: videoSize.width - 40, 
  height: videoSize.height - 40)

在這里乓序,您可以為圖層添加背景色寺酪,以便在運(yùn)行應(yīng)用程序時(shí)可以清楚地看到發(fā)生了什么坎背。 接下來,將圖層的frame設(shè)置為在每條邊上縮小20點(diǎn)寄雀,為視頻提供20點(diǎn)寬的綠色邊框得滤。

構(gòu)建并運(yùn)行該項(xiàng)目,選擇一個(gè)視頻盒犹,然后將其導(dǎo)出懂更,您將在視頻周圍看到一個(gè)邊框。

現(xiàn)在您可以看到視頻周圍的背景了急膀,通過在您剛剛寫的內(nèi)容下面添加以下代碼沮协,將圖像添加到背景層:

backgroundLayer.contents = UIImage(named: "background")?.cgImage
backgroundLayer.contentsGravity = .resizeAspectFill

要使用Core Animation顯示圖片,請(qǐng)?jiān)O(shè)置CALayercontents為圖片卓嫂。 還要將contentsGravity設(shè)置為.resizeAspectFill慷暂,以確保圖像始終填充圖層的大小,同時(shí)保持其縱橫比晨雳。

再次構(gòu)建并運(yùn)行呜呐,您將看到邊界,現(xiàn)在到處都是可愛的小五彩紙屑悍募!

設(shè)置好背景后蘑辑,您就可以處理視頻頂部的內(nèi)容了。

注意:iOS版本的Simulator中存在一個(gè)錯(cuò)誤坠宴,包括13.2.2洋魂,其中您要添加的背景和圖像將顯示為黑色。 如果遇到此錯(cuò)誤喜鼓,則需要在設(shè)備上運(yùn)行才能看到您的手工作品副砍。


Adding Images

您將通過在視頻中添加圖片來開始視頻疊加之旅。 入門項(xiàng)目已經(jīng)包含Swift庄岖,Android和恐龍(出于某種原因)的圖像豁翎,它們?cè)谏站蹠?huì)上玩得很開心。

VideoEditor.swift中添加一個(gè)新方法隅忿,該方法會(huì)將圖像添加到圖層:

private func addImage(to layer: CALayer, videoSize: CGSize) {
  let image = UIImage(named: "overlay")!
  let imageLayer = CALayer()
}

就像您對(duì)背景所做的一樣心剥,請(qǐng)使用普通的CALayer來保存圖像。 接下來背桐,將以下內(nèi)容添加到方法中以設(shè)置圖像的frame优烧,使其位于視頻底部的中心:

let aspect: CGFloat = image.size.width / image.size.height
let width = videoSize.width
let height = width / aspect
imageLayer.frame = CGRect(
  x: 0, 
  y: -height * 0.15, 
  width: width, 
  height: height)

在這里,您首先要計(jì)算圖像的長寬比链峭。 圖片將與視頻一樣寬畦娄,因此您必須使用寬高比來確定圖片的高度。 無論圖像的大小如何,都可以使它適合縱橫比熙卡。

您不希望顯示圖片的底部杖刷,因此將圖片的y坐標(biāo)設(shè)置為負(fù)值,這樣可以確保圖片在視頻下方開始播放驳癌。

接下來挺勿,將圖像添加到圖像層,并將圖像層添加到方法內(nèi)部傳遞的層喂柒。

imageLayer.contents = image.cgImage
layer.addSublayer(imageLayer)

返回makeBirthdayCard(fromVideoAt:forName:onComplete :),在設(shè)置contentsGravity后立即在后臺(tái)代碼之后調(diào)用您的方法:

addImage(to: overlayLayer, videoSize: videoSize)

您不再需要在后臺(tái)工作禾嫉; 相反灾杰,您想在視頻頂部顯示圖像。 這就是為什么您將overlayLayer傳遞給該方法的原因熙参。

構(gòu)建并運(yùn)行項(xiàng)目艳吠,選擇一個(gè)視頻,您應(yīng)該看到該圖像顯示在視頻上孽椰。

現(xiàn)在昭娩,您知道了如何向視頻添加疊加層。 看起來像是一場(chǎng)真正的派對(duì)黍匾! 在下一部分中栏渺,您將通過添加一些文字來改進(jìn)生日賀卡。


Adding Text

如果沒有說“Happy Birthday”锐涯,那將不會(huì)是一張生日賀卡磕诊,因此您需要在視頻中添加文字。 在VideoEditor.swift中纹腌,添加一個(gè)新方法:

private func add(text: String, to layer: CALayer, videoSize: CGSize) {
}

要顯示文本霎终,您將使用稱為CATextLayerCALayer子類。 由于子類具有一些繁瑣的API升薯,因此您將使用NSAttributedString創(chuàng)建和自定義文本莱褒。 將以下代碼添加到剛創(chuàng)建的方法中:

let attributedText = NSAttributedString(
  string: text,
  attributes: [
    .font: UIFont(name: "ArialRoundedMTBold", size: 60) as Any,
    .foregroundColor: UIColor(named: "rw-green")!,
    .strokeColor: UIColor.white,
    .strokeWidth: -3])

使用此代碼,您可以創(chuàng)建屬性字符串涎劈,并確保其使用大而圓的字體广凸。 您將文字設(shè)為綠色,因此很容易看到并添加白色描邊蛛枚,這樣文字就可以在所有背景上使用炮障。 如果希望文本既有描邊又有填充,則需要使描邊寬度為負(fù)坤候; 否則胁赢,只會(huì)顯示描邊。 不要懷疑為什么會(huì)這樣白筹,就是這樣智末。

接下來谅摄,使用屬性字符串創(chuàng)建一個(gè)文本層:

let textLayer = CATextLayer()
textLayer.string = attributedText
textLayer.shouldRasterize = true
textLayer.rasterizationScale = UIScreen.main.scale
textLayer.backgroundColor = UIColor.clear.cgColor
textLayer.alignmentMode = .center

這將創(chuàng)建一個(gè)文本層并設(shè)置文本。 您確保使用與當(dāng)前屏幕的比例相匹配的比例對(duì)文本進(jìn)行光柵化系馆,以使其看起來不會(huì)模糊送漠。 最后,為圖層提供透明的背景并將文本居中由蘑。

現(xiàn)在闽寡,設(shè)置文本的frame并將其添加到圖層中:

textLayer.frame = CGRect(
  x: 0, 
  y: videoSize.height * 0.66, 
  width: videoSize.width, 
  height: 150)
textLayer.displayIfNeeded()
layer.addSublayer(textLayer)

在將其添加到圖層之前,請(qǐng)?jiān)谖谋緢D層上調(diào)用displayIfNeeded()尼酿。 CALayersUIViews一樣爷狈,都是異步更新的,有時(shí)Core Animation不會(huì)在視頻的前一秒或后兩秒顯示文本裳擎。 通過調(diào)用此方法涎永,可以確保Core Animation盡快渲染文本。

最后鹿响,返回makeBirthdayCard(fromVideoAt:forName:onComplete :)羡微,在對(duì)addImage(to:videoSize :)的調(diào)用下直接調(diào)用新方法:

add(
  text: "Happy Birthday,\n\(name)", 
  to: overlayLayer, 
  videoSize: videoSize)

就像處理圖像一樣,將文本添加到覆蓋層惶我。

構(gòu)建并運(yùn)行該項(xiàng)目妈倔,選擇一個(gè)視頻,然后輸入朋友的名字绸贡。 本教程將把卡片發(fā)給Ray启涯。 導(dǎo)出后,您會(huì)看到一行文字恃轩,祝您的朋友生日快樂结洼!

今天是否實(shí)際上是您朋友的生日,都不應(yīng)阻止您慶祝叉跛,因?yàn)槟纳召R卡現(xiàn)在看起來不錯(cuò)松忍! 但是仍然缺少的一件事是運(yùn)動(dòng)。 繼續(xù)閱讀以了解如何向疊加層添加動(dòng)畫筷厘。


Adding Animations

關(guān)于CALayers的一件好事是鸣峭,正如Core Animation所暗示的那樣,它們是可動(dòng)畫的酥艳! 為了使您的生日賀卡更具動(dòng)態(tài)感摊溶,您將在文本中添加縮放動(dòng)畫以使其放大和縮小。

add(text:to:videoSize :)中充石,在最后一行之前添加以下行莫换,在該行中您將文本圖層添加為圖層的子圖層:

let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")

CABasicAnimation使您可以在兩個(gè)特定值之間向CALayers添加簡(jiǎn)單的動(dòng)畫。 CABasicAnimation使用鍵值觀察來設(shè)置和讀取動(dòng)畫值。 在這種情況下拉岁,您將設(shè)置變換的比例坷剧。

接下來,通過在剛添加的行下方添加以下代碼來設(shè)置動(dòng)畫:

scaleAnimation.fromValue = 0.8
scaleAnimation.toValue = 1.2
scaleAnimation.duration = 0.5
scaleAnimation.repeatCount = .greatestFiniteMagnitude
scaleAnimation.autoreverses = true
scaleAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)

此代碼在0.5秒內(nèi)將文本從0.8縮放為1.2喊暖。 您希望此動(dòng)畫無限期重復(fù)惫企,因此請(qǐng)將重復(fù)計(jì)數(shù)設(shè)置為.greatestFiniteMagnitude。 您還可以將autoreverses設(shè)置為true陵叽,以便scale0.81.2之間來回反彈狞尔。

最后,設(shè)置一些其他設(shè)置并將動(dòng)畫添加到圖層:

scaleAnimation.beginTime = AVCoreAnimationBeginTimeAtZero
scaleAnimation.isRemovedOnCompletion = false
textLayer.add(scaleAnimation, forKey: "scale")

在向視頻添加動(dòng)畫時(shí)巩掺,請(qǐng)務(wù)必將beginTime設(shè)置為AVCoreAnimationBeginTimeAtZero偏序;否則,動(dòng)畫將永遠(yuǎn)不會(huì)開始锌半。 您還需要確保動(dòng)畫不會(huì)在完成時(shí)被刪除。

構(gòu)建并運(yùn)行該項(xiàng)目寇漫,然后選擇一個(gè)視頻刊殉。

不用了無聊的靜態(tài)生日賀卡,現(xiàn)在您的文本可以上下縮放州胳!您使卡片更具動(dòng)態(tài)性记焊,但是為什么要停在那里?在下一部分中栓撞,您也將五彩紙屑添加到您的生日賀卡中遍膜!


Adding the Final Touches

除了CATextLayer,還有許多其他強(qiáng)大的CALayer子類瓤湘。例如瓢颅,CAGradientLayer顯示顏色漸變润梯。 CAReplicatorLayer允許您通過根據(jù)一組規(guī)則重復(fù)一層來創(chuàng)建樣式何暇。使用CAShapeLayer,您可以繪制圓形住涉,橢圓形木人,弧形信柿,多邊形甚至任意形狀。通過將其與CAAnimation結(jié)合使用醒第,您可以動(dòng)態(tài)更改形狀并為其設(shè)置動(dòng)畫效果渔嚷,從而產(chǎn)生一些很酷的效果。

沒有五彩紙屑的生日是不完整的稠曼,這就是為什么要最后添加五彩紙屑效果的原因形病。五彩紙屑將從視頻的頂部落下。幸運(yùn)的是,這是數(shù)字紙屑窒朋,因此您以后無需清理混亂搀罢。

要?jiǎng)?chuàng)建五彩紙屑,您將使用另一個(gè)稱為CAEmitterLayerCALayer子類侥猩,該子類可用于創(chuàng)建粒子效果榔至。您可以確定圖層的形狀,并設(shè)置所需的粒子出生率欺劳,??速度和其他設(shè)置唧取。然后,該層將根據(jù)其形狀發(fā)出粒子划提。

入門項(xiàng)目已經(jīng)包括一個(gè)名為addConfetti(to :)的方法枫弟,該方法通過將隨機(jī)的五彩紙屑圖像和隨機(jī)的顏色組合在一起來創(chuàng)建16個(gè)不同的粒子。然后鹏往,將CAEmitterLayer設(shè)置為具有這16個(gè)粒子的視頻層正上方的線淡诗。結(jié)果是五彩紙屑從視頻頂部掉落!

要添加五彩紙屑伊履,請(qǐng)?jiān)谧詈笠淮螡L動(dòng)到makeBirthdayCard(fromVideoAt:forName:onComplete :)韩容,然后在調(diào)用addImage(to:videoSize :)的上方調(diào)用該方法:

addConfetti(to: overlayLayer)

構(gòu)建并運(yùn)行該項(xiàng)目,并享受CALayers的榮耀唐瀑!

通過添加五彩紙屑群凶,您可以使生日賀卡獲得至少10倍的勝利。 相信我哄辣,我自己算了算请梢。

到目前為止,您應(yīng)該得到一張卡片力穗,祝賀您在此AVFoundation教程中學(xué)到的一切毅弧! 您學(xué)習(xí)了如何使用AVVideoCompositionCoreAnimationToolCALayers與視頻結(jié)合起來以添加背景和疊加層,如何制作視頻合成以及如何將視頻導(dǎo)出到文件当窗。

后記

本篇主要講述了向視頻層添加疊加層和動(dòng)畫形真,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市超全,隨后出現(xiàn)的幾起案子咆霜,更是在濱河造成了極大的恐慌,老刑警劉巖嘶朱,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛾坯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡疏遏,警方通過查閱死者的電腦和手機(jī)脉课,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門救军,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人倘零,你說我怎么就攤上這事唱遭。” “怎么了呈驶?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵拷泽,是天一觀的道長。 經(jīng)常有香客問我袖瞻,道長司致,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任聋迎,我火速辦了婚禮脂矫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霉晕。我一直安慰自己庭再,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布牺堰。 她就那樣靜靜地躺著拄轻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萌焰。 梳的紋絲不亂的頭發(fā)上哺眯,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天谷浅,我揣著相機(jī)與錄音扒俯,去河邊找鬼。 笑死一疯,一個(gè)胖子當(dāng)著我的面吹牛撼玄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播墩邀,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼掌猛,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了眉睹?” 一聲冷哼從身側(cè)響起荔茬,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竹海,沒想到半個(gè)月后慕蔚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斋配,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年孔飒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灌闺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坏瞄,死狀恐怖桂对,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸠匀,我是刑警寧澤蕉斜,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站狮崩,受9級(jí)特大地震影響蛛勉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睦柴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一诽凌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坦敌,春花似錦侣诵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蘸炸,卻和暖如春躬络,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搭儒。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工穷当, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淹禾。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓馁菜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铃岔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子汪疮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容