iOS视频开发初探

效果图:


Untitled.gif

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

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

播放器的创建

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

#import 

视频的播放需要放在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 *)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视频开发初探)