123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- //
- // YBIBVideoView.m
- // YBImageBrowserDemo
- //
- // Created by 波儿菜 on 2019/7/11.
- // Copyright © 2019 杨波. All rights reserved.
- //
- #import <AVFoundation/AVFoundation.h>
- #import "YBIBVideoView.h"
- #import "YBIBVideoActionBar.h"
- #import "YBIBVideoTopBar.h"
- #import "YBIBUtilities.h"
- #import "YBIBIconManager.h"
- @interface YBIBVideoView () <YBIBVideoActionBarDelegate>
- @property (nonatomic, strong) YBIBVideoTopBar *topBar;
- @property (nonatomic, strong) YBIBVideoActionBar *actionBar;
- @property (nonatomic, strong) UIButton *playButton;
- @end
- @implementation YBIBVideoView {
- AVPlayer *_player;
- AVPlayerItem *_playerItem;
- AVPlayerLayer *_playerLayer;
- BOOL _active;
- }
- #pragma mark - life cycle
- - (void)dealloc {
- [self removeObserverForSystem];
- [self reset];
- }
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- [self initValue];
- self.backgroundColor = UIColor.clearColor;
-
- [self addSubview:self.thumbImageView];
- [self addSubview:self.topBar];
- [self addSubview:self.actionBar];
- [self addSubview:self.playButton];
- [self addObserverForSystem];
-
- _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapGesture:)];
- [self addGestureRecognizer:_tapGesture];
- }
- return self;
- }
- - (void)initValue {
- _playing = NO;
- _active = YES;
- _needAutoPlay = NO;
- _autoPlayCount = 0;
- _playFailed = NO;
- _preparingPlay = NO;
- }
- #pragma mark - public
- - (void)updateLayoutWithExpectOrientation:(UIDeviceOrientation)orientation containerSize:(CGSize)containerSize {
- UIEdgeInsets padding = YBIBPaddingByBrowserOrientation(orientation);
- CGFloat width = containerSize.width - padding.left - padding.right, height = containerSize.height;
- self.topBar.frame = CGRectMake(padding.left, padding.top, width, [YBIBVideoTopBar defaultHeight]);
- self.actionBar.frame = CGRectMake(padding.left, height - [YBIBVideoActionBar defaultHeight] - padding.bottom - 10, width, [YBIBVideoActionBar defaultHeight]);
- self.playButton.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
- _playerLayer.frame = (CGRect){CGPointZero, containerSize};
- }
- - (void)reset {
- [self removeObserverForPlayer];
-
- // If set '_playerLayer.player = nil' or '_player = nil', can not cancel observeing of 'addPeriodicTimeObserverForInterval'.
- [_player pause];
- _playerItem = nil;
- [_playerLayer removeFromSuperlayer];
- _playerLayer = nil;
- [self finishPlay];
- }
- - (void)hideToolBar:(BOOL)hide {
- if (hide) {
- self.actionBar.hidden = YES;
- self.topBar.hidden = YES;
- } else if (self.isPlaying) {
- self.actionBar.hidden = NO;
- self.topBar.hidden = NO;
- }
- }
- - (void)hidePlayButton {
- self.playButton.hidden = YES;
- }
- #pragma mark - private
- - (void)videoJumpWithScale:(float)scale {
- CMTime startTime = CMTimeMakeWithSeconds(scale, _player.currentTime.timescale);
- AVPlayer *tmpPlayer = _player;
-
- if (CMTIME_IS_INDEFINITE(startTime) || CMTIME_IS_INVALID(startTime)) return;
- [_player seekToTime:startTime toleranceBefore:CMTimeMake(1, 1000) toleranceAfter:CMTimeMake(1, 1000) completionHandler:^(BOOL finished) {
- if (finished && tmpPlayer == self->_player) {
- [self startPlay];
- }
- }];
- }
- - (void)preparPlay {
- _preparingPlay = YES;
- _playFailed = NO;
-
- self.playButton.hidden = YES;
-
- [self.delegate yb_preparePlayForVideoView:self];
-
- if (!_playerLayer) {
- _playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
- _player = [AVPlayer playerWithPlayerItem:_playerItem];
-
- _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
- _playerLayer.frame = (CGRect){CGPointZero, [self.delegate yb_containerSizeForVideoView:self]};
- [self.layer insertSublayer:_playerLayer above:self.thumbImageView.layer];
-
- [self addObserverForPlayer];
- } else {
- [self videoJumpWithScale:0];
- }
- }
- - (void)startPlay {
- if (_player) {
- _playing = YES;
-
- [_player play];
- [self.actionBar play];
-
- self.topBar.hidden = NO;
- self.actionBar.hidden = NO;
-
- [self.delegate yb_startPlayForVideoView:self];
- }
- }
- - (void)finishPlay {
- self.playButton.hidden = NO;
- [self.actionBar setCurrentValue:0];
- self.actionBar.hidden = YES;
- self.topBar.hidden = YES;
-
- _playing = NO;
-
- [self.delegate yb_finishPlayForVideoView:self];
- }
- - (void)playerPause {
- if (_player) {
- [_player pause];
- [self.actionBar pause];
- }
- }
- - (BOOL)autoPlay {
- if (self.autoPlayCount == NSUIntegerMax) {
- [self preparPlay];
- } else if (self.autoPlayCount > 0) {
- --self.autoPlayCount;
- [self.delegate yb_autoPlayCountChanged:self.autoPlayCount];
- [self preparPlay];
- } else {
- return NO;
- }
- return YES;
- }
- #pragma mark - <YBIBVideoActionBarDelegate>
- - (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPlayButton:(UIButton *)playButton {
- [self startPlay];
- }
- - (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar clickPauseButton:(UIButton *)pauseButton {
- [self playerPause];
- }
- - (void)yb_videoActionBar:(YBIBVideoActionBar *)actionBar changeValue:(float)value {
- [self videoJumpWithScale:value];
- }
- #pragma mark - observe
- - (void)addObserverForPlayer {
- [_playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
- __weak typeof(self) wSelf = self;
- [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
- __strong typeof(wSelf) self = wSelf;
- if (!self) return;
- float currentTime = time.value / time.timescale;
- [self.actionBar setCurrentValue:currentTime];
- }];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
- }
- - (void)removeObserverForPlayer {
- [_playerItem removeObserver:self forKeyPath:@"status"];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
- if (![self.delegate yb_isFreezingForVideoView:self]) {
- if (object == _playerItem) {
- if ([keyPath isEqualToString:@"status"]) {
- [self playerItemStatusChanged];
- }
- }
- }
- }
- - (void)didPlayToEndTime:(NSNotification *)noti {
- if (noti.object == _playerItem) {
- [self finishPlay];
- [self.delegate yb_didPlayToEndTimeForVideoView:self];
- }
- }
- - (void)playerItemStatusChanged {
- if (!_active) return;
-
- _preparingPlay = NO;
-
- switch (_playerItem.status) {
- case AVPlayerItemStatusReadyToPlay: {
- // Delay to update UI.
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self startPlay];
- double max = CMTimeGetSeconds(self->_playerItem.duration);
- [self.actionBar setMaxValue:(isnan(max) || isinf(max)) ? 0 : max];
- });
- }
- break;
- case AVPlayerItemStatusUnknown: {
- _playFailed = YES;
- [self.delegate yb_playFailedForVideoView:self];
- [self reset];
- }
- break;
- case AVPlayerItemStatusFailed: {
- _playFailed = YES;
- [self.delegate yb_playFailedForVideoView:self];
- [self reset];
- }
- break;
- }
- }
- - (void)removeObserverForSystem {
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
- }
- - (void)addObserverForSystem {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarFrame) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
- [[AVAudioSession sharedInstance] setActive:YES error:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
- }
- - (void)applicationWillResignActive:(NSNotification *)notification {
- _active = NO;
- [self playerPause];
- }
- - (void)applicationDidBecomeActive:(NSNotification *)notification {
- _active = YES;
- }
- - (void)didChangeStatusBarFrame {
- if ([UIApplication sharedApplication].statusBarFrame.size.height > YBIBStatusbarHeight()) {
- [self playerPause];
- }
- }
- - (void)audioRouteChangeListenerCallback:(NSNotification*)notification {
- YBIB_DISPATCH_ASYNC_MAIN(^{
- NSDictionary *interuptionDict = notification.userInfo;
- NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
- switch (routeChangeReason) {
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
- [self playerPause];
- break;
- }
- })
- }
- #pragma mark - event
- - (void)respondsToTapGesture:(UITapGestureRecognizer *)tap {
- if (self.isPlaying) {
- self.actionBar.hidden = !self.actionBar.isHidden;
- self.topBar.hidden = !self.topBar.isHidden;
- } else {
- [self.delegate yb_respondsToTapGestureForVideoView:self];
- }
- }
- - (void)clickCancelButton:(UIButton *)button {
- [self.delegate yb_cancelledForVideoView:self];
- }
- - (void)clickPlayButton:(UIButton *)button {
- [self preparPlay];
- }
- #pragma mark - getters & setters
- - (void)setNeedAutoPlay:(BOOL)needAutoPlay {
- if (needAutoPlay && _asset && !self.isPlaying) {
- [self autoPlay];
- } else {
- _needAutoPlay = needAutoPlay;
- }
- }
- @synthesize asset = _asset;
- - (void)setAsset:(AVAsset *)asset {
- _asset = asset;
- if (!asset) return;
- if (self.needAutoPlay) {
- if (![self autoPlay]) {
- self.playButton.hidden = NO;
- }
- self.needAutoPlay = NO;
- } else {
- self.playButton.hidden = NO;
- }
- }
- - (AVAsset *)asset {
- if ([_asset isKindOfClass:AVURLAsset.class]) {
- _asset = [AVURLAsset assetWithURL:((AVURLAsset *)_asset).URL];
- }
- return _asset;
- }
- - (YBIBVideoTopBar *)topBar {
- if (!_topBar) {
- _topBar = [YBIBVideoTopBar new];
- [_topBar.cancelButton addTarget:self action:@selector(clickCancelButton:) forControlEvents:UIControlEventTouchUpInside];
- _topBar.hidden = YES;
- }
- return _topBar;
- }
- - (YBIBVideoActionBar *)actionBar {
- if (!_actionBar) {
- _actionBar = [YBIBVideoActionBar new];
- _actionBar.delegate = self;
- _actionBar.hidden = YES;
- }
- return _actionBar;
- }
- - (UIButton *)playButton {
- if (!_playButton) {
- _playButton = [UIButton buttonWithType:UIButtonTypeCustom];
- _playButton.bounds = CGRectMake(0, 0, 100, 100);
- [_playButton setImage:YBIBIconManager.sharedManager.videoBigPlayImage() forState:UIControlStateNormal];
- [_playButton addTarget:self action:@selector(clickPlayButton:) forControlEvents:UIControlEventTouchUpInside];
- _playButton.hidden = YES;
- _playButton.layer.shadowColor = UIColor.darkGrayColor.CGColor;
- _playButton.layer.shadowOffset = CGSizeMake(0, 1);
- _playButton.layer.shadowOpacity = 1;
- _playButton.layer.shadowRadius = 4;
- }
- return _playButton;
- }
- - (UIImageView *)thumbImageView {
- if (!_thumbImageView) {
- _thumbImageView = [UIImageView new];
- _thumbImageView.contentMode = UIViewContentModeScaleAspectFit;
- _thumbImageView.layer.masksToBounds = YES;
- }
- return _thumbImageView;
- }
- @end
|