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"];
}
效果图: