美文网首页详解demo
自定义AVPlayer视频播放器

自定义AVPlayer视频播放器

作者: 5a9c6f44e578 | 来源:发表于2017-09-26 19:42 被阅读357次

    这次自定义一个视频播放器
    先看效果:

    段宜恩.gif

    功能如下:

    1播放视频 (感觉是废话)
    2播放暂停
    3放大缩小
    4工具条自动隐藏,点击显示
    5进度条随时间移动
    6点击,拖拽移动条显示不同数据

    前四个比较简单放到一起讲,顺带把滑动条也加上
    先引用

    import <AVFoundation/AVFoundation.h>

    需要注册的属性

    //   视屏播放器背景图
    @property (strong, nonatomic) UIView *backView;
    //   播放视频控件
    @property (nonatomic,strong) AVPlayer *player;
    //   提供视频信息   创建AVPlayer使用的
    @property (nonatomic,strong) AVPlayerItem *playerItem;
    //   给AVPlayer一个播放的layer层
    @property (nonatomic,strong) AVPlayerLayer *playerLayer;
    //   工具栏 view  (放播放按钮等控件的view)
    @property (nonatomic,strong) UIView *bottomView;
    //   播放
    @property (nonatomic,strong) UIButton *playButton;
    //   全屏按钮
    @property (nonatomic,strong) UIButton *fullScreenButton;
    //   滑动条
    @property (nonatomic,strong) UISlider *slider;
    

    上代码

       // 初始化背景
        self.backView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, kScreenWidth, kScreenHeight / 2.5)];
        self.backView.backgroundColor = [UIColor clearColor];
        [self.view addSubview:self.backView];
    
        // 提供视频信息   创建AVPlayer使用的
        self.playerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]];
        //  通过AVPlayerItem创建AVPlayer
        self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];
        // 单纯使用AVPlayer类是无法显示视频的,要将视频层添加至AVPlayerLayer中,这样才能将视频显示出来  所以要给AVPlayer一个播放的layer层
        _playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        //  位置
        _playerLayer.frame = self.backView.bounds;
        //    插入指定位置             第二个参数,可以将一个图层插入到指定的下标位置
        [self.backView.layer insertSublayer:self.playerLayer atIndex:0];
    
        //添加手势动作,隐藏下面的进度条
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
        [self.backView addGestureRecognizer:tap];
        
        // 布局底部功能栏
        self.bottomView = [[UIView alloc] init];
        //  透明度
        self.bottomView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
        [self.backView addSubview:self.bottomView];
        [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.backView).with.offset(0);
            make.right.equalTo(self.backView).with.offset(0);
            make.bottom.equalTo(self.backView).with.offset(0);
            make.height.mas_equalTo(30);
        }];
        
        // 播放或暂停按钮
        self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
        //  选中状态
        self.playButton.selected = YES;
        [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
        [self.bottomView addSubview:self.playButton];
        [self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.bottomView).with.offset(5);
            make.centerY.equalTo(self.bottomView);
            make.size.mas_equalTo(CGSizeMake(30, 30));
        }];
        
        //  添加点击播放事件
        [self.playButton addTarget:self action:@selector(pauseOrPlay:) forControlEvents:UIControlEventTouchUpInside];
       
        
        //  底部全屏按钮
        self.fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.fullScreenButton setImage:[UIImage imageNamed:@"fullscreen"] forState:UIControlStateNormal];
        self.fullScreenButton.selected = YES;
        [self.bottomView addSubview:self.fullScreenButton];
        [self.fullScreenButton mas_makeConstraints:^(MASConstraintMaker *make) {
            make.right.equalTo(self.bottomView).with.offset(-5);
            make.centerY.equalTo(self.bottomView);
            make.size.mas_equalTo(CGSizeMake(30, 30));
        }];
        //   添加点击全屏事件
        [self.fullScreenButton addTarget:self action:@selector(clickFullScreen:) forControlEvents:UIControlEventTouchUpInside];
        
        // 底部进度条
        self.slider = [[UISlider alloc] init];
        //   已播放的部分颜色
        self.slider.minimumTrackTintColor = [UIColor whiteColor];
        //  未播放部分的进度条颜色
        self.slider.maximumTrackTintColor = [UIColor grayColor];
        // 初始位置  默认是0
        self.slider.value = 0.0;
        [self.slider setThumbImage:[UIImage imageNamed:@"dot"] forState:UIControlStateNormal];
        [self.bottomView addSubview:self.slider];
        [self.slider mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.bottomView).with.offset(45);
            make.right.equalTo(self.bottomView).with.offset(-45);
            make.centerY.equalTo(self.bottomView);
        }];
     
        //  播放视频
        [self.player play];
    

    布局基本就全了,在添加上对应的点击事件

    #pragma mark ==== 暂停或者播放
    - (void)pauseOrPlay:(UIButton *)sender{
        
        if (sender.selected == YES)
        {
            [sender setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
            //  暂停
            [self.player pause];
        }
        else
        {
            [sender setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
            [self.player play];
        }
        // 别忘记切换 状态
        sender.selected = !sender.selected;
    }
    
    
    #pragma mark ==== 点击全屏按钮
    - (void)clickFullScreen:(UIButton *)button{
        
        if (button.selected == YES)
        {   // 全屏
            [self toFullScreenWithInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
            [self.fullScreenButton setImage:[UIImage imageNamed:@"nonfullscreen@3x"] forState:UIControlStateNormal];
        }
        else
        {
            //  缩小
            [self toSmallScreen];
            [self.fullScreenButton setImage:[UIImage imageNamed:@"fullscreen@3x"] forState:UIControlStateNormal];
        }
        button.selected = !button.selected;
    }
    
    
    #pragma mark ====   全屏显示
    - (void)toFullScreenWithInterfaceOrientation:(UIInterfaceOrientation )interfaceOrientation{
    
        if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
            // 选择屏幕
            self.backView.transform = CGAffineTransformMakeRotation(-M_PI_2);
        }
        // BackView 全屏
        self.backView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
        // layer的方向宽和高对调
        self.playerLayer.frame = CGRectMake(0, 0, kScreenHeight, kScreenWidth);
        
        [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.height.mas_equalTo(50);
            make.top.mas_equalTo(kScreenWidth-50);
            make.left.equalTo(self.backView).with.offset(0);
            make.width.mas_equalTo(kScreenHeight);
        }];
        // 加到window上面
        [[UIApplication sharedApplication].keyWindow addSubview:self.backView];
        
    }
    
    
    #pragma mark ==== 缩小
    - (void)toSmallScreen{
    
            self.backView.transform = CGAffineTransformIdentity;
            self.backView.frame = CGRectMake(0, 100, kScreenWidth, kScreenHeight / 2.5);
            self.playerLayer.frame =  self.backView.bounds;
            [self.view addSubview:self.backView];
            
            [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(self.backView).with.offset(0);
                make.right.equalTo(self.backView).with.offset(0);
                make.height.mas_equalTo(50);
                make.bottom.equalTo(self.backView).with.offset(0);
            }];
    }
    
    #pragma mark ====  隐藏 显示  底部 工具栏
    - (void)singleTap:(UITapGestureRecognizer *)tap{
        
        [UIView animateWithDuration:1.0 animations:^{
            if (self.bottomView.alpha == 1)
            {
                self.bottomView.alpha = 0;
            }
            else if (self.bottomView.alpha == 0)
            {
                self.bottomView.alpha = 1;
            }
        }];
    }
    
    

    到此前四项只有4的自动隐藏还没有完成

    把功能拆开想:他不是立刻隐藏,而是停顿了一会再隐藏的,这需要计时器来设置时间,看视频播放软件暂停的时候是不隐藏的,那就是需要播放了一定时间再隐藏,这就需要监听播放器

    先写个时间属性,基于安全考虑设为属性好释放

    //   自动消失定时器
    @property (nonatomic,strong) NSTimer *autoDismissTimer;
    

    监听视频播放器,用KVO实现

      // 监听播放器状态变化   利用KVO 监听  只要属性一改变就会调用监听方法  暂停时不会更新,所以暂停不会继续调用通知
        [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    

    对应的监听方法

    #pragma mark ==== 监听播放器的变化属性
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    {
        //  播放时
        if ([keyPath isEqualToString:@"status"])
        {
            //  播放器状态
            AVPlayerItemStatus statues = [change[NSKeyValueChangeNewKey] integerValue];
            switch (statues) {
                case AVPlayerItemStatusReadyToPlay:
                    //  滑动条最大值  =   持续时间(播放总时间)
                    self.slider.maximumValue = CMTimeGetSeconds(self.playerItem.duration);
                    // 随时间移动
                    [self initTimer];
                   
                    //  计时器为空时创建计时器
                    if (!self.autoDismissTimer)
                    {
                         // 8秒执行一次  自动隐藏底栏
                        self.autoDismissTimer = [NSTimer timerWithTimeInterval:8.0 target:self selector:@selector(autoDismissView:) userInfo:nil repeats:YES];
                        // 主线程 不需要 return
                        [[NSRunLoop currentRunLoop] addTimer:self.autoDismissTimer forMode:NSDefaultRunLoopMode];
                    }
                    break;
                case AVPlayerItemStatusUnknown:
                    //   未知状态
                    break;
                case AVPlayerItemStatusFailed:
                    //    播放失败
                    break;
                    
                default:
                    break;
            }
        }
    }
    
    
    //调用plaer进行页面更新
    - (void)initTimer
    {
        //player的定时器
        __weak typeof(self)weakSelf = self;
        // 每秒更新一次UI Slider            CMTimeMake(表示 当前视频播放到的第几桢数,每秒的帧数)
        [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
                weakSelf.slider.value = CMTimeGetSeconds(weakSelf.playerItem.currentTime);
        }];
    }
    
    
    //自动隐藏底部功能栏
    - (void)autoDismissView:(NSTimer *)timer{
        // 0  暂停   1时正在播放视频
        if (self.player.rate == 1)
        {
            [UIView animateWithDuration:2.0 animations:^{
                self.bottomView.alpha = 0;
            }];
        }
    }
    
    

    现在只剩下6了
    拆开分析,点击播放应当是需要点击手势的,拖动还是用监听比较好

    点击手势

        //   点击
        UITapGestureRecognizer *tapSlider = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchSlider:)];
        [self.slider addGestureRecognizer:tapSlider];
        
        [self.bottomView addSubview:self.slider];
    

    拖动监听

        [self.slider addTarget:self action:@selector(sliderTapValueChange:) forControlEvents:UIControlEventTouchUpInside];
    

    对应的方法

    #pragma mark ==== 点击调用  或者 拖拽完毕的时候调用
    - (void)sliderTapValueChange:(UISlider *)slider
    {
        //  seektotime  获取精准定位
        // 直接用秒来获取CMTime
        // 当前视频播放到的帧数的具体时间,每秒的帧数
        [self.player seekToTime:CMTimeMakeWithSeconds(slider.value, self.playerItem.currentTime.timescale)];
    }
    
    
    #pragma mark    ====  点击Slider
    - (void)touchSlider:(UITapGestureRecognizer *)tap
    {
        // 根据点击的坐标计算对应的比例
        CGPoint touch = [tap locationInView:self.slider];
        //  占总厂的比例
        CGFloat scale = touch.x / self.slider.bounds.size.width;
        //CMTimeGetSeconds  获取时间的秒数
        self.slider.value = CMTimeGetSeconds(self.playerItem.duration) * scale;
        //  精准  具体时间,每秒的帧数
        [self.player seekToTime:CMTimeMakeWithSeconds(self.slider.value, self.playerItem.currentTime.timescale)];
    
    }
    

    再考虑一种情况暂停时拖拽和点击时

    - (void)touchSlider:(UITapGestureRecognizer *)tap
    {
        // 根据点击的坐标计算对应的比例
        CGPoint touch = [tap locationInView:self.slider];
        //  占总厂的比例
        CGFloat scale = touch.x / self.slider.bounds.size.width;
        //CMTimeGetSeconds  获取时间的秒数
        self.slider.value = CMTimeGetSeconds(self.playerItem.duration) * scale;
        //  精准  具体时间,每秒的帧数
        [self.player seekToTime:CMTimeMakeWithSeconds(self.slider.value, self.playerItem.currentTime.timescale)];
        if (self.player.rate != 1)
        {
            // 1的时候播放   0暂停
            [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
            [self.player play];
        }
    }
    
    

    这样就可以了

    到此自定义AVPlayer视频播放器功能已全部实现

    附上下载地址
    https://gitee.com/wwwzz/ZiDingYiAVPlayerShiPinBoFangQi.git

    相关文章

      网友评论

      • narutog17:AVplayer 是不是 要iOS 9 之后才能用啊
        narutog17:@踏月_留香 那我的项目最低适配 8.0 也可以用吧 ,终于可以把MPMoviePlayerController 扔了。。。
        5a9c6f44e578:@narutog17 并不是阿团长

      本文标题:自定义AVPlayer视频播放器

      本文链接:https://www.haomeiwen.com/subject/jcheextx.html