大致效果

不要介意。界面有点丑。。。

界面搭建

看下成员变量就知道我怎么搭建的了,这里我将video播放层的size作为参照量,对所有控件的size按照其video的size宽高进行比例缩放

@interface VideoPlayerView()
@property (nonatomic,copy) NSString *path; //播放地址 自动判断文件路径和网址路径
@property (nonatomic,strong) AVPlayer *player; //播放类
@property (nonatomic,strong) AVPlayerLayer *playerlayer; //显示区域
@property (nonatomic,strong) UIButton *playBtn; //播放暂停
@property (nonatomic,strong) UIButton *stopBtn; // 停止
@property (nonatomic,strong) UIButton *fullScreenBtn; //全屏
@property (nonatomic,strong) UISlider *playSlider; //进度选择
@property (nonatomic,strong) UIProgressView *progress; //进度
@property (nonatomic,strong) UILabel *currentTimeLab; //当前时间
@property (nonatomic,strong) UILabel *durationLab; //总时间
@property (nonatomic,strong) UIView *toolView; //工具栏view
@property (nonatomic,assign) BOOL isFullScreen; //全屏判断
@property (nonatomic,assign) CGFloat videoHeight; //video高
@property (nonatomic,assign) CGFloat videoWidth; //video宽
@end

所有控件使用懒加载 如下

//播放暂停
- (UIButton *)playBtn {
if (!_playBtn) {
_playBtn = [[UIButton alloc] initWithFrame:CGRectMake(kLrMargin, kUIy, kBtnWidth, kUIHeight)];
_playBtn.backgroundColor =[UIColor greenColor];
_playBtn.selected = NO;
_playBtn.enabled = NO;
_playBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
[_playBtn setTitle:@"播放" forState:UIControlStateNormal];
[_playBtn setTitle:@"暂停" forState:UIControlStateSelected];
[_playBtn addTarget:self action:@selector(play) forControlEvents:UIControlEventTouchUpInside];
}
return _playBtn;
}

屏幕适配

由于涉及到屏幕的旋转和适配。我这里没有使用第三方框架来做约束,而是使用最基本的按百分比设置frame。旋转屏幕时通过调用本类- (void)resetFrame:(CGSize)size;方法来重设frame。所以需要重设frame的控件在懒加载中设置frame,调用时即刷新frame。

  • 先看下初始化 对video的size设置是时始终用最小的边来确定高度,宽度与屏幕当前宽度相当
//初始化
- (instancetype)initWithFrame:(CGRect)frame andPath:(NSString*)path {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.path = path;
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
self.videoHeight = height > width ? width * 0.6 : height * 0.6;
self.videoWidth = [UIScreen mainScreen].bounds.size.width-2*kLrMargin;
[self.layer addSublayer:self.playerlayer];
[self addSubview:self.toolView];
}
return self;
}
  • 屏幕旋转时做一些事
//屏幕旋转时触发 这里写在父类中
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[self.videoView resetFrame:size];
}
//旋转屏幕重设frame
- (void)resetFrame:(CGSize)size {
CGFloat width = size.width;
CGFloat height = size.height;
self.videoHeight = height > width ? width * 0.6 : height * 0.6;
self.videoWidth = size.width - 2 * kLrMargin;
if (self.isFullScreen) {
//全屏时旋转
[self setPlayerWithPosition:CGPointZero andSize:size];
} else {
//普通旋转
[self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)];
//刷新frame
[self toolView];
[self playSlider];
[self progress];
}
}
  • 懒加载刷新frame
//进度懒加载 调用时只刷新frame
- (UIProgressView *)progress {
if (!_progress) {
_progress = [[UIProgressView alloc] init];
_progress.progress = 0;
}
_progress.frame = CGRectMake(2, self.playSlider.frame.size.height/2, self.playSlider.frame.size.width-2-2, kUIHeight);
return _progress;
}

AVPlayer的基本操作

基本操作包括 播放 、暂停、 停止、 播放指定位置、缓存进度

播放网络地址时 在info.plist中添加 App Transport Security Settings字典中添加Allow Arbitrary Loads元素 值为YES



使用AVPlayer播放视频就必须用到AVPlayerlayer用来显示播放视图。

//加载显示层
- (AVPlayerLayer*)playerlayer {
if (!_playerlayer) {
_playerlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
_playerlayer.bounds = CGRectMake(0, 0, self.videoWidth, self.videoHeight);
_playerlayer.anchorPoint = CGPointMake(0, 0);
_playerlayer.position = CGPointMake(kLrMargin, kTopMargin);
_playerlayer.backgroundColor = [UIColor blackColor].CGColor;
}
return _playerlayer;
} //加载播放类
- (AVPlayer *)player {
if (!_player) {
_player = [AVPlayer playerWithURL:[self getUrlPath:self.path]];
//kvo注册
[self addObservers];
}
return _player;
}
  • 使用KVO对状态和缓存进行检测,添加KVO时养成习惯写好移除操作
//注册kvo
- (void)addObservers{
[self.player.currentItem addObserver:self forKeyPath:kItemStatus options:NSKeyValueObservingOptionNew context:nil];
[self.player.currentItem addObserver:self forKeyPath:kItemLoadedTimeRanges options:NSKeyValueObservingOptionNew context:nil];
} //移除kvo
- (void)dealloc {
[self.player.currentItem removeObserver:self forKeyPath:kItemStatus];
[self.player.currentItem removeObserver:self forKeyPath:kItemLoadedTimeRanges];
} //kvo回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:kItemStatus]) {
AVPlayerItem *item = object;
if (item.status == AVPlayerItemStatusReadyToPlay) {
//准备播放
[self readyToplayWithItem:item];
}else if (item.status == AVPlayerItemStatusUnknown) {
//播放失败
//这里写个alertView提示下就行
}else if (item.status == AVPlayerItemStatusFailed) {
//播放失败
//同上
}
} else if ([keyPath isEqualToString:kItemLoadedTimeRanges]) {
AVPlayerItem *item = object;
[self getLoadedTimeRanges:item];
}
}
  • 基础功能
//播放 暂停
- (void)play {
if (self.playBtn.selected) {
self.playBtn.selected = NO;
[self.player pause];
} else {
self.playBtn.selected = YES;
[self.player play];
[self timerStar];
}
} //停止
- (void)stop {
[self.player pause];
[self.player seekToTime:CMTimeMake(0, 1)];
self.playBtn.selected = NO;
}
//全屏
- (void)fullScreen {
self.toolView.hidden = YES;
self.isFullScreen = YES;
self.backgroundColor = [UIColor blackColor];
self.playerlayer.bounds = [UIScreen mainScreen].bounds;
self.playerlayer.anchorPoint = CGPointMake(0, 0);
self.playerlayer.position = CGPointMake(0, 0);
} //播放指定位置
- (void)playCurrentVideo {
self.playBtn.selected = YES;
NSTimeInterval second = self.playSlider.value;
[self.player.currentItem seekToTime:CMTimeMake(second,1)];
[self.player play];
[self timerStar];
}
  • 具体操作

    • 包括格式化时间
    • 格式化路径
    • 播放准备
    • 缓存计算
    • 触摸关闭全屏
    • 设置video的大小位置
//设置video的frame
- (void)setPlayerWithPosition:(CGPoint)position andSize:(CGSize)size {
self.playerlayer.anchorPoint = CGPointMake(0, 0);
self.playerlayer.position = position;
self.playerlayer.bounds = CGRectMake(0, 0, size.width, size.height);
} //准备播放
- (void)readyToplayWithItem:(AVPlayerItem*)item {
self.playBtn.enabled = YES;
long long durationSecond = item.duration.value / item.duration.timescale;
self.durationLab.text = [NSString stringWithFormat:@" / %@",[self getFormatDate:durationSecond]];
self.playSlider.maximumValue = durationSecond;
self.playSlider.minimumValue = 0;
[self.playSlider addTarget:self action:@selector(playCurrentVideo) forControlEvents:UIControlEventValueChanged]; } //获得缓存
- (void)getLoadedTimeRanges:(AVPlayerItem*)item {
NSValue *value = [item.loadedTimeRanges lastObject];
CMTimeRange range = [value CMTimeRangeValue];
long long cacheSecond = range.start.value/range.start.timescale + range.duration.value/range.duration.timescale;
long long currentSecond = item.currentTime.value / item.currentTime.timescale;
self.progress.progress = (currentSecond + cacheSecond) * 0.1; } //格式化时间
- (NSString*)getFormatDate:(NSTimeInterval)time {
int seconds = (int)time % 60;
int minutes = (int)(time / 60) % 60;
int hours = (int)time / 3600;
return [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
} //格式化url路径
- (NSURL*)getUrlPath:(NSString*)path {
NSURL *url;
if ([self.path containsString:@"http"]) {
url = [NSURL URLWithString:self.path];
} else {
url = [NSURL fileURLWithPath:self.path];
}
return url;
} //开启定时
- (void)timerStar {
//定时回调
__weak typeof(self) weakSelf = self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
long long current = weakSelf.player.currentItem.currentTime.value / weakSelf.player.currentItem.currentTime.timescale;
weakSelf.playSlider.value = current;
NSString *currentFormat = [weakSelf getFormatDate:current];
weakSelf.currentTimeLab.text = [NSString stringWithFormat:@"%@",currentFormat];
}]; } //触摸关闭全屏
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.isFullScreen) {
self.toolView.hidden = NO;
self.backgroundColor = [UIColor clearColor];
[self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)]; [self toolView];
[self playSlider];
[self progress];
self.isFullScreen = NO;
}
}

这样一个简单AVPlayer的封装就做好了

Demo地址

https://github.com/gongxiaokai/AVPlayerDemo

Objective-C AVPlayer播放视频的使用与封装的更多相关文章

  1. TextureView+SurfaceTexture+OpenGL ES来播放视频(三)

    引自:http://www.jianshu.com/p/291ff6ddc164 做好的Demo截图 opengl-video 前言 讲了这么多,可能有人要问了,播放视频用个android封装的Vid ...

  2. ios开发视频播放后台下载功能实现 :1,ios播放视频 ,包含基于AVPlayer播放器,2,实现下载,iOS后台下载(多任务同时下载,单任务下载,下载进度,下载百分比,文件大小,下载状态)(真机调试功能正常)

    ABBPlayerKit ios开发视频播放后台下载功能实现 : 代码下载地址:https://github.com/niexiaobo/ABBPlayerKit github资料学习和下载地址:ht ...

  3. iOS开发-- 利用AVPlayer播放远程音乐和视频

    一.简单的播放音乐和视频,播放视频的工具栏需要自己写 二.利用老师封装的框架实现视频播放 链接:http://pan.baidu.com/s/1hrEKlus 密码:8e7g

  4. AVAssetReader+AVAssetReaderTrackOutput播放视频

    该文章引用自:http://www.jianshu.com/p/3d5ccbde0de1 IOS 微信聊天发送小视频的秘密(AVAssetReader+AVAssetReaderTrackOutput ...

  5. iOS中三种方式实现登录界面播放视频或gif效果

    现在app都做的越来越炫酷,各种动画效果,各种特效很好的提高了用户的体验.很多app在登录界面都使用了动画效果,比如Uber,Keep,QQ等等.这些动画效果基本都是使用gif或者MP4来实现的. 效 ...

  6. [Xcode 实际操作]六、媒体与动画-(17)使用MediaPlayer框架播放视频

    目录:[Swift]Xcode实际操作 本文将演示视频的播放功能. 在项目名称上点击鼠标右键,弹出右键菜单, 选择[Add Files to "DemoApp"],往项目中导入文件 ...

  7. iOS音频与视频的开发(一)-使用AVAudioPlayer播放音乐、使用AVPlayerViewController播放视频

    iOS的多媒体支持非常强大,它提供了多套支持多媒体的API,无论是音频.视频的播放,还是录制,iOS都提供了多种API支持.借助于这些API的支持,iOS应用既可以查看.播放手机相册中的照片.视频,也 ...

  8. Android实现播放视频

    转载:http://www.bdqn.cn/news/201311/12100.shtml 使用VideoView播放视频 VideoView,用于播放一段视频媒体,它继承了SurfaceView,位 ...

  9. Android使用TextureView播放视频

    1.引言 如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到. 1).TextureView的兄弟SurfaceView 应用 ...

随机推荐

  1. Hibernate入门(一)

    一 Hibernate介绍 Hibernate 是一个开源.轻量级的ORM(对象关系映射)工具,该工具简化了数据创建.数据处理和数据访问,它是一种将对象映射到数据库中表的编程技术.ORM工具内部使用J ...

  2. 原生javascript实现网页显示日期时钟效果

    刚接触javascript中Date内置对象时,以为这些方法都太简单了,结果要自己实际操作写一个时钟效果还真一时把我难住了,主要有几点大家要注意的.先看实际效果 要实现这样的效果 某年某月某日星期几几 ...

  3. 重温Android中的消息机制

    引入: 提到Android中的消息机制,大家应该都不陌生,我们在开发中不可避免的要和它打交道.从我们开发的角度来看,Handler是Android消息机制的上层接口.我们在平时的开发中只需要和Hand ...

  4. 数列[专杀Splay版]

    时间限制: 3 Sec  内存限制: 128 MB提交: 49  解决: 7 题目描述 输入一个数列,你需要进行如下操作:  1. 把编号为I的数值改为K  2. 输出从小到大排序后第k个数 输入 输 ...

  5. Matlab: 路径的操作

    添加相对路径 在matlab中当代码很多时常常将结果存在不同的文件夹下面,常常使用相对路径对函数进行调用,但有时会存在问题.举个栗子: 代码结构如下: /codes/A/AA/code1.m /cod ...

  6. 【面经】腾讯和YY实习生面试总结

    [前言] 之前的四月份和五月份各面试了腾讯和YY的暑假实习,腾讯的失败了,YY的成功了.面试中我总会遇到自己不懂的,所幸的是不懂的越来越少,自己也一步一脚印得攻克自己不懂的.此时六月份的我再回顾起来, ...

  7. go语言返回变量存储地址

    package main import "fmt" func main() { e:= fmt.Println(e) fmt.Println(&e) //&e; 将 ...

  8. CentOS下安装php的mbstring扩展

    php的mbstring扩展如果没有安装会导致一些问题: 例1:登陆phpMyAdmin的时候会提示没字符串编码和字符串处理库 php_mbstring,有些程序中会用到mb_substr函数没有ph ...

  9. 【Android Developers Training】 87. 序言:同步到云

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  10. ASP.NET 导出EXCEL文件处理多对应排列的

    这次项目遇到了一个导出excel需要对应排列的问题.本来在做这个项目之前都基本没做过excel导出的菜鸡,这次强行做还是有些忐忑的,加上那个表的结构比较奇特.    废话不多说,先介绍表结构吧 是数据 ...