DFPlayer.m 32 KB

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