FSAudioController.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. /*
  2. * This file is part of the FreeStreamer project,
  3. * (C)Copyright 2011-2018 Matias Muhonen <mmu@iki.fi> 穆马帝
  4. * See the file ''LICENSE'' for using the code.
  5. *
  6. * https://github.com/muhku/FreeStreamer
  7. */
  8. #import "FSAudioController.h"
  9. #import "FSPlaylistItem.h"
  10. #import "FSCheckContentTypeRequest.h"
  11. #import "FSParsePlaylistRequest.h"
  12. #import "FSParseRssPodcastFeedRequest.h"
  13. #import <AVFoundation/AVFoundation.h>
  14. /**
  15. * Private interface for FSAudioController.
  16. */
  17. @interface FSAudioController ()
  18. - (void)notifyRetrievingURL;
  19. @property (readonly) FSAudioStream *audioStream;
  20. @property (readonly) FSCheckContentTypeRequest *checkContentTypeRequest;
  21. @property (readonly) FSParsePlaylistRequest *parsePlaylistRequest;
  22. @property (readonly) FSParseRssPodcastFeedRequest *parseRssPodcastFeedRequest;
  23. @property (nonatomic,assign) BOOL readyToPlay;
  24. @property (nonatomic,assign) NSUInteger currentPlaylistItemIndex;
  25. @property (nonatomic,strong) NSMutableArray *playlistItems;
  26. @property (nonatomic,strong) NSMutableArray *streams;
  27. @property (nonatomic,assign) BOOL needToSetVolume;
  28. @property (nonatomic,assign) BOOL songSwitchInProgress;
  29. @property (nonatomic,assign) float outputVolume;
  30. - (void)audioStreamStateDidChange:(NSNotification *)notification;
  31. - (void)deactivateInactivateStreams:(NSUInteger)currentActiveStream;
  32. - (void)setAudioSessionActive:(BOOL)active;
  33. @end
  34. /**
  35. * Acts as a proxy object for FSAudioStream. Lazily initializes
  36. * the stream when it is needed.
  37. *
  38. * A call to deactivate releases the stream.
  39. */
  40. @interface FSAudioStreamProxy : NSObject {
  41. FSAudioStream *_audioStream;
  42. }
  43. @property (readonly) FSAudioStream *audioStream;
  44. @property (nonatomic,copy) NSURL *url;
  45. @property (nonatomic,weak) FSAudioController *audioController;
  46. - (void)deactivate;
  47. @end
  48. /*
  49. * =======================================
  50. * FSAudioStreamProxy implementation.
  51. * =======================================
  52. */
  53. @implementation FSAudioStreamProxy
  54. - (id)init
  55. {
  56. if (self = [super init]) {
  57. }
  58. return self;
  59. }
  60. - (id)initWithAudioController:(FSAudioController *)controller
  61. {
  62. if (self = [self init]) {
  63. self.audioController = controller;
  64. }
  65. return self;
  66. }
  67. - (void)dealloc
  68. {
  69. if (self.audioController.enableDebugOutput) {
  70. NSLog(@"[FSAudioController.m:%i] FSAudioStreamProxy.dealloc: %@", __LINE__, self.url);
  71. }
  72. [self deactivate];
  73. }
  74. - (FSAudioStream *)audioStream
  75. {
  76. if (!_audioStream) {
  77. FSStreamConfiguration *conf;
  78. if (self.audioController.configuration) {
  79. conf = self.audioController.configuration;
  80. } else {
  81. conf = [[FSStreamConfiguration alloc] init];
  82. }
  83. // Disable audio session handling for the audio stream; audio controller handles it
  84. conf.automaticAudioSessionHandlingEnabled = NO;
  85. _audioStream = [[FSAudioStream alloc] initWithConfiguration:conf];
  86. if (self.audioController.needToSetVolume) {
  87. _audioStream.volume = self.audioController.outputVolume;
  88. }
  89. if (self.url) {
  90. _audioStream.url = self.url;
  91. }
  92. }
  93. return _audioStream;
  94. }
  95. - (void)deactivate
  96. {
  97. [_audioStream stop];
  98. _audioStream = nil;
  99. }
  100. @end
  101. /*
  102. * =======================================
  103. * FSAudioController implementation
  104. * =======================================
  105. */
  106. @implementation FSAudioController
  107. -(id)init
  108. {
  109. if (self = [super init]) {
  110. _url = nil;
  111. _checkContentTypeRequest = nil;
  112. _parsePlaylistRequest = nil;
  113. _readyToPlay = NO;
  114. _playlistItems = [[NSMutableArray alloc] init];
  115. _streams = [[NSMutableArray alloc] init];
  116. self.preloadNextPlaylistItemAutomatically = YES;
  117. self.enableDebugOutput = NO;
  118. self.automaticAudioSessionHandlingEnabled = YES;
  119. self.configuration = [[FSStreamConfiguration alloc] init];
  120. [[NSNotificationCenter defaultCenter] addObserver:self
  121. selector:@selector(audioStreamStateDidChange:)
  122. name:FSAudioStreamStateChangeNotification
  123. object:nil];
  124. }
  125. return self;
  126. }
  127. - (id)initWithUrl:(NSURL *)url
  128. {
  129. if (self = [self init]) {
  130. self.url = url;
  131. }
  132. return self;
  133. }
  134. - (void)dealloc
  135. {
  136. [[NSNotificationCenter defaultCenter] removeObserver:self];
  137. [_checkContentTypeRequest cancel];
  138. [_parsePlaylistRequest cancel];
  139. [_parseRssPodcastFeedRequest cancel];
  140. for (FSAudioStreamProxy *proxy in _streams) {
  141. if (self.enableDebugOutput) {
  142. NSLog(@"[FSAudioController.m:%i] dealloc. Deactivating stream %@", __LINE__, proxy.url);
  143. }
  144. [proxy deactivate];
  145. }
  146. [self setAudioSessionActive:NO];
  147. }
  148. - (void)audioStreamStateDidChange:(NSNotification *)notification
  149. {
  150. if (notification.object == self) {
  151. // URL retrieving notification from ourselves, ignore
  152. return;
  153. }
  154. if (!(notification.object == self.audioStream)) {
  155. // This doesn't concern us, return
  156. return;
  157. }
  158. NSDictionary *dict = [notification userInfo];
  159. int state = [[dict valueForKey:FSAudioStreamNotificationKey_State] intValue];
  160. if (state == kFSAudioStreamEndOfFile) {
  161. if (self.enableDebugOutput) {
  162. NSLog(@"[FSAudioController.m:%i] EOF reached for %@", __LINE__, self.audioStream.url);
  163. }
  164. if (!self.preloadNextPlaylistItemAutomatically) {
  165. // No preloading wanted, skip
  166. if (self.enableDebugOutput) {
  167. NSLog(@"[FSAudioController.m:%i] Preloading disabled, return.", __LINE__);
  168. }
  169. return;
  170. }
  171. // Reached EOF for this stream, do we have another item waiting in the playlist?
  172. if ([self hasNextItem]) {
  173. FSAudioStreamProxy *proxy = [_streams objectAtIndex:self.currentPlaylistItemIndex + 1];
  174. FSAudioStream *nextStream = proxy.audioStream;
  175. if (self.enableDebugOutput) {
  176. NSLog(@"[FSAudioController.m:%i] Preloading %@", __LINE__, nextStream.url);
  177. }
  178. if ([self.delegate respondsToSelector:@selector(audioController:allowPreloadingForStream:)]) {
  179. if ([self.delegate audioController:self allowPreloadingForStream:nextStream]) {
  180. [nextStream preload];
  181. } else {
  182. if (self.enableDebugOutput) {
  183. NSLog(@"[FSAudioController.m:%i] Preloading disallowed for stream %@", __LINE__, nextStream.url);
  184. }
  185. }
  186. } else {
  187. // Start preloading the next stream; we can load this as there is no override
  188. [nextStream preload];
  189. }
  190. if ([self.delegate respondsToSelector:@selector(audioController:preloadStartedForStream:)]) {
  191. [self.delegate audioController:self preloadStartedForStream:nextStream];
  192. }
  193. }
  194. } else if (state == kFsAudioStreamStopped && !self.songSwitchInProgress) {
  195. if (self.enableDebugOutput) {
  196. NSLog(@"Stream %@ stopped. No next playlist items. Deactivating audio session", self.audioStream.url);
  197. }
  198. [self setAudioSessionActive:NO];
  199. } else if (state == kFsAudioStreamPlaybackCompleted && [self hasNextItem]) {
  200. self.currentPlaylistItemIndex = self.currentPlaylistItemIndex + 1;
  201. self.songSwitchInProgress = YES;
  202. [self play];
  203. } else if (state == kFsAudioStreamFailed) {
  204. if (self.enableDebugOutput) {
  205. NSLog(@"Stream %@ failed. Deactivating audio session", self.audioStream.url);
  206. }
  207. [self setAudioSessionActive:NO];
  208. } else if (state == kFsAudioStreamBuffering) {
  209. if (self.enableDebugOutput) {
  210. NSLog(@"Stream buffering. Activating audio session");
  211. }
  212. self.songSwitchInProgress = NO;
  213. if (self.automaticAudioSessionHandlingEnabled) {
  214. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  215. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
  216. #endif
  217. }
  218. [self setAudioSessionActive:YES];
  219. } else if (state == kFsAudioStreamPlaying) {
  220. self.currentPlaylistItem.audioDataByteCount = self.activeStream.audioDataByteCount;
  221. }
  222. }
  223. - (void)deactivateInactivateStreams:(NSUInteger)currentActiveStream
  224. {
  225. NSUInteger streamIndex = 0;
  226. for (FSAudioStreamProxy *proxy in _streams) {
  227. if (streamIndex != currentActiveStream) {
  228. if (self.enableDebugOutput) {
  229. NSLog(@"[FSAudioController.m:%i] Deactivating stream %@", __LINE__, proxy.url);
  230. }
  231. [proxy deactivate];
  232. }
  233. streamIndex++;
  234. }
  235. }
  236. - (void)setAudioSessionActive:(BOOL)active
  237. {
  238. if (self.automaticAudioSessionHandlingEnabled) {
  239. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  240. [[AVAudioSession sharedInstance] setActive:active withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  241. #else
  242. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  243. [[AVAudioSession sharedInstance] setActive:active error:nil];
  244. #endif
  245. #endif
  246. }
  247. }
  248. /*
  249. * =======================================
  250. * Properties
  251. * =======================================
  252. */
  253. - (FSAudioStream *)audioStream
  254. {
  255. FSAudioStream *stream = nil;
  256. if ([_streams count] == 0) {
  257. if (self.enableDebugOutput) {
  258. NSLog(@"[FSAudioController.m:%i] Stream count %lu, creating a proxy object", __LINE__, (unsigned long)[_streams count]);
  259. }
  260. FSAudioStreamProxy *proxy = [[FSAudioStreamProxy alloc] initWithAudioController:self];
  261. [_streams addObject:proxy];
  262. }
  263. FSAudioStreamProxy *proxy = [_streams objectAtIndex:self.currentPlaylistItemIndex];
  264. stream = proxy.audioStream;
  265. return stream;
  266. }
  267. - (FSCheckContentTypeRequest *)checkContentTypeRequest
  268. {
  269. if (!_checkContentTypeRequest) {
  270. __weak FSAudioController *weakSelf = self;
  271. _checkContentTypeRequest = [[FSCheckContentTypeRequest alloc] init];
  272. _checkContentTypeRequest.url = self.url;
  273. _checkContentTypeRequest.onCompletion = ^() {
  274. if (weakSelf.checkContentTypeRequest.playlist) {
  275. // The URL is a playlist; retrieve the contents
  276. [weakSelf.parsePlaylistRequest start];
  277. } else if (weakSelf.checkContentTypeRequest.xml) {
  278. // The URL may be an RSS feed, check the contents
  279. [weakSelf.parseRssPodcastFeedRequest start];
  280. } else {
  281. // Not a playlist; try directly playing the URL
  282. weakSelf.readyToPlay = YES;
  283. [weakSelf play];
  284. }
  285. };
  286. _checkContentTypeRequest.onFailure = ^() {
  287. // Failed to check the format; try playing anyway
  288. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  289. NSLog(@"FSAudioController: Failed to check the format, trying to play anyway, URL: %@", weakSelf.audioStream.url);
  290. #endif
  291. weakSelf.readyToPlay = YES;
  292. [weakSelf play];
  293. };
  294. }
  295. return _checkContentTypeRequest;
  296. }
  297. - (FSParsePlaylistRequest *)parsePlaylistRequest
  298. {
  299. if (!_parsePlaylistRequest) {
  300. __weak FSAudioController *weakSelf = self;
  301. _parsePlaylistRequest = [[FSParsePlaylistRequest alloc] init];
  302. _parsePlaylistRequest.onCompletion = ^() {
  303. [weakSelf playFromPlaylist:weakSelf.parsePlaylistRequest.playlistItems];
  304. };
  305. _parsePlaylistRequest.onFailure = ^() {
  306. // Failed to parse the playlist; try playing anyway
  307. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  308. NSLog(@"FSAudioController: Playlist parsing failed, trying to play anyway, URL: %@", weakSelf.audioStream.url);
  309. #endif
  310. weakSelf.readyToPlay = YES;
  311. [weakSelf play];
  312. };
  313. }
  314. return _parsePlaylistRequest;
  315. }
  316. - (FSParseRssPodcastFeedRequest *)parseRssPodcastFeedRequest
  317. {
  318. if (!_parseRssPodcastFeedRequest) {
  319. __weak FSAudioController *weakSelf = self;
  320. _parseRssPodcastFeedRequest = [[FSParseRssPodcastFeedRequest alloc] init];
  321. _parseRssPodcastFeedRequest.onCompletion = ^() {
  322. [weakSelf playFromPlaylist:weakSelf.parseRssPodcastFeedRequest.playlistItems];
  323. };
  324. _parseRssPodcastFeedRequest.onFailure = ^() {
  325. // Failed to parse the XML file; try playing anyway
  326. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  327. NSLog(@"FSAudioController: Failed to parse the RSS feed, trying to play anyway, URL: %@", weakSelf.audioStream.url);
  328. #endif
  329. weakSelf.readyToPlay = YES;
  330. [weakSelf play];
  331. };
  332. }
  333. return _parseRssPodcastFeedRequest;
  334. }
  335. - (void)notifyRetrievingURL
  336. {
  337. if (self.onStateChange) {
  338. self.onStateChange(kFsAudioStreamRetrievingURL);
  339. }
  340. }
  341. - (BOOL)isPlaying
  342. {
  343. return [self.audioStream isPlaying];
  344. }
  345. /*
  346. * =======================================
  347. * Public interface
  348. * =======================================
  349. */
  350. - (void)play
  351. {
  352. if (!self.readyToPlay) {
  353. /*
  354. * Not ready to play; start by checking the content type of the given
  355. * URL.
  356. */
  357. [self.checkContentTypeRequest start];
  358. NSDictionary *userInfo = @{FSAudioStreamNotificationKey_State: @(kFsAudioStreamRetrievingURL)};
  359. NSNotification *notification = [NSNotification notificationWithName:FSAudioStreamStateChangeNotification object:self userInfo:userInfo];
  360. [[NSNotificationCenter defaultCenter] postNotification:notification];
  361. [NSTimer scheduledTimerWithTimeInterval:0
  362. target:self
  363. selector:@selector(notifyRetrievingURL)
  364. userInfo:nil
  365. repeats:NO];
  366. return;
  367. }
  368. if ([self.playlistItems count] > 0) {
  369. if (self.currentPlaylistItem.originatingUrl) {
  370. self.audioStream.url = self.currentPlaylistItem.originatingUrl;
  371. } else {
  372. self.audioStream.url = self.currentPlaylistItem.url;
  373. }
  374. } else {
  375. self.audioStream.url = self.url;
  376. }
  377. if (self.onStateChange) {
  378. self.audioStream.onStateChange = self.onStateChange;
  379. }
  380. if (self.onMetaDataAvailable) {
  381. self.audioStream.onMetaDataAvailable = self.onMetaDataAvailable;
  382. }
  383. if (self.onFailure) {
  384. self.audioStream.onFailure = self.onFailure;
  385. }
  386. FSAudioStream *stream = self.audioStream;
  387. if (self.enableDebugOutput) {
  388. NSLog(@"Playing %@", stream);
  389. }
  390. [stream play];
  391. }
  392. - (void)playFromURL:(NSURL*)url
  393. {
  394. if (!url) {
  395. return;
  396. }
  397. [_playlistItems removeAllObjects];
  398. [self stop];
  399. self.url = url;
  400. [self play];
  401. }
  402. - (void)playFromPlaylist:(NSArray *)playlist
  403. {
  404. [self playFromPlaylist:playlist itemIndex:0];
  405. }
  406. - (void)playFromPlaylist:(NSArray *)playlist itemIndex:(NSUInteger)index
  407. {
  408. [self stop];
  409. self.playlistItems = [[NSMutableArray alloc] init];
  410. _streams = [[NSMutableArray alloc] init];
  411. self.currentPlaylistItemIndex = 0;
  412. [self.playlistItems addObjectsFromArray:playlist];
  413. for (FSPlaylistItem *item in playlist) {
  414. FSAudioStreamProxy *proxy = [[FSAudioStreamProxy alloc] initWithAudioController:self];
  415. proxy.url = item.url;
  416. if (self.enableDebugOutput) {
  417. NSLog(@"[FSAudioController.m:%i] playFromPlaylist. Adding stream proxy for %@", __LINE__, proxy.url);
  418. }
  419. [_streams addObject:proxy];
  420. }
  421. [self playItemAtIndex:index];
  422. }
  423. - (void)playItemAtIndex:(NSUInteger)index
  424. {
  425. NSUInteger count = [self countOfItems];
  426. if (count == 0) {
  427. return;
  428. }
  429. if (index >= count) {
  430. return;
  431. }
  432. [self.audioStream stop];
  433. self.currentPlaylistItemIndex = index;
  434. self.readyToPlay = YES;
  435. [self deactivateInactivateStreams:index];
  436. [self play];
  437. }
  438. - (NSUInteger)countOfItems
  439. {
  440. return [self.playlistItems count];
  441. }
  442. - (void)addItem:(FSPlaylistItem *)item
  443. {
  444. if (!item) {
  445. return;
  446. }
  447. [self.playlistItems addObject:item];
  448. FSAudioStreamProxy *proxy = [[FSAudioStreamProxy alloc] initWithAudioController:self];
  449. proxy.url = item.url;
  450. if (self.enableDebugOutput) {
  451. NSLog(@"[FSAudioController.m:%i] addItem. Adding stream proxy for %@", __LINE__, proxy.url);
  452. }
  453. [_streams addObject:proxy];
  454. }
  455. - (void)insertItem:(FSPlaylistItem *)item atIndex:(NSInteger)index
  456. {
  457. if (!item) {
  458. return;
  459. }
  460. if (index > self.playlistItems.count) {
  461. return;
  462. }
  463. if(self.playlistItems.count == 0 && index == 0) {
  464. [self addItem:item];
  465. return;
  466. }
  467. [self.playlistItems insertObject:item
  468. atIndex:index];
  469. FSAudioStreamProxy *proxy = [[FSAudioStreamProxy alloc] initWithAudioController:self];
  470. proxy.url = item.url;
  471. [_streams insertObject:proxy
  472. atIndex:index];
  473. if(index <= self.currentPlaylistItemIndex) {
  474. _currentPlaylistItemIndex++;
  475. }
  476. }
  477. - (void)replaceItemAtIndex:(NSUInteger)index withItem:(FSPlaylistItem *)item
  478. {
  479. NSUInteger count = [self countOfItems];
  480. if (count == 0) {
  481. return;
  482. }
  483. if (index >= count) {
  484. return;
  485. }
  486. if (self.currentPlaylistItemIndex == index) {
  487. // If the item is currently playing, do not allow the replacement
  488. return;
  489. }
  490. [self.playlistItems replaceObjectAtIndex:index withObject:item];
  491. FSAudioStreamProxy *proxy = [[FSAudioStreamProxy alloc] initWithAudioController:self];
  492. proxy.url = item.url;
  493. [_streams replaceObjectAtIndex:index withObject:proxy];
  494. }
  495. - (void)moveItemAtIndex:(NSUInteger)from toIndex:(NSUInteger)to {
  496. NSUInteger count = [self countOfItems];
  497. if (count == 0) {
  498. return;
  499. }
  500. if (from >= count || to >= count) {
  501. return;
  502. }
  503. if(from == self.currentPlaylistItemIndex) {
  504. _currentPlaylistItemIndex = to;
  505. }
  506. else if(from < self.currentPlaylistItemIndex && to > self.currentPlaylistItemIndex) {
  507. _currentPlaylistItemIndex--;
  508. }
  509. else if(from > self.currentPlaylistItemIndex && to <= self.currentPlaylistItemIndex) {
  510. _currentPlaylistItemIndex++;
  511. }
  512. id object = [self.playlistItems objectAtIndex:from];
  513. [self.playlistItems removeObjectAtIndex:from];
  514. [self.playlistItems insertObject:object atIndex:to];
  515. id obj = [_streams objectAtIndex:from];
  516. [_streams removeObjectAtIndex:from];
  517. [_streams insertObject:obj atIndex:to];
  518. }
  519. - (void)removeItemAtIndex:(NSUInteger)index
  520. {
  521. NSUInteger count = [self countOfItems];
  522. if (count == 0) {
  523. return;
  524. }
  525. if (index >= count) {
  526. return;
  527. }
  528. if (self.currentPlaylistItemIndex == index && self.isPlaying) {
  529. // If the item is currently playing, do not allow the removal
  530. return;
  531. }
  532. FSPlaylistItem *current = self.currentPlaylistItem;
  533. [self.playlistItems removeObjectAtIndex:index];
  534. if (self.enableDebugOutput) {
  535. FSAudioStreamProxy *proxy = [_streams objectAtIndex:index];
  536. NSLog(@"[FSAudioController.m:%i] removeItemAtIndex. Removing stream proxy %@", __LINE__, proxy.url);
  537. }
  538. [_streams removeObjectAtIndex:index];
  539. // Update the current playlist item to be correct after the removal
  540. NSUInteger itemIndex = 0;
  541. for (FSPlaylistItem *item in self.playlistItems) {
  542. if (item == current) {
  543. self.currentPlaylistItemIndex = itemIndex;
  544. break;
  545. }
  546. itemIndex++;
  547. }
  548. }
  549. - (void)stop
  550. {
  551. if ([_streams count] > 0) {
  552. // Avoid creating an instance if we don't have it
  553. [self.audioStream stop];
  554. }
  555. [_checkContentTypeRequest cancel];
  556. [_parsePlaylistRequest cancel];
  557. [_parseRssPodcastFeedRequest cancel];
  558. self.readyToPlay = NO;
  559. }
  560. - (void)pause
  561. {
  562. [self.audioStream pause];
  563. }
  564. -(BOOL)hasMultiplePlaylistItems
  565. {
  566. return ([self.playlistItems count] > 1);
  567. }
  568. -(BOOL)hasNextItem
  569. {
  570. return [self hasMultiplePlaylistItems] && (self.currentPlaylistItemIndex + 1 < [self.playlistItems count]);
  571. }
  572. -(BOOL)hasPreviousItem
  573. {
  574. return ([self hasMultiplePlaylistItems] && (self.currentPlaylistItemIndex != 0));
  575. }
  576. -(void)playNextItem
  577. {
  578. if ([self hasNextItem]) {
  579. if (self.enableDebugOutput) {
  580. NSLog(@"[FSAudioController.m:%i] playNexItem. Stopping stream %@", __LINE__, self.audioStream.url);
  581. }
  582. [self.audioStream stop];
  583. [self deactivateInactivateStreams:self.currentPlaylistItemIndex];
  584. self.currentPlaylistItemIndex = self.currentPlaylistItemIndex + 1;
  585. [self play];
  586. }
  587. }
  588. -(void)playPreviousItem
  589. {
  590. if ([self hasPreviousItem]) {
  591. if (self.enableDebugOutput) {
  592. NSLog(@"[FSAudioController.m:%i] playPreviousItem. Stopping stream %@", __LINE__, self.audioStream.url);
  593. }
  594. [self.audioStream stop];
  595. [self deactivateInactivateStreams:self.currentPlaylistItemIndex];
  596. self.currentPlaylistItemIndex = self.currentPlaylistItemIndex - 1;
  597. [self play];
  598. }
  599. }
  600. /*
  601. * =======================================
  602. * Properties
  603. * =======================================
  604. */
  605. - (void)setVolume:(float)volume
  606. {
  607. self.outputVolume = volume;
  608. self.needToSetVolume = YES;
  609. if ([_streams count] > 0) {
  610. self.audioStream.volume = self.outputVolume;
  611. }
  612. }
  613. - (float)volume
  614. {
  615. return self.outputVolume;
  616. }
  617. - (void)setUrl:(NSURL *)url
  618. {
  619. [self stop];
  620. if (url) {
  621. NSURL *copyOfURL = [url copy];
  622. _url = copyOfURL;
  623. self.checkContentTypeRequest.url = _url;
  624. self.parsePlaylistRequest.url = _url;
  625. self.parseRssPodcastFeedRequest.url = _url;
  626. if ([_url isFileURL]) {
  627. /*
  628. * Local file URLs can be directly played
  629. */
  630. self.readyToPlay = YES;
  631. }
  632. } else {
  633. _url = nil;
  634. }
  635. }
  636. - (NSURL* )url
  637. {
  638. if (!_url) {
  639. return nil;
  640. }
  641. NSURL *copyOfURL = [_url copy];
  642. return copyOfURL;
  643. }
  644. - (FSAudioStream *)activeStream
  645. {
  646. if ([_streams count] > 0) {
  647. return self.audioStream;
  648. }
  649. return nil;
  650. }
  651. - (FSPlaylistItem *)currentPlaylistItem
  652. {
  653. if (self.readyToPlay) {
  654. if ([self.playlistItems count] > 0) {
  655. FSPlaylistItem *playlistItem = (self.playlistItems)[self.currentPlaylistItemIndex];
  656. return playlistItem;
  657. }
  658. }
  659. return nil;
  660. }
  661. - (void (^)(FSAudioStreamState state))onStateChange
  662. {
  663. return _onStateChangeBlock;
  664. }
  665. - (void (^)(NSDictionary *metaData))onMetaDataAvailable
  666. {
  667. return _onMetaDataAvailableBlock;
  668. }
  669. - (void (^)(FSAudioStreamError error, NSString *errorDescription))onFailure
  670. {
  671. return _onFailureBlock;
  672. }
  673. - (void)setOnStateChange:(void (^)(FSAudioStreamState))newOnStateValue
  674. {
  675. _onStateChangeBlock = newOnStateValue;
  676. if ([_streams count] > 0) {
  677. self.audioStream.onStateChange = _onStateChangeBlock;
  678. }
  679. }
  680. - (void)setOnMetaDataAvailable:(void (^)(NSDictionary *))newOnMetaDataAvailableValue
  681. {
  682. _onMetaDataAvailableBlock = newOnMetaDataAvailableValue;
  683. if ([_streams count] > 0) {
  684. self.audioStream.onMetaDataAvailable = _onMetaDataAvailableBlock;
  685. }
  686. }
  687. - (void)setOnFailure:(void (^)(FSAudioStreamError error, NSString *errorDescription))newOnFailureValue
  688. {
  689. _onFailureBlock = newOnFailureValue;
  690. if ([_streams count] > 0) {
  691. self.audioStream.onFailure = _onFailureBlock;
  692. }
  693. }
  694. @end