视频播放器的类别
iOS开发中不可避免地会遇到音视频播放方面的需求。
常用的音频播放器有 AVAudioPlayer、AVPlayer 等。不同的是,AVAudioPlayer 只支持本地音频的播放,而 AVPlayer 既支持本地音频播放,也支持网络音频播放。
常用的视频播放器有 MPMoviePlayerController、AVPlayer 等。不同的是,MPMoviePlayerController 内部做了高度封装,包含了播放控件,几乎不用写几行代码就能完成一个播放器,但是正是由于它的高度封装使得要自定义这个播放器变得很复杂,甚至是不可能完成。而 AVPlayer 更加接近于底层,所以灵活性也更强,更加方便自定义。
今天我们要介绍的主角就是强大的 AVPlayer。
AVPlayer
AVPlayer 存在于 AVFoundation
框架中,所以要使用 AVPlayer,要先在工程中导入 AVFoundation
框架。
AVPlayer 播放界面中不带播放控件,想要播放视频,必须要加入 AVPlayerLayer 中,并添加到其他能显示的 layer 当中。
AVPlayer 中音视频的播放、暂停功能对应着两个方法 play、pause 来实现。
大多播放器都是通过通知来获取播放器的播放状态、加载状态等,而 AVPlayer 中对于获得播放状态和加载状态有用的通知只有一个:AVPlayerItemDidPlayToEndTimeNotification(播放完成通知)
。播放器的播放状态判断可以通过播放器的播放速度 rate 来获得,如果 rate 为0说明是停止状态,为1时则是正常播放状态。想要获取视频播放情况、缓冲情况等的实时变化,可以通过 KVO
监控 AVPlayerItem
的 status、loadedTimeRanges
等属性来获得。当 AVPlayerItem
的 status
属性为 AVPlayerStatusReadyToPlay
时说明可以开始播放,只有处于这个状态时才能获得视频时长等信息;当 loadedTimeRanges
改变时(每缓冲一部分数据就会更新此属性),可以获得本次缓冲加载的视频范围(包含起始时间、本次加载时长),这样一来就可以实时获得缓冲情况。
AVPlayer 中播放进度的获取通常是通过:- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block
方法。这个方法会在设定的时间间隔内定时更新播放进度,通过time
参数通知客户端。至于播放进度的跳转则是依靠 - (void)seekToTime:(CMTime)time
方法。
AVPlayer 还提供了 - (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item
方法用于在不同视频之间的切换(事实上在AVFoundation
内部还有一个AVQueuePlayer
专门处理播放列表切换,有兴趣的朋友可以自行研究,这里不再赘述)。
代码示例
#import "ViewController.h"
#import "TableViewCell.h"
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
@property (nonatomic, strong, readwrite) UITableView* tableView;
@property (nonatomic, strong) AVPlayerViewController* avPlayerVC;
@property (nonatomic, strong) AVPlayer* avPlayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 100, 428, 826) style:UITableViewStylePlain];
self.tableView.backgroundColor = [UIColor systemGray6Color];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 250;
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"111"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"111"];
}
NSString *webVideoPath = @"https://webdownload.wmupd.com/webdownload/client/sandianlingxiandaopv.mp4";
//用视频路径的字符串生成网络接口
NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath];
//创建AVPlayer(同时引用上方创建好的网络接口)
self.avPlayer = [[AVPlayer alloc] initWithURL:webVideoUrl];
//使用AVPlayer创建AVPlayerViewController
_avPlayerVC =[[AVPlayerViewController alloc] init];
_avPlayerVC.player = _avPlayer;
//设置AVPlayerViewController的位置
_avPlayerVC.view.frame = cell.bounds;
[cell.contentView addSubview:_avPlayerVC.view];
return cell;
}
@end