iOS14 画中画调研

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的直播,因为AVPlayer 不支持 flv,只能播放 hls 的流。HLS是苹果主导的音视频传输协议,其主要的格式是一个索引文件(M3U8)+ ts分片的视频文件。

  • 如果业务使用系统播放器,那么这里一般不会有问题,因为播放器已经正确显示了。但是如果业务使用了自定义播放器,同时也希望具有画中画能力,那么通常会创建一个隐藏的系统播放器,作为画中画功能的承载。这时候就要注意保证用于渲染的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渲染,没有经过服务器取流,所以画中画暂时无法实现。

实现步骤:如果现在不支持,后续如果想实现的话,可以在点击画中画后去服务请求视频的HLS直播地址,和一些token的验证拼接,之后将其赋值给self.playLayer.player = [AVPlayer playerWithURL:[NSURL URLWithString:urlStr]];,之后就可以开启画中画了。否则就按失败处理。

//欢迎大家交流并指出错误,谢谢

官方文档

你可能感兴趣的:(iOS14 画中画调研)