iOS14 画中画 调研
画中画(PictureInPicture)在iOS9就已经推出了,不过之前都只能在iPad使用,iPhone要使用画中画就得更新到iOS14才能使用。
苹果在iOS14中开放了手机端对画中画的支持,方便在app退出前台时仍然可以播放视频。 画中画的开启,本质上还是需要依赖系统播放器,同时UI、动画等都是系统原生提供的,因此自定义空间很小。
目前有三种方式实现画中画:
- WKWebView自带
- 如果对播放器
要求不大
的可以直接使用AVPlayerViewController
,自身就提供画中画功能可直接使用,设置allowsPictureInPicturePlayback = YES
即可展示出画中画按钮。 - 而
自定义的播放器
要开启画中画那就使用AVPictureInPictureController
来包装,也是很简单易用,并且动画效果内部已经实现,只需要注意画中画按钮需要用户自己去实现,画中画图标系统已有相关APIpictureInPictureButtonStartImage
。
总共需要以下几步:
-
开启后台模式,即Xcode中后台能力中的第一个勾选框
-
创建
AVPictureInPictureController
(你一定要让用户通过操作(点击按钮等)来开始画中画显示。 不能直接在代码中直接startPictureInPicture ,这样的话你的APP上架审核会被拒绝,因为苹果本身是不希望用户自定义画中画的。) -
在
[AVPictureInPictureController isPictureInPictureSupported]
允许的情况下,设置AVAudioSession的category为AVAudioSessionCategoryPlayback
-
可以播放遵守HLS协议的.m3u8的直播
-
如果业务使用系统播放器,那么这里一般不会有问题,因为播放器已经正确显示了。但是如果业务使用了自定义播放器,同时也希望具有画中画能力,那么通常会创建一个隐藏的系统播放器,作为画中画功能的承载。这时候就要注意保证
用于渲染的view、layer正确地加入到了层级中
。
具体代码如下:
// init player
NSURL *url = [NSURL URLWithString:@"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"];
//使用playerItem初始化AVPlayer的目的是为了监听播放器的`status`状态
self.playerItem = [AVPlayerItem playerItemWithURL:url];
[self.playerItem addObserver:self
forKeyPath:@"status"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:nil];
self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem ];
// init player layer
AVPlayerLayer * playerLayer = (AVPlayerLayer *)self.playerView.layer;
playerLayer.player = self.player;
// init pip controller
self.pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:playerLayer];
self.pipController.delegate = self;
//监听'pictureInPicturePossible'是为了让自定义的画中画按钮是否显示/隐藏 或者 能否点击,因为有些时候即使设备支持,但是需要AVPlayer等到直播流可以播放的一个状态才可以为possible
[self.pipController addObserver:self
forKeyPath:@"pictureInPicturePossible"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:nil];
if ([AVPictureInPictureController isPictureInPictureSupported]) {
NSLog(@"该设备支持画中画功能");
//开启画中画后台声音权限
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
if (error) {
NSLog(@"请求权限失败的原因为%@",error);
}
} else {
NSLog(@"该设备不支持画中画功能");
}
//在点击画中画按钮的时候 开启画中画
if (self.pipController.isPictureInPictureActive) {
[self.pipController stopPictureInPicture];
} else {
[self.pipController startPictureInPicture];
}
//status状态一共三种 可以在不同的状态下设置不同的交互
AVPlayerItemStatusReadyToPlay:
AVPlayerItemStatusFailed:
AVPlayerItemStatusUnknown:
注意点:
一、 AVPictureInPictureController的初始化通过将要展示视频的视图的view的 AVPlayerLayer
来初始化。为了达到这种目的视频播放的视图的layer返回应该是AVPlayerLayer,重写+layerClass
方法使得在创建的时候能返回一个不同的图层子类。UIView会在初始化的时候调用+layerClass方法,然后用它的返回类型来创建宿主图层。即需要在展示视频的view视图里有如下类方法
+ (Class)layerClass {
return [AVPlayerLayer class];
}
二、 startPictureInPicture并不是立马触发,而是需要在视频可以开始播放时才允许打开
。所以如果不加载一个本地加载的动画视频的话,界面上不会立马显示出画中画,给用户造成一种不灵敏的感觉。所以 先循环播放一个本地的 loading 视频来开启画中画,同时等待直播可以开始播放时,再将画中画的画面切换成真正的直播内容。
三、 因为AVPlayer只能播放本地视频或者HLS直播流,而AVPictureInPictureController的初始化需要用到AVPlayer。但公司目前的设备推流机制是直接客户端接受YUV数据并用OpenGL渲染,没有经过服务器取流,所以画中画暂时无法实现。
//欢迎大家交流并指出错误,谢谢
网友评论