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

版本記錄

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

前言

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

開始

首先看下主要內容:

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

接著看下寫作環(huán)境:

Swift 5, iOS 14, Xcode 12

下面就是正文了。

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

具體來說,您將了解:

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

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

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

下載項目材料。

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

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

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


Adding Background Modes

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

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

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

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

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

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


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:)的最后一行是呈現(xiàn)播放器控制器的方法:

presentPlayerController(with: player, customPlayer: false)

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

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

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

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

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

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

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

1. Starting and Stopping PiP

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

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

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

pictureInPictureController?.startPictureInPicture()

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

pictureInPictureController?.stopPictureInPicture()

當用戶點擊適當?shù)陌粹o時,這些方法告訴畫中畫控制器啟動或停止畫中畫。

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

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

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


Dismissing the Custom Player Controller When PiP Starts

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

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

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

dismiss(animated: true, completion: nil)

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

activeCustomPlayerViewControllers.insert(self)

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

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

1. Handling PiP controller failure and closing

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

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

activeCustomPlayerViewControllers.remove(self)

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

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

activeCustomPlayerViewControllers.remove(self)

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

現(xiàn)在,構建并運行。 播放視頻并進入畫中畫模式。

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


Restoring the Player Controller

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

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

delegate?.playerViewController(
  self,
  restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: 
    completionHandler)

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

現(xiàn)在打開 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)
  }
}

下面是上面代碼中發(fā)生的事情:

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

構建并運行。

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

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

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

presentPlayerController(with: player, customPlayer: false)

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

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

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

后記

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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容