DFPlayer.m 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. //
  2. // DFPlayer.m
  3. // DFPlayer
  4. //
  5. // Created by ihoudf on 2017/7/18.
  6. // Copyright © 2017年 ihoudf. All rights reserved.
  7. //
  8. #import "DFPlayer.h"
  9. #import "DFPlayerFileManager.h"
  10. #import "DFPlayerResourceLoader.h"
  11. #import "DFPlayerTool.h"
  12. #import <MediaPlayer/MediaPlayer.h>
  13. //hxd add 20240716
  14. #import "audioPlayListManager.h"
  15. #import "audioPlayingView.h"
  16. /**Asset KEY*/
  17. NSString * const DFPlayableKey = @"playable";
  18. /**PlayerItem KEY*/
  19. NSString * const DFStatusKey = @"status";
  20. NSString * const DFLoadedTimeRangesKey = @"loadedTimeRanges";
  21. NSString * const DFPlaybackBufferEmptyKey = @"playbackBufferEmpty";
  22. NSString * const DFPlaybackLikelyToKeepUpKey = @"playbackLikelyToKeepUp";
  23. @interface DFPlayer()<DFPlayerResourceLoaderDelegate>
  24. {
  25. BOOL _isOtherPlaying; // 其他应用是否正在播放
  26. BOOL _isBackground; // 是否进入后台
  27. BOOL _isCached; // 当前音频是否缓存
  28. BOOL _isSeek; // 正在seek
  29. BOOL _isSeekWaiting; // seek 等待
  30. BOOL _isNaturalToEndTime; // 是否是自然结束
  31. dispatch_group_t _netGroupQueue; // 组队列-网络
  32. dispatch_group_t _dataGroupQueue; // 组队列-数据
  33. NSInteger _currentAudioId; // 当前正在播放的音频Id
  34. NSInteger _randomIndex; // 随机数组元素index
  35. NSInteger _playIndex1; // 播放顺序标识
  36. NSInteger _playIndex2; // 播放顺序标识
  37. CGFloat _seekValue; // seek value
  38. NSMutableDictionary *_remoteInfoDictionary; // 控制中心信息
  39. }
  40. /** player */
  41. @property (nonatomic, strong) AVPlayer *player;
  42. /** playerItem */
  43. @property (nonatomic, strong) AVPlayerItem *playerItem;
  44. /** 播放进度监测 */
  45. @property (nonatomic, strong) id timeObserver;
  46. /** 随机数组 */
  47. @property (nonatomic, strong) NSMutableArray *randomIndexArray;
  48. /** 资源下载器 */
  49. @property (nonatomic, strong) DFPlayerResourceLoader *resourceLoader;
  50. @property (nonatomic, copy) void(^seekCompletionBlock)(void);
  51. @property (nonatomic, readwrite, strong) DFPlayerModel *currentAudioModel;
  52. @property (nonatomic, readwrite, strong) DFPlayerInfoModel *currentAudioInfoModel;
  53. @property (nonatomic, readwrite, assign) DFPlayerState state;
  54. @property (nonatomic, readwrite, assign) CGFloat bufferProgress;
  55. @property (nonatomic, readwrite, assign) CGFloat progress;
  56. @property (nonatomic, readwrite, assign) CGFloat currentTime;
  57. @property (nonatomic, readwrite, assign) CGFloat totalTime;
  58. @end
  59. @implementation DFPlayer
  60. #pragma mark - 初始化
  61. + (DFPlayer *)sharedPlayer {
  62. static DFPlayer *player = nil;
  63. static dispatch_once_t predicate;
  64. dispatch_once(&predicate, ^{
  65. player = [[[self class] alloc] init];
  66. });
  67. return player;
  68. }
  69. - (void)dealloc{
  70. [[NSNotificationCenter defaultCenter] removeObserver:self];
  71. }
  72. #pragma mark - 初始化播放器
  73. - (void)df_initPlayerWithUserId:(NSString *)userId{
  74. [DFPlayerFileManager df_saveUserId:userId];
  75. //[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
  76. //hxd change 20240723
  77. //[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryMultiRoute error:nil];
  78. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
  79. [[AVAudioSession sharedInstance] setActive:YES error:nil];
  80. //[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
  81. //[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback mode:(nonnull AVAudioSessionMode) options:<#(AVAudioSessionCategoryOptions)#> error:<#(NSError *__autoreleasing _Nullable * _Nullable)#>
  82. _isOtherPlaying = [AVAudioSession sharedInstance].otherAudioPlaying;
  83. self.playMode = DFPlayerModeOnlyOnce;
  84. if(self.state == DFPlayerStateFailed){
  85. self.state = DFPlayerStateStopped;
  86. }
  87. self.isObserveProgress = YES;
  88. self.isObserveBufferProgress = YES;
  89. self.isNeedCache = YES;
  90. self.isObserveFileModifiedTime = NO;
  91. self.isObserveWWAN = NO;
  92. _isCached = NO;
  93. _isBackground = NO;
  94. _randomIndex = -1;
  95. _playIndex2 = 0;
  96. _netGroupQueue = dispatch_group_create();
  97. _dataGroupQueue = dispatch_group_create();
  98. [self addNetObserver];
  99. [self addPlayerObserver];
  100. [self addRemoteControlHandler];
  101. }
  102. - (void)addNetObserver{
  103. static dispatch_once_t token1;
  104. dispatch_once(&token1, ^{
  105. dispatch_group_enter(self->_netGroupQueue);
  106. });
  107. dispatch_group_async(_netGroupQueue, DFPlayerDefaultGlobalQueue, ^{
  108. [DFPlayerTool startMonitoringNetworkStatus:^(DFPlayerNetworkStatus networkStatus) {
  109. static dispatch_once_t token2;
  110. dispatch_once(&token2, ^{
  111. dispatch_group_leave(self->_netGroupQueue);
  112. });
  113. }];
  114. });
  115. }
  116. - (void)addPlayerObserver{
  117. //将要进入后台
  118. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  119. [notificationCenter addObserver:self
  120. selector:@selector(df_playerWillResignActive)
  121. name:UIApplicationWillResignActiveNotification
  122. object:nil];
  123. //已经进入前台
  124. [notificationCenter addObserver:self
  125. selector:@selector(df_playerDidEnterForeground)
  126. name:UIApplicationDidBecomeActiveNotification
  127. object:nil];
  128. //监测耳机
  129. [notificationCenter addObserver:self
  130. selector:@selector(df_playerAudioRouteChange:)
  131. name:AVAudioSessionRouteChangeNotification
  132. object:nil];
  133. //监听播放器被打断(别的软件播放音乐,来电话)
  134. [notificationCenter addObserver:self
  135. selector:@selector(df_playerAudioBeInterrupted:)
  136. name:AVAudioSessionInterruptionNotification
  137. object:[AVAudioSession sharedInstance]];
  138. //监测其他app是否占据AudioSession
  139. // [notificationCenter addObserver:self
  140. // selector:@selector(df_playerSecondaryAudioHint:)
  141. // name:AVAudioSessionSilenceSecondaryAudioHintNotification
  142. // object:nil];
  143. [notificationCenter addObserver:self
  144. selector:@selector(handleAudioSessionSilenceSecondaryAudioHintNotification:)
  145. name:AVAudioSessionSilenceSecondaryAudioHintNotification
  146. object:nil];
  147. // 监听中断通知
  148. [[NSNotificationCenter defaultCenter] addObserver:self
  149. selector:@selector(handleAudioSessionInterruption:)
  150. name:AVAudioSessionInterruptionNotification
  151. object:[AVAudioSession sharedInstance]];
  152. }
  153. - (void)df_playerWillResignActive{
  154. _isBackground = YES;
  155. }
  156. - (void)df_playerDidEnterForeground{
  157. _isBackground = NO;
  158. }
  159. - (void)df_playerAudioRouteChange:(NSNotification *)notification{
  160. NSInteger routeChangeReason = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] integerValue];
  161. switch (routeChangeReason) {
  162. case AVAudioSessionRouteChangeReasonNewDeviceAvailable://耳机插入
  163. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:isHeadphone:)]) {
  164. [self.delegate df_player:self isHeadphone:YES];
  165. }
  166. break;
  167. case AVAudioSessionRouteChangeReasonOldDeviceUnavailable://耳机拔出,停止播放操作
  168. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:isHeadphone:)]) {
  169. [self.delegate df_player:self isHeadphone:NO];
  170. }else{
  171. [self df_pause];
  172. }
  173. break;
  174. case AVAudioSessionRouteChangeReasonCategoryChange:
  175. //
  176. break;
  177. default:
  178. break;
  179. }
  180. }
  181. - (void)df_playerAudioBeInterrupted:(NSNotification *)notification{
  182. NSDictionary *dic = notification.userInfo;
  183. if ([dic[AVAudioSessionInterruptionTypeKey] integerValue] == 1) {//打断开始
  184. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:isInterrupted:)]) {
  185. [self.delegate df_player:self isInterrupted:YES];
  186. }else{
  187. [self df_pause];
  188. }
  189. }else {//打断结束
  190. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:isInterrupted:)]) {
  191. [self.delegate df_player:self isInterrupted:NO];
  192. }else{
  193. if ([notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue] == 1) {
  194. [self df_play];
  195. }
  196. }
  197. }
  198. }
  199. - (void)df_playerSecondaryAudioHint:(NSNotification *)notification{
  200. // NSInteger type = [notification.userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] integerValue];
  201. }
  202. -(void)df_playerDidPlayToEndTime:(NSNotification *)notification{
  203. if (self.delegate && [self.delegate respondsToSelector:@selector(df_playerDidPlayToEndTime:)]) {
  204. [self.delegate df_playerDidPlayToEndTime:self];
  205. }else{
  206. _isNaturalToEndTime = YES;
  207. [self df_next];
  208. }
  209. }
  210. /**远程线控*/
  211. - (void)addRemoteControlHandler{
  212. if (@available (iOS 7.1, *)) {
  213. [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  214. MPRemoteCommandCenter *center = [MPRemoteCommandCenter sharedCommandCenter];
  215. [self addRemoteCommand:center.playCommand selector:@selector(df_play)];
  216. [self addRemoteCommand:center.pauseCommand selector:@selector(df_pause)];
  217. [self addRemoteCommand:center.previousTrackCommand selector:@selector(df_last)];
  218. [self addRemoteCommand:center.nextTrackCommand selector:@selector(df_next)];
  219. [center.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
  220. if ([DFPlayer sharedPlayer].state == DFPlayerStatePlaying) {
  221. [[DFPlayer sharedPlayer] df_pause];
  222. }else{
  223. [[DFPlayer sharedPlayer] df_play];
  224. }
  225. return MPRemoteCommandHandlerStatusSuccess;
  226. }];
  227. if (@available (iOS 9.1,*)) {
  228. [center.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
  229. MPChangePlaybackPositionCommandEvent *positionEvent = (MPChangePlaybackPositionCommandEvent *)event;
  230. if (self.totalTime > 0) {
  231. [self df_seekToTime:positionEvent.positionTime / self.totalTime completionBlock:nil];
  232. }
  233. return MPRemoteCommandHandlerStatusSuccess;
  234. }];
  235. }
  236. }
  237. }
  238. - (void)addRemoteCommand:(MPRemoteCommand *)command selector:(SEL)selector{
  239. [command addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
  240. if ([self respondsToSelector:selector]) {
  241. IMP imp = [self methodForSelector:selector];
  242. void (*func)(id, SEL) = (void *)imp;
  243. func(self, selector);
  244. }
  245. return MPRemoteCommandHandlerStatusSuccess;
  246. }];
  247. }
  248. #pragma mark - 数据源
  249. - (void)df_reloadData{
  250. if (self.dataSource && [self.dataSource respondsToSelector:@selector(df_audioDataForPlayer:)]) {
  251. if (!self.playerModelArray) {
  252. self.playerModelArray = [NSMutableArray array];
  253. }
  254. if (self.playerModelArray.count != 0) {
  255. [self.playerModelArray removeAllObjects];
  256. }
  257. dispatch_group_enter(_dataGroupQueue);
  258. dispatch_group_async(_dataGroupQueue, DFPlayerHighGlobalQueue, ^{
  259. dispatch_async(DFPlayerHighGlobalQueue, ^{
  260. [self.playerModelArray addObjectsFromArray:[self.dataSource df_audioDataForPlayer:self]];
  261. //更新随机数组
  262. [self updateRandomIndexArray];
  263. //更新currentAudioId
  264. if (self.currentAudioModel.audioUrl) {
  265. [self.playerModelArray enumerateObjectsWithOptions:(NSEnumerationConcurrent) usingBlock:^(DFPlayerModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  266. if ([obj.audioUrl.absoluteString isEqualToString:self.currentAudioModel.audioUrl.absoluteString]) {
  267. self.currentAudioModel.audioId = idx;
  268. self->_currentAudioId = idx;
  269. *stop = YES;
  270. }
  271. }];
  272. }
  273. dispatch_group_leave(self->_dataGroupQueue);
  274. });
  275. });
  276. }
  277. }
  278. #pragma mark - 播放 IMPORTANT
  279. - (void)df_playWithAudioId:(NSUInteger)audioId{
  280. dispatch_group_notify(_dataGroupQueue, DFPlayerHighGlobalQueue, ^{
  281. if (self.playerModelArray.count > audioId) {
  282. self.currentAudioModel = self.playerModelArray[audioId];
  283. self->_currentAudioId = audioId;
  284. [self audioPrePlay];
  285. }
  286. });
  287. }
  288. - (void)audioPrePlay{
  289. [self reset];
  290. if (![DFPlayerTool isNSURL:self.currentAudioModel.audioUrl]) {
  291. return;
  292. }
  293. //hxd add 20240716 去下载 自己管理缓存
  294. [[audioPlayListManager shareManager] beginToDownloadByUrl:self.currentAudioModel.audioUrl.absoluteString];
  295. if (self.dataSource && [self.dataSource respondsToSelector:@selector(df_audioInfoForPlayer:)]) {
  296. self.currentAudioInfoModel = [self.dataSource df_audioInfoForPlayer:self];
  297. }
  298. if (self.delegate && [self.delegate respondsToSelector:@selector(df_playerAudioAddToPlayQueue:)]) {
  299. mainBlock(^{
  300. [self.delegate df_playerAudioAddToPlayQueue:self];
  301. });
  302. }
  303. //hxd add 20240716 判断下载完成了没有 完成了 就修改 audioUrl
  304. if ([DFPlayerTool isLocalAudio:self.currentAudioModel.audioUrl]){
  305. NSString*filePath = [[audioPlayListManager shareManager] checkFileToDownloadDonewith:self.currentAudioModel.audioUrl.absoluteString];
  306. if(filePath.length > 0)
  307. {
  308. NSURL *loaclUrl = [NSURL fileURLWithPath:filePath];
  309. if(loaclUrl){
  310. self.currentAudioModel.audioUrl = loaclUrl;
  311. }
  312. }
  313. }
  314. [[audioPlayingView sharedInstance] setAudioTitleFunBy:self.currentAudioModel.audioId];
  315. [[audioPlayingView sharedInstance] setAudioPlayingStateFunBy:YES];
  316. if ([DFPlayerTool isLocalAudio:self.currentAudioModel.audioUrl]) {
  317. // NSLog(@"-- DFPlayer:本地音频,Id:%ld",(unsigned long)self.currentAudioModel.audioId);
  318. _isCached = YES;
  319. [self loadPlayerItemWithURL:self.currentAudioModel.audioUrl];
  320. }else{
  321. NSString *cachePath = [DFPlayerFileManager df_cachePath:self.currentAudioModel.audioUrl];
  322. _isCached = cachePath ? YES : NO;
  323. // NSLog(@"-- DFPlayer:网络音频,Id:%ld 缓存:%@",(unsigned long)self.currentAudioModel.audioId, cachePath ? @"有" : @"无");
  324. dispatch_group_notify(_netGroupQueue, DFPlayerDefaultGlobalQueue, ^{
  325. if ([DFPlayerTool networkStatus] == DFPlayerNetworkStatusUnknown ||
  326. [DFPlayerTool networkStatus] == DFPlayerNetworkStatusNotReachable){
  327. if (cachePath){//有缓存,播放缓存
  328. [self loadPlayerItemWithURL:[NSURL fileURLWithPath:cachePath]];
  329. }else{//无缓存,提示联网
  330. [self df_getStatusCode:DFPlayerStatusNoNetwork];
  331. }
  332. }else{
  333. if (!self.isNeedCache){//不需要缓存
  334. // WWAN网络警告
  335. if (self.isObserveWWAN && [DFPlayerTool networkStatus] == DFPlayerNetworkStatusReachableViaWWAN) {
  336. [self df_getStatusCode:DFPlayerStatusViaWWAN];
  337. return;
  338. }
  339. [self loadPlayerItemWithURL:self.currentAudioModel.audioUrl];
  340. }else{//需要缓存
  341. if (cachePath && !self.isObserveFileModifiedTime) {
  342. //有缓存且不监听改变时间,直接播放缓存
  343. [self loadPlayerItemWithURL:[NSURL fileURLWithPath:cachePath]];
  344. }else{//无缓存 或 需要兼听
  345. // WWAN网络警告
  346. if (self.isObserveWWAN && [DFPlayerTool networkStatus] == DFPlayerNetworkStatusReachableViaWWAN) {
  347. [self df_getStatusCode:DFPlayerStatusViaWWAN];
  348. return;
  349. }
  350. [self loadAudioWithCachePath:cachePath];
  351. }
  352. }
  353. }
  354. });
  355. }
  356. }
  357. - (void)loadAudioWithCachePath:(NSString *)cachePath{
  358. self.resourceLoader = [[DFPlayerResourceLoader alloc] init];
  359. self.resourceLoader.delegate = self;
  360. self.resourceLoader.isCached = _isCached;
  361. self.resourceLoader.isObserveFileModifiedTime = self.isObserveFileModifiedTime;
  362. NSURL *customUrl = [DFPlayerTool customURL:self.currentAudioModel.audioUrl];
  363. if (!customUrl) {
  364. return;
  365. }
  366. AVURLAsset *asset = [AVURLAsset URLAssetWithURL:customUrl options:nil];
  367. [asset.resourceLoader setDelegate:self.resourceLoader queue:dispatch_get_main_queue()];
  368. [asset loadValuesAsynchronouslyForKeys:@[DFPlayableKey] completionHandler:^{
  369. dispatch_async( dispatch_get_main_queue(),^{
  370. if (!asset.playable) {
  371. self.state = DFPlayerStateFailed;
  372. [self.resourceLoader stopDownload];
  373. [asset cancelLoading];
  374. }
  375. });
  376. }];
  377. DFPlayerWeakSelf
  378. self.resourceLoader.checkStatusBlock = ^(NSInteger statusCode){
  379. DFPlayerStrongSelf
  380. if (statusCode == 200) {
  381. [sSelf loadPlayerItemWithAsset:asset];
  382. }else if (statusCode == 304) { // 服务器文件未变化
  383. [sSelf loadPlayerItemWithURL:[NSURL fileURLWithPath:cachePath]];
  384. }else if (statusCode == 206){
  385. }
  386. };
  387. }
  388. - (void)loadPlayerItemWithURL:(NSURL *)URL{
  389. self.playerItem = [[AVPlayerItem alloc] initWithURL:URL];
  390. [self loadPlayer];
  391. }
  392. - (void)loadPlayerItemWithAsset:(AVURLAsset *)asset{
  393. self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
  394. [self loadPlayer];
  395. }
  396. - (void)loadPlayer{
  397. self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];
  398. if (@available(iOS 10.0,*)) {
  399. self.player.automaticallyWaitsToMinimizeStalling = NO;
  400. }
  401. [self df_play];
  402. [self addProgressObserver];
  403. [self addPlayingCenterInfo];
  404. NSInteger index = self.currentAudioModel.audioId;
  405. //HLog(@"audioId:%ld",self.currentAudioModel.audioId);
  406. [self audioPlayerByIndex:index];
  407. }
  408. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
  409. if (object == self.player.currentItem) {
  410. if ([keyPath isEqualToString:DFStatusKey]) {
  411. AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
  412. switch (status) {
  413. case AVPlayerItemStatusUnknown:
  414. self.state = DFPlayerStateFailed;
  415. [self df_getStatusCode:DFPlayerStatusUnknown];
  416. break;
  417. case AVPlayerItemStatusReadyToPlay:
  418. if (self.delegate && [self.delegate respondsToSelector:@selector(df_playerReadyToPlay:)]) {
  419. [self.delegate df_playerReadyToPlay:self];
  420. }
  421. break;
  422. case AVPlayerItemStatusFailed:
  423. //NSError *error = nil;//self.player.currentItem.error;
  424. NSLog(@"111:%@",self.player.currentItem.error);
  425. self.state = DFPlayerStateFailed;
  426. [self df_getStatusCode:DFPlayerStatusFailed];
  427. break;
  428. default:
  429. break;
  430. }
  431. }else if ([keyPath isEqualToString:DFLoadedTimeRangesKey]) {
  432. [self addBufferProgressObserver];
  433. }else if ([keyPath isEqualToString:DFPlaybackBufferEmptyKey]) {
  434. if (self.playerItem.playbackBufferEmpty) {
  435. self.state = DFPlayerStateBuffering;
  436. }
  437. }else if ([keyPath isEqualToString:DFPlaybackLikelyToKeepUpKey]) {
  438. }
  439. }else{
  440. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  441. }
  442. }
  443. #pragma mark - DFPlayerResourceLoaderDelegate
  444. /**下载出错*/
  445. - (void)loader:(DFPlayerResourceLoader *)loader requestError:(NSInteger)errorCode{
  446. if (errorCode == NSURLErrorTimedOut) {
  447. [self df_getStatusCode:DFPlayerStatusTimeOut];
  448. }else if ([DFPlayerTool networkStatus] == DFPlayerNetworkStatusNotReachable ||
  449. [DFPlayerTool networkStatus] == DFPlayerNetworkStatusUnknown) {
  450. [self df_getStatusCode:DFPlayerStatusNoNetwork];
  451. }
  452. }
  453. /**是否完成缓存*/
  454. - (void)loader:(DFPlayerResourceLoader *)loader isCached:(BOOL)isCached{
  455. _isCached = isCached;
  456. NSUInteger status = isCached ? DFPlayerStatusCacheSucc : DFPlayerStatusCacheFail;
  457. [self df_getStatusCode:status];
  458. }
  459. #pragma mark - 缓冲进度 播放进度 歌曲锁屏信息 音频跳转
  460. - (void)addBufferProgressObserver{
  461. self.totalTime = CMTimeGetSeconds(self.playerItem.duration);
  462. if (!self.isObserveBufferProgress) {
  463. return;
  464. }
  465. CMTimeRange timeRange = [self.playerItem.loadedTimeRanges.firstObject CMTimeRangeValue];
  466. CGFloat startSeconds = CMTimeGetSeconds(timeRange.start);
  467. CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration);
  468. if (self.totalTime != 0) {//避免出现inf
  469. self.bufferProgress = (startSeconds + durationSeconds) / self.totalTime;
  470. }
  471. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:bufferProgress:)]) {
  472. [self.delegate df_player:self bufferProgress:self.bufferProgress];
  473. }
  474. if (_isSeekWaiting) {
  475. if (self.bufferProgress > _seekValue) {
  476. _isSeekWaiting = NO;
  477. [self didSeekToTime:_seekValue completionBlock:^{
  478. if (self.seekCompletionBlock) {
  479. self.seekCompletionBlock();
  480. }
  481. }];
  482. }
  483. }
  484. }
  485. - (void)addProgressObserver{
  486. if (!self.isObserveProgress) {
  487. return;
  488. }
  489. DFPlayerWeakSelf
  490. self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0, 1.0) queue:nil usingBlock:^(CMTime time){
  491. DFPlayerStrongSelf
  492. if (sSelf->_isSeek) {
  493. return;
  494. }
  495. AVPlayerItem *currentItem = sSelf.playerItem;
  496. NSArray *loadedRanges = currentItem.seekableTimeRanges;
  497. if (loadedRanges.count > 0 && currentItem.duration.timescale != 0){
  498. CGFloat currentT = (CGFloat)CMTimeGetSeconds(time);
  499. sSelf.currentTime = currentT;
  500. if (sSelf.totalTime != 0) {//避免出现inf
  501. sSelf.progress = CMTimeGetSeconds([currentItem currentTime]) / sSelf.totalTime;
  502. }
  503. if (sSelf.delegate && [sSelf.delegate respondsToSelector:@selector(df_player:progress:currentTime:)]) {
  504. [sSelf.delegate df_player:sSelf progress:sSelf.progress currentTime:currentT];
  505. }
  506. [sSelf updatePlayingCenterInfo];
  507. }
  508. }];
  509. }
  510. - (void)addPlayingCenterInfo{
  511. _remoteInfoDictionary = [NSMutableDictionary dictionary];
  512. if (self.currentAudioInfoModel.audioName) {
  513. _remoteInfoDictionary[MPMediaItemPropertyTitle] = self.currentAudioInfoModel.audioName;
  514. }
  515. if (self.currentAudioInfoModel.audioAlbum) {
  516. _remoteInfoDictionary[MPMediaItemPropertyAlbumTitle] = self.currentAudioInfoModel.audioAlbum;
  517. }
  518. if (self.currentAudioInfoModel.audioSinger) {
  519. _remoteInfoDictionary[MPMediaItemPropertyArtist] = self.currentAudioInfoModel.audioSinger;
  520. }
  521. if ([self.currentAudioInfoModel.audioImage isKindOfClass:[UIImage class]] && self.currentAudioInfoModel.audioImage) {
  522. if (@available(iOS 10.0, *)) {
  523. DFPlayerWeakSelf
  524. MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:self.currentAudioInfoModel.audioImage.size
  525. requestHandler:^UIImage * _Nonnull(CGSize size) {
  526. return wSelf.currentAudioInfoModel.audioImage;
  527. }];
  528. _remoteInfoDictionary[MPMediaItemPropertyArtwork] = artwork;
  529. } else {
  530. MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:self.currentAudioInfoModel.audioImage];
  531. _remoteInfoDictionary[MPMediaItemPropertyArtwork] = artwork;
  532. }
  533. }
  534. _remoteInfoDictionary[MPNowPlayingInfoPropertyPlaybackRate] = [NSNumber numberWithFloat:1.0];
  535. [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = _remoteInfoDictionary;
  536. }
  537. - (void)updatePlayingCenterInfo{
  538. if (!_isBackground) {return;}
  539. _remoteInfoDictionary[MPNowPlayingInfoPropertyElapsedPlaybackTime] = [NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.currentTime)];
  540. _remoteInfoDictionary[MPMediaItemPropertyPlaybackDuration] = [NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.duration)];
  541. [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = _remoteInfoDictionary;
  542. }
  543. - (void)df_seekToTime:(CGFloat)value completionBlock:(void (^)(void))completionBlock{
  544. _isSeek = YES;
  545. // 先暂停
  546. if (self.state == DFPlayerStatePlaying) {
  547. [self df_pause];
  548. }
  549. if (self.bufferProgress < value) {
  550. _isSeekWaiting = YES;
  551. _seekValue = value;
  552. if (completionBlock) {
  553. self.seekCompletionBlock = completionBlock;
  554. }
  555. }else{
  556. _isSeekWaiting = NO;
  557. [self didSeekToTime:value completionBlock:completionBlock];
  558. }
  559. }
  560. - (void)didSeekToTime:(CGFloat)value completionBlock:(void (^)(void))completionBlock{
  561. [self.player seekToTime:CMTimeMake(floorf(self.totalTime * value), 1)
  562. toleranceBefore:kCMTimeZero
  563. toleranceAfter:kCMTimeZero
  564. completionHandler:^(BOOL finished) {
  565. if (finished) {
  566. [self df_play];
  567. self->_isSeek = NO;
  568. if (completionBlock) {
  569. completionBlock();
  570. }
  571. }
  572. }];
  573. }
  574. /**倍速播放*/
  575. - (void)df_setRate:(CGFloat)rate{
  576. for (AVPlayerItemTrack *track in self.playerItem.tracks){
  577. if ([track.assetTrack.mediaType isEqual:AVMediaTypeAudio]){
  578. track.enabled = YES;
  579. }
  580. }
  581. self.player.rate = rate;
  582. }
  583. /**释放播放器*/
  584. - (void)df_deallocPlayer{
  585. [self reset];
  586. self.state = DFPlayerStateStopped;
  587. [DFPlayerTool stopMonitoringNetwork];
  588. if (@available(iOS 7.1, *)) {
  589. [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
  590. MPRemoteCommandCenter *center = [MPRemoteCommandCenter sharedCommandCenter];
  591. [[center playCommand] removeTarget:self];
  592. [[center pauseCommand] removeTarget:self];
  593. [[center nextTrackCommand] removeTarget:self];
  594. [[center previousTrackCommand] removeTarget:self];
  595. [[center togglePlayPauseCommand] removeTarget:self];
  596. if(@available(iOS 9.1, *)) {
  597. [center.changePlaybackPositionCommand removeTarget:self];
  598. }
  599. }
  600. if (_isOtherPlaying) {
  601. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  602. }else{
  603. [[AVAudioSession sharedInstance] setActive:NO error:nil];
  604. }
  605. [self.player.currentItem cancelPendingSeeks];
  606. [self.player.currentItem.asset cancelLoading];
  607. if (self.randomIndexArray) {
  608. self.randomIndexArray = nil;
  609. }
  610. if (self.playerModelArray) {
  611. self.playerModelArray = nil;
  612. }
  613. if (self.playerItem) {
  614. self.playerItem = nil;
  615. }
  616. if (self.player) {
  617. self.player = nil;
  618. }
  619. [DFPlayer sharedPlayer].currentAudioModel = nil;
  620. [[DFPlayerUIManager sharedManager] dfUI_deallocPlayer];
  621. }
  622. - (void)reset{
  623. if (self.state == DFPlayerStatePlaying) {
  624. [self df_pause];
  625. }
  626. //停止下载
  627. if (self.resourceLoader) {
  628. [self.resourceLoader stopDownload];
  629. }
  630. //移除进度观察者
  631. if (self.timeObserver) {
  632. [self.player removeTimeObserver:self.timeObserver];
  633. self.timeObserver = nil;
  634. }
  635. //重置
  636. self.progress = .0f;
  637. self.bufferProgress = .0f;
  638. self.currentTime = .0f;
  639. self.totalTime = .0f;
  640. _isSeekWaiting = NO;
  641. }
  642. #pragma mark - 播放 暂停 下一首 上一首
  643. /**播放*/
  644. -(void)df_play{
  645. self.state = DFPlayerStatePlaying;
  646. [self.player play];
  647. //hxd add 20240729
  648. [[audioPlayingView sharedInstance] setAudioPlayingStateFunBy:YES];
  649. }
  650. /**暂停*/
  651. -(void)df_pause{
  652. self.state = DFPlayerStatePause;
  653. [self.player pause];
  654. //hxd add 20240721
  655. [[audioPlayingView sharedInstance] setAudioPlayingStateFunBy:NO];
  656. }
  657. /**下一首*/
  658. - (void)df_next{
  659. switch (self.playMode) {
  660. case DFPlayerModeOnlyOnce:
  661. if (_isNaturalToEndTime) {
  662. _isNaturalToEndTime = NO;
  663. [self df_pause];
  664. }else{
  665. [self next];
  666. }
  667. break;
  668. case DFPlayerModeSingleCycle:
  669. if (_isNaturalToEndTime) {
  670. _isNaturalToEndTime = NO;
  671. [self audioPrePlay];
  672. }else{
  673. [self next];
  674. }
  675. break;
  676. case DFPlayerModeOrderCycle:
  677. [self next];
  678. break;
  679. case DFPlayerModeShuffleCycle:{
  680. _playIndex2++;
  681. _currentAudioId = [self randomAudioId];
  682. self.currentAudioModel = self.playerModelArray[_currentAudioId];
  683. [self audioPrePlay];
  684. break;
  685. }
  686. default:
  687. break;
  688. }
  689. }
  690. /**上一首*/
  691. - (void)df_last{
  692. if (self.playMode == DFPlayerModeShuffleCycle) {
  693. if (_playIndex2 == 1) {
  694. _playIndex2 = 0;
  695. self.currentAudioModel = self.playerModelArray[_playIndex1];
  696. }else{
  697. _currentAudioId = [self randomAudioId];
  698. self.currentAudioModel = self.playerModelArray[_currentAudioId];
  699. }
  700. [self audioPrePlay];
  701. }else{
  702. _currentAudioId--;
  703. if (_currentAudioId < 0) {
  704. _currentAudioId = self.playerModelArray.count - 1;
  705. }
  706. self.currentAudioModel = self.playerModelArray[_currentAudioId];
  707. [self audioPrePlay];
  708. }
  709. }
  710. - (void)next{
  711. _currentAudioId++;
  712. if (_currentAudioId < 0 || _currentAudioId >= self.playerModelArray.count) {
  713. _currentAudioId = 0;
  714. }
  715. _playIndex1 = _currentAudioId;
  716. _playIndex2 = 0;
  717. self.currentAudioModel = self.playerModelArray[_currentAudioId];
  718. [self audioPrePlay];
  719. }
  720. #pragma mark - 随机播放相关
  721. - (void)updateRandomIndexArray{
  722. NSInteger startIndex = 0;
  723. NSInteger length = self.playerModelArray.count;
  724. NSInteger endIndex = startIndex+length;
  725. NSMutableArray *arr = [NSMutableArray arrayWithCapacity:length];
  726. NSMutableArray *arr1 = [NSMutableArray arrayWithCapacity:length];
  727. for (NSInteger i = startIndex; i < endIndex; i++) {
  728. @autoreleasepool {
  729. NSString *str = [NSString stringWithFormat:@"%ld",(long)i];
  730. [arr1 addObject:str];
  731. }
  732. }
  733. for (NSInteger i = startIndex; i < endIndex; i++) {
  734. @autoreleasepool {
  735. int index = arc4random()%arr1.count;
  736. int radom = [arr1[index] intValue];
  737. NSNumber *num = [NSNumber numberWithInt:radom];
  738. [arr addObject:num];
  739. [arr1 removeObjectAtIndex:index];
  740. }
  741. }
  742. _randomIndexArray = [NSMutableArray arrayWithArray:arr];
  743. }
  744. - (NSInteger)randomAudioId{
  745. _randomIndex++;
  746. if (_randomIndex >= self.randomIndexArray.count) {
  747. _randomIndex = 0;
  748. }
  749. if (_randomIndex < 0) {
  750. _randomIndex = self.randomIndexArray.count - 1;
  751. }
  752. NSInteger index = [self.randomIndexArray[_randomIndex] integerValue];
  753. //去重
  754. //hxd change 20240721 仅有一首歌时 一直为0 不能去重
  755. if (index == _currentAudioId && index != 0) {
  756. index = [self randomAudioId];
  757. }
  758. return index;
  759. }
  760. #pragma mark - setter
  761. - (void)setCategory:(AVAudioSessionCategory)category{
  762. [[AVAudioSession sharedInstance] setCategory:category error:nil];
  763. }
  764. - (void)setPlayerItem:(AVPlayerItem *)playerItem{
  765. if (_playerItem == playerItem) {
  766. return;
  767. }
  768. if (_playerItem) {
  769. [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
  770. [_playerItem removeObserver:self forKeyPath:DFStatusKey];
  771. [_playerItem removeObserver:self forKeyPath:DFLoadedTimeRangesKey];
  772. [_playerItem removeObserver:self forKeyPath:DFPlaybackBufferEmptyKey];
  773. [_playerItem removeObserver:self forKeyPath:DFPlaybackLikelyToKeepUpKey];
  774. }
  775. _playerItem = playerItem;
  776. if (playerItem) {
  777. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(df_playerDidPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
  778. [playerItem addObserver:self forKeyPath:DFStatusKey options:NSKeyValueObservingOptionNew context:nil];
  779. [playerItem addObserver:self forKeyPath:DFLoadedTimeRangesKey options:NSKeyValueObservingOptionNew context:nil];
  780. [playerItem addObserver:self forKeyPath:DFPlaybackBufferEmptyKey options:NSKeyValueObservingOptionNew context:nil];
  781. [playerItem addObserver:self forKeyPath:DFPlaybackLikelyToKeepUpKey options:NSKeyValueObservingOptionNew context:nil];
  782. }
  783. }
  784. #pragma mark - 缓存相关
  785. - (NSString *)df_cachePath:(NSURL *)audioUrl{
  786. if ([DFPlayerTool isLocalAudio:audioUrl] || ![DFPlayerTool isNSURL:audioUrl] || !audioUrl) {
  787. return nil;
  788. }
  789. return [DFPlayerFileManager df_cachePath:audioUrl];
  790. }
  791. - (CGFloat)df_cacheSize:(BOOL)currentUser{
  792. return [DFPlayerFileManager df_cacheSize:currentUser];
  793. }
  794. - (BOOL)df_clearAudioCache:(NSURL *)audioUrl{
  795. return [DFPlayerFileManager df_clearAudioCache:audioUrl];
  796. }
  797. - (BOOL)df_clearUserCache:(BOOL)currentUser{
  798. return [DFPlayerFileManager df_clearUserCache:currentUser];
  799. }
  800. #pragma mark - 统一状态代理
  801. - (void)df_getStatusCode:(NSUInteger)statusCode{
  802. dispatch_async(dispatch_get_main_queue(), ^{
  803. if (self.delegate && [self.delegate respondsToSelector:@selector(df_player:didGetStatusCode:)]) {
  804. [self.delegate df_player:self didGetStatusCode:statusCode];
  805. }
  806. });
  807. }
  808. #pragma mark - hxd add 20240725 音频播放呗中断检测
  809. - (void)handleAudioSessionInterruption:(NSNotification *)notification {
  810. if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
  811. AVAudioSessionInterruptionType type = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
  812. switch (type) {
  813. case AVAudioSessionInterruptionTypeBegan:
  814. // 播放被中断,此处可以暂停播放
  815. HLog(@"Audio Session Interruption Began");
  816. // 暂停你的音频播放
  817. [self df_pause];
  818. break;
  819. case AVAudioSessionInterruptionTypeEnded:
  820. // 播放中断结束,检查是否应该恢复播放
  821. HLog(@"Audio Session Interruption Ended");
  822. // 获取中断选项,判断是否可以恢复播放
  823. AVAudioSessionInterruptionOptions options = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
  824. if (options == AVAudioSessionInterruptionOptionShouldResume) {
  825. // 如果应该恢复播放,则恢复播放
  826. [self df_play];
  827. }
  828. break;
  829. default:
  830. break;
  831. }
  832. }
  833. }
  834. - (void)handleAudioSessionSilenceSecondaryAudioHintNotification:(NSNotification *)notification {
  835. // 这里处理通知,比如暂停或恢复你的音频播放
  836. //AVAudioSession *session = [notification object];
  837. BOOL willSilenceOthers = [[notification.userInfo valueForKey:AVAudioSessionSilenceSecondaryAudioHintTypeKey] boolValue];
  838. if (willSilenceOthers) {
  839. // 其他音频将被暂停
  840. HLog(@"Other audio will be silenced.");
  841. // 这里可以添加代码来暂停你的音频播放(如果需要的话)
  842. [self df_pause];
  843. } else {
  844. // 其他音频将恢复播放
  845. HLog(@"Other audio will resume.");
  846. // 这里可以添加代码来恢复你的音频播放(如果需要的话)
  847. [self df_play];
  848. }
  849. }
  850. #pragma mark 音频可以播放 保存最近文件和歌曲缓存
  851. - (void)audioPlayerByIndex:(NSInteger)index{
  852. if(index >=0 && index < _allAudioDataArray.count){
  853. NASFileAudioDataModel *dataModel = _allAudioDataArray[index];
  854. lastFileModel *lastFileMod = [lastFileModel new];
  855. lastFileMod.path = dataModel.path;
  856. lastFileMod.name = dataModel.name;
  857. lastFileMod.time = dataModel.time;
  858. lastFileMod.size = dataModel.size;
  859. lastFileMod.duration = dataModel.duration;
  860. lastFileMod.type = @"audio";
  861. lastFileMod.lastPreTime = [iTools getNowTimeStamp];
  862. //本地缓存
  863. lastFileMod.localPath = [[audioPlayListManager shareManager] getAudioCacheFullPathBy:dataModel.name];
  864. [[lastFileManager shareManager] saveFileInfoWith:lastFileMod with:dataModel.path];
  865. [[audioPlayListManager shareManager] saveFileInfoWith:lastFileMod with:dataModel.path];
  866. }
  867. }
  868. @end