用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。

来源:互联网 发布:校园网络电视台方案 编辑:程序博客网 时间:2024/05/01 11:53

转自:

用AVFoundation的AVPlayer做的一个小视频播放器,包含左滑快退,右滑快进,上滑音量加,下滑音量减等基本功能。
支持HLS协议和HTTP协议的视频。

#import "AppDelegate.h"#import "PlayViewController.h"@interface AppDelegate ()@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    PlayViewController *player=[[PlayViewController alloc]init];    //HLS协议视屏    //http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8    //HTTP视频    //http://www.modrails.com/videos/passenger_nginx.mov    player.urlString=@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";    player.title=@"播放测试";    self.window.rootViewController=player;    return YES;}@end

#import "PlayViewController.h"#import <AVFoundation/AVFoundation.h>#import <MediaPlayer/MediaPlayer.h>#import "Tools.h"#import "FGGReachability.h"@interface PlayViewController ()@property(nonatomic,strong)AVPlayer *player;@property(nonatomic,strong)AVPlayerItem *item;@property(nonatomic,strong)NSString *totalTime;@property(nonatomic,assign)BOOL isPlaying;@property(nonatomic,strong)id timeObserver;@property(nonatomic,strong)UIProgressView *progress;@property(nonatomic,strong)UISlider *slider;      //进度slider@property(nonatomic,strong)UISlider *volumeSlider;//音量slider@property(nonatomic,strong)UIButton *playBtn;@property(nonatomic,strong)UILabel *timeLabel;@property(nonatomic,strong)id playbackTimeObserver;//快进@property(nonatomic,strong)UIImageView *forward;//快退@property(nonatomic,strong)UIImageView *backward;@property(nonatomic,strong)UILabel *volumeLabel;//调节音量时显示当前音量@property(nonatomic,strong)UIButton *shareBtn;//分享//加载指示器@property(nonatomic,strong)UIActivityIndicatorView *indicator;//判断网络@property(nonatomic,strong)Reachability *reach;@end#define kWidth ([UIScreen mainScreen].bounds.size.width)#define kHeight ([UIScreen mainScreen].bounds.size.height)@implementation PlayViewController-(void)viewWillAppear:(BOOL)animated{    [super viewWillAppear:animated];//    隐藏导航栏    self.navigationController.navigationBarHidden=YES;}-(void)showIndicator{    _indicator=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];    _indicator.center=CGPointMake(kHeight/2, kWidth/2);    [self.view addSubview:_indicator];    [_indicator startAnimating];}//隐藏状态栏- (BOOL)prefersStatusBarHidden{    return YES;}- (void)viewDidLoad{    [super viewDidLoad];    self.view.backgroundColor=[UIColor blackColor];    //显示网络记载指示器    [self showIndicator];    NSLog(@"\n-->%@\n-->%@",self.urlString,self.videoTitle);    //通知中心注册一条监听网络状态的通知    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(estimateNetworkStatus) name:kReachabilityChangedNotification object:nil];    _reach=[Reachability reachabilityForInternetConnection];    [_reach startNotifier];    [self estimateNetworkStatus];    _item=[[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:self.urlString]];    _player=[[AVPlayer alloc]initWithPlayerItem:_item];    [_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性    [_item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];// 监听loadedTimeRanges属性    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_item];    AVPlayerLayer *layer=[AVPlayerLayer playerLayerWithPlayer:_player];    layer.videoGravity=AVLayerVideoGravityResizeAspect;    layer.frame=CGRectMake(0, 0, kHeight, kWidth);    layer.backgroundColor=[UIColor clearColor].CGColor;    [self.view.layer addSublayer:layer];    //    标题    [self createTitleView];    //    界面    [self createUI];    //旋转视图    //self.view.transform=CGAffineTransformMakeRotation(3*M_PI_2);    //layer.transform=CATransform3DMakeRotation(M_PI/2, 0, 0, 1);    self.view.layer.transform=CATransform3DMakeRotation(3*M_PI_2, 0, 0, 1);    [self addGestures];}/** *  添加手势 */-(void)addGestures{    //添加一个手势 隐藏与显示进度条    UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(shiftStatus)];    [self.view addGestureRecognizer:tap];    //增加音量的手势    UISwipeGestureRecognizer *increaseGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(increaseVolume:)];    increaseGesture.direction=UISwipeGestureRecognizerDirectionUp;    [self.view addGestureRecognizer:increaseGesture];    //减小音量的手势    UISwipeGestureRecognizer *decreaseGesture=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(decreaseVolume:)];    decreaseGesture.direction=UISwipeGestureRecognizerDirectionDown;    [self.view addGestureRecognizer:decreaseGesture];    //快进    UISwipeGestureRecognizer *stepForward=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(stepForward10Seconds)];    stepForward.direction=UISwipeGestureRecognizerDirectionRight;    //快退    UISwipeGestureRecognizer *stepBackward=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(stepBackward10Senconds)];    stepBackward.direction=UISwipeGestureRecognizerDirectionLeft;    [self.view addGestureRecognizer:stepForward];    [self.view addGestureRecognizer:stepBackward];}/** *  评估网络状态 */-(void)estimateNetworkStatus{    FGGNetWorkStatus status=[FGGReachability networkStatus];    if(status==FGGNetWorkStatusNotReachable)    {        UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示"                                                     message:@"当前网络不可用"                                                    delegate:nil                                           cancelButtonTitle:@"知道了"                                           otherButtonTitles:nil, nil];        [alert show];    }    else if(status!=FGGNetWorkStatusWifi)    {        UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示"                                                     message:@"非Wifi环境播放视屏会耗费流量!"                                                    delegate:nil                                           cancelButtonTitle:@"知道了"                                           otherButtonTitles:nil, nil];        [alert show];    }}/** *  向上滑动增加音量 * *  @param sender 滑动手势 */-(void)increaseVolume:(UISwipeGestureRecognizer *)sender{    if(sender.direction==UISwipeGestureRecognizerDirectionUp)    {        if(_player.volume>=1.0)            return;        _player.volume+=0.1;//        NSLog(@"+++++音量:%f++++++",10*_player.volume);        weak PlayViewController *weakSelf=self;        _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceilf(_player.volume*10)];        [UIView animateWithDuration:0.2 animations:^{            weakSelf.volumeLabel.alpha=1.0;        } completion:^(BOOL finished) {            [UIView animateWithDuration:0.2 animations:^{                weakSelf.volumeLabel.alpha=0.0;            }];        }];    }}/** *  向下滑动减小音量 * *  @param sender 滑动手势对象 */-(void)decreaseVolume:(UISwipeGestureRecognizer *)sender{    if(sender.direction==UISwipeGestureRecognizerDirectionDown)    {        if(_player.volume<=0.0)            return;        _player.volume-=0.1;//        NSLog(@"------音量:%f------",10*_player.volume);        weak PlayViewController *weakSelf=self;        _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceilf(_player.volume*10)];        [UIView animateWithDuration:0.3 animations:^{            weakSelf.volumeLabel.alpha=1.0;        } completion:^(BOOL finished) {            [UIView animateWithDuration:0.3 animations:^{                weakSelf.volumeLabel.alpha=0.0;            }];        }];    }}#pragma mark -#pragma mark - 快退快进//@qustion:seekToTime和seekToTime:toleranceBefore:toleranceAfter的区别是什么?//快进10秒-(void)stepForward10Seconds{    if(_isPlaying)    {        [_item seekToTime:CMTimeMakeWithSeconds(_item.currentTime.value/_item.currentTime.timescale+10, _item.currentTime.timescale) toleranceBefore:CMTimeMake(1, _item.currentTime.timescale) toleranceAfter:CMTimeMake(1, _item.currentTime.timescale)];        weak typeof(self) weakSelf=self;        [UIView animateWithDuration:0.3 animations:^{            weakSelf.forward.alpha=1.0;        }completion:^(BOOL finished) {            [UIView animateWithDuration:0.3 animations:^{                weakSelf.forward.alpha=0.0;            }];        }];    }}//快退10秒-(void)stepBackward10Senconds{    if(_isPlaying)    {        [_item seekToTime:CMTimeMakeWithSeconds(_item.currentTime.value/_item.currentTime.timescale-10, _item.currentTime.timescale)];        weak typeof(self) weakSelf=self;        [UIView animateWithDuration:0.3 animations:^{            weakSelf.backward.alpha=1.0;        }completion:^(BOOL finished) {            [UIView animateWithDuration:0.3 animations:^{                weakSelf.backward.alpha=0.0;            }];        }];    }}//界面-(void)createUI{    _playBtn=[[UIButton alloc]initWithFrame:CGRectMake(50, 20, 60, 40)];    [_playBtn setTitle:@"Play" forState:UIControlStateNormal];    [_playBtn setTitle:@"Pause" forState:UIControlStateSelected];    [_playBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];    _playBtn.titleLabel.font=[UIFont boldSystemFontOfSize:16];    [_playBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];    [_playBtn addTarget:self action:@selector(playAction:) forControlEvents:UIControlEventTouchUpInside];    _playBtn.backgroundColor=[UIColor clearColor];    [self.view  addSubview:_playBtn];    _progress=[[UIProgressView alloc]initWithFrame:CGRectMake(110, 41, kHeight-230, 2)];    //    设置轨道颜色,将要走到的路径的颜色    _progress.trackTintColor=[UIColor whiteColor];    //    设置进度颜色,已经走过的路径    _progress.progressTintColor=[UIColor blueColor];    _slider=[[UISlider alloc]initWithFrame:CGRectMake(110, 26,kHeight-230, 31)];    [_slider setThumbImage:[UIImage imageNamed:@"thumb"] forState:UIControlStateNormal];    _slider.minimumTrackTintColor=[UIColor blueColor];//设置左边颜色    _slider.maximumTrackTintColor=[UIColor clearColor];//设置右边颜色    _slider.continuous=NO;    [_slider addTarget:self action:@selector(sliderDidSlided:) forControlEvents:UIControlEventValueChanged];    [self.view addSubview:_progress];    [self.view addSubview:_slider];    _timeLabel=[Tools createLabelWithFrame:CGRectMake(kHeight-130, 30, 110, 20) text:nil textColor:[UIColor whiteColor] textAligment:NSTextAlignmentRight andBgColor:[UIColor clearColor] font:[UIFont systemFontOfSize:14]];    _timeLabel.text=@"--:--/--:--";    [self.view addSubview:_timeLabel];    //    音量label    _volumeLabel=[[UILabel alloc]initWithFrame:CGRectMake(kHeight-100, kWidth/2-20, 80, 40)];    _volumeLabel.text=[NSString stringWithFormat:@"音量:%d",(int)ceil(_player.volume)*10];    _volumeLabel.alpha=0.0;    _volumeLabel.backgroundColor=[UIColor clearColor];    _volumeLabel.font=[UIFont boldSystemFontOfSize:22];    _volumeLabel.textColor=[UIColor whiteColor];    [self.view addSubview:_volumeLabel];    //快进    _forward=[[UIImageView alloc]initWithFrame:CGRectMake(kHeight-164, kWidth/2-32, 64, 64)];    _forward.image=[UIImage imageNamed:@"stepforward"];    [self.view addSubview:_forward];    _forward.alpha=0.0;    //快退    _backward=[[UIImageView alloc]initWithFrame:CGRectMake(100, kWidth/2-32, 64, 64)];    _backward.image=[UIImage imageNamed:@"stepbackward"];    [self.view addSubview:_backward];    _backward.alpha=0.0;}//标题视图-(void)createTitleView{    UILabel *titleLabel=[Tools createLabelWithFrame:CGRectMake(50, 10, kHeight-100, 20) text:self.videoTitle textColor:[UIColor whiteColor] textAligment:NSTextAlignmentCenter andBgColor:[UIColor clearColor] font:[UIFont boldSystemFontOfSize:12]];    [self.view addSubview:titleLabel];    titleLabel.tag=111;    //    返回按钮    UIButton *backBtn=[[UIButton alloc]initWithFrame:CGRectMake(10, 20, 40, 40)];    [backBtn setBackgroundImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];    [backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:backBtn];}//返回-(void)backAction{    [self.navigationController popViewControllerAnimated:YES];    [self dismissViewControllerAnimated:YES completion:nil];}/** *  滑动进度条时执行该方法 * *  @param sender slider对象 */-(void)sliderDidSlided:(UISlider *)sender{    if(_player.status==AVPlayerStatusReadyToPlay)    {        CGFloat value=sender.value;        [_item seekToTime:CMTimeMake(value, 1)];    }}//隐藏上方的条条-(void)hideStatusLabels{    weak PlayViewController *weakSelf=self;    if(!_isPlaying)        return;    UILabel *label=(UILabel *)[self.view viewWithTag:111];    [UIView animateWithDuration:0.5 animations:^{//        自动播放视频        [weakSelf.player play];        weakSelf.isPlaying=YES;        label.alpha=0;        weakSelf.timeLabel.alpha=0;        weakSelf.slider.alpha=0;        weakSelf.progress.alpha=0;        weakSelf.playBtn.alpha=0;    } completion:^(BOOL finished) {    }];}//显示上方的条条-(void)showStatusLabels{    weak PlayViewController *weakSelf=self;    UILabel *label=(UILabel *)[self.view viewWithTag:111];    [UIView animateWithDuration:0.5 animations:^{        weakSelf.timeLabel.alpha=1;        weakSelf.slider.alpha=1;        weakSelf.progress.alpha=1;        weakSelf.playBtn.alpha=1;        label.alpha=1;    } completion:^(BOOL finished) {    }];}//手势点击屏幕触发的方法-(void)shiftStatus{    if(_playBtn.alpha==0)    {//        显示上方条条       [self performSelector:@selector(showStatusLabels) withObject:nil];//        6秒后 自动隐藏上方条条        [self performSelector:@selector(hideStatusLabels) withObject:nil afterDelay:6];    }    else//        隐藏条条        [self performSelector:@selector(hideStatusLabels) withObject:nil];}/** *  点击播放按钮时执行的方法 * *  @param sender 播放按钮 */-(void)playAction:(UIButton *)sender{    sender.selected=!sender.isSelected;    if(_isPlaying)    {        [_player pause];    }    else    {        [_player play];    }    _isPlaying=!_isPlaying;}/** *  收到播放完毕的通知时执行该方法 * *  @param notification 通知 */- (void)moviePlayDidEnd:(NSNotification *)notification{    NSLog(@"Play end");    [_player seekToTime:kCMTimeZero completionHandler:^(BOOL finished)     {        [self updateVideoSlider:0.0];        [_playBtn setTitle:@"Play" forState:UIControlStateNormal];    }];}- (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");//            开始播放视频            [_player play];//            播放状态置为YES            _isPlaying=YES;            _playBtn.selected=YES;//            隐藏上方的条条            [self hideStatusLabels];//            隐藏加载指示器            [_indicator stopAnimating];            [_indicator removeFromSuperview];            CMTime duration = _item.duration;// 获取视频总长度            CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒            _totalTime = [self convertTime:totalSecond];// 转换成播放时间            [self customVideoSlider:duration];            NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration));            [self monitoringPlayback:_item];// 监听播放状态        }        else if ([playerItem status] == AVPlayerStatusFailed)        {            //移除网络加载指示器            [_indicator stopAnimating];            [_indicator removeFromSuperview];            //NSLog(@"AVPlayerStatusFailed");            UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示" message:@"加载失败" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];            [alert show];            [alert dismissWithClickedButtonIndex:0 animated:YES];        }    } else if ([keyPath isEqualToString:@"loadedTimeRanges"])    {        NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度//        NSLog(@"Time Interval:%f",timeInterval);        CMTime duration = _item.duration;        CGFloat totalDuration = CMTimeGetSeconds(duration);//      设置进度处理器的进度值        [_progress setProgress:timeInterval / totalDuration animated:YES];    }}/** *  获取缓冲总进度 * *  @return 缓冲总进度 */- (NSTimeInterval)availableDuration{    NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域    float startSeconds = CMTimeGetSeconds(timeRange.start);    float durationSeconds = CMTimeGetSeconds(timeRange.duration);    NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度    return result;}/** *  将视频播放的当前时间和总时间,格式化显示在界面上 * *  @param second 播放器的当前时间 * *  @return 格式化后的显示时间 */- (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;}- (void)monitoringPlayback:(AVPlayerItem *)playerItem{    __weak PlayViewController  *weakSelf=self;    self.playbackTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time)    {        CGFloat currentSecond = playerItem.currentTime.value/playerItem.currentTime.timescale;// 计算当前在第几秒        [weakSelf updateVideoSlider:currentSecond];        NSString *timeString = [weakSelf convertTime:currentSecond];        weakSelf.timeLabel.text = [NSString stringWithFormat:@"%@/%@",timeString,weakSelf.totalTime];    }];}- (void)customVideoSlider:(CMTime)duration{    _slider.maximumValue = CMTimeGetSeconds(duration);    UIGraphicsBeginImageContextWithOptions((CGSize){ 1, 1 }, NO, 0.0f);    UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    [_slider setMinimumTrackImage:transparentImage forState:UIControlStateNormal];    [_slider setMaximumTrackImage:transparentImage forState:UIControlStateNormal];}/** *  更新滑块的值 * *  @param currentSecond 当前的播放时间 */- (void)updateVideoSlider:(CGFloat)currentSecond{    [_slider setValue:currentSecond animated:YES];}/***  视图即将消失时移除观察者对象,注销通知,如果播放器在播放,让其暂停,让播放器指向nil * *  @param animated 动画效果 */- (void)viewWillDisappear:(BOOL)animated{    [super viewWillDisappear:animated];    [_item removeObserver:self forKeyPath:@"status" context:nil];    [_item removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_item];    [_player removeTimeObserver:self.playbackTimeObserver];    if(_isPlaying)    {        [_player pause];        _player=nil;    }}@end

附上GitHub代码地址:AVFoundationPlayer


作者:CGPointZero
链接:http://www.jianshu.com/p/dad62fecd5df
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

阅读全文
0 0
原创粉丝点击