zoukankan      html  css  js  c++  java
  • Objective-C AVPlayer播放视频的使用与封装

    大致效果

    不要介意。界面有点丑。。。

    AVPlayer封装.gif

    界面搭建

    看下成员变量就知道我怎么搭建的了,这里我将video播放层的size作为参照量,对所有控件的size按照其video的size宽高进行比例缩放

    @interface VideoPlayerView()
    @property (nonatomic,copy) NSString *path;                  //播放地址 自动判断文件路径和网址路径
    @property (nonatomic,strong) AVPlayer *player;              //播放类
    @property (nonatomic,strong) AVPlayerLayer *playerlayer;    //显示区域
    @property (nonatomic,strong) UIButton *playBtn;             //播放暂停
    @property (nonatomic,strong) UIButton *stopBtn;             // 停止
    @property (nonatomic,strong) UIButton *fullScreenBtn;       //全屏
    @property (nonatomic,strong) UISlider *playSlider;          //进度选择
    @property (nonatomic,strong) UIProgressView *progress;      //进度
    @property (nonatomic,strong) UILabel *currentTimeLab;       //当前时间
    @property (nonatomic,strong) UILabel *durationLab;          //总时间
    @property (nonatomic,strong) UIView *toolView;              //工具栏view
    @property (nonatomic,assign) BOOL isFullScreen;             //全屏判断
    @property (nonatomic,assign) CGFloat videoHeight;           //video高
    @property (nonatomic,assign) CGFloat videoWidth;            //video宽
    @end
    

    所有控件使用懒加载 如下

    //播放暂停
    - (UIButton *)playBtn {
        if (!_playBtn) {
            _playBtn = [[UIButton alloc] initWithFrame:CGRectMake(kLrMargin, kUIy, kBtnWidth, kUIHeight)];
            _playBtn.backgroundColor  =[UIColor greenColor];
            _playBtn.selected = NO;
            _playBtn.enabled = NO;
            _playBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
            [_playBtn setTitle:@"播放" forState:UIControlStateNormal];
            [_playBtn setTitle:@"暂停" forState:UIControlStateSelected];
            [_playBtn addTarget:self action:@selector(play) forControlEvents:UIControlEventTouchUpInside];
        }
        return _playBtn;
    }
    

    屏幕适配

    由于涉及到屏幕的旋转和适配。我这里没有使用第三方框架来做约束,而是使用最基本的按百分比设置frame。旋转屏幕时通过调用本类- (void)resetFrame:(CGSize)size;方法来重设frame。所以需要重设frame的控件在懒加载中设置frame,调用时即刷新frame。

    • 先看下初始化 对video的size设置是时始终用最小的边来确定高度,宽度与屏幕当前宽度相当
    //初始化
    - (instancetype)initWithFrame:(CGRect)frame andPath:(NSString*)path {
        self = [super initWithFrame:frame];
        if (self) {
            self.backgroundColor = [UIColor clearColor];
            self.path = path;
            CGFloat width = [UIScreen mainScreen].bounds.size.width;
            CGFloat height = [UIScreen mainScreen].bounds.size.height;
            self.videoHeight = height > width ? width * 0.6 : height * 0.6;
            self.videoWidth = [UIScreen mainScreen].bounds.size.width-2*kLrMargin;
            [self.layer addSublayer:self.playerlayer];
            [self addSubview:self.toolView];
        }
        return self;
    }
    
    • 屏幕旋转时做一些事
    //屏幕旋转时触发 这里写在父类中
    - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {    
        [self.videoView resetFrame:size];
    }
    
    //旋转屏幕重设frame
    - (void)resetFrame:(CGSize)size {
        CGFloat width = size.width;
        CGFloat height = size.height;
        self.videoHeight = height > width ? width * 0.6 : height * 0.6;
        self.videoWidth = size.width - 2 * kLrMargin;
        if (self.isFullScreen) {
            //全屏时旋转
            [self setPlayerWithPosition:CGPointZero andSize:size];
        } else {
            //普通旋转
            [self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)];
            //刷新frame
            [self toolView];
            [self playSlider];
            [self progress];
        }    
    }
    
    • 懒加载刷新frame
    //进度懒加载 调用时只刷新frame
    - (UIProgressView *)progress {
        if (!_progress) {
            _progress = [[UIProgressView alloc] init];
            _progress.progress = 0;
        }
        _progress.frame = CGRectMake(2, self.playSlider.frame.size.height/2, self.playSlider.frame.size.width-2-2, kUIHeight);
        return _progress;
    }
    

    AVPlayer的基本操作

    基本操作包括 播放 、暂停、 停止、 播放指定位置、缓存进度
    播放网络地址时 在info.plist中添加 App Transport Security Settings字典中添加Allow Arbitrary Loads元素 值为YES

    添加项.png
    使用AVPlayer播放视频就必须用到AVPlayerlayer用来显示播放视图。

    //加载显示层
    - (AVPlayerLayer*)playerlayer {
        if (!_playerlayer) {
            _playerlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
            _playerlayer.bounds = CGRectMake(0, 0, self.videoWidth, self.videoHeight);
            _playerlayer.anchorPoint = CGPointMake(0, 0);
            _playerlayer.position = CGPointMake(kLrMargin, kTopMargin);
            _playerlayer.backgroundColor = [UIColor blackColor].CGColor;
        }
        return _playerlayer;
    }
    
    //加载播放类
    - (AVPlayer *)player {
        if (!_player) {
            _player = [AVPlayer playerWithURL:[self getUrlPath:self.path]];
            //kvo注册
            [self addObservers];
        }
        return _player;
    }
    
    • 使用KVO对状态和缓存进行检测,添加KVO时养成习惯写好移除操作
    //注册kvo
    - (void)addObservers{
        [self.player.currentItem addObserver:self forKeyPath:kItemStatus options:NSKeyValueObservingOptionNew context:nil];
        [self.player.currentItem addObserver:self forKeyPath:kItemLoadedTimeRanges options:NSKeyValueObservingOptionNew context:nil];
    }
    
    //移除kvo
    - (void)dealloc {
        [self.player.currentItem removeObserver:self forKeyPath:kItemStatus];
        [self.player.currentItem removeObserver:self forKeyPath:kItemLoadedTimeRanges];
    }
    
    //kvo回调
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:kItemStatus]) {
            AVPlayerItem *item = object;
            if (item.status == AVPlayerItemStatusReadyToPlay) {
                //准备播放
                [self readyToplayWithItem:item];
            }else if (item.status == AVPlayerItemStatusUnknown) {
                //播放失败
               //这里写个alertView提示下就行
            }else if (item.status == AVPlayerItemStatusFailed) {
                //播放失败
                //同上
            }
        } else if ([keyPath isEqualToString:kItemLoadedTimeRanges]) {
            AVPlayerItem *item = object;
            [self getLoadedTimeRanges:item];
        }
    }
    
    • 基础功能
    //播放 暂停
    - (void)play {
        if (self.playBtn.selected) {
            self.playBtn.selected = NO;
            [self.player pause];
        } else {
            self.playBtn.selected = YES;
            [self.player play];
            [self timerStar];
        }
    }
    
    //停止
    - (void)stop {
        [self.player pause];
        [self.player seekToTime:CMTimeMake(0, 1)];
        self.playBtn.selected = NO;
    }
    //全屏
    - (void)fullScreen {
        self.toolView.hidden = YES;
        self.isFullScreen = YES;
        self.backgroundColor = [UIColor blackColor];
        self.playerlayer.bounds = [UIScreen mainScreen].bounds;
        self.playerlayer.anchorPoint = CGPointMake(0, 0);
        self.playerlayer.position = CGPointMake(0, 0);
    }
    
    //播放指定位置
    - (void)playCurrentVideo {
        self.playBtn.selected = YES;
        NSTimeInterval second = self.playSlider.value;
        [self.player.currentItem seekToTime:CMTimeMake(second,1)];
        [self.player play];
        [self timerStar];
    }
    
    • 具体操作
      • 包括格式化时间
      • 格式化路径
      • 播放准备
      • 缓存计算
      • 触摸关闭全屏
      • 设置video的大小位置
    //设置video的frame
    - (void)setPlayerWithPosition:(CGPoint)position andSize:(CGSize)size {
        self.playerlayer.anchorPoint = CGPointMake(0, 0);
        self.playerlayer.position = position;
        self.playerlayer.bounds = CGRectMake(0, 0, size.width, size.height);
    }
    
    //准备播放
    - (void)readyToplayWithItem:(AVPlayerItem*)item {
        self.playBtn.enabled = YES;
        long long durationSecond = item.duration.value / item.duration.timescale;
        self.durationLab.text = [NSString stringWithFormat:@" / %@",[self getFormatDate:durationSecond]];
        self.playSlider.maximumValue = durationSecond;
        self.playSlider.minimumValue = 0;
        [self.playSlider addTarget:self action:@selector(playCurrentVideo) forControlEvents:UIControlEventValueChanged];
    
    }
    
    //获得缓存
    - (void)getLoadedTimeRanges:(AVPlayerItem*)item {
        NSValue *value = [item.loadedTimeRanges lastObject];
        CMTimeRange range = [value CMTimeRangeValue];
        long long cacheSecond = range.start.value/range.start.timescale + range.duration.value/range.duration.timescale;
        long long currentSecond = item.currentTime.value / item.currentTime.timescale;
        self.progress.progress = (currentSecond + cacheSecond) * 0.1;
      
    }
    
    
    //格式化时间
    - (NSString*)getFormatDate:(NSTimeInterval)time {
        int seconds = (int)time % 60;
        int minutes = (int)(time / 60) % 60;
        int hours = (int)time / 3600;
        return [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
    }
    
    //格式化url路径
    - (NSURL*)getUrlPath:(NSString*)path {
        NSURL *url;
        if ([self.path containsString:@"http"]) {
            url = [NSURL URLWithString:self.path];
        } else {
            url = [NSURL fileURLWithPath:self.path];
        }
        return url;
    }
    
    //开启定时
    - (void)timerStar {
        //定时回调
        __weak typeof(self) weakSelf = self;
        [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
            long long current = weakSelf.player.currentItem.currentTime.value / weakSelf.player.currentItem.currentTime.timescale;
            weakSelf.playSlider.value = current;
            NSString *currentFormat = [weakSelf getFormatDate:current];
            weakSelf.currentTimeLab.text = [NSString stringWithFormat:@"%@",currentFormat];
        }];
        
    }
    
    
    //触摸关闭全屏
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        if (self.isFullScreen) {
            self.toolView.hidden = NO;
            self.backgroundColor = [UIColor clearColor];
            [self setPlayerWithPosition:CGPointMake(kLrMargin, kTopMargin) andSize:CGSizeMake(self.videoWidth, self.videoHeight)];
            
            [self toolView];
            [self playSlider];
            [self progress];
            self.isFullScreen = NO;
        }
    }
    

    这样一个简单AVPlayer的封装就做好了

    Demo地址

    https://github.com/gongxiaokai/AVPlayerDemo

  • 相关阅读:
    关于js计算非等宽字体宽度的方法
    [NodeJs系列]聊一聊BOM
    Vue.js路由管理器 Vue Router
    vue 实践技巧合集
    微任务、宏任务与Event-Loop
    事件循环(EventLoop)的学习总结
    Cookie、Session和LocalStorage
    MySQL 树形结构 根据指定节点 获取其所在全路径节点序列
    MySQL 树形结构 根据指定节点 获取其所有父节点序列
    MySQL 创建函数报错 This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators
  • 原文地址:https://www.cnblogs.com/gongxiaokai/p/7123824.html
Copyright © 2011-2022 走看看