//
// SMAVPlayerViewController.m
// AVPlayerDemo
//
// Created by Yx on 15/11/29.
// Copyright © 2015年 WuhanBttenMobileTechnologyCo.,Ltd. All rights reserved.
//
#import "SMAVPlayerViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "SMSliderBar.h"
#import "MBMessageTip.h"
#import "VedioModel.h"
#import "VedioHistory.h"
#define ScreenHeight [[UIScreen mainScreen] bounds].size.height
#define ScreenWidth [[UIScreen mainScreen] bounds].size.width
@interface SMAVPlayerViewController ()<SMSliderDelegate>
@property (weak, nonatomic) IBOutlet UIView *viewHead; //显示返回按钮View
@property (weak, nonatomic) IBOutlet UIView *viewLogin; //加载view
@property (weak, nonatomic) IBOutlet UIImageView *imageViewLogin; //加载image
@property (weak, nonatomic) IBOutlet UIView *viewAvPlayer; //播放视图
@property (weak, nonatomic) IBOutlet SMSliderBar *slider; //进度条
@property (weak, nonatomic) IBOutlet UIView *viewBottom; //底部控制view
@property (weak, nonatomic) IBOutlet UIButton *btnPause; //暂停播放按钮
@property (weak, nonatomic) IBOutlet UIButton *btnNetx; //下一个按钮
@property (weak, nonatomic) IBOutlet UILabel *labelTimeNow; //当前时间label
@property (weak, nonatomic) IBOutlet UILabel *labelTimeTotal; //总时间label
@property (strong, nonatomic) AVPlayer *player; //播放器对象
@property (strong, nonatomic) id timeObserver; //视频播放时间观察者
@property (assign, nonatomic) float totalTime; //视频总时长
@property (assign, nonatomic) BOOL isHasMovie; //是否进行过移动
@property (assign, nonatomic) BOOL isBottomViewHide; //底部的view是否隐藏
@property (assign, nonatomic) NSInteger subscript; //数组下标,记录当前播放视频
@property (assign, nonatomic) NSInteger currentTime; //当前视频播放时间位置
@end
@implementation SMAVPlayerViewController
@synthesize subscript;
- (void)viewDidLoad {
[super viewDidLoad];
self.slider.type = SMSliderTypeHoz;
self.slider.progressBgColor = [UIColor whiteColor];
self.slider.isAllowDrag = YES;
self.slider.delegate=self;
subscript = 0;
self.currentTime = 0;
[self prohibitOperation];
[self setMediaPlayer];
[self addPlayerClick];//双击,单击事件
[self addProgressObserver];//进度监听
[self addNotification];//广播监听播放状态
[self setupObservers];//监听应用状态
self.viewLogin.layer.cornerRadius = 8;
self.imageViewLogin.layer.masksToBounds = YES;
self.imageViewLogin.layer.cornerRadius = 34/2;
splashTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(roteImageView) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:splashTimer forMode:NSRunLoopCommonModes];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication]setStatusBarHidden:YES];
//接受远程控制事件
[[UIApplication sharedApplication]beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[splashTimer invalidate];
}
- (void)setMediaPlayer{
//创建播放器层
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
playerLayer.frame = CGRectMake(0, 0, ScreenHeight ,ScreenWidth );
[self.viewAvPlayer.layer addSublayer:playerLayer];
[self.player seekToTime:CMTimeMakeWithSeconds(self.startTime, 1000)];//设置播放位置1000 为帧率
[_player play];
}
#pragma mark - set/get
- (AVPlayer *)player{
if (!_player) {
AVPlayerItem *playerItem = [self getPlayItem:subscript];
[self addObserverToPlayerItem:playerItem];
_player = [AVPlayer playerWithPlayerItem:playerItem];
}
return _player;
}
- (AVPlayerItem *)getPlayItem:(NSUInteger)videoIndex{
/*播放本地视频
NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"testav.mp4" ofType:nil];
NSURL *url=[NSURL fileURLWithPath:urlStr];
AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];
return playerItem;*/
//NSString *urlStr= @"http://7xoour.com2.z0.glb.qiniucdn.com/9605401000";
_currentTime = 0;
[self savePayHistory];
VedioModel *vedioModel = _arrVedio[videoIndex];
NSString *urlStr =[vedioModel.strURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url=[NSURL URLWithString:urlStr];
/*获取总帧数与帧率
NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
AVURLAsset *myAsset = [[AVURLAsset alloc] initWithURL:url options:opts];
CMTimeValue value = myAsset.duration.value;//总帧数
CMTimeScale timeScale = myAsset.duration.timescale; //timescale为帧率 fps
*/
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
return playerItem;
}
#pragma mark - 通知
//给AVPlayerItem添加播放完成通知
- (void)addNotification{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
}
// 播放完成通知
- (void)playbackFinished:(NSNotification *)notification{
NSLog(@"视频播放完成.");
[self prohibitOperation];
[self savePayHistory];
[self nextClick:nil];
}
//注册通知
- (void)setupObservers
{
NSNotificationCenter *notification = [NSNotificationCenter defaultCenter];
[notification addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
[notification addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationWillResignActiveNotification object:[UIApplication sharedApplication]];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
if (self.player.rate == 0) {
[self.player play];
[self.btnPause setTitle:@"暂停" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_stop.png"] forState:UIControlStateNormal];
}
}
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
if (self.player.rate == 1){
[self.player pause];
[self.btnPause setTitle:@"播放" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_start.png"] forState:UIControlStateNormal];
}
}
- (void)removeNotification{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - KVO
- (void)addProgressObserver{
AVPlayerItem *playerItem = self.player.currentItem;
//这里设置每秒执行一次
__weak __typeof(self) weakself = self;
self.timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
NSLog(@"当前已经播放%f",current);
weakself.currentTime = current;
if (current) {
[weakself setTime:current withTotal:total];
}
}];
}
/**
* 给AVPlayerItem添加监控
* @param playerItem AVPlayerItem对象
*/
- (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{
//监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放状态
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
//监控网络加载情况属性
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
//监听播放的区域缓存是否为空
[playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
//缓存可以播放的时候调用
[playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{
[playerItem removeObserver:self forKeyPath:@"status"];
[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
[playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[playerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
}
/**
* 通过KVO监控播放器状态
* @param keyPath 监控属性
* @param object 监视器
* @param change 状态改变
* @param context 上下文
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
AVPlayerItem *playerItem = object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status= [[change objectForKey:@"new"] intValue];
if(status == AVPlayerStatusReadyToPlay){
self.totalTime = CMTimeGetSeconds(playerItem.duration);
NSLog(@"开始播放,视频总长度:%.2f",CMTimeGetSeconds(playerItem.duration));
[self.btnPause setTitle:@"暂停" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_stop.png"] forState:UIControlStateNormal];
[_player play];
[self.viewLogin setHidden:YES];
[self clearProhibitOperation];
}else if(status == AVPlayerStatusUnknown){
NSLog(@"%@",@"AVPlayerStatusUnknown");
}else if (status == AVPlayerStatusFailed){
NSLog(@"%@",@"AVPlayerStatusFailed");
}
}else if([keyPath isEqualToString:@"loadedTimeRanges"]){
NSArray *array = playerItem.loadedTimeRanges;
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
if (self.currentTime < (startSeconds + durationSeconds + 8)) {
self.viewLogin.hidden = YES;
if ([self.btnPause.titleLabel.text isEqualToString:@"暂停"]) {
[_player play];
}
}else{
self.viewLogin.hidden = NO;
}
self.slider.bufferValue = totalBuffer/self.totalTime;
NSLog(@"缓冲:%.2f",totalBuffer);
}else if ([keyPath isEqualToString:@"playbackBufferEmpty"]){
NSLog(@"playbackBufferEmpty");
[self.viewLogin setHidden:YES];
}else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){
[self.viewLogin setHidden:NO];
NSLog(@"playbackLikelyToKeepUp");
}
}
#pragma mark - 手势监听
- (void)addPlayerClick{
//单机手势监听
UITapGestureRecognizer * tapGes=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playerSingleClick:)];
tapGes.numberOfTapsRequired=1;
[self.viewAvPlayer addGestureRecognizer:tapGes];
/*
//双击手势监听
UITapGestureRecognizer * tapGesDouble=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playerDoubleTap:)];
tapGesDouble.numberOfTapsRequired=2;
[self.viewAvPlayer addGestureRecognizer:tapGesDouble];
//双击手势确定监测失败才会触发单击手势的相应操作
[tapGes requireGestureRecognizerToFail:tapGesDouble];*/
}
//显示或隐藏控制view
- (void)playerSingleClick:(UITapGestureRecognizer*)recognizer{
if (self.isBottomViewHide) {
//显示
[UIView animateWithDuration:0.3 animations:^{
[self.viewBottom setAlpha:1];
[self.viewHead setAlpha:1];
}];
}else{
//隐藏
[UIView animateWithDuration:0.3 animations:^{
[self.viewBottom setAlpha:0.0];
[self.viewHead setAlpha:0.0];
}];
}
self.isBottomViewHide=!self.isBottomViewHide;
}
//改变播放模式
- (void)playerDoubleTap:(UITapGestureRecognizer*)recognizer{
AVPlayerLayer *playerLayer=[AVPlayerLayer playerLayerWithPlayer:self.player];
if ([playerLayer.videoGravity isEqualToString:AVLayerVideoGravityResizeAspect]) {
playerLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;
}else if ([playerLayer.videoGravity isEqualToString:AVLayerVideoGravityResizeAspectFill]){
playerLayer.videoGravity=AVLayerVideoGravityResize;
}else if ([playerLayer.videoGravity isEqualToString:AVLayerVideoGravityResize]){
playerLayer.videoGravity=AVLayerVideoGravityResizeAspect;
}
}
#pragma mark - smSliderDelegate
- (void)SMSliderBar:(UIView *)slider valueChanged:(float)value{
self.isHasMovie = YES;
}
- (void)SMSliderBarBeginTouch:(UIView *)slider{
[_player pause];
self.isHasMovie = NO;
}
- (void)SMSliderBarEndTouch:(UIView *)slider{
if (self.isHasMovie) {
[_player seekToTime:CMTimeMakeWithSeconds(self.totalTime*self.slider.value, _player.currentItem.duration.timescale)completionHandler:^(BOOL finished) {
if (finished) {
[_player play];
}
}];
}
}
#pragma mark - IBAction
- (IBAction)pauseClick:(id)sender {
if (_player.rate == 0) {
[_player play];
[self.btnPause setTitle:@"暂停" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_stop.png"] forState:UIControlStateNormal];
}else{
[_player pause];
[self.btnPause setTitle:@"播放" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_start.png"] forState:UIControlStateNormal];
}
}
- (IBAction)nextClick:(id)sender {
++subscript;
self.labelTimeNow.text = @"00:00:00";
self.labelTimeTotal.text = @"00:00:00";
self.slider.value = 0;
if (subscript < self.arrVedio.count) {
[self.player seekToTime:CMTimeMakeWithSeconds(0, _player.currentItem.duration.timescale)];
[self removeNotification];
[self removeObserverFromPlayerItem:self.player.currentItem];
[self.player removeTimeObserver:self.timeObserver];
[_player replaceCurrentItemWithPlayerItem:[self getPlayItem:subscript]];
[self addObserverToPlayerItem:self.player.currentItem];
[self addNotification];
[self addProgressObserver];
if (self.player.rate == 0 ) {
[self.player play];
}
}else{
--subscript;
/*
[self.player.currentItem cancelPendingSeeks];
[self.player.currentItem.asset cancelLoading];
*/
[self.btnPause setTitle:@"播放" forState:UIControlStateNormal];
[self.btnPause setBackgroundImage:[UIImage imageNamed:@"play_start.png"] forState:UIControlStateNormal];
self.labelTimeTotal.text = @"00:00:00";
self.labelTimeNow.text = @"00:00:00";
[MBMessageTip messageWithTip:self.view withMessage:@"没有更多了" ];
}
}
- (IBAction)backAction:(id)sender {
[self savePayHistory];
[self.player pause];
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - interface rotation
//横屏
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
NSString *frameString = NSStringFromCGRect(self.viewAvPlayer.frame);
NSLog(@"视频窗口的frame:%@",frameString);
}
#pragma mark - 私有方法
//设置播放时长
- (void)setTime:(float)current withTotal:(float)total{
self.slider.value = current/total;
self.labelTimeNow.text = [self displayTime:(int)current];
self.labelTimeTotal.text = [self displayTime:(int)total];
}
- (NSString*)displayTime:(int)timeInterval{
NSString * time = @"";
int seconds = timeInterval % 60;
int minutes = (timeInterval / 60) % 60;
int hours = timeInterval / 3600;
NSString * secondsStr=seconds<10?[NSString stringWithFormat:@"%@%d",@"0",seconds]:[NSString stringWithFormat:@"%d",seconds];
NSString * minutesStr=minutes<10?[NSString stringWithFormat:@"%@%d",@"0",minutes]:[NSString stringWithFormat:@"%d",minutes];
NSString * hoursStr=hours<10?[NSString stringWithFormat:@"%@%d",@"0",hours]:[NSString stringWithFormat:@"%d",hours];
time = [NSString stringWithFormat:@"%@%@%@%@%@",hoursStr,@":",minutesStr,@":",secondsStr];
return time;
}
//视频加载指示条
- (void)roteImageView{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 ];
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1;
rotationAnimation.duration = 1;
[self.imageViewLogin.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
//保存播放历史
- (void)savePayHistory{
NSDate *currentDate = [NSDate date];//获取当前时间,日期
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"YYYY-MM-dd"];
NSString *dateString = [dateFormatter stringFromDate:currentDate];
VedioModel *vedioModel = _arrVedio[subscript];
VedioHistory *vedioHistory = [[VedioHistory alloc] init];
[vedioHistory insertTitle:vedioModel.strTitle createTime:dateString userId:[NSNumber numberWithInt:[vedioModel.strUserID intValue]] videoType:[NSNumber numberWithInt:vedioModel.vedioType] playTime:[NSNumber numberWithInteger:self.currentTime] videoUrl:vedioModel.strURL picUrl:vedioModel.strImage];
}
//视频未播放时禁止点击
- (void)prohibitOperation{
self.btnNetx.enabled = NO;
self.btnPause.enabled = NO;
self.slider.isAllowDrag = NO;
}
//视频播放时取消禁止点击
- (void)clearProhibitOperation{
self.btnNetx.enabled = YES;
self.btnPause.enabled = YES;
self.slider.isAllowDrag = YES;
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self removeObserverFromPlayerItem:self.player.currentItem];
[self.player removeTimeObserver:self.timeObserver];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end