音视频及流媒体

iOS视频播放、AVFoundation使用

2018-03-29  本文已影响739人  张聪_2048

点击下载 Demo

视频播放demo.gif

一、AVPlayer简介

AVPlayer存在于AVFoundation中,它更加接近于底层,所以灵活性极高。
AVPlayer本身并不能显示视频,如果AVPlayer要显示必须创建一个播放器图层AVPlayerLayer用于展示,该播放器图层继承于CALayer。

AVPlayer视频播放使用步骤:

1、创建视频资源地址URL,可以是网络URL
2、通过URL创建视频内容对象AVPlayerItem,一个视频对应一个AVPlayerItem
3、创建AVPlayer视频播放器对象,需要一个AVPlayerItem进行初始化
4、创建AVPlayerLayer播放图层对象,添加到显示视图上去
5、播放器播放play,播放器暂停pause
6、添加通知中心监听视频播放完成,使用KVO监听播放内容的属性变化
7、进度条监听是调用AVPlayer的对象方法

二、设置视频数据

// 设置视频数据
- (void)playWithViedeoUrl:(NSString *)videoUrl {
    // 获取播放内容
    self.playerItem = [self getPlayItemWithUrl:videoUrl];
    // 创建视频播放器
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    // 添加播放进度监听
    [self addProgressObserver];
    // 添加播放内容KVO监听
    [self addPlayerItemObserver];
    // 添加通知中心监听播放完成
    [self addNotificationToPlayerItem];
    
    // 初始化播放器图层对象
    [self initAVPlayerLayer];
}

初始化AVPlayerItem视频内容对象。AVPlayerItem:该类主要是用于管理资源对象,提供播放数据源,旨在表示由AVPlayer播放的资产的呈现状态,并允许观察该状态,它控制着视频从创建到销毁的诸多状态。AVPlayerItem提供了AVPlayer播放需要的媒体文件,时间、状态、文件大小等信息,是AVPlayer媒体文件的载体。

// 初始化AVPlayerItem视频内容对象
- (AVPlayerItem *)getPlayItemWithUrl:(NSString *)videoUrl {
    // 编码文件名,以防含有中文,导致存储失败
    NSCharacterSet *set = [NSCharacterSet URLQueryAllowedCharacterSet];
    NSString *urlStr = [videoUrl stringByAddingPercentEncodingWithAllowedCharacters:set];
    NSURL *url = [NSURL URLWithString:urlStr];
    // 创建播放内容对象
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
    return item;
}

初始化播放器图层对象。AVPlayerLayer是AVFoundation的底层图层。拥有AVPlayer属性,可以播放媒体文件。

- (void)initAVPlayerLayer {
    // 创建视频播放器图层对象
    AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    // 尺寸大小
    layer.frame = self.bounds;
    // 视频填充模式
    layer.videoGravity = AVLayerVideoGravityResizeAspect;
    layer.backgroundColor = [UIColor blackColor].CGColor;
    // 添加进控件图层
    [self.layer addSublayer:layer];
    self.playerLayer = layer;
    self.layer.masksToBounds = YES;
}

三、监听视频数据

通过KVO监听AVPlayerItem的属性status获取状态改,来判断视频加载成功或失败。监听AVPlayerItem的属性loadedTimeRanges,可以实时知道当前视频的进度缓冲。

// 添加KVO,监听播放状态和缓冲加载状况
- (void)addPlayerItemObserver {
    // 监控状态属性
    [self.playerItem addObserver:self
                      forKeyPath:@"status"
                         options:NSKeyValueObservingOptionNew
                         context:nil];
    // 监控缓冲加载情况属性
    [self.playerItem addObserver:self
                      forKeyPath:@"loadedTimeRanges"
                         options:NSKeyValueObservingOptionNew
                         context:nil];
}

// 属性发生变化,KVO响应函数
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
    AVPlayerItem *playerItem = (AVPlayerItem *)object;
    
    // 状态发生改变
    if ([keyPath isEqualToString:@"status"]) {
        [self.activity stopAnimating];
        AVPlayerStatus status = [[change objectForKey:@"new"] integerValue];
        if (status == AVPlayerStatusReadyToPlay) {
            self.totalLab.text = [self timeFormatted:CMTimeGetSeconds(playerItem.duration)];
            self.bgImgView.hidden = YES;
        } else {
            self.errView.hidden = NO;
        }
    }
    // 缓冲区域变化
    else if ( [keyPath isEqualToString:@"loadedTimeRanges"] ) {
        NSArray *array = playerItem.loadedTimeRanges;
        // 已缓冲范围
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;
        NSTimeInterval totalDuration = CMTimeGetSeconds(playerItem.duration);
        self.cacheProgress.progress = totalBuffer / totalDuration;
    }
}

AVPlayer提供了一个Block回调,当播放进度改变的时候回主动回调该Block,但是当视频卡顿的时候是不会回调的,可以在该回调里面处理进度条以及播放时间的刷新

// 进度监听
- (void)addProgressObserver {
    __weak typeof(self) weakSelf = self;
    AVPlayerItem *item = self.player.currentItem;
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0)
                                              queue:dispatch_get_main_queue()
                                         usingBlock:^(CMTime time) {
         // CMTime是表示视频时间信息的结构体,包含视频时间点、每秒帧数等信息
         // 获取当前播放到的秒数
         float current = CMTimeGetSeconds(time);
         // 获取视频总播放秒数
         float total = CMTimeGetSeconds(item.duration);
         if (current && !weakSelf.activity.isAnimating) {
             weakSelf.slider.value = current/total;
             weakSelf.timeLab.text = [weakSelf timeFormatted:current];
         }
     }];
}

四、销毁视频

视频播放完,需要销毁视频,包括通知及KVO等

- (void)removePlayer {
    if (self.player) {
        [self.player pause];
        [self.player.currentItem cancelPendingSeeks];
        [self.player.currentItem.asset cancelLoading];
        [self.playerItem removeObserver:self forKeyPath:@"status"];
        [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        self.playerItem = nil;
        self.player = nil;
        self.playerLayer = nil;
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    [self removeTimer];
}



Demo 中有具体的使用,仿照网易、今日头条短视频的播放。包括视频的全屏的切换、播放暂停、进度设置等基本操作。

上一篇下一篇

猜你喜欢

热点阅读