画中画功能探究

最近开始研究iOS14画中画功能的实现,最终分别通过使用AVPlayerViewController构建播放器和AVPlayerLayer构建播放器,分别实现相应的画中画的功能
实现的效果图为:


AVPlayerViewController实现画中画效果

AVPlayerLayer实现画中画效果

项目的demo地址为:
demo地址

AVPlayerViewController实现画中画的功能

画中画功能的实现主要还是阅读苹果开发文档
开发文档中提到了AVPlayerViewController构建播放器时,这个是最简单的,只需要申请相应的权限,系统自带的控制器会带有开启画中画的按钮

首先,我们先使用AVPlayerViewController搭建一个播放视频的基本界面,(项目中对应的文件名是FirstViewController)

项目中使用的视频链接为:https://youku.cdn4-okzy.com/20200618/8130_4b8b9a06/1000k/hls/index.m3u8(是一个m3u8文件)
首先定义相应的属性和懒加载属性

@property (nonatomic, strong) AVPlayerViewController *avPlayer;//播放器
@property (nonatomic, strong) AVPlayer *player;//播放器播放的项目
@property (nonatomic, strong) UIButton *playButton;//播放按钮(也可以不用这个)

相应的懒加载为:

- (AVPlayerViewController *)avPlayer {
    if (_avPlayer) {
        return _avPlayer;
    }
    _avPlayer = [[AVPlayerViewController alloc] init];
    _avPlayer.showsPlaybackControls = YES;
   //AVPlayerViewController开启画中画只需要设置这个属性为YES即可(也可以不设置,默认为YES)
    _avPlayer.allowsPictureInPicturePlayback = YES;
    return _avPlayer;
}

- (AVPlayer *)player {
    if (_player) {
        return _player;
    }
    _player = [[AVPlayer alloc] initWithURL:[NSURL URLWithString:@"https://youku.cdn4-okzy.com/20200618/8130_4b8b9a06/1000k/hls/index.m3u8"]];
    return _player;
}

- (UIButton *)playButton {
    if (_playButton) {
        return _playButton;
    }
    _playButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_playButton setTitle:@"播放" forState:UIControlStateNormal];
    [_playButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_playButton addTarget:self action:@selector(playVideo) forControlEvents:UIControlEventTouchUpInside];
    return _playButton;
}

//自己添加的按钮的点击事件
- (void)playVideo {
    NSLog(@"开始播放视频");
    [self.avPlayer.player play];
}

在viewDidLoad中将播放器添加到view上

- (void)viewDidLoad {
     [super viewDidLoad];
     self.avPlayer.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, 300);
    [self.view addSubview:self.avPlayer.view];
    self.avPlayer.player = self.player;
    self.playButton.frame = CGRectMake((self.view.bounds.size.width - 100) / 2, 320, 100, 50);
    [self.view addSubview:self.playButton];
}

运行就可以发现,可以播放视频

但是没有画中画的按钮,因为我们没有申请权限,另外,画中画开启前我们需要判断当前设备是否支持画中画的功能
在viewDidLoad中添加以下代码即可

if ([AVPictureInPictureController isPictureInPictureSupported]) {
        NSLog(@"该设备支持画中画功能");
        //开启画中画权限
        NSError *error = nil;
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
        if (error) {
            NSLog(@"请求权限失败的原因为%@",error);
        }
    } else {
        NSLog(@"该设备不支持画中画功能");
    }

最终,我们得到的效果图为:


AVPlayerViewController实现画中画效果

AVPlayerLayer实现画中画的功能

首先,和原先一样,我们需要搭建一个能够播放视频的项目(在demo中文件名为secondViewController)

对于AVPlayerLayer是继承与CALayer,这个就是在view上添加一层layer用于播放视频。
(注意:这种方法播放视频没有系统提供控制栏,控制栏需要自己定义,下面我是定义了几个按钮)

对于AVPlayerLayer,我们打开苹果开发者文档,里面有给出其相应的封装例子,我们直接原样使用即可


开发者文档中的AVPlayerLayer封装的例子

在项目中,我们使用同样的代码,将其命名为playView
下面我们开始搭建基本的播放项目
定义相应的属性

@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) PlayerView *playerView;
@property (nonatomic, strong) UIButton *playButton;//播放按钮
@property (nonatomic, strong) UIButton *pauseButton;//暂停按钮
@property (nonatomic, strong) UIButton *startPipButton;//开启画中画按钮

实现相应的懒加载

- (AVPlayer *)player {
    if (_player) {
        return _player;
    }
    _player = [[AVPlayer alloc] initWithURL:[NSURL URLWithString:@"https://youku.cdn4-okzy.com/20200618/8130_4b8b9a06/1000k/hls/index.m3u8"]];
    return _player;
}

- (PlayerView *)playerView {
    if (_playerView) {
        return _playerView;
    }
    _playerView = [[PlayerView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 300)];
    _playerView.player = self.player;
    return _playerView;
}

- (UIButton *)playButton {
    if (_playButton) {
        return _playButton;
    }
    _playButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_playButton setTitle:@"播放" forState:UIControlStateNormal];
    [_playButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_playButton addTarget:self action:@selector(playVideo) forControlEvents:UIControlEventTouchUpInside];
    return _playButton;
}

- (UIButton *)pauseButton {
    if (_pauseButton) {
        return _pauseButton;
    }
    _pauseButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_pauseButton setTitle:@"暂停" forState:UIControlStateNormal];
    [_pauseButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_pauseButton addTarget:self action:@selector(pauseVideo) forControlEvents:UIControlEventTouchUpInside];
    return _pauseButton;
}

- (UIButton *)startPipButton {
    if (_startPipButton) {
        return _startPipButton;;
    }
    _startPipButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 50)];
    [_startPipButton setTitle:@"开启画中画功能" forState:UIControlStateNormal];
    [_startPipButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_startPipButton addTarget:self action:@selector(startPip) forControlEvents:UIControlEventTouchUpInside];
    return _startPipButton;
}

//播放按钮的点击事件
- (void)playVideo {
    NSLog(@"点击开始播放视频");
    [self.playerView.player play];
}

//暂停按钮的点击事件
- (void)pauseVideo {
    NSLog(@"暂停播放视频");
    [self.playerView.player pause];
}

//开启画中画按钮的点击事件
- (void)startPip {
    NSLog(@"点击开启画中画功能");
    if (self.pipVC.pictureInPicturePossible) {
        NSLog(@"允许开启画中画功能");
        [self.pipVC startPictureInPicture];
    } else {
        NSLog(@"不允许开启画中画功能");
    }
}

然后在viewDidLoad中实现界面的基本布局

[self.view addSubview:self.playerView];
    self.playButton.frame = CGRectMake((self.view.bounds.size.width - 100) / 2, 320, 100, 50);
    [self.view addSubview:self.playButton];
    self.pauseButton.frame = CGRectMake((self.view.bounds.size.width - 100) / 2, 400, 100, 50);
    [self.view addSubview:self.pauseButton];
    self.startPipButton.frame = CGRectMake((self.view.bounds.size.width - 200) / 2, 460, 200, 50);
    [self.view addSubview:self.startPipButton];

正常运行后,发现可以正常播放视频
接下来就可以添加画中画功能了

相应的控件及属性苹果开发者文档中都有提及,有兴趣的可以去阅读一下
实现画中画功能最重要的控件是AVPictureInPictureViewController是NSObject的子类(不是UIViewController的子类,所以我们不能将其添加到界面上)
定义相应的属性

@property (nonatomic, strong) AVPictureInPictureController *pipVC;

相应的懒加载

- (AVPictureInPictureController *)pipVC {
    if (_pipVC) {
        return _pipVC;
    }
    _pipVC = [[AVPictureInPictureController alloc] initWithPlayerLayer:self.playerView.playerLayer];
    _pipVC.delegate = self;//需要遵守AVPictureInPictureControllerDelegate(协议是optional,不是必须要实现的,可以不实现)
    return _pipVC;
}

实现相应的协议(协议是optional,不是必须要实现的,可以不实现)

- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
    NSLog(@"即将开启画中画功能");
}

- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
    NSLog(@"已经开启画中画功能");
}

- (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
    NSLog(@"即将停止画中画功能");
}

- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController {
    NSLog(@"已经停止画中画功能");
}

- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error {
    NSLog(@"开启画中画功能失败,原因是%@",error);
}

同样的我们需要在开始时申请权限
在viewDidLoad中添加以下代码即可

if ([AVPictureInPictureController isPictureInPictureSupported]) {
        NSLog(@"该设备支持画中画功能");
        //开启画中画权限
        NSError *error = nil;
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
        if (error) {
            NSLog(@"请求权限失败的原因为%@",error);
        }
    } else {
        NSLog(@"该设备不支持画中画功能");
    }

点击开启画中画按钮的方法实现

- (void)startPip {
    NSLog(@"点击开启画中画功能");
//pictureInPicturePossible这个属性为YES才允许开启画中画,存在其他的软件正在开启画中画时,这个属性为NO
    if (self.pipVC.pictureInPicturePossible) {
        NSLog(@"允许开启画中画功能");
        [self.pipVC startPictureInPicture];
    } else {
        NSLog(@"不允许开启画中画功能");
    }
}

最终运行的效果图为:


AVPlayerLayer实现画中画效果

总结

项目的demo地址为:
demo地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272