大多数浏览器和
Developer App 均支持流媒体播放。
-
精通 Apple tvOS 画中画
画中画即将登陆 Apple TV:Apple tvOS 拥有实时视频回放、随时切换画中画与全屏画面的功能,为您带来前所未有的多任务灵活度。探索如何将 AVPictureInPictureController 加入您的项目,配合熟悉的 API,创建自定义回放界面,并使用您的 app 为用户提供最佳的视频回放体验。由于“向上滑动”手势现已改由 Apple tvOS 14 中的 AVPlayerViewController 使用,我们还将向您展示如何更改 customOverlayViewController 的启动手势。 为了提升教学效率,建议您掌握基础的 AVKit 知识后再收看本段视频。要获得更多信息,请观看“使用 AVKit 制作方便易懂的媒体回放功能”。 衷心期待您能熟练掌握方法,以 AVPlayerViewController 使用 Apple tvOS 独特的画中画功能。
资源
- Adopting Picture in Picture in a Custom Player
- Adopting Picture in Picture in a Standard Player
- Adopting Picture in Picture Playback in tvOS
- Human Interface Guidelines: Playing video
相关视频
WWDC21
WWDC19
-
下载
(你好) (2020 全球开发者大会) 你好 欢迎参加全球开发者大会
你好 欢迎观看 (精通 Apple tvOS 画中画) 我叫 Jad Osseiran 是主攻 Apple tvOS 的一名软件工程师 在本次讲座中 我们首先将从一个较高的层面 对 Apple tvOS 的画中画模式进行概览 然后再为大家讲解 在你的 app 中实现画中画模式 都需要进行哪些工作 之后画面会切换到 Dan 那边 让他来为大家讲解 如何针对画中画模式 来对你现有的 app 进行调整 最后 Dan 会通过一个演示版本 来归纳我们在本次讲座中所学习过的内容 好的 那么我们先来对 Apple tvOS 的画中画模式概览一番 或者用更简洁的叫法:PiP 这是某个 app 的浏览视图 里面有两个视频 我们来看看 如何用画中画模式播放这两个视频 甚至让它们主副交换 首先 我们把一个视频调成全屏 调出播放界面之后 我们可以选择“开始 PiP”按钮 让这个视频进入画中画模式 Apple tvOS 独有的一个功能是 我们可以打开另一个视频 让它们并排同时播放
而我们随时都可以调出播放用户界面 让全屏模式下的视频和 PiP 里的内容主副交换 如果我们现在返回浏览视图…
可以看到 PiP 里的视频 正是我们所播放的第二个视频 我们成功将这两个视频调成了 PiP 模式 然后又对它们进行了主副交换 我们可以看到 Apple tvOS 下的 PiP 体验与 iPadOS 十分相似 而且还可以添加同步播放的视频 因此较之更胜一筹 那么 如何才能让你的 app 利用这项功能呢?
接下来 我们就来深入了解一下 如何在 Apple tvOS 下实现画中画模式 (实现 Apple tvOS 端的画中画模式) 第一步是对你的 Xcode 项目进行设置 在编写任何代码之前 你需要先在 Xcode 12 中 为你的 app 添加画中画功能 方法是在“添加功能”窗口里 选择“背景模式” 然后只需在该项目的 “签名与功能”标签页里 勾选“画中画”一项即可 接下来 你需要配置音频会话 把 app 的 AVAudioCategory 设为“播放” 你 app 里的代码可能会是这个样子 以上就是项目的具体设置 如果你之前曾在 iPadOS 上实现过 PiP 这些步骤对你来说应该很熟悉了 因为各个平台上的设置方式都是一样的 在 Apple tvOS 下实现 PiP 的第二步是 让你的播放用户界面适配这项功能 那么我们就来看看 对于标准的播放用户界面 这意味着什么
标准的播放用户界面 或者叫 AVPlayerViewController 会显示 PiP 模式下 正确的用户界面功能可供性 如果你已经按照我们之前所讲的设置步骤 完成了项目配置 那么播放用户界面上 就会自动显示出这些内容
为帮助你针对 PiP 的生命周期 作出相应调整 你的 app 会利用 AVPlayerViewController 的委托方法 现在 Apple tvOS 已经内置这些方法了 如果这个 API 对你来说比较陌生 或者你想听我快速回顾一下相关知识 可以看一看我列出的这几种委托方法 我们稍后再本次讲座中还会再次见到 这些方法覆盖了 PiP 的整个生命周期 包括启动 PiP 终止 PiP 以及还原 PiP 现在我想重点讲一下 关于还原的委托方法 使用这个方法 你的 app 有机会还原自己的用户界面 这样 即使不在画中画模式下 也可以与播放器相适应 实现这一方法时 请务必记住 要以迅速还原为目标 避免加入动画效果 这样可以有效确保用户眼前的 界面能快速恢复原貌 如果你的 app 花费了太长时间 正在还原中的播放器将被终止进程 导致用户体验大大降低 开始使用标准播放用户界面 是个非常简单的过程 与现有的 iPadOS 实现方式也非常相似 稍后 Dan 将进一步为大家讲解 如何通过 AVPlayerViewController 尽可能挖掘 PiP 的优势 不过我们了解到 你们编写的某些 app 需要使用自定义的播放用户界面 如果你的 app 也是这种情况 那么你在 Apple tvOS 平台实现 PiP 时 所要做的第二步 就与我们刚才所讲的有所不同 我们现在就来看一看这个问题 (使用你自己的播放用户界面 API 概览) AVPictureInPictureController 现在已经可以在 Apple tvOS 平台使用了 就像在 iPadOS 和 iOS 上一样 你的 app 需要它来控制 PiP
AVPictureInPictureController 现在有了一个新的 Apple tvOS API 可以帮助你管理自己的自定义用户界面
我们来看一看这个 API
你的 app 会利用 canStopPictureInPicture 来帮助你显示合适的功能可供性 并在你的自定义播放用户界面中 提供正确的行为 我们稍微再深入研究一下这个问题 如果 canStopPictureInPicture 在你的全屏播放器里被设为“假”… (显示正确的用户界面) …这会告诉你的 app 你的自定义播放用户界面 应该显示一个 start PiP 功能可供性
当你的用户选择 start PiP 功能可供性的时候 你的 app 应该调用管理 AVPictureInPictureController 下面的 startPictureInPicture 以便按计划启动 PiP
反过来 如果 canStopPictureInPicture 为“真” 你的 app 应该显示 用户界面方面的功能可供性 以便在你的自定义播放用户界面里 进行主副交换和终止 PiP
值得注意的是 并不存在一个 专门用来发起某次主副交换的调用命令 进行主副交换的时候 你的 app 应该调用 startPictureInPicture 余下工作则由系统负责完成 如果我们从全屏播放器的角度 来思考这个问题 它启动 PiP 模式 是在一个简约的开头 和一个主副交换的场景下 对于这两个情景来说 startPictureInPicture 都十分合适 要在全屏播放器里终止一个现有的 PiP 你的 app 应该调用 stopPictureInPicture 下面是负责管理这个全屏播放器的 AVPictureInPictureController
当用户选择“终止”这个功能可供性时 你的 app 应该只调用 stopPictureInPicture 这一点很重要
CanStopPictureInPicture 随时都有可能发生变化 举例来讲 用户可能会在 PiP 窗口内 终止 PiP 在这种情况下 你的用户界面必须一直保持在最新版本 要实现这一点 你的 app 需要观察 canStopPictureInPicture 是否有变 并对你的用户界面进行相应更新 你的代码看起来可能会是这个样子 我们强烈建议你阅读最新版的 Human Interface Guidelines 它详细讲述了 如何以最佳效果显示这些功能可供性 接下来 我们要讲一讲 Apple tvOS 是如何允许发布到 多个“正在播放”信息中心的 当两个视频同时播放的时候 它们各自对应的“正在播放”信息 也应该得到更新 你可以通过在 MPNowPlayingSession 中输入 AVPlayers 来实现这一点 你的 app 可以有多个 MPNowPlayingSessions 就 PiP 而言 app 里会有一个对应 PiP 内容的 “正在播放”会话 和一个对应全屏内容的“正在播放”会话 实际上 为了能在 Apple tvOS 平台上使用 PiP AVPictureInPictureController 中 所使用的同一个播放器 必须要被绑定到一个 MPNowPlayingSession 上 我们来看一看用代码该如何表现这一点
自定义播放用户界面可能会是这个样子 我们看到 用来创建 AVPictureInPictureController 的 播放器 也用在了 MPNowPlayingSession 的初始化器里 之后 在条件合适的情况下 你可以对这个会话的“正在播放”信息中心 进行设置 让它变成活动状态 这样可以让 MediaPlayer 开始代表你发布这些信息 在讲解下一个问题之前 在 MPNowPlayingSession 的使用上 还有几件事需要注意 (发布“正在播放”状态) 需要注意的第一件事是 更新到 MPNowPlayingSession 会让系统忽略来自默认的 MPNowPlayingInfoCenter 的更新 在这种情况下 你应该确保自己 在实现 PiP 时 整个 app 都要切换到 MPNowPlayingSession 因为现在有多个“正在播放”信息中心 会由系统来决定要在 “正在播放”系统用户界面里播放哪些信息 不过 有一件重要的事情是 请务必一直发布“正在播放”信息 即便你的会话 现在并没有在系统用户界面中显示出来 你的 app 会想要这样去做 因为系统随时都有可能 选择显示你的会话 到现在为止 我们所涉及的内容包括设置 以及将播放用户界面与 PiP 相整合的 两种不同方式 对于在 Apple tvOS 平台上实现 PiP 我们要讲的最后一点 是你的 app 在执行主副交换时 所应期待的生命周期
第一个情景是 在你自己的 app 内主副交换 假设这些视频来自你的 app 我们来看一看你的体育 PiP 内容 和你的全屏动画电影之间的生命周期
播放器委托对象可能会是这个样子
首先 你的 app 会看到 即将进入 PiP 模式的播放器的 willStart 委托回调函数 接下来 这个即将终止 PiP 的播放器 会要求你的 app 还原用户界面 以便与离开 PiP 模式后的播放器相适应 记住 快速还原在这里是必需的 待用户界面还原完毕之后 你便会得到这个即将终止 PiP 的播放器 的一个 willStop 回调函数 最后 待主副交换完毕 你的两个播放器便会得到 它们各自对应的 didStart 和 didStop 委托回调函数 我们要讲到的第二个情景是 在不同 app 间主副交换 你的 app 既可以先以画中画模式启动 然后与另一个 app 的 PiP 主副交换 也可以让你的 app 在全屏模式下调出自己的画中画模式 我们先来讲一讲 让你的 app 以 PiP 模式启动的情况
假设你的 app 要提供这部全屏动画电影 在这个情景下 PiP 体育内容 将由另一个 app 来提供 当我们主副交换的时候 你的内容会进入 PiP 模式 那么就用我们之前使用过的 这个播放器委托函数 以之前谈到的这个委托回调函数为重点
你会得到一个 willStart 回调函数 如你所料 动画结束后 会出现一个与之相匹配的 didStart
随后你的 app 就会变成背景 这是因为 作为主副交换过程的一部分 另一个 app 变成了前景 以便播放它们的全屏内容 如果你在 iPadOS 下实现过画中画模式 这些内容对你来说可能比较熟悉 当用户转到主界面 你的 app 被自动切成 PiP 模式时 你所看到的次序是与这个完全一样的
最后 我们再来讲一下跨 app 的情况 也就是说 你的 app 要从 PiP 变成全屏 在最后这个例子里 假设现在是你的 app 在以 PiP 模式播放内容 而另一个 app 正在全屏模式下播放 在位置互换的过程中 你的 PiP 内容 将占据全屏
那么我们再一次以这个播放委托函数为例 讲解这个过程
首先 你的 app 会被调到前景 然后你就有机会快速还原你的用户界面了
然后它被告知画中画模式将要终止
最后 待动画完成之后 你会被告知 你的播放器画中画模式已经终止 好的 关于如何从头开始实现画中画模式 我们到这里已经讲完了 配置完你的项目之后 我们探索了将你的播放用户界面 与 PiP 相整合的两种方式 如果你的 app 用的是 AVPlayerViewController 那么系统已经 自动替你处理好了那些繁重的工作 不过 如果你有 使用自定义播放用户界面的需求 你可以使用 AVPictureInPictureController 我们讲到的最后一个实现步骤 是你的 app 在位置互换过程中 所应有的期待 遵照以上这些步骤 你会发现 在 Apple tvOS 下 设置和运行画中画模式 真是太轻松了 但你现有的那些 app 呢? 下面画面就切换到 Dan 那边 让他来为你讲解这方面的问题 他会告诉你该如何尽可能地利用 AVPlayerViewController 的优势
谢了 Jad 在 Apple TV 上 AVPlayerViewController 可以提供 标准的视频播放体验 它支持各种各样的遥控器 跳过、扫描、控制播放进度 Siri 命令、对插页式广告的支持 此外还有很多 我们鼓励 Apple tvOS 平台的所有 app 都采用它来进行视频播放 如果你已经发布过使用 AVPlayerViewController 的 app 我们来看看你需要做些什么 才能让你的 app 适配画中画模式 首先 我们来讲一讲互动式叠加层
互动式叠加层会在视频上方 显示自定义 app 控件 比如频道指南 或是观看其他相关节目的建议 就现在的绝大多数 app 而言 用户只需拿着遥控器向上轻扫 即可看到这些叠加层 (互动式叠加层) 自 Apple tvOS 13 开始 向上轻扫手势就 被 AVPlayerViewController 所保留 用户想要使用画中画控件 就必须使用这个手势 所以 对于你的互动式上划控件 请使用 AVPlayerViewController 的 customOverlayViewController 属性 这项属性去年就被加入到了 Apple tvOS 13 中 如果你到现在还没有采用 customOverlayViewController 等你开始使用最新版 SDK 后 请务必这样做 因为你的自定义向上轻扫手势识别器 会停止工作 用户只需迅速向上轻扫遥控器 或是在转移栏变为可见时 点击屏幕上的某个按钮 即可看到你的自定义叠加层 如果想更深入地了解自定义叠加层 请参看去年全球开发者大会上的 一场演示 名为 《使用 AVKit 传输直观式的媒体播放》 转移栏变为可见时 自定义叠加层控件的按钮 会显示在左边 接下来 我们来讲一讲“正在播放”信息 “正在播放”信息是有关视频的元数据 同时包含静态与动态 可中继至 OS、其他 app 以及远程设备 (“正在播放”信息) AVKit 会代表你的 app 发布“正在播放”信息 所以 如果你需要增加这项元数据 请使用 AVPlayerItem 的 外部元数据属性 而不要直接与 MediaPlayer 框架交谈
在 Apple tvOS 14 中 旧版本的共享式 “正在播放”信息中心已经退出使用了 所以 待你将自己的 app 更新到最新版本的 SDK 之后 请一定不要再试着自己发布这一信息了 这个代码所描绘的是 利用一个辅助函数 来创建要放到外部元数据属性里的元数据 大部分开发者都已经在使用类似的代码 来提供标题 说明、媒体内容评级 以及信息面板中所要显示的其他信息 如你所见 它也可以被未显示的元数据使用 例如服务器标识符之类
接下来 我们要讲一讲这对你的 app 架构 可能会造成哪些负面影响 AVPlayerViewController 委托函数 会接收和处理由 AVkit 发来的 有关用户操作和其他事件的信息 其中也包括画中画 过去 你或许已经将父级属性 或 presentingViewController 属性 作为委托函数使用过了 (AVPLAYERVIEWCONTROLLER 委托函数) 然而 委托函数必须继续存在 并在画中画期间对信息加以回应 不过与此同时 用户可以浏览你 app 里的所有内容 这就意味着 你的委托函数绝不能是 视图层级里的一部分 它或许应该是一个单独的对象 在你的视频以 PiP 模式显示时 能够持续存在 我们来想一想 当用户互换 两个视频的位置时 会发生什么情况 (架构方面的问题 互换视频位置) 从视觉上讲 两个视频仅仅是在屏幕上 交换了一下位置 配有流畅的动画效果 然而你需要进行一些工作 才能实现这样的功能 如果你只是想呈现播放器视图控制器 那么你只需要忽略一个视图控制器 就是要变成画中画模式的那个 再呈现另一个 即可实现位置互换这项功能 在另一个视图控制器中 嵌入 AVPlayerViewController 会对你提出更多挑战 你是否想要或需要 在一个 PiP 视频转为全屏时 重新创建父视图控制器? 想一想 如果这种情况发生 导航层级又会发生什么样的情况
用户忽略掉这个视频之后 看到的应该是你的 app 里的哪个部分? 是否应该是用户打开上一个视频之前 所看到的内容? 抑或是他们刚刚切成全屏的 那个视频的着陆页
或许应该是你这个 app 的主界面吧
视频互换位置时可能还需要 执行一些别的工作 具体取决于你想要做什么事 不要忽视这些细节 在开发过程中 请务必留一些时间出来 妥善地为你的 app 和客户 设计并实现合适的解决方案 如果你做了一个自定义播放用户界面 现在 当你的 app 处于停用状态时 你可能需要暂停播放 (其他问题) 然而 当视频处于画中画模式时 请你不要这样做 如果你用了 AVPlayerViewController 那就根本不需要这样做 我们也建议你不要这样做 至此 我们讲了 如何用 AVkit 实现画中画模式 下面就来看看实际会是什么样子 (AVPLAYERVIEWCONTROLLER APPLE TVOS 下的新变化) 好 这里已经有一个视频在播放了 让我们来把它移到画中画里 我要向上轻扫 将视线聚焦在 PiP 的几个按钮上 然后点击 进入画中画模式 现在再在我们的 app 里打开另一个视频
很好 我们再来向上轻扫一次 调出各个 PiP 控件 主副交换和终止 PiP 这两项功能都在中间 我们可以这样将两个视频主副交换
很好 我们再来试一次 以免你错过这段内容 我们再向上轻扫一次 然后点击 这样就完成了
那么我们来看一下自定义叠加层 自定义叠加层的按钮在左边 点击一下 我们的自定义叠加层就出现了 你会发现 PiP 窗口自动离开了焦点所在的位置 这样一来 你的用户不必把它 手动挪到屏幕上的另一个角落 就能看清你的控件了 现在我们来终止画中画模式 好了 总之 使用 AVPlayerViewController 即可获得完整的 Apple tvOS 播放体验 包括画中画模式 无需对你的 app 进行多少调整 如果你有使用自定义播放用户界面的需求 请务必遵循 Human Interface Guidelines 以确保用户在 使用你的 app 观看 PiP 视频时 与使用 Apple TV 上的 其他 app 一样舒适 (摘要) 使用 AVPlayerViewController 时 请勿为你自己的叠加式用户界面安装 自定义向上轻扫手势识别器 而且请务必多花一些时间 来调整你的 app 架构 以便为画中画模式 提供尽可能到位的支持 感谢观看本场讲座 请尽情享受本届大会余下的内容吧
-
-
2:13 - Setting up your app's audio session
let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playback) } catch { print("Setting category to AVAudioSessionCategoryPlayback failed.") }
-
5:57 - Observering canStopPictureInPicture
_ = pipController.observe(\.canStopPictureInPicture) { controller, change in // Update your UI if controller.canStopPictureInPicture { pipActions = [.swap, .stop] } else { pipActions = [.start] } }
-
7:06 - Tying AVPlayer with MPNowPlayingSession
final class CustomPlayerViewController: UIViewController { init(player: AVPlayer) { let playerLayer = AVPlayerLayer(player: player) pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer) nowPlayingSession = MPNowPlayingSession(players: [player]) } private func publishNowPlayingMetadata() { nowPlayingSession.nowPlayingInfoCenter.nowPlayingInfo = // Your Now Playing info nowPlayingSession.becomeActiveIfPossible() } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。