iOS视频开发初探

作者: Double_Chen | 来源:发表于2017-02-18 16:56 被阅读124次

    效果图:


    Untitled.gif

    最近想要仿内涵段子练练手,就抓了包并开发了各个模块,尤其视频这部分使我收获颇多,跟大家分享一下。

    内容包括:
    1.播放器的创建
    2.设置视频链接
    3.视频加载过程中的监控
    4.播放、暂停、跳转进度
    5.全屏效果

    播放器的创建

    视频开发首先需要导入AVFoundation框架,

    #import <AVFoundation/AVFoundation.h>
    

    视频的播放需要放在AVPlayer上,AVPlayer通过AVPlayerItem管理资源,同时视频需要依靠AVPlayerLayer进行显示,AVPlayerLayer是CALayer类的子类,实例化后被添加到UIView上。这些是准备条件。

    设置视频链接

    @property(nonatomic,strong) AVPlayer *player;   //播放器对象
    @property(nonatomic,strong) AVPlayerLayer *playerLayer; //player需要在playerLayer上才能展示
    
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
        
        //如果已存在资源则替换,否则创建
        if (_player.currentItem) {
            //移除之前的监听
            [self removeObserverFromPlayerItem:_player.currentItem];
            [_player replaceCurrentItemWithPlayerItem:playerItem];
        }else {
            _player = [AVPlayer playerWithPlayerItem:playerItem];
        }
        
        [_playerLayer removeFromSuperlayer];
        _playerLayer = nil;
        
        _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
        _playerLayer.frame = videoView.bounds;
        
        /*
         AVLayerVideoGravityResizeAspect    默认,按视频比例显示,直到宽或高占满,未达到的地方显示父视图
         AVLayerVideoGravityResizeAspectFill    按原比例显示视频,直到两边屏幕占满,但视频部分内容可能无法显示
         AVLayerVideoGravityResize  按父视图尺寸显示,可能与原视频比例不同
         */
        _playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;    //视频填充模式
        [videoView.layer addSublayer:_playerLayer];
    

    视频加载过程中的监控

    上面的步骤执行完毕后,视频就已经正在加载中了,这个时候可以通过KVO进行视频加载的监控,获取到视频长度、缓存进度、播放进度等信息。

        [self addObserverFromPlayerItem:_player.currentItem];
        [self addPlayFinishNotification];
        [self addProgressNotification];
    
    #pragma mark - 通知、监听
    //KVO监听播放器播放状态
    - (void)addObserverFromPlayerItem:(AVPlayerItem *)playerItem {
        [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];    //开始或暂停
        [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];  //播放进度
    }
    
    //移除KVO监控
    - (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem {
        [playerItem removeObserver:self forKeyPath:@"status"];
        [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
    }
    
    //KVO监听回调
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        AVPlayerItem *playerItem = object;
        if ([keyPath isEqualToString:@"status"]) {
            AVPlayerStatus status = [[change valueForKey:@"new"] integerValue];
            if (status ==AVPlayerStatusReadyToPlay) {
                CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
                NSLog(@"正在播放,视频总长度为 %.2f",totalTime);
            }
        }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
            NSArray *array = playerItem.loadedTimeRanges;
            //本次缓冲时间范围
            CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
            CGFloat startSecond = CMTimeGetSeconds(timeRange.start);
            CGFloat durationSecond = CMTimeGetSeconds(timeRange.duration);
            CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
            //缓冲总长度
            NSTimeInterval totalBuffer = startSecond + durationSecond;
            bufferProgress = totalBuffer / totalTime;
            [self callBackWith:ABVideoDelegateCallbackBufferProgress];
        }
    }
    
    //播放进度监听
    - (void)addProgressNotification {
        AVPlayerItem *playerItem = _player.currentItem;
        
        static id playProgressObserver;
        
        //先移除上一个视频的监听
        if (playProgressObserver) {
            [_player removeTimeObserver:playProgressObserver];
        }
        
        //每秒监听一次播放进度
        typeof(self) weekSelf = self;
        playProgressObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            CGFloat currentTime = CMTimeGetSeconds(time);
            CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
            
            if (currentTime && !_isDrag) {
                videoPlayTime = currentTime;
                videoTotalTime = totalTime;
                playProgress = currentTime / totalTime;
                [weekSelf callBackWith:ABVideoDelegateCallbackProgress];
            }
        }];
    }
    
    //添加播放完成监控
    - (void)addPlayFinishNotification {
        _status = ABVideoStatusFinish;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinishNotification) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
    }
    
    //播放完成回调
    - (void)playFinishNotification {
        [self callBackWith:ABVideoDelegateCallbackFinish];
    }
    
    - (void)dealloc  {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
        [self removeObserverFromPlayerItem:self.player.currentItem];
    }
    

    播放、暂停、跳转进度

    //播放
    - (void)play {
        if (_player && _player.rate == 0) {
            [_player play];
        }
    }
    
    //暂停
    - (void)pause {
        if (_player && _player.rate != 0) {
            [_player pause];
        }
    }
    
    //跳转进度
    - (void)jumpProgressWith:(CGFloat)value {
        [self.player seekToTime:CMTimeMakeWithSeconds((value == 1 ? 0.99 : value) * CMTimeGetSeconds(_player.currentItem.duration), _player.currentItem.currentTime.timescale)];    //直接拖动到结束是不允许的
    }
    

    全屏效果

    全屏效果的实现就是UI上的变动了,将播放器进行旋转放大,同时将子视图如播放按钮、底部进度条等进行相应的约束即可。不同的UI有不同的约束,这里贴一下我的例子

    - (void)rotation {
        CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
        CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
        
        self.transform = CGAffineTransformMakeRotation(M_PI_2); //顺时针旋转
        self.frame = CGRectMake(0, 0, screenWidth, screenHeight);
        [_playBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self).offset(screenHeight/2 - 25);
            make.top.equalTo(self).offset(screenWidth/2 - 25);
            make.width.equalTo(@50);
            make.height.equalTo(@50);
        }];
        
        [_bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self).offset(screenWidth - 50);
            make.left.equalTo(self);
            make.height.equalTo(@40);
            make.width.equalTo([NSNumber numberWithFloat:screenHeight]);
        }];
    }
    

    上面大概说明了iOS视频开发的基本,我在仿内涵段子里面将其运动到了tableView上,后面有时间就再贴出来。

    相关文章

      网友评论

        本文标题:iOS视频开发初探

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