代码地址如下:
http://www.demodashi.com/demo/11168.html

一、运行效果

二、实现过程

①、创建播放器avPlayer

       //创建播放器
url = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:url]];
self.avPlayer = player;

②、创建显示屏_videoLayer

- (void)createDisplay{

    // 显示图像的
_videoLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
//锚点的坐标
_videoLayer.position = CGPointMake(KPLAYVIEWWIDTH/2, KPLAYVIEWHEIGHT/2);
_videoLayer.bounds = CGRectMake(0, 0, KPLAYVIEWWIDTH, KPLAYVIEWHEIGHT);
// 锚点,值只能是0,1之间
_videoLayer.anchorPoint = CGPointMake(0.5, 0.5); // AVLayerVideoGravityResizeAspect 按比例压缩,视频不会超出Layer的范围(默认)
// AVLayerVideoGravityResizeAspectFill 按比例填充Layer,不会有黑边
// AVLayerVideoGravityResize 填充整个Layer,视频会变形
// 视频内容拉伸的选项
_videoLayer.videoGravity = AVLayerVideoGravityResizeAspect;
// //播放时,视频实际占的区域
// NSLog(@"%@", NSStringFromCGRect(videoLayer.videoRect)); //Layer只能添加到Layer上面
[self.displayView.layer addSublayer:_videoLayer];
}

③、添加观察者和通知,用于监测更新视频播放的状态、进度、屏幕方向、状态栏属性、音量等等。

主要代码如下:

//监测屏幕旋转
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; //添加AVPlayerItem播放结束通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayer.currentItem]; //添加AVPlayerItem开始缓冲通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bufferStart:) name:AVPlayerItemPlaybackStalledNotification object:self.avPlayer.currentItem]; //KOV监控 播放器进度更新
- (void)addObserverForAVPlayer
{
AVPlayerItem *playerItem = self.avPlayer.currentItem; // 给AVPlayer添加观察者 必须实现 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; //监控播放速率
[self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
//监控状态属性(AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态)
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
//监控网络加载缓冲情况属性
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//监控是否可播放
[playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; //播放进度观察者 //设置每0.1秒执行一次
__weak GOVVideoView *weakSelf = self;
_playerTimeObserver = [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { if (weakSelf.dragSlider) {
return ;
} CGFloat current = CMTimeGetSeconds(time);
weakSelf.current = current;
CMTime totalTime = weakSelf.avPlayer.currentItem.duration;
CGFloat total = CMTimeGetSeconds(totalTime);
weakSelf.total = total;
weakSelf.slider.value = current/total;
weakSelf.currentTimeLabel.text = [weakSelf timeFormatted:current];
weakSelf.totalTimeLabel.text = [NSString stringWithFormat:@"/%@",[weakSelf timeFormatted:total]] ; }];
} //通过KVO监控回调
//keyPath 监控属性 object 监视器 change 状态改变 context 上下文
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if ([keyPath isEqualToString:@"loadedTimeRanges"]) { //监控网络加载情况属性
NSArray *array = self.avPlayer.currentItem.loadedTimeRanges;
//本次缓冲时间范围
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
//现有缓冲总长度
CGFloat totalBuffer = startSeconds + durationSeconds;
//视频总时长
CMTime totalTime = self.avPlayer.currentItem.duration;
CGFloat total = CMTimeGetSeconds(totalTime);
if (totalBuffer/total <= 1.0 ) {
[self.progressView setProgress:totalBuffer/total animated:YES];
} }else if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){ if (self.avPlayer.currentItem.playbackLikelyToKeepUp == YES) { if (_activityView != nil) {
[self.activityView startAnimating];
[self.activityView removeFromSuperview];
_activityView = nil;
}
}
}else if ([keyPath isEqualToString:@"status"]){ //监控状态属性
AVPlayerStatus status = [[change objectForKey:@"new"] intValue]; switch ((status)) {
case AVPlayerStatusReadyToPlay: break;
case AVPlayerStatusUnknown: break;
case AVPlayerStatusFailed: break; }
}else if ([keyPath isEqualToString:@"rate"]){
if (self.avPlayer.rate == 1) {
}
} }

隐藏/显示状态栏的方法:

[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];这个方法在iOS9之后弃用了,并且需要

将View controller-based status bar appearance设置为NO;而下面的重写方法需要将View controller-based status bar appearance设置为YES,这个方法在iOS7之后就有了;

//刷新状态栏状态

[self setNeedsStatusBarAppearanceUpdate];

#pragma mark -- 隐藏/显示状态栏的方法
/*[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];这个方法在iOS9之后弃用了,并且需要
将View controller-based status bar appearance设置为NO;而下面的重写方法需要将View controller-based status bar appearance设置为YES,这个方法在iOS7之后就有了;
//刷新状态栏状态
[self setNeedsStatusBarAppearanceUpdate];
*/
//设置样式
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
//设置是否隐藏
- (BOOL)prefersStatusBarHidden {
return self.isHiddenStatusBar;
}
//设置隐藏动画
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationNone;
}

④、创建用于处理播放结束、关闭播放器、全屏/退出全屏、隐藏/展示footBar和topBar的Block回调方法和代理方法,方便用户做些自定义的操作。

//Block回调方法
typedef void(^FullScreenBlock)(BOOL isFull); typedef void(^ClosePLayerBlock)(); typedef void(^ShowBarBlock)(BOOL isShow); typedef void(^PlayFinishedBlock)(); @class GOVVideoPlayer;
//代理方法
@protocol GOVVideoPlayerDelegate <NSObject> //播放结束
- (void)videoPlayerPlayFinished:(GOVVideoPlayer *)videoPlayer; //关闭播放器
- (void)videoPlayerClosePlayer:(GOVVideoPlayer *)videoPlayer; //全屏按钮
- (void)videoPlayerFullScreen:(GOVVideoPlayer *)videoPlayer withIsFull:(BOOL)isFull; //隐藏/展示footBar和topBar
- (void)videoPlayerShowBar:(GOVVideoPlayer *)videoPlayer withIsShow:(BOOL)isShow;

三、项目结构图

四、补充

GOVVideoPlayer是在继承于UIView的基础上封装的视频View;

GOVVideoController是在继承于UIViewController的基础上封装的视频视图控制器。

两者最大的不同是在全屏和取消全屏的处理上面:前者是一个视图View,可以直接加在父视图上面,全屏时是加在 [UIApplication sharedApplication].keyWindow上的,而后者,小屏时是取GOVVideoController的View加在父视图上,全屏和取消全屏时是采用present和dismiss模态化转场的方法 。

iOS AVPlayer视频播放器

代码地址如下:
http://www.demodashi.com/demo/11168.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

最新文章

  1. ORACLE的DUAL表及DB2的SYSIBM.SYSDUMMY1
  2. Sprint3(12.18)总结
  3. 微软BI 之SSIS 系列 - 使用 SQL Profilling Task (数据探测) 检测数据源数据
  4. struts2 基础demo1
  5. Linux 日志服务器 rsyslog
  6. paip.操作符重载的缺失 Java 的一个大缺点
  7. Effective Java 64 Strive for failure atomicity
  8. javascript this 详解
  9. NYOJ 110 剑客决斗
  10. POJ 3169 Layout (图论-差分约束)
  11. javascript1
  12. 其他应用和技巧-eval()函数大行其道
  13. Centos 7 上安装使用 vscode
  14. c++简单线程池实现
  15. Day10 空时编码理论之无线信道、分集和复用
  16. 文鹏教育_jmeter培训_逻辑控制器_循环取样器
  17. Java接口自动化测试之集成MyBatis和MySQL (五)
  18. Solr和Lucene的区别?
  19. Cancel-Based Recovery
  20. 目标检测比赛---Google AI Open Images - Object Detection Track

热门文章

  1. VB查询数据库之结账——机房收费系统总结(五)
  2. 【贪心】【后缀自动机】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem E. Enter the Word
  3. Problem F: 程序填充(函数、指针):去数组负数
  4. Educational Codeforces Round 8 B. New Skateboard 暴力
  5. php函数前面加&amp;符号 和 变量前面加&amp;符号的意义
  6. iOS 自定义对象及子类及模型套模型的拷贝、归档存储的通用代码
  7. 微信小程序官方文档
  8. Linux下KVM虚拟机基本管理及常用命令(转)
  9. SQL 的四种分类 DDL,DML,DCL,TCL
  10. [java] 模拟QPS