iOS 视频播放从零开始(二)

iOS 9之后苹果推荐使用AVPlayer进行视频播放,AVPlayer相对于MPMediaPlayerViewController功能更加强大,有更多的灵活性,当然,也需要你去自定义构建UI。其他优势,例如其扩展的AVQueuePlayer,可以实现视频无缝队列播放、多视频同时播放、视频转换、编解码等功能。

1、简单的播放

 // 1、得到视频的URL
    NSURL *movieURL = [NSURL fileURLWithPath:filePath];
    // 2、根据URL创建AVPlayerItem
    self.playerItem   = [AVPlayerItem playerItemWithURL:movieURL];
    // 3、把AVPlayerItem 提供给 AVPlayer
    self.player     = [AVPlayer playerWithPlayerItem:self.playerItem];
    // 4、AVPlayerLayer 显示视频。
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame       = self.view.bounds;
    //设置边界显示方式
    playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [self.view.layer addSublayer:playerLayer];
    self.player play];

2、监听视频加载状态/设置缓存进度

#pragma mark - 观察者、通知
- (void) addObserverAndNotification {
    // 观察status属性
    [_playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:(NSKeyValueObservingOptionNew) context:nil];
    [_playerItem addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew) context:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

}

#pragma mark - observe
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    AVPlayerItem *item = (AVPlayerItem *)object;
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; // 获取更改后的状态
        if (status == AVPlayerStatusReadyToPlay) {
            CMTime duration = item.duration; // 获取视频长度
            // 设置视频时间
            [self setMaxDuration:CMTimeGetSeconds(duration)];
            // 播放
            [self play];
            //监听播放进度
            [self monitoringPlayback:self.playerItem];
        } else if (status == AVPlayerStatusFailed) {
            NSLog(@"AVPlayerStatusFailed");
        } else {
            NSLog(@"AVPlayerStatusUnknown");
        }

    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        NSTimeInterval timeInterval = [self availableDuration]; // 缓冲时间
        CGFloat totalDuration = CMTimeGetSeconds(_playerItem.duration); // 总时间
        CGFloat progress = timeInterval / totalDuration;
        //在这里设置缓存进度
        [self.progressView setProgress:progress animated:YES]; // 更新缓冲条
    }
}

3、监听播放进度

#pragma mark - 观察播放进度
- (void)monitoringPlayback:(AVPlayerItem *)item {
    __weak typeof(self)WeakSelf = self;
    // 观察间隔, CMTime 为30分之一秒
    _playTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 30.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

        // 获取 item 当前播放秒
        float currentPlayTime = (double)item.currentTime.value/ item.currentTime.timescale;
        [WeakSelf updateVideoSlider:currentPlayTime];
        // 更新slider, 如果正在滑动则不更新
        if (_isSliding == NO) {
            [WeakSelf updateVideoSlider:currentPlayTime];
        }
    }];
}

4、设置进度

- (void)playerSliderValueChanged {
    _isSliding = YES;
    [self pause];    // 跳转到拖拽秒处
    CMTime changedTime = CMTimeMakeWithSeconds(self.slider.value, 1.0);
    [_playerItem seekToTime:changedTime completionHandler:^(BOOL finished) {
//        // 跳转完成后
//        _isSliding = NO;
//        [self play];
    }];
}

完整demo

@import AVFoundation;
@import MediaPlayer;
@interface ViewController ()
@property(nonatomic,strong) AVPlayer *player;
@property(nonatomic,strong) AVPlayerItem *playerItem;
@property(nonatomic,strong) id playTimeObserver;
@property(nonatomic,assign) BOOL isSliding; //是否正在滑动
/**
 *  视频总秒数
 */
@property (nonatomic, assign) NSInteger totalSeconds;
@property (nonatomic, weak) UISlider *slider;
@property (nonatomic, weak) UIProgressView *progressView;
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *path = [[NSBundle mainBundle]pathForResource:@"loginmovie" ofType:@".mp4"];
    //    NSURL *url     = [NSURL fileURLWithPath:path];
    [self createAVPlayer:path];

    UIProgressView *progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(5, 510,[UIScreen mainScreen].bounds.size.width-10, 0)];
    //进度条背景色
    progressView.trackTintColor = [UIColor lightGrayColor];
    //进度条缓存颜色
    progressView.progressTintColor = [UIColor greenColor];
    self.progressView = progressView;
    [self.view addSubview:progressView];
    //添加slide
    UISlider *slider = [[UISlider alloc]initWithFrame:CGRectMake(0, 500,[UIScreen mainScreen].bounds.size.width, 21)];
    slider.minimumTrackTintColor = [UIColor redColor];
    slider.maximumTrackTintColor = [UIColor clearColor];
    self.slider      = slider;
    [slider addTarget:self action:@selector(playerSliderValueChanged) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:slider];
}


#pragma mark - MPMoviePlayerController
- (void)createAVPlayer:(NSString *)filePath {
    //在使用 AVPlayer 播放视频时,提供视频信息的是 AVPlayerItem,一个 AVPlayerItem 对应着一个URL视频资源
    // 1、得到视频的URL
    NSURL *movieURL = [NSURL fileURLWithPath:filePath];
    // 2、根据URL创建AVPlayerItem
    self.playerItem   = [AVPlayerItem playerItemWithURL:movieURL];
    // 3、把AVPlayerItem 提供给 AVPlayer
    self.player     = [AVPlayer playerWithPlayerItem:self.playerItem];
    // 4、AVPlayerLayer 显示视频。
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame       = self.view.bounds;
    //设置边界显示方式
    playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [self.view.layer addSublayer:playerLayer];

    // 注册观察者,通知
    [self addObserverAndNotification];
}

#pragma mark - 更新视频
- (void)updatePlayerWithURL:(NSURL *)url {
    _playerItem = [AVPlayerItem playerItemWithURL:url]; // create item
    [_player  replaceCurrentItemWithPlayerItem:_playerItem]; // replaceCurrentItem
    [self addObserverAndNotification]; // 注册观察者,通知
}


#pragma mark - 观察者、通知
- (void) addObserverAndNotification {
    // 观察status属性
    [_playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:(NSKeyValueObservingOptionNew) context:nil];
    [_playerItem addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew) context:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

}

#pragma mark - observe
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    AVPlayerItem *item = (AVPlayerItem *)object;
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; // 获取更改后的状态
        if (status == AVPlayerStatusReadyToPlay) {
            CMTime duration = item.duration; // 获取视频长度
            // 设置视频时间
            [self setMaxDuration:CMTimeGetSeconds(duration)];
            // 播放
            [self play];
            //监听播放进度
            [self monitoringPlayback:self.playerItem];
        } else if (status == AVPlayerStatusFailed) {
            NSLog(@"AVPlayerStatusFailed");
        } else {
            NSLog(@"AVPlayerStatusUnknown");
        }

    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        NSTimeInterval timeInterval = [self availableDuration]; // 缓冲时间
        CGFloat totalDuration = CMTimeGetSeconds(_playerItem.duration); // 总时间
        CGFloat progress = timeInterval / totalDuration;
        //在这里设置缓存进度
        [self.progressView setProgress:progress animated:YES]; // 更新缓冲条
    }
}

#pragma mark - 设置视频时间总秒数
- (void) setMaxDuration:(CGFloat)seconds{
    self.slider.minimumValue = 0;
    self.slider.maximumValue = seconds;
    self.totalSeconds = seconds;
    NSLog(@"总秒数===%f",seconds);
}

#pragma mark - 播放
- (void) play{
    [self.player play];
}

#pragma mark - 暂停
- (void) pause{
    [self.player pause];
}

#pragma mark - 观察播放进度
- (void)monitoringPlayback:(AVPlayerItem *)item {
    __weak typeof(self)WeakSelf = self;
    // 观察间隔, CMTime 为30分之一秒
    _playTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 30.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

        // 获取 item 当前播放秒
        float currentPlayTime = (double)item.currentTime.value/ item.currentTime.timescale;
        [WeakSelf updateVideoSlider:currentPlayTime];
        // 更新slider, 如果正在滑动则不更新
        if (_isSliding == NO) {
            [WeakSelf updateVideoSlider:currentPlayTime];
        }
    }];
}



- (void)playerSliderValueChanged {
    _isSliding = YES;
    [self pause];    // 跳转到拖拽秒处
    CMTime changedTime = CMTimeMakeWithSeconds(self.slider.value, 1.0);
    [_playerItem seekToTime:changedTime completionHandler:^(BOOL finished) {
//        // 跳转完成后
//        _isSliding = NO;
//        [self play];
    }];
}

#pragma mark - 获取缓存进度
- (NSTimeInterval)availableDuration {
    NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges];
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
    float startSeconds = CMTimeGetSeconds(timeRange.start);
    float durationSeconds = CMTimeGetSeconds(timeRange.duration);
    NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
    return result;
}

- (NSString *)convertTime:(CGFloat)second{
    NSDate *d = [NSDate dateWithTimeIntervalSince1970:second];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    if (second/3600 >= 1) {
        [formatter setDateFormat:@"HH:mm:ss"];
    } else {
        [formatter setDateFormat:@"mm:ss"];
    }
    NSString *showtimeNew = [formatter stringFromDate:d];
    return showtimeNew;
}

#pragma mark - 设置播放seconds
- (void) updateVideoSlider:(float) time{
    NSLog(@"time=%f",time);
    self.slider.value = time;
}

#pragma mark - 播放完成后
- (void)playbackFinished:(NSNotification *)notification {
    NSLog(@"视频播放完成通知");
    _playerItem = [notification object];
    [_playerItem seekToTime:kCMTimeZero]; // item 跳转到初始
    //[_player play]; // 循环播放
}

#pragma mark - 释放资源
- (void)dealloc {
    [self removeObserveAndNOtification];
}

#pragma mark - 移除通知和观察者
- (void)removeObserveAndNOtification{
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    [_player removeTimeObserver:_playTimeObserver]; // 移除playTimeObserver}
    [_player removeObserver:self forKeyPath:@"loadedTimeRanges"];
    [_player removeObserver:self forKeyPath:@"status"];
}

效果图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值