版本记录
版本号 | 时间 |
---|---|
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
,但示例应用程序是跨平台的,也适用于tvOS
和 macOS
。 PiP
是 AVKit
的一部分,可在所有平台上使用。
您需要一个物理设备来学习本教程。 如果您没有可用的 iPhone、iPad 或 Apple TV,您可以使用 Mac
使用 Xcode
中的 My Mac
target
来测试画中画功能。
下载项目材料。
构建并运行启动项目:RickTV
应用程序。
RickTV
有各种各样的内容,但出于某种原因,无论您选择什么视频,都只会播放 Rick Astley
的 Never Gonna Give You Up
。 该死的那些互联网巨魔。
行。 是时候学习如何在PiP
中观看 RickTV
!
Adding Background Modes
要在您的应用程序中启用画中画功能,您需要添加Background Modes功能。
在项目导航器中单击 RickTV
项目,然后单击Signing & Capabilities。
注意:对
RickTV target
执行以下步骤时,Xcode 可能会崩溃。 如果发生这种情况,只需重新启动它。
您需要为 RickTV
和 RickTV-iOS
的targetss
重复以下步骤:
- 1) 选择
RickTV
或RickTV-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
并滚动到标有注释的 UICollectionViewDataSource
的Implementation
部分。
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
模式,请转到实现 CustomPlayerControlsViewDelegate
的 CustomPlayerViewController
的扩展。
你会看到两个相关的方法: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.swift
的 pictureInPictureController(_: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
而不是自定义播放器控制器来测试画中画,请修改 CategoryListViewController
的 collectionView(_:didSelectItemAt:)
最后一行中的 customPlayer
,将其更改为 false
:
presentPlayerController(with: player, customPlayer: false)
这将显示系统播放器控制器而不是您的控制器,您可以看到相同的播放器恢复行为也有效。
要了解有关画中画的更多信息,请查看 WWDC 2020 的 Master Picture in Picture on tvOS。
您还可以了解有关 AVKit 的更多信息learn more about AVKit,它支持 Apple 平台上的视频播放。
后记
本篇主要讲述了基于视频播放器的画中画实现,感兴趣的给个赞或者关注~~~