zoukankan      html  css  js  c++  java
  • OC AVPlayerItem,AVPlayer,AVPlayerLayer的使用

    音量调节以及跳转到指定的秒数,停止和暂停,只是学习,没有封装

    #import <Masonry.h>
    #import <AVFoundation/AVFoundation.h>
    @interface RecordVideoplayVC ()
    @property(nonatomic,strong)AVPlayerItem *avPlayerItem;
    @property(nonatomic,strong)AVPlayer *avPlayer;
    @property (nonatomic ,strong) id playbackTimeObserver;
    @end
    
    @implementation RecordVideoplayVC
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    //    AVPlayerItem:提供视频信息,一个AVPlayerItem对应着你提供的一个视频Url资源,这个理解它的时候可以把它比作一个Model, 你初始化了AVPlayerItem之后,并不是马上就可以使用它了,因为凡是和Url网络扯上关系的,都需要时间,等AVPlayerItem加载好之后就可以使用它了,那这一步我们怎么处理呢?
    //    答案是利用KVO观察statues属性为 AVPlayerStatusReadyToPlay,看看这个属性的定义:
        
    //    @property (nonatomic, readonly) AVPlayerStatus status  它是一个只读属性,这点也需要注意,其实也就理解利用KVO的原因。
    //
    //    2>: 顺便总结要是你要显示当前视屏的缓存进度,你需要监测它的loadedTimeRanges属性。
    //
    //    2、AVPlayerLayer
    //
    //    它主要负责的就是视频的显示,继承自CALayer,其实你可以把它理解成我们的View。我们自定义的那些播放时候的控件就是添加在它上面的,比如我们能看到的播放按钮,停止按钮,或者播放进度条等等。
      
    //    3、 AVPlayer
    //
    //    它主要负责的是管理视频播放,暂停等等,相当于一个视频管理器,要是类比的话他就是一个ViewController(当然不是真正的ViewController),这三者就基本含括了一个基本的视频播,基于着三者我们总结一下播放一个视频的基本的过程:
        
    //    首先,得到视频的URL
    //    根据URL创建AVPlayerItem
    //    把AVPlayerItem 提供给 AVPlayer
    //    AVPlayerLayer 显示视频。
    //    AVPlayer 控制视频, 播放, 暂停, 跳转 等等。
    //    播放过程中获取缓冲进度,获取播放进度。
    //    视频播放完成后做些什么,是暂停还是循环播放,还是获取最后一帧图像。
    
    //    4、AVPlayerViewController
    //
    //    它是Apple 帮我们封装好的可以一个视频播放控制器,它就有一个  @property (nonatomic, strong, nullable) AVPlayer *player 的属性,前面的AVPlayer也就像相应的需要赋值给它,它里面还有一些我们需要理解一下的属性,我们也把它写出来,具体代码我们下面再看:
    //
    //    player:                                    设置播放器
    //    showsPlaybackControls:           设置是否显示媒体播放组件,默认YES
    //    videoGravity:                           设置视频拉伸模式
    //    allowsPictureInPicturePlayback: 设置是否允许画中画回放,默认YES
    //    delegate:                                设置代理
    //    5、AVPlayerViewControllerDelegate
    //
    //    这个代理就是前面说的AVPlayerViewController的协议,它主要的是为画中画的设置的代理,前面介绍 AVPlayerViewController 的时候有看到过一个是否允许画中画的属性,具体什么是画中画相信大家都了解,看过直接的朋友应该都看到了这个技术点的具体应用。我们看看它里面的饭法规主要都干了些什么?
    //    // 1、即将开始画中画
    //    - (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController;
    //    // 2、开始画中画
    //    - (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController;
    //    // 3、画中画失败
    //    - (void)playerViewController:(AVPlayerViewController *)playerViewController failedToStartPictureInPictureWithError:(NSError *)error;
    //    // 4、即将结束画中画
    //    - (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController;
    //    // 5、结束画中画
    //    - (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController;
        
        
        
        // Do any additional setup after loading the view.
        
        
        self.avPlayerItem   = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:MovieURL]];
        self.avPlayer       = [[AVPlayer alloc]initWithPlayerItem:self.avPlayerItem];
        AVPlayerLayer *avPlayerLayer  = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
        avPlayerLayer.frame = CGRectMake(10, 100, 355, 200);
        [self.view.layer addSublayer:avPlayerLayer];
    
        //状态添加观察者
        [self.avPlayerItem  addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew) context:nil];
        // 缓存进度添加观察者
        [self.avPlayerItem  addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
        // 添加视频播放结束通知
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayerItem];
        
        
        
        self.avPlayer.volume = 0.5;//调节音量 0-1
        
    }
    
    - (void)moviePlayDidEnd:(NSNotification *)notification {
        NSLog(@"Play end");
        
        __weak typeof(self) weakSelf = self;
        [self.avPlayer seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
            //把进度跳转到0,
        }];
    }
    
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        
        AVPlayerItem * avplayeritem = (AVPlayerItem *)object;
        if ([keyPath isEqualToString:@"status"]) {
            
            AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
            if (status == AVPlayerStatusReadyToPlay) {
                NSLog(@"准备好播放");
                CMTime duration = avplayeritem.duration;
                NSLog(@"视频总时长:%.2f",CMTimeGetSeconds(duration));//总时长
                // 播放
                [self.avPlayer play];
                [self monitoringPlayback];// 监听播放状态
               
            }else if (status == AVPlayerStatusFailed){
                NSLog(@"视频准备发生错误");
            }else{
                NSLog(@"位置错误");
            }
        }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){
            
            // 可以自定义缓存进度
            NSTimeInterval timeInterval = [self alreadyCacheVideoProgress];
            NSLog(@"视频已经缓存的时长:%.2f",timeInterval);
        }
    }
    
    #pragma mark --
    #pragma mark -- alreadyCacheVideoProgress
    -(NSTimeInterval)alreadyCacheVideoProgress{
        // 先获取到它的缓存的进度
        NSArray * cacheVideoTime = [self.avPlayerItem loadedTimeRanges];
        // CMTimeRange 结构体 start duration 表示起始位置 和 持续时间
        // 获取缓冲区域
        CMTimeRange timeRange = [cacheVideoTime.firstObject CMTimeRangeValue];//NSValue
        float startSeconds    = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
       
        // 计算总缓冲时间 = start + duration
        NSTimeInterval result = startSeconds + durationSeconds;
        return result;
    }
    
    
    
    -(void)dealloc{
        [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
        [self.avPlayerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayerItem];
        [self.avPlayer removeTimeObserver:self.playbackTimeObserver];
    }
    
    //监听播放状态
    - (void)monitoringPlayback {
        /*
        CMTime CMTimeMake(
        int64_t value, //秒
        int32_t timescale)//次数
        */
     self.playbackTimeObserver = [self.avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 5) queue:NULL usingBlock:^(CMTime time) {
            CGFloat currentSecond = self.avPlayerItem.currentTime.value/self.avPlayerItem.currentTime.timescale;// 计算当前在第几秒
            //如果需要进度条的话,这里可以更新进度条
        }];
    }
    //跳转到指定的秒数
    -(void)jumptoValue:(float)seconds{
         CMTime changedTime = CMTimeMakeWithSeconds(seconds, 1);
        [self.avPlayer seekToTime:changedTime completionHandler:^(BOOL finished) {
            [self.avPlayer play];
        }];
    
    }
    
    
    //
    
    @end
    
  • 相关阅读:
    破解版sublimeText3重新安装Package Control方法
    javascript中对象的属性的特性
    正则表达式元字符详解
    正则表达式学习笔记
    ExtJs布局大全
    js如何获取select下拉框的value以及文本内容
    ExtJS4之Ext.MessageBox的各种用法
    CODING 代码多仓库实践
    2019 DevOps 必备面试题——容器化和虚拟化
    拥抱微服务,CODING 即将上线单项目多仓库功能
  • 原文地址:https://www.cnblogs.com/hualuoshuijia/p/11534036.html
Copyright © 2011-2022 走看看