kSNewPlayer.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. //
  2. // kSNewPlayer.m
  3. // StudentDaya
  4. //
  5. // Created by 王智 on 2023/5/17.
  6. // Copyright © 2023 DayaMusic. All rights reserved.
  7. //
  8. #import "kSNewPlayer.h"
  9. #import "UrlDecode.h"
  10. @interface kSNewPlayer ()
  11. @property(nonatomic,strong) AVPlayerItem *songItem;
  12. @property(nonatomic,strong) AVPlayer *player;
  13. @property(nonatomic,retain)id timeObserver;//时间观察
  14. @property (nonatomic, assign) BOOL cacheFinish;
  15. @property (nonatomic, assign) BOOL hasFreeObserver;
  16. @end
  17. @implementation kSNewPlayer
  18. + (instancetype)shareInstance {
  19. kSNewPlayer * manager = [[kSNewPlayer alloc] init];
  20. return manager;
  21. }
  22. - (instancetype)init {
  23. self = [super init];
  24. if (self) {
  25. }
  26. return self;
  27. }
  28. - (void)resetPlayer {
  29. self.player = nil;
  30. }
  31. - (void)preparePlaySongWithUrl:(NSString *)urlStr {
  32. self.cacheFinish = NO;
  33. urlStr = [urlStr getUrlEndcodeString];
  34. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:urlStr] options:nil];
  35. if (_songItem) {
  36. [self removeAllNoticeAndObserver];
  37. [self resetPlayer];
  38. }
  39. _songItem = [[AVPlayerItem alloc] initWithAsset:asset];
  40. _player = [AVPlayer playerWithPlayerItem:_songItem];
  41. //添加监听
  42. [self addNotificationAndObserver];
  43. self.volume = 1.0;
  44. self.isMute = NO;
  45. // AVAudioPlayer *player = [[AVAudioPlayer alloc] init];
  46. }
  47. // 播放本地文件
  48. - (void)preparePlayNativeSongWithUrl:(NSString *)urlStr {
  49. self.cacheFinish = NO;
  50. NSURL *url = [NSURL fileURLWithPath:urlStr];
  51. AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
  52. if (_songItem) {
  53. [self removeAllNoticeAndObserver];
  54. [self resetPlayer];
  55. }
  56. _songItem = [AVPlayerItem playerItemWithAsset:asset];
  57. _player = [AVPlayer playerWithPlayerItem:_songItem];
  58. //添加监听
  59. [self addNotificationAndObserver];
  60. self.volume = 1.0;
  61. self.isMute = NO;
  62. }
  63. - (void)preparePlayNativeSongWithPath:(NSURL *)urlStr {
  64. self.cacheFinish = NO;
  65. AVURLAsset *asset = [AVURLAsset URLAssetWithURL:urlStr options:nil];
  66. if (_songItem) {
  67. [self removeAllNoticeAndObserver];
  68. [self resetPlayer];
  69. }
  70. _songItem = [AVPlayerItem playerItemWithAsset:asset];
  71. _player = [AVPlayer playerWithPlayerItem:_songItem];
  72. //添加监听
  73. [self addNotificationAndObserver];
  74. self.volume = 1.0;
  75. self.isMute = NO;
  76. }
  77. - (void)addNotificationAndObserver {
  78. if (self.songItem) {
  79. self.hasFreeObserver = NO;
  80. //添加播放器状态的监听
  81. [self addAVPlayerStatusObserver];
  82. //添加数据缓存的监听
  83. [self addNetDataStatusObserver];
  84. //添加时间的监听
  85. [self addTimeObserve];
  86. //添加播放完成的通知
  87. [self addPlayToEndObserver];
  88. }
  89. }
  90. - (void)startPlay {
  91. if (_isPlaying) {
  92. [_player pause];
  93. }
  94. _isPlaying = YES;
  95. MJWeakSelf;
  96. CMTime toleranceTime = CMTimeMake(1, 1000);
  97. [_player seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
  98. [weakSelf.player play];
  99. }];
  100. }
  101. - (void)resumePlay {
  102. if (_isPlaying) {
  103. [_player pause];
  104. }
  105. _isPlaying = YES;
  106. [self.player play];
  107. }
  108. - (void)puasePlay {
  109. if (_isPlaying) {
  110. _isPlaying = NO;
  111. [_player pause];
  112. }
  113. }
  114. - (void)freePlayer {
  115. if (_isPlaying) {
  116. _isPlaying = NO;
  117. [_player pause];
  118. }
  119. [self removeAllNoticeAndObserver];
  120. }
  121. - (void)seekToStart {
  122. CMTime toleranceTime = CMTimeMake(1, 1000);
  123. [_player seekToTime:kCMTimeZero toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
  124. }];
  125. }
  126. // 从某个位置开始播放 ms
  127. - (void)seekToTimePlay:(NSInteger)time {
  128. if (_isPlaying) {
  129. [_player pause];
  130. }
  131. _isPlaying = YES;
  132. MJWeakSelf;
  133. CMTime offsetCTTime = CMTimeMake(labs(time), 1000);
  134. CMTime toleranceTime = CMTimeMake(1, 1000);
  135. [_player seekToTime:offsetCTTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
  136. [weakSelf.player play];
  137. }];
  138. }
  139. - (void)seekOffsetTime:(NSInteger)offsetTime {
  140. CMTime newTime = CMTimeMake(offsetTime, 1000);
  141. CMTime toleranceTime = CMTimeMake(1, 1000);
  142. [_player seekToTime:newTime toleranceBefore:toleranceTime toleranceAfter:toleranceTime completionHandler:^(BOOL finished) {
  143. }];
  144. }
  145. - (CMTime)getCurrentPlayTime {
  146. return [self.player currentTime];
  147. }
  148. - (void)setIsMute:(BOOL)isMute {
  149. _isMute = isMute;
  150. [self.player setMuted:isMute];
  151. }
  152. - (float)getTotalDuration {
  153. float total = CMTimeGetSeconds(self.player.currentItem.asset.duration);
  154. return total;
  155. }
  156. #pragma mark----设置player的volume
  157. - (void)setVolume:(CGFloat)volume {
  158. _volume = volume;
  159. _player.volume=_volume;
  160. }
  161. -(void)addTimeObserve
  162. {
  163. __block typeof(self) bself = self;
  164. _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 100) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  165. //设置player的声音
  166. // [bself setPlayerVolume];
  167. //当前时间
  168. float current = CMTimeGetSeconds(time);
  169. //总共时间
  170. float total = CMTimeGetSeconds(bself.player.currentItem.asset.duration);
  171. //进度
  172. float progress = current/total;
  173. //将值传入知道delegate方法中
  174. // NSLog(@"--current %f -- total %f -----%f", current, total, progress);
  175. if (bself.delegate && [bself.delegate respondsToSelector:@selector(getSongCurrentTime:andTotalTime:andProgress:currentInterval:playTime:inPlayer:)]) {
  176. NSDate *date = [NSDate date];
  177. NSTimeInterval inteveral = [date timeIntervalSince1970];
  178. // NSLog(@"----- start play %f", inteveral * 1000);
  179. [bself.delegate getSongCurrentTime:current*1000 andTotalTime:total*1000 andProgress:progress currentInterval:inteveral playTime:current*1000 inPlayer:bself];
  180. }
  181. }];
  182. }
  183. #pragma mark---移除时间观察者
  184. -(void)removeTimeObserver
  185. {
  186. if (_timeObserver) {
  187. [_player removeTimeObserver:_timeObserver];
  188. _timeObserver = nil;
  189. }
  190. }
  191. #pragma mark---float转00:00类型
  192. - (NSString *)formatTime:(float)num{
  193. int sec =(int)num%60;
  194. int min =(int)num/60;
  195. if (num < 60) {
  196. return [NSString stringWithFormat:@"00:%02d",(int)num];
  197. }
  198. return [NSString stringWithFormat:@"%02d:%02d",min,sec];
  199. }
  200. #pragma mark----监听播放器的加载状态
  201. -(void)addAVPlayerStatusObserver
  202. {
  203. [_songItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  204. }
  205. #pragma mark----数据缓冲状态的监听
  206. -(void)addNetDataStatusObserver
  207. {
  208. [_songItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
  209. }
  210. #pragma mark----KVO方法
  211. -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
  212. {
  213. //播放器缓冲状态
  214. if ([keyPath isEqualToString:@"status"]) {
  215. switch (_player.status) {
  216. case AVPlayerStatusUnknown:{
  217. _isPlaying = NO;
  218. NSLog(@"未知状态,此时不能播放");
  219. }
  220. break;
  221. case AVPlayerStatusReadyToPlay:{
  222. NSLog(@"准备完毕,可以播放");
  223. }
  224. break;
  225. case AVPlayerStatusFailed:{
  226. _isPlaying = NO;
  227. NSLog(@"加载失败,网络或者服务器出现问题");
  228. }
  229. break;
  230. default:
  231. break;
  232. }
  233. }
  234. //数据缓冲状态
  235. if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
  236. // 计算缓冲进度
  237. NSTimeInterval timeInterval = [self availableDuration];
  238. CGFloat totalDuration = CMTimeGetSeconds(self.player.currentItem.asset.duration);
  239. if (timeInterval == totalDuration && self.cacheFinish == NO) {
  240. self.cacheFinish = YES;
  241. dispatch_main_sync_safe(^{
  242. if (self.delegate && [self.delegate respondsToSelector:@selector(preparePlay:)]) {
  243. [self.delegate preparePlay:self];
  244. }
  245. });
  246. }
  247. }
  248. }
  249. #pragma mark - 计算缓冲进度
  250. /**
  251. * 计算缓冲进度
  252. *
  253. * @return 缓冲进度
  254. */
  255. - (NSTimeInterval)availableDuration {
  256. NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];
  257. CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
  258. CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
  259. CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
  260. CGFloat result = startSeconds + durationSeconds;// 计算缓冲总进度
  261. return result;
  262. }
  263. #pragma mark---移除媒体加载状态的监听
  264. -(void)removeAVPlayerObserver
  265. {
  266. [_songItem removeObserver:self forKeyPath:@"status"];
  267. }
  268. #pragma mark---移除数据加载状态的监听
  269. -(void)removeNetDataObserver
  270. {
  271. [_songItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
  272. }
  273. #pragma mark----播放完成后发送通知
  274. -(void)addPlayToEndObserver
  275. {
  276. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_songItem];
  277. }
  278. #pragma mark---通知的方法
  279. -(void)playFinished:(NSNotification *)notice
  280. {
  281. //移除所有监听
  282. _isPlaying = NO;
  283. if ([self.delegate respondsToSelector:@selector(playFinished:)]) {
  284. [self.delegate playFinished:self];
  285. }
  286. }
  287. #pragma mark----移除通知
  288. - (void)removePlayToEndNotice
  289. {
  290. [[NSNotificationCenter defaultCenter] removeObserver:self];
  291. }
  292. #pragma mark-----移除所有监听
  293. -(void)removeAllNoticeAndObserver
  294. {
  295. if (self.hasFreeObserver) {
  296. return;
  297. }
  298. self.hasFreeObserver = YES;
  299. //移除时间进度的监听
  300. [self removeTimeObserver];
  301. //移除播放完成的通知
  302. [self removePlayToEndNotice];
  303. //移除播放器状态的监听
  304. [self removeAVPlayerObserver];
  305. //移除数据缓存的监听
  306. [self removeNetDataObserver];
  307. }
  308. - (void)dealloc {
  309. NSLog(@" -------- player dealloc ");
  310. }
  311. /*
  312. // Only override drawRect: if you perform custom drawing.
  313. // An empty implementation adversely affects performance during animation.
  314. - (void)drawRect:(CGRect)rect {
  315. // Drawing code
  316. }
  317. */
  318. @end