在iOS开发中,播放视频一般有两种方式,一种是使用MPMoviePlayerController(须要导入MediaPlayer.Framework),还有一种是使用AVPlayer。关于这两个类的区别能够参考http://stackoverflow.com/questions/8146942/avplayer-and-mpmovieplayercontroller-differences,简而言之就是MPMoviePlayerController使用更简单,功能不如AVPlayer强大,而AVPlayer使用稍微麻烦点,不过功能更增强大。这篇博客主要介绍下AVPlayer的基本使用,因为博主也是刚刚接触,因此有问题你们直接指出~ html
在开发中,单纯使用AVPlayer类是没法显示视频的,要将视频层添加至AVPlayerLayer中,这样才能将视频显示出来,因此先在ViewController的@interface中添加如下属性 git
@property (nonatomic ,strong) AVPlayer *player; @property (nonatomic ,strong) AVPlayerItem *playerItem; @property (nonatomic ,weak) IBOutletPlayerView *playerView;
其中playerView继承自UIView,不太重写了set和get方法,用于将player添加至playerView的AVPlayerLayer中,这样才能顺利将视频显示出来 github
在PlayerView.h中声明一个AVPlayer对象,因为默认的layer是CALayer,而AVPlayer只能添加至AVPlayerLayer中,因此咱们改变一下layerClass,这样PlayerView的默认layer就变了,以后咱们能够把在viewController中初始化的AVPlayer对象赋给AVPlayerLayer的player属性。 app
PlayerView.h ide
@property (nonatomic ,strong) AVPlayer *player; atom
PlayerView.m spa
+ (Class)layerClass { return [AVPlayerLayer class]; } - (AVPlayer *)player { return [(AVPlayerLayer *)[self layer] player]; } - (void)setPlayer:(AVPlayer *)player { [(AVPlayerLayer *)[self layer] setPlayer:player]; }
而后在viewDidLoad中执行初始化: 线程
NSURL *videoUrl = [NSURL URLWithString:@"http://www.jxvdy.com/file/upload/201405/05/18-24-58-42-627.mp4"]; self.playerItem = [AVPlayerItem playerItemWithURL:videoUrl]; [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性 [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];// 监听loadedTimeRanges属性 self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; [[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotificationobject:self.playerItem];
先将在线视频连接存放在videoUrl中,而后初始化playerItem,playerItem是管理资源的对象(A player item manages the presentation state of an asset with which it is associated. A player item contains player item tracks—instances of AVPlayerItemTrack—that correspond to the tracks in the asset.) code
而后监听playerItem的status和loadedTimeRange属性,status有三种状态: orm
AVPlayerStatusUnknown,
AVPlayerStatusReadyToPlay,
AVPlayerStatusFailed
当status等于AVPlayerStatusReadyToPlay时表明视频已经能够播放了,咱们就能够调用play方法播放了。
loadedTimeRange属性表明已经缓冲的进度,监听此属性能够在UI中更新缓冲进度,也是颇有用的一个属性。
最后添加一个通知,用于监听视频是否已经播放完毕,而后实现KVO的方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { AVPlayerItem *playerItem = (AVPlayerItem *)object; if ([keyPath isEqualToString:@"status"]) { if ([playerItem status] == AVPlayerStatusReadyToPlay) { NSLog(@"AVPlayerStatusReadyToPlay"); self.stateButton.enabled = YES; CMTime duration = self.playerItem.duration;// 获取视频总长度 CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒 _totalTime = [self convertTime:totalSecond];// 转换成播放时间 [self customVideoSlider:duration];// 自定义UISlider外观 NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration)); [self monitoringPlayback:self.playerItem];// 监听播放状态 } else if ([playerItem status] == AVPlayerStatusFailed) { NSLog(@"AVPlayerStatusFailed"); } } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度 NSLog(@"Time Interval:%f",timeInterval); CMTime duration = self.playerItem.duration; CGFloat totalDuration = CMTimeGetSeconds(duration); [self.videoProgress setProgress:timeInterval / totalDuration animated:YES]; } } - (NSTimeInterval)availableDuration { NSArray *loadedTimeRanges = [[self.playerView.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; }
此方法主要对status和loadedTimeRanges属性作出响应,status状态变为AVPlayerStatusReadyToPlay时,说明视频已经能够播放了,这时咱们能够获取一些视频的信息,包含视频长度等,把播放按钮设备enabled,点击就能够调用play方法播放视频了。在AVPlayerStatusReadyToPlay的底部还有个monitoringPlayback方法:
- (void)monitoringPlayback:(AVPlayerItem *)playerItem { self.playbackTimeObserver = [self.playerView.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) { CGFloat currentSecond = playerItem.currentTime.value/playerItem.currentTime.timescale;// 计算当前在第几秒 [self updateVideoSlider:currentSecond]; NSString *timeString = [self convertTime:currentSecond]; self.timeLabel.text = [NSString stringWithFormat:@"%@/%@",timeString,_totalTime]; }]; }
monitoringPlayback用于监听每秒的状态,- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;此方法就是关键,interval参数为响应的间隔时间,这里设为每秒都响应,queue是队列,传NULL表明在主线程执行。能够更新一个UI,好比进度条的当前时间等。
做为播放器,除了播放,暂停等功能外。还有一个必不可少的功能,那就是显示当前播放进度,还有缓冲的区域,个人思路是这样,用UIProgressView显示缓冲的可播放区域,用UISlider显示当前正在播放的进度,固然这里要对UISlider作一些自定义,代码以下:
- (void)customVideoSlider:(CMTime)duration { self.videoSlider.maximumValue = CMTimeGetSeconds(duration); UIGraphicsBeginImageContextWithOptions((CGSize){ 1, 1 }, NO, 0.0f); UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.videoSlider setMinimumTrackImage:transparentImage forState:UIControlStateNormal]; [self.videoSlider setMaximumTrackImage:transparentImage forState:UIControlStateNormal]; }
这样UISlider就只有中间的ThumbImage了,而ThumbImage左右的颜色都变成透明的了,仅仅是用于显示当前的播放时间。UIProgressView则用于显示当前缓冲的区域,不作任何自定义的修改,在StoryBoard看起来是这样的:
把UISlider添加至UIProgressView上面,运行起来的效果就变成了这样:
这样基本的缓冲功能就作好了,固然还有一些功能没作,好比音量大小,滑动屏幕快进快退等,你们有时间能够本身作着玩儿下~ 最后的效果以下:
最后附上demo连接:https://github.com/mzds/AVPlayerDemo