123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- //
- // KSVideoPlayerView.m
- // KulexiuSchoolStudent
- //
- // Created by 王智 on 2023/9/5.
- //
- #import "KSVideoPlayerView.h"
- #import <AVFoundation/AVFoundation.h>
- #import <CloudAccompanyLibrary/AVPlayer+KSSeekSmoothly.h>
- @interface KSVideoPlayerView ()
- @property (nonatomic, strong) AVPlayer *videoPlayer;
- @property(nonatomic,strong) AVPlayerItem *currentItem;
- @property (nonatomic,retain) AVPlayerLayer *playerLayer;
- @property(nonatomic,retain) id timeObserver;//时间观察
- @property (nonatomic, assign) BOOL cacheFinish;
- @property (nonatomic, assign) BOOL hasFreeObserver;
- @property (nonatomic, assign) BOOL needResume; // 是否需要恢复播放
- @end
- @implementation KSVideoPlayerView
- -(void)layoutSubviews{
- [super layoutSubviews];
- self.playerLayer.frame = self.bounds;
- }
- - (instancetype)init {
- self = [super init];
- if (self) {
- self.backgroundColor = [UIColor blackColor];
- }
- return self;
- }
- - (void)initPlayer {
- [UIApplication sharedApplication].idleTimerDisabled=YES;
- }
- - (void)resetPlayer {
- if (self.playerLayer) {
- [self.playerLayer removeFromSuperlayer];
- self.playerLayer = nil;
- }
- [self.videoPlayer replaceCurrentItemWithPlayerItem:nil];
- self.videoPlayer = nil;
- }
- - (void)preparePlayNativeVideoWithPath:(NSURL *)urlStr {
- self.cacheFinish = NO;
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:urlStr options:nil];
- if (_currentItem) {
- [self removeAllNoticeAndObserver];
- [self resetPlayer];
- if (self.playerLayer) {
- [self.playerLayer removeFromSuperlayer];
- }
- }
- _currentItem = [AVPlayerItem playerItemWithAsset:asset];
- _videoPlayer = [AVPlayer playerWithPlayerItem:_currentItem];
- if ([self.videoPlayer respondsToSelector:@selector(automaticallyWaitsToMinimizeStalling)]) {
- self.videoPlayer.automaticallyWaitsToMinimizeStalling = YES;
- }
- self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer];
- self.playerLayer.frame = self.frame;
- [self.layer insertSublayer:self.playerLayer atIndex:0];
- //添加监听
- [self addNotificationAndObserver];
- self.volume = 1.0;
- self.isMute = NO;
- }
- - (void)addNotificationAndObserver {
- if (self.currentItem) {
- self.hasFreeObserver = NO;
- //添加播放器状态的监听
- [self addAVPlayerStatusObserver];
- //添加数据缓存的监听
- [self addNetDataStatusObserver];
-
- //添加时间的监听
- [self addTimeObserve];
- //添加播放完成的通知
- [self addPlayToEndObserver];
- }
- }
- - (void)startPlay {
- if (_isPlaying) {
- [_videoPlayer pause];
- }
- _isPlaying = YES;
- @weakObj(self);
- CMTime toleranceTime = kCMTimeZero;
- [_videoPlayer ss_seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
- @strongObj(self);
- [self.videoPlayer play];
- }];
- }
- - (void)resumePlay {
- if (_isPlaying) {
- [_videoPlayer pause];
- }
- _isPlaying = YES;
- [self.videoPlayer play];
- }
- - (void)puasePlay {
- self.needResume = NO;
- if (_isPlaying) {
- _isPlaying = NO;
- [_videoPlayer pause];
- }
- }
- - (void)freePlayer {
- if (_isPlaying) {
- _isPlaying = NO;
- [_videoPlayer pause];
- }
- [self removeAllNoticeAndObserver];
- [self resetPlayer];
- }
- - (void)seekToStart {
- CMTime toleranceTime = CMTimeMake(1, 1000);
- [_videoPlayer ss_seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
-
- }];
- }
- // 从某个位置开始播放 ms
- - (void)seekToTimePlay:(NSInteger)time {
- if (_isPlaying) {
- [_videoPlayer pause];
- }
- _isPlaying = YES;
-
- CMTime offsetCTTime = CMTimeMake(labs(time), 1000);
- CMTime toleranceTime = CMTimeMake(1, 1000);
- @weakObj(self);
- [_videoPlayer ss_seekToTime:offsetCTTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
- @strongObj(self);
- [self.videoPlayer play];
- }];
- }
- - (void)seekOffsetTime:(NSInteger)offsetTime {
- CMTime newTime = CMTimeMake(offsetTime, 1000);
- CMTime toleranceTime = CMTimeMake(1, 1000);
- if (self.isPlaying) {
- self.needResume = YES;
- }
- [self.videoPlayer pause];
- @weakObj(self);
- [self.videoPlayer ss_seekToTime:newTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
- @strongObj(self);
- if (self.needResume) {
- [self.videoPlayer play];
- }
- self.needResume = NO;
- }];
- }
- - (CMTime)getCurrentPlayTime {
- return [self.videoPlayer currentTime];
- }
- - (void)setIsMute:(BOOL)isMute {
- _isMute = isMute;
- [self.videoPlayer setMuted:isMute];
- }
- #pragma mark----设置player的volume
- - (void)setVolume:(CGFloat)volume {
- _volume = volume;
- _videoPlayer.volume=_volume;
- }
- -(void)addTimeObserve
- {
- __block typeof(self) bself = self;
- _timeObserver = [_videoPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 100) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
- //设置player的声音
- // [bself setPlayerVolume];
- //当前时间
- float current = CMTimeGetSeconds(time);
- //总共时间
- float total = CMTimeGetSeconds(bself.videoPlayer.currentItem.asset.duration);
- //进度
- float progress = current/total;
- //将值传入知道delegate方法中
- // NSLog(@"--current %f -- total %f -----%f", current, total, progress);
- if (bself.delegate && [bself.delegate respondsToSelector:@selector(getSongCurrentTime:andTotalTime:andProgress:currentInterval:playTime:inPlayer:)]) {
- NSDate *date = [NSDate date];
- NSTimeInterval inteveral = [date timeIntervalSince1970];
- // NSLog(@"----- start play %f", inteveral * 1000);
- [bself.delegate getSongCurrentTime:current*1000 andTotalTime:total*1000 andProgress:progress currentInterval:inteveral playTime:current*1000 inPlayer:bself.videoPlayer];
- }
- }];
- }
- #pragma mark---移除时间观察者
- -(void)removeTimeObserver
- {
- if (_timeObserver) {
- [_videoPlayer removeTimeObserver:_timeObserver];
- _timeObserver = nil;
- }
- }
- #pragma mark---float转00:00类型
- - (NSString *)formatTime:(float)num{
-
- int sec =(int)num%60;
- int min =(int)num/60;
- if (num < 60) {
- return [NSString stringWithFormat:@"00:%02d",(int)num];
- }
- return [NSString stringWithFormat:@"%02d:%02d",min,sec];
- }
- #pragma mark----监听播放器的加载状态
- -(void)addAVPlayerStatusObserver
- {
- [_currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
- }
- #pragma mark----数据缓冲状态的监听
- -(void)addNetDataStatusObserver
- {
- [_currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
- }
- #pragma mark----KVO方法
- -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
- {
- //播放器缓冲状态
- if ([keyPath isEqualToString:@"status"]) {
- switch (_videoPlayer.status) {
- case AVPlayerStatusUnknown:{
- _isPlaying = NO;
- NSLog(@"未知状态,此时不能播放");
- }
- break;
- case AVPlayerStatusReadyToPlay:{
- NSLog(@"准备完毕,可以播放");
- dispatch_main_sync_safe(^{
- self.isReady = YES;
- if (self.delegate && [self.delegate respondsToSelector:@selector(videoPlayerIsReadyPlay:)]) {
- [self.delegate videoPlayerIsReadyPlay:self.videoPlayer];
- }
- });
- }
- break;
- case AVPlayerStatusFailed:{
- _isPlaying = NO;
- NSLog(@"加载失败,网络或者服务器出现问题");
- if (self.delegate && [self.delegate respondsToSelector:@selector(videoPlayerOccurError:)]) {
- [self.delegate videoPlayerOccurError:self.videoPlayer];
- }
- }
- break;
- default:
- break;
- }
- }
- //数据缓冲状态
- if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
- // 计算缓冲进度
- NSTimeInterval timeInterval = [self availableDuration];
- CGFloat totalDuration = CMTimeGetSeconds(self.videoPlayer.currentItem.asset.duration);
- if (timeInterval == totalDuration && self.cacheFinish == NO) {
- self.cacheFinish = YES;
- dispatch_main_sync_safe(^{
- if (self.delegate && [self.delegate respondsToSelector:@selector(preparePlay:)]) {
- [self.delegate preparePlay:self.videoPlayer];
- }
- });
- }
- }
- }
- #pragma mark - 计算缓冲进度
- /**
- * 计算缓冲进度
- *
- * @return 缓冲进度
- */
- - (NSTimeInterval)availableDuration {
- NSArray *loadedTimeRanges = [[_videoPlayer currentItem] loadedTimeRanges];
- CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
- CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
- CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
- CGFloat result = startSeconds + durationSeconds;// 计算缓冲总进度
- return result;
- }
- #pragma mark---移除媒体加载状态的监听
- -(void)removeAVPlayerObserver
- {
- [_currentItem removeObserver:self forKeyPath:@"status"];
- }
- #pragma mark---移除数据加载状态的监听
- -(void)removeNetDataObserver
- {
- [_currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
- }
- #pragma mark----播放完成后发送通知
- -(void)addPlayToEndObserver
- {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_currentItem];
- }
- #pragma mark---通知的方法
- -(void)playFinished:(NSNotification *)notice
- {
- //移除所有监听
- _isPlaying = NO;
- if ([self.delegate respondsToSelector:@selector(videoPlayerFinishedPlay:)]) {
- [self.delegate videoPlayerFinishedPlay:self.videoPlayer];
- }
- }
- #pragma mark----移除通知
- - (void)removePlayToEndNotice
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- #pragma mark-----移除所有监听
- -(void)removeAllNoticeAndObserver
- {
- if (self.hasFreeObserver) {
- return;
- }
- self.hasFreeObserver = YES;
- //移除时间进度的监听
- [self removeTimeObserver];
- //移除播放完成的通知
- [self removePlayToEndNotice];
- //移除播放器状态的监听
- [self removeAVPlayerObserver];
- //移除数据缓存的监听
- [self removeNetDataObserver];
- }
- - (void)dealloc {
- NSLog(@" -------- video player dealloc ");
- }
- /*
- // Only override drawRect: if you perform custom drawing.
- // An empty implementation adversely affects performance during animation.
- - (void)drawRect:(CGRect)rect {
- // Drawing code
- }
- */
- @end
|