
视频 SDK 的画中画功能如何适配移动端
说到画中画(Picture-in-Picture,简称 PiP)这个功能,可能很多用户第一时间想到的是在刷视频的时候,弹出一个小型窗口继续播放,同时去聊微信或者看文章。这确实是画中画最常见的使用场景,但作为一个开发者,我们更需要关心的是如何在移动端应用中实现并优化这个功能,尤其是当它和实时音视频 SDK 结合的时候。
我之前在项目中做过一次画中画功能的适配,期间遇到了不少坑,也总结了一些经验。这篇文章就想把这些东西分享出来,内容不会太学术化,都是实打实的踩坑经验,希望能给正在做类似功能的开发者一些参考。
为什么画中画功能值得关注
在移动端设备上,屏幕空间是极其宝贵的资源。用户在使用视频通话或者直播功能时,往往会有同时处理其他任务的需求。比如在视频通话时需要查看日程安排,或者在观看直播时想切换到其他应用回复消息。画中画功能恰恰解决了这个痛点,它允许用户在进行视频通话或观看直播的同时,灵活使用手机的其他功能。
从实际使用数据来看,开启画中画功能后,用户的应用留存时长和互动频率都有明显提升。这说明画中画不仅仅是一个花哨的功能,而是真正能提升用户体验的核心能力。作为开发者,我们需要从技术层面理解它的实现原理,才能在各种场景下都能保证稳定性和流畅度。
画中画功能的技术原理
在深入技术细节之前,我们先来聊聊画中画功能的基本工作原理。移动操作系统为了支持画中画,提供了系统级别的能力支持。Android 从 8.0 版本开始原生支持画中画模式,而 iOS 则是在 iOS 15 之后对第三方应用开放了更完整的画中画 API。
所谓系统级支持,意思是当应用进入画中画模式时,视频播放或通话的窗口会脱离应用的主体 UI,由系统接管并进行统一的渲染和管理。这样做的好处是,即使应用退到后台,画中画窗口依然可以正常运行,用户也可以自由地在不同应用之间切换。

对于实时音视频场景来说,画中画功能的实现会比普通视频播放复杂一些。因为普通视频播放只需要维护一个解码器和播放器实例,而实时音视频还需要处理网络连接、音频采集、编码推流等一系列操作。当应用进入画中画模式时,如何保持这些后台操作的稳定运行,就是我们需要解决的核心问题。
Android 平台的适配要点
Android 平台的画中画功能实现主要依赖于 PictureInPictureParams 这个类,它是配置画中画窗口各种属性的核心载体。我们在开发时需要特别注意以下几个方面。
生命周期处理是关键
很多开发者在适配画中画功能时容易忽略一个重要问题:应用进入画中画模式后,Activity 的生命周期会发生变化。具体来说,当用户主动切换到画中画模式时,当前的 Activity 会经历一个从运行到暂停的过程。如果我们的代码没有正确处理这个生命周期切换,可能会导致视频流中断、音频采集异常等各种问题。
我的建议是在 onPause() 和 onResume() 方法中明确区分普通的后台暂停和画中画模式的暂停。可以通过 isInPictureInPictureMode() 方法来判断当前是否处于画中画状态。如果是画中画模式,就需要保持音视频引擎的运行,而不是像普通后台那样暂停所有操作。
配置画中画窗口行为
PictureInPictureParams 提供了丰富的配置选项,我们需要根据实际场景合理设置。比较重要的几个参数包括宽高比(aspect ratio)、是否支持自动启动(autoEnterEnabled)、以及可操作的动作列表(actions)。
宽高比的设置需要考虑原始视频的分辨率比例。如果强制设置一个不匹配的比例,会导致视频画面被拉伸或裁剪,严重影响观看体验。我通常建议使用视频原始的宽高比作为默认值,这样能最大程度保证画面不变形。

关于是否启用自动进入画中画,这个要看具体业务场景。对于视频通话类应用,建议设置为手动触发比较合理,因为用户在通话过程中可能并不希望突然切换到画中画模式。而对于直播类应用,自动进入画中画可以提升用户的使用便利性。
处理用户交互
画中画窗口默认只显示视频画面,我们可以通过设置 Action 来添加一些基本的交互按钮。比如在视频通话场景中,可以提供挂断、静音等快捷操作。这些 Action 需要通过 RemoteAction 来实现,每个 Action 包含图标、标题和广播意图。
这里有个小技巧,因为画中画窗口空间有限,Action 的图标和文字要尽量简洁直观。我一般在设计时会参考系统原生应用的样式,确保用户能够快速理解每个按钮的功能。
iOS 平台的适配要点
iOS 平台的画中画实现思路和 Android 有相似之处,但具体 API 和实现方式有所不同。从 iOS 15 开始,系统提供了 AVPictureInPictureController 这个类,让第三方应用也能够方便地实现画中画功能。
基础配置与权限设置
在 iOS 上启用画中画功能,首先需要在项目的 Info.plist 文件中添加 NSPictureInPictureMediaCapabilities 键,并配置支持画中画的媒体类型。这个配置项的作用是告诉系统你的应用哪些内容类型支持画中画显示。
除了 Info.plist 的配置,还需要确保在合适的时机调用 startPictureInPicture 方法来启动画中画模式。需要注意的是,系统对启动时机有严格限制,只有当满足特定条件(比如至少有一帧视频帧渲染完成)时才能成功启动。如果在条件不满足时调用,会收到启动失败的回调。
后台播放音频的处理
p>很多开发者在 iOS 平台上会遇到一个问题:进入画中画模式后,音频突然断了。这个问题通常是由于没有正确配置后台音频播放权限导致的。iOS 要求应用在后台继续播放音频时,必须声明相应的 audio mode。解决方案是在项目的 Capabilities 页面中开启 "Background Modes",并勾选 "Audio, AirPlay, and Picture in Picture" 选项。同时,在代码中需要正确设置 AVAudioSession 的 category,确保音频会话支持后台播放。
状态监听与回调处理
iOS 的 AVPictureInPictureController 提供了多个回调方法,用于监听画中画状态的变化。比如 pictureInPictureControllerWillStartPictureInPicture 会在进入画中画前触发,pictureInPictureControllerDidStopPictureInPicture 则在退出画中画后触发。
合理利用这些回调可以帮我们做很多有价值的事情。比如在进入画中画前保存当前通话的状态,在退出后恢复正常的 UI 布局。我建议在每个回调中都加入适当的日志,便于排查问题。
实时音视频场景的特殊考量
前面提到过,实时音视频场景下的画中画实现比普通视频播放更复杂。这是因为除了视频渲染外,我们还需要维护网络连接、音频处理等一系列后台操作。
音频焦点的管理
当应用进入画中画模式后,用户可能会同时使用其他音频应用(比如音乐播放器)。这时就需要正确处理音频焦点的竞争问题。如果处理不当,可能会导致两个应用的声音混合在一起,或者画中画的音频被系统强制静音。
Android 平台可以通过 AudioManager 的 requestAudioFocus 方法来申请音频焦点,iOS 则通过 AVAudioSession 的设置来管理音频行为。建议在进入画中画时主动释放音频焦点,让用户在其他应用播放音频时,画中画仍然可以看到视频画面,但不会产生音频冲突。
网络连接的保活策略
很多开发者担心应用进入后台后网络连接会被系统断开。这个担心是有道理的,尤其是对于 Android 6.0 以上的系统 doze 模式和后台限制。但对于画中画这种系统级功能,系统通常会给予特殊的网络访问权限。
不过,我们仍然建议在代码层面做一些保活处理。比如使用持久化的长连接而不是短连接,在网络断开时实现自动重连逻辑,定期发送心跳包保持连接活跃。这些措施能够显著提升画中画模式下的连接稳定性。
相机资源的释放与获取
这是一个很容易被忽略的问题。当应用从画中画模式恢复到全屏时,可能会遇到相机被其他应用占用导致无法打开的情况。这是因为在画中画模式下,部分设备会释放相机资源以节省电量。
解决方案是在恢复全屏显示时,添加相机获取重试逻辑。如果第一次获取失败,可以等待一小段时间后重试,同时向用户显示友好的提示信息。
测试与优化建议
画中画功能的测试比普通功能更复杂,因为涉及到多个应用之间的切换和系统资源的竞争。我建议从以下几个方面进行充分测试。
多场景覆盖测试
需要覆盖的场景包括但不限于:通话中进入画中画、通话中退出画中画、画中画模式下接听另一个电话、画中画模式下切换到其他音视频应用、画中画模式下的网络切换(WiFi 和移动数据)等。
每个场景都要验证音视频是否正常同步、画面是否清晰、音频是否连续。建议使用真机进行测试,模拟器上的表现可能会和真实设备有差异。
性能与功耗监控
画中画模式下的性能表现直接影响用户体验。需要关注 CPU 占用率、内存消耗、电池耗电速度等指标。如果发现性能不达标,需要分析是渲染层面的问题还是编码推流环节的瓶颈。
一个实用的优化策略是在画中画模式下降低视频的编码码率和分辨率。因为画中画窗口本身很小,使用高清参数完全是浪费资源,还会增加功耗和带宽消耗。
主流平台的能力对比
为了让大家更直观地了解不同平台在画中画功能上的支持情况,我整理了一个简单的对比表格供参考:
| 功能特性 | Android | iOS |
| 系统原生支持版本 | Android 8.0+ | iOS 15+ |
| 窗口大小调整 | 支持 | 支持 |
| 自定义操作按钮 | 支持 | 支持 |
| 后台音频播放 | 需要配置 | 需要配置 |
| 横竖屏切换 | 支持 | 支持 |
可以看到,两个平台在基础能力上的差异不大,主要区别在于 API 的具体实现方式。对于需要同时支持两个平台的 SDK 来说,建议封装一层统一的接口,将平台差异在底层屏蔽。
写在最后
画中画功能的适配工作虽然不算特别复杂,但涉及到的细节确实不少。从生命周期管理到状态恢复,从音频焦点到网络保活,每个环节都需要认真对待。我的经验是,在开发初期就把这些边界情况都考虑进去,后续会省去很多返工的时间。
另外,画中画功能的用户体验非常重要。如果用户在切换到画中画后遇到音视频卡顿、画面模糊或者音频中断的问题,很容易对这个功能产生负面印象。所以在追求功能完整性的同时,稳定性同样不可忽视。
希望这篇文章能给正在做画中画适配的开发者带来一些帮助。如果有什么问题或者不同的见解,也欢迎一起交流讨论。

