AVKit框架詳細解析(二) —— 基于視頻播放器的畫中畫實現(一)

版本記錄

版本號 時間
V1.0 2021.08.14 星期六

前言

AVKit框架為媒體播放創建視圖級別的服務,包含用戶控件,章節導航以及對字幕和隱藏式字幕的支持。接下來幾篇我們就一起看一下這個框架。感興趣的可以看下面幾篇文章。
1. AVKit框架詳細解析(一) —— 基本概覽(一)

開始

首先看下主要內容:

了解如何為所有應用平臺的默認和自定義視頻播放器實現畫中畫,內容來自翻譯

接著看下寫作環境:

Swift 5, iOS 14, Xcode 12

下面就是正文了。

如今,用戶希望能夠使用Picture in Picture (PiP) - 畫中畫播放視頻。 PiP 模式將視頻內容最小化到一個小窗口中,允許用戶進行多任務處理。 在本教程中,您將學習如何向使用 UIKit 構建的現有視頻應用程序添加畫中畫支持。

具體來說,您將了解:

  • Background modes
  • 設置 AVAudioSession
  • 控制畫中畫顯示
  • PIP與自定義播放器控制器結合使用

本教程使用iPhone,但示例應用程序是跨平臺的,也適用于tvOSmacOSPiPAVKit 的一部分,可在所有平臺上使用。

您需要一個物理設備來學習本教程。 如果您沒有可用的 iPhone、iPad 或 Apple TV,您可以使用 Mac 使用 Xcode 中的 My Mac target來測試畫中畫功能。

下載項目材料。

構建并運行啟動項目:RickTV 應用程序。

RickTV 有各種各樣的內容,但出于某種原因,無論您選擇什么視頻,都只會播放 Rick AstleyNever Gonna Give You Up。 該死的那些互聯網巨魔。

行。 是時候學習如何在PiP中觀看 RickTV


Adding Background Modes

要在您的應用程序中啟用畫中畫功能,您需要添加Background Modes功能。

在項目導航器中單擊 RickTV 項目,然后單擊Signing & Capabilities

注意:對 RickTV target執行以下步驟時,Xcode 可能會崩潰。 如果發生這種情況,只需重新啟動它。

您需要為 RickTVRickTV-iOStargetss重復以下步驟:

  • 1) 選擇 RickTVRickTV-iOS target
  • 2) 單擊 + Capabilit
  • 3) 搜索Background Modes,然后雙擊將其添加為功能。
  • 4) 在新添加的Background Modes部分,選中Audio, AirPlay, and Picture in Picture復選框。

很好! 現在您已經設置了所有內容,您可以在您的應用程序中實現畫中畫。


Implementing PiP

打開 AppDelegate.swift

AppDelegate 內的 application(_:didFinishLaunchingWithOptions:) 中,添加以下代碼:

let audioSession = AVAudioSession.sharedInstance()

在上面的代碼中,您引用了 AVAudioSession 的共享實例。

接下來,將以下內容添加到您在上一步中添加的代碼中:

do {
  try audioSession.setCategory(.playback, mode: .moviePlayback)
} catch {
  print("Failed to set audioSession category to playback")
}

通過這樣做,您將音頻會話的類別設置為 .playback,將播放模式設置為 .moviePlayback。 此操作可能會失敗,因此您將其包裝在 do catch 塊中。

構建并運行。 播放視頻,您將在播放器控制器中看到畫中畫圖標。

成功! 點按畫中畫圖標以查看它是否有效。

你已經看到,如果你使用標準的 AVPlayerViewController,畫中畫幾乎是自動的。 如果您的應用程序具有自定義播放控制器,則您需要做一些額外的工作來支持畫中畫。 接下來您將了解這一點。


Enabling PiP in a Custom Player Controller

你很幸運——示例項目有一個內置的自定義播放器控制器。 要使用它而不是默認的 AVPlayerViewController,您需要更改點擊視頻調用的代碼行。

打開 CategoryListViewController.swift 并滾動到標有注釋的 UICollectionViewDataSourceImplementation部分。

collectionView(_:didSelectItemAt:)的最后一行是呈現播放器控制器的方法:

presentPlayerController(with: player, customPlayer: false)

customPlayer 更改為 true 以使用自定義播放器控制器。

構建并運行。 點擊視頻以顯示自定義播放器控制器。

很好! 視頻在自定義控制器中播放。 但是……如果您點擊畫中畫按鈕,則什么也不會發生。 別擔心,你現在會解決這個問題的。

打開 CustomPlayerViewController.swift。 在 viewDidLoad()中,在 view.layer.addSublayer(playerLayer)下,添加以下代碼:

pictureInPictureController = AVPictureInPictureController(
  playerLayer: playerLayer)
pictureInPictureController?.delegate = self

此代碼初始化pictureInPictureController 并設置其代理。

接下來,您將添加功能,以便您的用戶可以在自定義播放器控制器中啟動和停止畫中畫。

1. Starting and Stopping PiP

要允許您的用戶停止和啟動 PiP 模式,請轉到實現 CustomPlayerControlsViewDelegateCustomPlayerViewController 的擴展。

你會看到兩個相關的方法:controlsViewDidRequestStartPictureInPicture(_:)controlsViewDidRequestStopPictureInPicture(_:)

controlsViewDidRequestStartPictureInPicture(_:)中,將// Start PiP 替換為:

pictureInPictureController?.startPictureInPicture()

然后,在 controlViewDidRequestStopPictureInPicture(_:) 中,將// Stop PiP替換為:

pictureInPictureController?.stopPictureInPicture()

當用戶點擊適當的按鈕時,這些方法告訴畫中畫控制器啟動或停止畫中畫。

確保僅在收到用戶輸入時調用關聯的 AVPictureInPictureController 方法。 如果您違反此規則,App Review 將不會批準您的應用!

構建并運行。 打開視頻并點擊按鈕以啟動畫中畫。

太棒了! PiP 開始在自定義控制器中播放,但您還沒有完成。如果用戶選擇播放視頻畫中畫,可以合理地假設他們不希望您的應用程序的屏幕顯示有關視頻現在如何播放畫中畫的大量信息。他們可能想繼續使用您的應用程序的其余部分。此外,如果您點擊按鈕從畫中畫返回標準播放,則不會發生任何事情。接下來您將解決這些問題中的第一個。


Dismissing the Custom Player Controller When PiP Starts

當用戶啟動畫中畫時,您可以假設這是因為他們想在繼續欣賞視頻的同時在您的應用程序中執行其他操作。目前,當視頻在畫中畫窗口中播放時,示例應用程序會顯示一條消息。您可以使用畫中畫控制器代理中的方法來控制畫中畫播放開始和結束時發生的情況。

CustomPlayerViewController.swift 中,滾動到標有 AVPictureInPictureDelegate 的擴展。代理方法都帶有空實現,以節省您的輸入時間!

首先,在pictureInPictureControllerDidStartPictureInPicture(_:)中,添加以下代碼:

dismiss(animated: true, completion: nil)

在這里,您可以在畫中畫啟動時關閉自定義播放器控制器。 但是,如果您構建并運行并嘗試此操作,您將看到畫中畫窗口立即關閉。 這是因為您的自定義播放器對象被釋放,這是唯一保留畫中畫控制器的東西,因此也被釋放。 為了防止這種情況,將以下代碼添加到 pictureInPictureControllerWillStartPictureInPicture(_:)

activeCustomPlayerViewControllers.insert(self)

activeCustomPlayerViewControllers 是一個全局 Set,它將您的播放器對象保存在內存中,這意味著您可以安全地關閉它。

如果畫中畫控制器出現故障或被用戶關閉,您需要清理它。

1. Handling PiP controller failure and closing

當用戶使用關閉按鈕關閉畫中畫或畫中畫模式失敗時,您需要從活動控制器集中刪除自定義播放器控制器。

pictureInPictureController(_:failedToStartPictureInPictureWithError:)中,添加以下代碼:

activeCustomPlayerViewControllers.remove(self)

這會在畫中畫無法啟動時從活動控制器集中刪除自定義控制器。

接下來,在pictureInPictureControllerDidStopPictureInPicture(_:) 中,寫入同一行:

activeCustomPlayerViewControllers.remove(self)

這與上面的工作相同,但在用戶關閉畫中畫窗口時調用。

現在,構建并運行。 播放視頻并進入畫中畫模式。

現在啟動畫中畫會關閉自定義播放器控制器,并關閉畫中畫窗口。 但是,如果您點按按鈕以從畫中畫返回標準全屏播放,繼續播放相同的視頻,則沒有任何反應。 你現在會處理這個問題。


Restoring the Player Controller

現在,當您開始以畫中畫模式播放視頻時,您可以完全關閉窗口,但無法返回全屏。 這對于默認的 AVPlayerViewController 和自定義播放器控制器都是如此。 要擺脫困境,您需要添加播放器控制器恢復功能。

CustomPlayerViewController.swiftpictureInPictureController(_:restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:)中,插入以下代碼:

delegate?.playerViewController(
  self,
  restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: 
    completionHandler)

CustomPlayerViewController 有一個代理,它反映了 AVPlayerViewControllerDelegate 中包含的許多方法。 您在此處調用的方法等效于當用戶請求從畫中畫返回標準播放時標準播放器將調用的方法。

現在打開 CategoryListViewController.swift。 在文件的底部,你會看到一個類的擴展,它有一個方法:restore(playerViewController:completionHandler:)

對于這兩種類型的播放器控制器,當用戶在畫中畫窗口中點擊Restore時,代理擴展會調用此方法。

在方法內部,添加以下代碼:

// 1
if let presentedViewController = presentedViewController {
  // 2
  presentedViewController.dismiss(animated: false) { [weak self] in
    // 3
    self?.present(playerViewController, animated: false) {
      completionHandler(true)
    }
  }
} else {
  // 4
  present(playerViewController, animated: false) {
    completionHandler(true)
  }
}

下面是上面代碼中發生的事情:

  • 1) 檢查是否已經存在任何其他視圖控制器。 也許您的用戶正在同時觀看兩個視頻,它們的效果如何!
  • 2) 如果有一個展示的控制器,在沒有動畫的情況下關閉它,因為用戶希望盡快讓他們的視頻恢復正常并且對任何視圖控制器動畫不感興趣。
  • 3) 一旦關閉完成,呈現原始播放器控制器,再次沒有動畫,然后調用completion block,以便系統知道將回放手動返回到原始播放器層。
  • 4) 如果沒有展示控制器,只需再次呈現原始控制器并調用completion block

構建并運行。

上面的 GIF顯示了兩個代碼路徑:

  • 1) 進入畫中畫然后恢復繼續全屏顯示畫中畫視頻。
  • 2)進入畫中畫,開始第二個視頻,然后恢復畫中畫會用畫中畫內容替換全屏視頻。

要使用 AVPlayerViewController 而不是自定義播放器控制器來測試畫中畫,請修改 CategoryListViewControllercollectionView(_:didSelectItemAt:) 最后一行中的 customPlayer,將其更改為 false

presentPlayerController(with: player, customPlayer: false)

這將顯示系統播放器控制器而不是您的控制器,您可以看到相同的播放器恢復行為也有效。

要了解有關畫中畫的更多信息,請查看 WWDC 2020 的 Master Picture in Picture on tvOS

您還可以了解有關 AVKit 的更多信息learn more about AVKit,它支持 Apple 平臺上的視頻播放。

后記

本篇主要講述了基于視頻播放器的畫中畫實現,感興趣的給個贊或者關注~~~

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容