效果图:
Untitled.gif
最近想要仿内涵段子练练手,就抓了包并开发了各个模块,尤其视频这部分使我收获颇多,跟大家分享一下。
内容包括:
1.播放器的创建
2.设置视频链接
3.视频加载过程中的监控
4.播放、暂停、跳转进度
5.全屏效果
播放器的创建
视频开发首先需要导入AVFoundation框架,
#import <AVFoundation/AVFoundation.h>
视频的播放需要放在AVPlayer上,AVPlayer通过AVPlayerItem管理资源,同时视频需要依靠AVPlayerLayer进行显示,AVPlayerLayer是CALayer类的子类,实例化后被添加到UIView上。这些是准备条件。
设置视频链接
@property(nonatomic,strong) AVPlayer *player; //播放器对象
@property(nonatomic,strong) AVPlayerLayer *playerLayer; //player需要在playerLayer上才能展示
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
//如果已存在资源则替换,否则创建
if (_player.currentItem) {
//移除之前的监听
[self removeObserverFromPlayerItem:_player.currentItem];
[_player replaceCurrentItemWithPlayerItem:playerItem];
}else {
_player = [AVPlayer playerWithPlayerItem:playerItem];
}
[_playerLayer removeFromSuperlayer];
_playerLayer = nil;
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.frame = videoView.bounds;
/*
AVLayerVideoGravityResizeAspect 默认,按视频比例显示,直到宽或高占满,未达到的地方显示父视图
AVLayerVideoGravityResizeAspectFill 按原比例显示视频,直到两边屏幕占满,但视频部分内容可能无法显示
AVLayerVideoGravityResize 按父视图尺寸显示,可能与原视频比例不同
*/
_playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; //视频填充模式
[videoView.layer addSublayer:_playerLayer];
视频加载过程中的监控
上面的步骤执行完毕后,视频就已经正在加载中了,这个时候可以通过KVO进行视频加载的监控,获取到视频长度、缓存进度、播放进度等信息。
[self addObserverFromPlayerItem:_player.currentItem];
[self addPlayFinishNotification];
[self addProgressNotification];
#pragma mark - 通知、监听
//KVO监听播放器播放状态
- (void)addObserverFromPlayerItem:(AVPlayerItem *)playerItem {
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //开始或暂停
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; //播放进度
}
//移除KVO监控
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem {
[playerItem removeObserver:self forKeyPath:@"status"];
[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
//KVO监听回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
AVPlayerItem *playerItem = object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status = [[change valueForKey:@"new"] integerValue];
if (status ==AVPlayerStatusReadyToPlay) {
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
NSLog(@"正在播放,视频总长度为 %.2f",totalTime);
}
}else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSArray *array = playerItem.loadedTimeRanges;
//本次缓冲时间范围
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
CGFloat startSecond = CMTimeGetSeconds(timeRange.start);
CGFloat durationSecond = CMTimeGetSeconds(timeRange.duration);
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
//缓冲总长度
NSTimeInterval totalBuffer = startSecond + durationSecond;
bufferProgress = totalBuffer / totalTime;
[self callBackWith:ABVideoDelegateCallbackBufferProgress];
}
}
//播放进度监听
- (void)addProgressNotification {
AVPlayerItem *playerItem = _player.currentItem;
static id playProgressObserver;
//先移除上一个视频的监听
if (playProgressObserver) {
[_player removeTimeObserver:playProgressObserver];
}
//每秒监听一次播放进度
typeof(self) weekSelf = self;
playProgressObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
CGFloat currentTime = CMTimeGetSeconds(time);
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
if (currentTime && !_isDrag) {
videoPlayTime = currentTime;
videoTotalTime = totalTime;
playProgress = currentTime / totalTime;
[weekSelf callBackWith:ABVideoDelegateCallbackProgress];
}
}];
}
//添加播放完成监控
- (void)addPlayFinishNotification {
_status = ABVideoStatusFinish;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinishNotification) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}
//播放完成回调
- (void)playFinishNotification {
[self callBackWith:ABVideoDelegateCallbackFinish];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
[self removeObserverFromPlayerItem:self.player.currentItem];
}
播放、暂停、跳转进度
//播放
- (void)play {
if (_player && _player.rate == 0) {
[_player play];
}
}
//暂停
- (void)pause {
if (_player && _player.rate != 0) {
[_player pause];
}
}
//跳转进度
- (void)jumpProgressWith:(CGFloat)value {
[self.player seekToTime:CMTimeMakeWithSeconds((value == 1 ? 0.99 : value) * CMTimeGetSeconds(_player.currentItem.duration), _player.currentItem.currentTime.timescale)]; //直接拖动到结束是不允许的
}
全屏效果
全屏效果的实现就是UI上的变动了,将播放器进行旋转放大,同时将子视图如播放按钮、底部进度条等进行相应的约束即可。不同的UI有不同的约束,这里贴一下我的例子
- (void)rotation {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
self.transform = CGAffineTransformMakeRotation(M_PI_2); //顺时针旋转
self.frame = CGRectMake(0, 0, screenWidth, screenHeight);
[_playBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(screenHeight/2 - 25);
make.top.equalTo(self).offset(screenWidth/2 - 25);
make.width.equalTo(@50);
make.height.equalTo(@50);
}];
[_bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self).offset(screenWidth - 50);
make.left.equalTo(self);
make.height.equalTo(@40);
make.width.equalTo([NSNumber numberWithFloat:screenHeight]);
}];
}
上面大概说明了iOS视频开发的基本,我在仿内涵段子里面将其运动到了tableView上,后面有时间就再贴出来。
网友评论