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 平台上的视频播放。

后记

本篇主要讲述了基于视频播放器的画中画实现,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(AVKit框架详细解析(二) —— 基于视频播放器的画中画实现(一))