|
@@ -1,456 +0,0 @@
|
|
|
-//
|
|
|
-// kSNewPlayer.m
|
|
|
-// StudentDaya
|
|
|
-//
|
|
|
-// Created by 王智 on 2023/5/17.
|
|
|
-// Copyright © 2023 DayaMusic. All rights reserved.
|
|
|
-//
|
|
|
-
|
|
|
-#import "kSNewPlayer.h"
|
|
|
-#import "AVPlayer+KSSeekSmoothly.h"
|
|
|
-
|
|
|
-@interface kSNewPlayer ()
|
|
|
-
|
|
|
-@property(nonatomic,strong) AVPlayer *player;
|
|
|
-
|
|
|
-@property(nonatomic,strong) AVPlayerItem *songItem;
|
|
|
-
|
|
|
-@property(nonatomic,retain)id timeObserver;//时间观察
|
|
|
-
|
|
|
-@property (nonatomic, assign) BOOL cacheFinish;
|
|
|
-
|
|
|
-@property (nonatomic, assign) BOOL hasFreeObserver;
|
|
|
-
|
|
|
-@end
|
|
|
-
|
|
|
-@implementation kSNewPlayer
|
|
|
-
|
|
|
-+ (instancetype)shareInstance {
|
|
|
- kSNewPlayer * manager = [[kSNewPlayer alloc] init];
|
|
|
- return manager;
|
|
|
-}
|
|
|
-
|
|
|
-- (instancetype)init {
|
|
|
- self = [super init];
|
|
|
- if (self) {
|
|
|
-
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)resetPlayer {
|
|
|
- self.player = nil;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)preparePlaySongWithUrl:(NSString *)urlStr {
|
|
|
- self.cacheFinish = NO;
|
|
|
- urlStr = [urlStr getUrlEndcodeString];
|
|
|
- AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:urlStr] options:nil];
|
|
|
- if (_songItem) {
|
|
|
- [self removeAllNoticeAndObserver];
|
|
|
- [self resetPlayer];
|
|
|
- }
|
|
|
- _songItem = [[AVPlayerItem alloc] initWithAsset:asset];
|
|
|
- _player = [AVPlayer playerWithPlayerItem:_songItem];
|
|
|
- if ([_player respondsToSelector:@selector(automaticallyWaitsToMinimizeStalling)]) {
|
|
|
- _player.automaticallyWaitsToMinimizeStalling = NO;
|
|
|
- }
|
|
|
-
|
|
|
- //添加监听
|
|
|
- [self addNotificationAndObserver];
|
|
|
- self.volume = 1.0;
|
|
|
- self.isMute = NO;
|
|
|
- // AVAudioPlayer *player = [[AVAudioPlayer alloc] init];
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// 播放本地文件
|
|
|
-- (void)preparePlayNativeSongWithUrl:(NSString *)urlStr {
|
|
|
- self.cacheFinish = NO;
|
|
|
- NSURL *url = [NSURL fileURLWithPath:urlStr];
|
|
|
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
|
|
|
- if (_songItem) {
|
|
|
- [self removeAllNoticeAndObserver];
|
|
|
- [self resetPlayer];
|
|
|
- }
|
|
|
- _songItem = [AVPlayerItem playerItemWithAsset:asset];
|
|
|
- _player = [AVPlayer playerWithPlayerItem:_songItem];
|
|
|
- if ([_player respondsToSelector:@selector(automaticallyWaitsToMinimizeStalling)]) {
|
|
|
- _player.automaticallyWaitsToMinimizeStalling = NO;
|
|
|
- }
|
|
|
- //添加监听
|
|
|
- [self addNotificationAndObserver];
|
|
|
- self.volume = 1.0;
|
|
|
- self.isMute = NO;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)preparePlayNativeSongWithPath:(NSURL *)urlStr {
|
|
|
- self.cacheFinish = NO;
|
|
|
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:urlStr options:nil];
|
|
|
- if (_songItem) {
|
|
|
- [self removeAllNoticeAndObserver];
|
|
|
- [self resetPlayer];
|
|
|
- }
|
|
|
- _songItem = [AVPlayerItem playerItemWithAsset:asset];
|
|
|
- _player = [AVPlayer playerWithPlayerItem:_songItem];
|
|
|
- if ([_player respondsToSelector:@selector(automaticallyWaitsToMinimizeStalling)]) {
|
|
|
- _player.automaticallyWaitsToMinimizeStalling = NO;
|
|
|
- }
|
|
|
- //添加监听
|
|
|
- [self addNotificationAndObserver];
|
|
|
- self.volume = 1.0;
|
|
|
- self.isMute = NO;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)addNotificationAndObserver {
|
|
|
- if (self.songItem) {
|
|
|
- self.hasFreeObserver = NO;
|
|
|
- //添加播放器状态的监听
|
|
|
- [self addAVPlayerStatusObserver];
|
|
|
- //添加数据缓存的监听
|
|
|
- [self addNetDataStatusObserver];
|
|
|
-
|
|
|
- //添加时间的监听
|
|
|
- [self addTimeObserve];
|
|
|
- //添加播放完成的通知
|
|
|
- [self addPlayToEndObserver];
|
|
|
- // 添加异常通知
|
|
|
- [self addExceptionObserver];
|
|
|
- }
|
|
|
-}
|
|
|
-- (void)configPlayerRate:(CGFloat)rate {
|
|
|
- if (_isPlaying) {
|
|
|
- if (rate >= 0) {
|
|
|
- self.player.rate = rate;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)startPlay {
|
|
|
- if (_isPlaying) {
|
|
|
- [_player pause];
|
|
|
- }
|
|
|
- _isPlaying = YES;
|
|
|
- @weakObj(self);
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
- [self.player ss_seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
- @strongObj(self);
|
|
|
- [self.player play];
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)startPlayNoSeek {
|
|
|
- if (_isPlaying) {
|
|
|
- [_player pause];
|
|
|
- }
|
|
|
- _isPlaying = YES;
|
|
|
- [self.player play];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)resumePlay {
|
|
|
- if (_isPlaying) {
|
|
|
- [_player pause];
|
|
|
- }
|
|
|
- _isPlaying = YES;
|
|
|
- [self.player play];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)puasePlay {
|
|
|
- if (_isPlaying) {
|
|
|
- _isPlaying = NO;
|
|
|
- [_player pause];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)freePlayer {
|
|
|
- if (_isPlaying) {
|
|
|
- _isPlaying = NO;
|
|
|
- [_player pause];
|
|
|
- }
|
|
|
- [self removeAllNoticeAndObserver];
|
|
|
- [self resetPlayer];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)seekToStart {
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
- [self.player ss_seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
-
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-// 从某个位置开始播放 ms
|
|
|
-- (void)seekToTimePlay:(NSInteger)time {
|
|
|
- _isPlaying = YES;
|
|
|
- CMTime offsetCTTime = CMTimeMake(labs(time), 1000);
|
|
|
- @weakObj(self);
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
- [self.player ss_seekToTime:offsetCTTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
- @strongObj(self);
|
|
|
- [self.player play];
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-// 调整进度到某个位置
|
|
|
-- (void)seekToTime:(NSInteger)time callback:(void(^)(void))callback {
|
|
|
- [self.player pause];
|
|
|
- CMTime offsetCTTime = CMTimeMake(labs(time), 1000);
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
- [self.player ss_seekToTime:offsetCTTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
- callback();
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)seekOffsetTime:(NSInteger)offsetTime {
|
|
|
- CMTime newTime = CMTimeMake(offsetTime, 1000);
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
-
|
|
|
- [self.player pause];
|
|
|
- @weakObj(self);
|
|
|
- [self.player ss_seekToTime:newTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
- @strongObj(self);
|
|
|
- if (self.isPlaying) {
|
|
|
- [self.player play];
|
|
|
- }
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-// 调整进度
|
|
|
-- (void)seekOffsetTimeNoPuase:(NSInteger)offsetTime {
|
|
|
- CMTime newTime = CMTimeMake(offsetTime, 1000);
|
|
|
- CMTime toleranceTime = CMTimeMake(1, 1000);
|
|
|
-
|
|
|
- [self.player ss_seekToTime:newTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
|
|
|
-
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-- (CMTime)getCurrentPlayTime {
|
|
|
- return [self.player currentTime];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)setIsMute:(BOOL)isMute {
|
|
|
- _isMute = isMute;
|
|
|
- [self.player setMuted:isMute];
|
|
|
-}
|
|
|
-
|
|
|
-- (float)getTotalDuration {
|
|
|
- float total = CMTimeGetSeconds(self.player.currentItem.asset.duration);
|
|
|
- return total;
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark----设置player的volume
|
|
|
-- (void)setVolume:(CGFloat)volume {
|
|
|
- _volume = volume;
|
|
|
- _player.volume=_volume;
|
|
|
-}
|
|
|
-
|
|
|
--(void)addTimeObserve
|
|
|
-{
|
|
|
-// __block typeof(self) bself = self;
|
|
|
- @weakObj(self);
|
|
|
- _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 100) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
|
|
|
- @strongObj(self);
|
|
|
- //设置player的声音
|
|
|
- // [self setPlayerVolume];
|
|
|
- //当前时间
|
|
|
- float current = CMTimeGetSeconds(time);
|
|
|
- //总共时间
|
|
|
- float total = CMTimeGetSeconds(self.player.currentItem.asset.duration);
|
|
|
- //进度
|
|
|
- float progress = current/total;
|
|
|
- //将值传入知道delegate方法中
|
|
|
- // NSLog(@"--current %f -- total %f -----%f", current, total, progress);
|
|
|
- if (self.delegate && [self.delegate respondsToSelector:@selector(getSongCurrentTime:andTotalTime:andProgress:currentInterval:playTime:inPlayer:)]) {
|
|
|
- NSDate *date = [NSDate date];
|
|
|
- NSTimeInterval inteveral = [date timeIntervalSince1970];
|
|
|
- // NSLog(@"----- start play %f", inteveral * 1000);
|
|
|
- [self.delegate getSongCurrentTime:current*1000 andTotalTime:total*1000 andProgress:progress currentInterval:inteveral playTime:current*1000 inPlayer:self];
|
|
|
- }
|
|
|
- }];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark---移除时间观察者
|
|
|
--(void)removeTimeObserver
|
|
|
-{
|
|
|
- if (_timeObserver) {
|
|
|
- [_player 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
|
|
|
-{
|
|
|
- [_songItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
|
|
|
-}
|
|
|
-#pragma mark----数据缓冲状态的监听
|
|
|
--(void)addNetDataStatusObserver
|
|
|
-{
|
|
|
- [_songItem 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 (_player.status) {
|
|
|
- case AVPlayerStatusUnknown:{
|
|
|
- _isPlaying = NO;
|
|
|
- NSLog(@"未知状态,此时不能播放");
|
|
|
- }
|
|
|
- break;
|
|
|
- case AVPlayerStatusReadyToPlay: {
|
|
|
- _isReady = YES;
|
|
|
- NSLog(@"准备完毕,可以播放");
|
|
|
- dispatch_main_sync_safe(^{
|
|
|
- if (self.delegate && [self.delegate respondsToSelector:@selector(playerIsReadyPlay:)]) {
|
|
|
- [self.delegate playerIsReadyPlay:self];
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- break;
|
|
|
- case AVPlayerStatusFailed:{
|
|
|
- _isPlaying = NO;
|
|
|
- NSLog(@"加载失败,网络或者服务器出现问题");
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- //数据缓冲状态
|
|
|
- if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
|
|
|
- // 计算缓冲进度
|
|
|
- NSTimeInterval timeInterval = [self availableDuration];
|
|
|
- CGFloat totalDuration = CMTimeGetSeconds(self.player.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];
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - 计算缓冲进度
|
|
|
-
|
|
|
-/**
|
|
|
- * 计算缓冲进度
|
|
|
- *
|
|
|
- * @return 缓冲进度
|
|
|
- */
|
|
|
-- (NSTimeInterval)availableDuration {
|
|
|
- NSArray *loadedTimeRanges = [[_player 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
|
|
|
-{
|
|
|
- [_songItem removeObserver:self forKeyPath:@"status"];
|
|
|
-}
|
|
|
-#pragma mark---移除数据加载状态的监听
|
|
|
--(void)removeNetDataObserver
|
|
|
-{
|
|
|
- [_songItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
|
|
|
-}
|
|
|
-#pragma mark----播放完成后发送通知
|
|
|
--(void)addPlayToEndObserver
|
|
|
-{
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_songItem];
|
|
|
-}
|
|
|
-#pragma mark ---- 异常通知
|
|
|
-- (void)addExceptionObserver {
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerError:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:_songItem];
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerError:) name:AVPlayerItemPlaybackStalledNotification object:_songItem];
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerError:) name:AVPlayerItemPlaybackStalledNotification object:_songItem];
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerError:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerError:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
|
|
|
-}
|
|
|
-
|
|
|
--(void)playerError:(NSNotification *)notice {
|
|
|
- //移除所有监听
|
|
|
- dispatch_main_sync_safe(^{
|
|
|
- if ([notice.name isEqualToString:AVAudioSessionRouteChangeNotification]) {
|
|
|
- NSDictionary *info = notice.userInfo;
|
|
|
- AVAudioSessionRouteChangeReason routeChangeReason = [[info valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
|
|
- if (routeChangeReason == AVAudioSessionRouteChangeReasonCategoryChange) {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- if ([self.delegate respondsToSelector:@selector(playerDidError:)]) {
|
|
|
- [self.delegate playerDidError:self];
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark---通知的方法
|
|
|
--(void)playFinished:(NSNotification *)notice
|
|
|
-{
|
|
|
- //移除所有监听
|
|
|
- _isPlaying = NO;
|
|
|
- dispatch_main_sync_safe(^{
|
|
|
- if ([notice.name isEqualToString:AVAudioSessionRouteChangeNotification]) {
|
|
|
- NSDictionary *info = notice.userInfo;
|
|
|
- AVAudioSessionRouteChangeReason routeChangeReason = [[info valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
|
|
- if (routeChangeReason == AVAudioSessionRouteChangeReasonCategoryChange) {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- if ([self.delegate respondsToSelector:@selector(playFinished:)]) {
|
|
|
- [self.delegate playFinished:self];
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-#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(@" -------- 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
|