AVSampleBufferDisplayLayer实现画中画(PIP)仅显示加载指示器问题排查
问题根因与修复方案
你的实现存在以下几处问题,按优先级排序如下:
- 未配置AVAudioSession权限
画中画功能强制要求App提前激活播放类型的音频会话,即使你的视频内容没有音轨也必须配置,否则系统会拒绝渲染PIP窗口内容,仅显示加载指示器。
修复代码:在viewDidLoad或viewDidAppear中添加如下配置:do { try AVAudioSession.sharedInstance().setCategory(.playback) try AVAudioSession.sharedInstance().setActive(true) } catch { print(error) } - 未开启后台PIP权限
需要在Xcode项目的Signing & Capabilities中添加Background Modes能力,勾选Audio, AirPlay, and Picture in Picture选项,否则PIP功能无法正常调用。 - 播放时间范围返回逻辑错误
你当前在pictureInPictureControllerTimeRangeForPlayback代理方法中依赖缓冲队列首个元素的时长返回时间范围,当PIP启动时缓冲队列为空,会返回无效的时间区间,导致PIP无法识别流类型。由于你是实时生成流内容,应该统一返回直播流的时间范围:func pictureInPictureControllerTimeRangeForPlayback(_ pictureInPictureController: AVPictureInPictureController) -> CMTimeRange { // 直播流固定返回开始为0,时长为正无穷 return CMTimeRange(start: .zero, duration: .positiveInfinity) } setPlaying代理方法空实现
PIP启动时会主动调用该方法通知你需要开始/暂停播放内容,你需要在该方法中保证缓冲入队逻辑正常运行,不要阻塞流输出,简单标记播放状态保证requestMediaDataWhenReady的回调持续入队有效缓冲即可。- SampleBuffer时间戳不连续
你每次录制1秒视图生成的视频文件,提取的SampleBuffer时间戳都是从0开始计数,入队到同一个AVSampleBufferDisplayLayer时会出现时间戳回退的问题,导致渲染队列卡住。你需要维护一个全局的时间偏移量,给每个新入队的SampleBuffer重新打时间戳,保证时间线单调递增。 - 频繁调用flush清空缓冲
你每次录制完成都调用layer.flush(),会清空所有已入队的缓冲,PIP启动时如果刚好触发flush操作,就会出现无缓冲可渲染的问题,建议移除不必要的flush调用,仅在需要重置播放时调用。 - PIP控制器初始化时机过晚
你当前在第一次录制完成的异步回调中才初始化PIP控制器,如果在录制完成前点击启动PIP按钮,控制器为nil不会正常启动,建议将PIP控制器的初始化逻辑提前到viewDidLoad或viewDidAppear中,不要依赖录制回调。
内容的提问来源于stack exchange,提问作者Emma Labbé
相关产品推荐
相关产品推荐

