DFPlayer.m 32 KB

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