一.基本概念
1.AVPlayer
AVPlayer是一个用来播放的对象,支持播放本地,分步下载,或者通过HLS协议得到的流媒体.
它是一个不可见的组件,如果播放mp3类的音频文件,可以.但是如果要想播放视频文件,我们就需要了解另外一个类AVPlayerLayer
AVPlayerLayer
它是对于CALayer类的扩展,通过框架在屏幕上显示内容,作为视频的渲染面,然后在用户面前进行展示.在创建AVPlayerLayer时,同时需要一个指向 AVPlayer的指针,把两者联系在一起.
2.AVPlayerItem
An AVPlayerItem carries a reference to an AVAsset as well as presentation settings for that asset.
This class is intended to represent presentation state for an asset that's played by an AVPlayer and to permit observation of that state.
说白了,AVPlayerItem是一个载体,承载AVAsset,然后通过AVPlayer进行播放.我们要想对一个资源进行播放,那么就要通过AVPlayerItem和AVPlayerItemTrack来构建对应的动态内容
二.基本使用
2.1 初始化一个简单的播放器
通过下面一段简单的代码,来进行一个视频的播放
NSURL *assetUrl = [NSURL URLWithString:@"http://flv2.bn.netease.com/videolib3/1606/23/RiTxE9164/SD/RiTxE9164-mobile.mp4"];
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
self.player = [AVPlayer playerWithPlayerItem:item];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
playerLayer.frame = CGRectMake(0, 50, 375, 200);
[self.view.layer addSublayer:playerLayer];
2.2 AVPlayerItem的status属性
虽然现在已经初始化完毕,但是视频还没有播放,此时我们需要对AVPlayerItem的一个名为<code>"status"</code>的AVPlayerItemStatus类型的属性进行监听
当创建之初,AVPlayerItem的status状态为<code>AVPlayerItemStatusUnknown</code>,该状态标识当前媒体还没有载入并不在播放队列中,当媒体加载完毕后,status状态会发生改变,会变成<code>AVPlayerItemStatusReadyToPlay</code>或者<code>AVPlayerItemStatusFailed</code>.
当状态为<code>AVPlayerItemStatusReadyToPlay</code>时,表示视频加载完毕,可以进行播放
下面是添加监听的代码
先定一一个全局变量
<code>static NSString * const PlayerItemStatusContext = @"PlayerItemStatusContext";</code>
对item进行监听
[item addObserver:self forKeyPath:@"status" options:0 context:(__bridge void * _Nullable)(PlayerItemStatusContext)];
然后通过调用<code>observeValueForKeyPath: ofObject: change: context:</code>方法做监听回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
if (context == (__bridge void * _Nullable)(PlayerItemStatusContext)) {
if ([keyPath isEqualToString:@"status"]) {
AVPlayerItem * item = (AVPlayerItem *)object;
if (item.status == AVPlayerItemStatusReadyToPlay) { //准备好播放
[self.player play];
}else if (item.status == AVPlayerItemStatusFailed){ //失败
NSLog(@"failed");
}
}
}
}
2.3 loadedTimeRanges状态
通常情况下,在加载网络视频时,我们需要获取视频的缓冲进度,这时候,我们可以通过监听AVPlayerItem的<code>loadedTimeRanges</code>状态,获取缓冲进度
定义一个全局变量
static NSString * const PlayerPreloadObserverContext = @"PlayerPreloadObserverContext";
为item添加监听
[item addObserver:self forKeyPath:@"loadedTimeRanges" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:(__bridge void * _Nullable)(PlayerPreloadObserverContext)];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
if (context == (__bridge void * _Nullable)(PlayerPreloadObserverContext)){
if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSArray *timeRanges = (NSArray *)[change objectForKey:NSKeyValueChangeNewKey];
[self updateLoadedTimeRanges:timeRanges];
}
}
}
- (void)updateLoadedTimeRanges:(NSArray *)timeRanges {
if (timeRanges && [timeRanges count]) {
CMTimeRange timerange = [[timeRanges firstObject] CMTimeRangeValue];
CMTime bufferDuration = CMTimeAdd(timerange.start, timerange.duration);
// 获取到缓冲的时间,然后除以总时间,得到缓冲的进度
NSLog(@"%f",CMTimeGetSeconds(bufferDuration));
}
}
2.4移除监听
在移除播放器的时候,一定要记得移除监听事件
- (void)removeObserverWithPlayerItem:(AVPlayerItem *)playerItem {
[playerItem removeObserver:self forKeyPath:XHPlayerStatusKey context:(__bridge void *)XHPlayerItemObserverContext];
[playerItem removeObserver:self forKeyPath:XHPlayerPlayerLoadedTimeRanges context:(__bridge void *)XHPlayerPreloadObserverContext];
}