美文网首页good
iOS-AVPlayer视频播放封装

iOS-AVPlayer视频播放封装

作者: 香橙柚子 | 来源:发表于2020-12-06 22:30 被阅读0次

    JJPlayerView

    AVPlayer依赖于AVFoundation,另外AVPlayer不包含UI,想要完成播放功能,需要自己写UI。

    项目中往往会用到视频播放功能,GitHub上面也有很多star很高的库也能满足需求,但是当我们需要高度自定义UI的时候,他们却不能满足要求。

    所以对于要求较高的播放功能,我们需要自己封装一个播放器。
    今天我们就来实现一个简单的播放器,目的是了解播放器的使用。

    iOS开发中会有音视频播放的需求。目前常用的音视频播放器有 AVPlayer,AVPlayer支持本地播放,分步下载,在线流媒体播放。

    👇 👇 👇 👇 👇 👇 👇

    AVPlayer基础知识简介

    1. AVPlayer是用于管理媒体资源播放的控制器,它提供了播放器的诸多功能:播放、暂停、倍速等。AVPlayer一次只能播放一个资源文件(本地或者远程)。
      AVQueuePlayer可以播放一个列表,是AVPlayer的子类。
      然后我们根据视频资源,创建我们需要的AVPlayer。系统为我们也提供了几个初始化方法。
    + (instancetype)playerWithURL:(NSURL *)URL;
    + (instancetype)playerWithPlayerItem:(nullable AVPlayerItem *)item;
    - (instancetype)initWithURL:(NSURL *)URL;
    - (instancetype)initWithPlayerItem:(nullable AVPlayerItem *)item;
    
    1. AVPlayerItem代表了一个播放资源,每播放一个视频需要一个数据资源,我们需要初始化一个AVPlayerItem。
      系统提供了几个初始化AVPlayerItem的方法。
    /// - AVPlayerItem的初始化方法
    + (instancetype)playerItemWithURL:(NSURL *)URL;
    + (instancetype)playerItemWithAsset:(AVAsset *)asset;
    - (instancetype)initWithURL:(NSURL *)URL;
    - (instancetype)initWithAsset:(AVAsset *)asset;
    
    1. 单纯使用AVPlayer是无法显示视频的,需要将视频添加至AVPlayerLayer上,然后添加到我们自己创建的视图layer上面。

    封装JJPlayerView

    封装播放器可以分为两个部分:

    • 第一是播放视频的功能
    • 第二个是UI交互。
      两个部分不是完全分开的,UI就是调用这个方法,达到播放视频的功能。
    1. 创建一个JJPalyerView,作为承载视图。
    2. 创建必要的属性
      AVPlayer,AVPlayerItem,AVPlayerLayer
    /// 播放器
    @property (nonatomic, strong)   AVPlayer         *player;
    /// 播放器item
    @property (nonatomic, strong)   AVPlayerItem     *playerItem;
    /// 播放器layer
    @property (nonatomic, strong)   AVPlayerLayer    *playerLayer;
    /// 控件的原始frame
    @property (nonatomic, assign)   CGRect           customFrame;
    /// 父类控件
    @property (nonatomic, strong)   UIView           *fatherView;
    /// 视图拉伸模式
    @property (nonatomic, copy)     NSString         *fillMode;
    /// 是否处于全屏状态
    @property (nonatomic, assign)   BOOL             isFullScreen;
    /// 工具条是否隐藏
    @property (nonatomic, assign)   BOOL             isDisappear;
    /// 用户播放标记
    @property (nonatomic, assign)   BOOL             isUserPlay;
    /// 点击最大化标记
    @property (nonatomic, assign)   BOOL             isUserTapMaxButton;
    /// 是否播放完毕
    @property (nonatomic, assign)   BOOL             isFinish;
    /// 是否在调节音量:YES为音量,NO为屏幕亮度
    @property (nonatomic, assign)   BOOL isVolume;
    /// 是否在拖拽
    @property (nonatomic, assign)   BOOL isDragged;
    /// 缓冲
    @property (nonatomic, assign)   BOOL isBuffering;
    /// 用来保存快进的总时长
    @property (nonatomic, assign)   CGFloat sumTime;
    /// 播放器配置信息
    @property (nonatomic, strong) JJPlayerConfigure *playerConfigure;
    /// 视频播放控制面板(遮罩)
    @property (nonatomic, strong) JJPlayerMaskView  *playerMaskView;
    /// 滑动方向
    @property (nonatomic, assign) JJPanDirection     panDirection;
    /// 音量滑杆
    @property (nonatomic, strong) UISlider *volumeViewSlider;
    /// 点击屏幕定时器
    @property (nonatomic, strong) NSTimer *tapTimer;
    /// 播放器的播放状态
    @property (nonatomic, assign) JJPlayerState playerState;
    /// 记录点击屏幕定时器的时间
    @property (nonatomic, assign)   NSInteger tapTimeCount;
    /// 是否已经移除了KVO
    @property (nonatomic, assign)   BOOL isRemoveObserver;
    
    1. 创建播放完成和返回按钮回调事件
    /// 返回按钮回调
    @property (nonatomic, copy) void(^BackBlock) (UIButton *backButton);
    /// 播放完成回调
    @property (nonatomic, copy) void(^EndBlock)(void);
    
    1. 添加通知,监听播放器的各种状态
    //开启
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
        /// 监听横竖屏的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
        /// 进入后台
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
        /// 进入前台
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterPlayground:) name:UIApplicationDidBecomeActiveNotification object:nil];
        
        // 添加播放完成通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
        // 添加打断播放的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruptionComing:) name:AVAudioSessionInterruptionNotification object:nil];
        // 添加插拔耳机的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
    
    1. 将AVPlayerLayer添加到视图上
      定义视频在AVPlayerLayer中的显示方式,默认方式AVLayerVideoGravityResizeAspect;
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame = frame;
    playerLayer.backgroundColor = [UIColor blackColor].CGColor;
    playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [view.layer addSublayer: playerLayer];
    

    我们看到了,需要设置AVPlayerLayerframe,还需要通过videoGravity设置资源填充展示的样式.

    但是这里有更好的写法,创建一个PlayerLayerView,将这个PlayerLayerView的layer返回class,设置为AVPlayerLayer类型,这样就不用设置layer的frame了,直接设置view的frame就可以,更加直观方便。

    @interface JJPlayerLayer : UIView
    
    @end
    
    @implementation JJPlayerLayer
    
    + (Class)layerClass {
        return AVPlayerLayer.class;
    }
    
    @end
    
    /// 播放器layer
    @property (nonatomic, strong)   JJPlayerLayer    *playerLayer;
    
    1. AVPlayer提供了音视频的播放和暂停功能,与之对应的方法: playpause
    /// 开始播放
    - (void)play{
         // 这里不只是播放,点击播放的时候还要设置相关UI,判断当前状态是否可以播放。并不是直接播放就可以。
         // 判断当前是否播放完成,播放完成后要从头开始播放
        self.playerMaskView.playButton.selected = YES;
         // [self.layer insertSublayer:self.playerLayer atIndex:0];
        if (self.isFinish && self.playerMaskView.slider.value == 1) {
            _isFinish = NO;
            [_player seekToTime:CMTimeMake(0, 1) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
        } else {
            [self.player play];
        }
    }
    /// 暂停
    - (void)pause{
        /// 设置暂停相关UI样式。
        [self.player pause];
    }
    
    1. 播放器一次只能给你管理一个播放资源,但我们需要切换资源的时候,可以调用相关replaceCurrentItemWithPlayerItem方法。AVFoundation框架还提供了一个专门的类:AVQueuePlayer,用来管理资源列表,我们暂时不讨论,后面再延伸.
    - (void)replaceCurrentItemWithPlayerItem:(nullable AVPlayerItem *)item;
    

    但是我们为了更方便使用,当切换资源的时候,重置播放器。也就是销毁原来的资源,重新创建。

    #pragma mark - 重置播放器
    - (void)resetPlayer{
        //重置状态
        self.playerState = JJPlayerStatePause;
        _isUserPlay = YES;//用户点击标志
        _isDisappear = NO;//工具条隐藏标记
        //移除之前的
        [self pause];//先暂停
        [self.playerLayer removeFromSuperlayer];
        self.playerLayer = nil;
        self.player = nil;
        //还原进度条和缓冲条
        self.playerMaskView.slider.value = 0;
        self.playerMaskView.progressView.progress = 0;
        //重置时间
        self.playerMaskView.currentTimeLabel.text = @"00:00";
        self.playerMaskView.totalTimeLabel.text = @"00:00";
    
        [self destoryToolBarTimer];
        //重置Toolbar
        [UIView animateWithDuration:0.25 animations:^{
            self.playerMaskView.topToolBar.alpha = 1.0;
            self.playerMaskView.bottomToolBar.alpha = 1.0;
        }];
        //重新添加工具条消失定时器
        [self resetTooBarDisappearTimer];
        self.playerMaskView.failButton.hidden = YES;
        //开始转子动化
        [self.playerMaskView.loadingView startAnimation];
    }
    

    然后再重新添加新的资源

       self.playerItem = [AVPlayerItem playerItemWithURL:_url];
        //创建播放器
        _player = [AVPlayer playerWithPlayerItem:_playerItem];
        _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
        _playerLayer.videoGravity = _fillMode;
        [self.layer insertSublayer:_playerLayer atIndex:0];
    
    1. 监听资源的状态
      我们可以使用KVO监听playerItem的几个属性statusloadedTimeRangesplaybackBufferEmpty
    [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];
    
    • 只有当status发生改变,并且值为AVPlayerItemStatusReadyToPlay的时候,说明视频资源准备完成,才可以播放。
      结果是AVPlayerItemStatusFailed的时候,说明解析失败,需要展示相关UI和文案。

    • loadedTimeRanges可以计算缓冲进度

    • playbackBufferEmpty说明当前缓冲为空,需要做暂停处理,加载一会...........

    - (void)setPlayerItem:(AVPlayerItem *)playerItem{
        if (_playerItem == playerItem) {
            return;
        }
        
        if (_playerItem) {
            if (!self.isRemoveObserver) {
                [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
                [_playerItem removeObserver:self forKeyPath:@"status"];
                [_playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
                [_playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
            }
            self.isRemoveObserver = YES;
            //重置播放器
            [self resetPlayer];
        }
        _playerItem = playerItem;
        if (playerItem) {
            self.isRemoveObserver = NO;
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
            [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];
        }
    }
    #pragma mark - kvo监听
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        if ([keyPath isEqualToString:@"status"]) {
            if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {//加载完成,可以播放
                //加载完成后,再添加平移手势
                //添加平移手势,用来控制音量/亮度/快进快退
                UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureDirection:)];
                pan.delegate = self;
                pan.maximumNumberOfTouches = 1;  //一根手指
                pan.delaysTouchesBegan = YES;
                pan.delaysTouchesEnded = YES;
                pan.cancelsTouchesInView = YES;
                [self.playerMaskView addGestureRecognizer:pan];
                self.player.muted = self.playerConfigure.isMute;
                [self addPeriodicTimeObserver];
                if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
                    self.playerState = JJPlayerStatePlaying;
                }else if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
                    self.playerState = JJPlayerStateFailed;
                }
                
            }else if (self.player.currentItem.status == AVPlayerItemStatusFailed){
                // 解析失败(播放失败)
                self.playerState = JJPlayerStateFailed;
            }
        }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){
            //计算缓冲进度
            NSTimeInterval timeInterval = [self calculateBufferProgress];
            CMTime duration = self.playerItem.duration;
            CGFloat totalDuratuon = CMTimeGetSeconds(duration);
            [self.playerMaskView.progressView setProgress:(timeInterval / totalDuratuon) animated:NO];
        }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]){
            //当前缓冲时空的时候
            if (self.playerItem.isPlaybackBufferEmpty) {
                self.playerState = JJPlayerStateBuffering;
                [self bufferingSomeSecond];//卡顿一会,缓冲几秒
            }
        }
    }
    

    我们可以看到添加KVO之前。先移除,是因为我们可能会替换资源,如果不移除,监听到的就是之前的资源信息。

    1. 添加通知监听其他状态,实现UI交互功能
      其实这个时候其实就一个可以将视频播放出来了,但是我们为了更好地体验,需要获取到播放器的状态:播放、加载、播放完成等等。

    这里我们可以通过通知来获取当前的具体状态。
    通过通知AVPlayerItemDidPlayToEndTimeNotification可以接收到视频播放完成通知.
    通过通知AVAudioSessionInterruptionNotification可以接收到打断播放的通知.
    通过通知AVAudioSessionRouteChangeNotification可以接收到插拔耳机的通知.

     // 添加播放完成通知
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
     // 添加打断播放的通知
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interruptionComing:) name:AVAudioSessionInterruptionNotification object:nil];
     // 添加插拔耳机的通知
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
    
    1. AVPlayer 中播放进度的获取如下,通过回传的time参数,可以更新进度.
    - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
    

    当拿到回传的时间,我们需要更新进度指示器。

    @weakify(self);
        [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 10) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            @strongify(self);
            [self sliderTimerAction];
        }];
    
    - (void)sliderTimerAction{
        if (self.playerItem.duration.timescale != 0) {
            self.playerMaskView.slider.maximumValue = 1;
            CGFloat total = _playerItem.duration.value / _playerItem.duration.timescale;
            self.playerMaskView.slider.value = CMTimeGetSeconds(self.playerItem.currentTime) / total;
            //判断是否正在播放
            if (self.playerItem.isPlaybackLikelyToKeepUp && self.playerMaskView.slider.value > 0) {
                self.playerState = JJPlayerStatePlaying;
            }
            
            //当前时长
            NSInteger proMin = (NSInteger)CMTimeGetSeconds([_player currentTime]) / 60;//当前分钟
            NSInteger proSec = (NSInteger)CMTimeGetSeconds([_player currentTime]) % 60;//当前秒
            self.playerMaskView.currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", (long)proMin, (long)proSec];
            //总时长
            NSInteger durMin = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale / 60;//总分钟
            NSInteger durSec = (NSInteger)_playerItem.duration.value / _playerItem.duration.timescale % 60;//总秒
            self.playerMaskView.totalTimeLabel.text   = [NSString stringWithFormat:@"%02ld:%02ld", (long)durMin, (long)durSec];
        }
    }
    
    1. 有的时候用户可以快进,或者手动拖动到指定时间点播放资源:
    - (void)seekToTime:(CMTime)time;
    

    当我们滑动播放器进度的时候,或者活动屏幕的时候,可以调用次方法,改变播放进度。

    1. 其他通知的监听
      电话打断播放,home键回到主页面,屏幕翻转等等:
      /// 添加打断播放的通知
      [[NSNotificationCenter defaultCenter] addObserver: self selector:@selector(interruptionComing:) name: AVAudioSessionInterruptionNotification object:nil];
      /// 监听横竖屏的通知
      [[NSNotificationCenter defaultCenter] addObserver: self selector:@selector(orientationChanged:) name: UIDeviceOrientationDidChangeNotification object:nil];
      /// 进入后台
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil]
      /// 进入前台
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterPlayground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    
    1. 创建一个UISlider,作为食品播放器的滑竿。
      系统的滑竿是非常好用的,但是UI不满足要求,我们继承系统UISlider,稍作修改:
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface JJSlider : UISlider
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    @implementation JJSlider
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            [self setupUI];
        }
        return self;
    }
    
    - (void)setupUI {
        UIImage *thumbImage = [UIImage imageWithName:@"JJRoundButton"];
        [self setThumbImage:thumbImage forState:UIControlStateHighlighted];
        [self setThumbImage:thumbImage forState:UIControlStateNormal];
    }
    @end
    

    修改滑竿的按钮图片即可。

    当拖动滑竿的时候改变视频播放的进度。

    1. 当可以播放的时候,通过参数rate可以获取到当前是在加载中还是播放中。也可以通过它设置播放速度。
    2. 我们需要创建一个遮罩层,添加一些基本的功能,比如,胡奥明屏幕实现快进,快退功能,改变音量,改变屏幕亮度,还要添加一些标题,播放按钮等功能。
      我们创建一个JJPlayerMaskView作为遮罩层。
      遮罩层里面包含的属性:标题,返回按钮,播放按钮,暂停按钮,进度条,缓冲条,播放失败提示,全屏按钮等等。

    页面上需要添加手势,点击隐藏/显示 这些控件。
    创建一个协议,将这些事件传递出去。

    @protocol JJPlayerMaskViewDelegate <NSObject>
    
    /// 返回按钮点击事件代理
    - (void)jj_playerMaskViewBackButtonAction:(UIButton *_Nonnull)button;
    /// 播放按钮点击事件代理
    - (void)jj_playerMaskViewPlayButtonAction:(UIButton *_Nonnull)button;
    /// 全屏按钮点击事件代理
    - (void)jj_playerMaskViewFullButtonAction:(UIButton *_Nonnull)button;
    /// 开始滑动
    - (void)jj_playerMaskViewProgressSliderTouchBegan:(JJSlider *_Nullable)slider;
    /// 滑动中
    - (void)jj_playerMaskViewProgressSliderTouchChanged:(JJSlider *_Nullable)slider;
    /// 滑动结束
    - (void)jj_playerMaskViewProgressSliderTouchEnd:(JJSlider *_Nullable)slider;
    /// 播放失败按钮事件
    - (void)jj_playerMaskViewFailButtonAction:(UIButton *_Nonnull)button;
    
    /// 代理事件
    @property (nonatomic, weak)   id<JJPlayerMaskViewDelegate > delegate;
    /// 顶部工具条
    @property (nonatomic, strong) UIView              *topToolBar;
    /// 标题
    @property (nonatomic, strong) UILabel             *titleLabel;
    /// 底部工具条
    @property (nonatomic, strong) UIView              *bottomToolBar;
    /// 加载动画
    @property (nonatomic, strong, nullable) JJAnimationView     *loadingView;
    /// 顶部工具条返回按钮(会和工具条一起消失显示)
    @property (nonatomic, strong) UIButton            *backButton;
    /// 顶部返回按钮,一直显示
    @property (nonatomic, strong) UIButton            *backOnlyButton;
    
    
    @property (nonatomic, strong) UIButton            *lockButton;
    
    /// 底部工具条播放/暂停按钮
    @property (nonatomic, strong) UIButton            *playButton;
    /// 底部工具条全屏按钮
    @property (nonatomic, strong) UIButton            *fullButton;
    /// 底部工具条当前时间
    @property (nonatomic, strong) UILabel             *currentTimeLabel;
    /// 底部工具条总时间
    @property (nonatomic, strong) UILabel             *totalTimeLabel;
    /// 缓冲进度条
    @property (nonatomic, strong) UIProgressView      *progressView;
    /// 播放进度条
    @property (nonatomic, strong) JJSlider            *slider;
    /// 加载失败按钮
    @property (nonatomic, strong) UIButton            *failButton;
    
    1. 判断滑动方向,并记录
      水平滑动的时候
    if (self.panDirection == JJPanDirectionHorizontalMoved) {
        [self panHorizontalMoved:thePoint.x];
    }
    

    水平滑动的时候改变slider的UI,并调节进度

    #pragma mark - 水平滑动,调节进度
    - (void)panHorizontalMoved:(CGFloat)value{
        //水平滑动进度,逻辑多,多做一些判断
        if (value == 0) {
            return;
        }
        //每次滑动时间需要叠加.
        self.sumTime += value/200.0;
        //需要给sumTime限制范围
        CMTime totalTime = self.playerItem.duration;
        CGFloat totalMovieDuration = (CGFloat)(totalTime.value/totalTime.timescale);
        if (self.sumTime > totalMovieDuration) {
            self.sumTime = totalMovieDuration;
        }
        if (self.sumTime < 0) {
            self.sumTime = 0;
        }
        self.isDragged = YES;
        //计算出拖动的当前秒数
        CGFloat dragedSeconds = self.sumTime;
        //滑杆进度
        CGFloat sliderValue = dragedSeconds/totalMovieDuration;
        //设置滑杆
        self.playerMaskView.slider.value = sliderValue;
        
        //转换成CMTime才能player来控制进度
        CMTime dragedCMTime = CMTimeMake(dragedSeconds, 1);
        [self.player seekToTime:dragedCMTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
        NSInteger proMin = (NSInteger)CMTimeGetSeconds(dragedCMTime) / 60;//当前秒
        NSInteger proSec = (NSInteger)CMTimeGetSeconds(dragedCMTime) % 60;//当前分钟
        self.playerMaskView.currentTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld",proSec,proMin];  
    }
    
    1. 创建音量滑竿
    /// 音量滑杆
    @property (nonatomic, strong) UISlider *volumeViewSlider;
    

    当垂直滑动的时候,以中心线为标准,判断滑动位置,一侧改变音量,另一侧改变屏幕亮度

    if (self.panDirection == JJPanDirectionVerticalMoved){
        [self panVerticalMoved:thePoint.y];
    }
    #pragma mark - 垂直滑动,调节音量和亮度
    - (void)panVerticalMoved:(CGFloat)value{
        if (self.isVolume) { //音量
            self.volumeViewSlider.value -= value/10000.0;
        }else{ //亮度
            [UIScreen mainScreen].brightness -= value/10000.0;
        }
    }
    
    1. 全屏处理。
      视频播放一般有全屏播放的功能。
      我们做法是,全屏的时候将播放器添加至keyWindow上面,并记录原来父视图,返回时,添加到圆的父视图上面。
      点击全屏的时候
    - (UIWindow *)getKeyWindow{
        if (@available(iOS 13.0, *)){
            return [UIApplication sharedApplication].windows.lastObject;
        }else{
            return [UIApplication sharedApplication].keyWindow;
        }
    }
    
    //添加到keyWindow上
     UIWindow *keyWindow = [self getKeyWindow];
     [keyWindow addSubview:self];
     [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"];
    

    只设置翻转一个方向即可!

    还原的时候:

    [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
    self.frame = _customFrame;
    // 还原到原有父类上
    [_fatherView addSubview:self];
    self.playerMaskView.fullButton.selected = NO;
    
    1. 其他细节处理
      锁屏功能,点击后页面不再处理事件。
      这个功能简单,记录锁屏按钮状态,处理各事件前,判断锁屏状态。或者锁屏的时候,将控件隐藏起来。

    弹幕功能,在遮罩上面的一部分区域,添加弹幕即可。

    demo地址

    相关文章

      网友评论

        本文标题:iOS-AVPlayer视频播放封装

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