FSAudioStream.mm 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905
  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 "FSAudioStream.h"
  9. #import "Reachability.h"
  10. #include "audio_stream.h"
  11. #include "stream_configuration.h"
  12. #include "input_stream.h"
  13. #import <AVFoundation/AVFoundation.h>
  14. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  15. #import <AudioToolbox/AudioToolbox.h>
  16. #import <UIKit/UIKit.h>
  17. #endif
  18. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  19. static NSMutableDictionary *fsAudioStreamPrivateActiveSessions = nil;
  20. #endif
  21. @interface FSCacheObject : NSObject {
  22. }
  23. @property (strong,nonatomic) NSString *path;
  24. @property (strong,nonatomic) NSString *name;
  25. @property (strong,nonatomic) NSDictionary *attributes;
  26. @property (nonatomic,readonly) unsigned long long fileSize;
  27. @property (nonatomic,readonly) NSDate *modificationDate;
  28. @end
  29. @implementation FSCacheObject
  30. - (unsigned long long)fileSize
  31. {
  32. NSNumber *fileSizeNumber = [self.attributes objectForKey:NSFileSize];
  33. return [fileSizeNumber longLongValue];
  34. }
  35. - (NSDate *)modificationDate
  36. {
  37. NSDate *date = [self.attributes objectForKey:NSFileModificationDate];
  38. return date;
  39. }
  40. @end
  41. static NSInteger sortCacheObjects(id co1, id co2, void *keyForSorting)
  42. {
  43. FSCacheObject *cached1 = (FSCacheObject *)co1;
  44. FSCacheObject *cached2 = (FSCacheObject *)co2;
  45. NSDate *d1 = cached1.modificationDate;
  46. NSDate *d2 = cached2.modificationDate;
  47. return [d1 compare:d2];
  48. }
  49. @implementation FSStreamConfiguration
  50. - (id)init
  51. {
  52. self = [super init];
  53. if (self) {
  54. NSMutableString *systemVersion = [[NSMutableString alloc] init];
  55. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  56. [systemVersion appendString:@"iOS "];
  57. [systemVersion appendString:[[UIDevice currentDevice] systemVersion]];
  58. #else
  59. [systemVersion appendString:@"OS X"];
  60. #endif
  61. self.bufferCount = 64;
  62. self.bufferSize = 8192;
  63. self.maxPacketDescs = 512;
  64. self.httpConnectionBufferSize = 8192;
  65. self.outputSampleRate = 44100;
  66. self.outputNumChannels = 2;
  67. self.bounceInterval = 10;
  68. self.maxBounceCount = 4; // Max number of bufferings in bounceInterval seconds
  69. self.startupWatchdogPeriod = 30; // If the stream doesn't start to play in this seconds, the watchdog will fail it
  70. #ifdef __LP64__
  71. /* Increase the max in-memory cache to 10 MB with newer 64 bit devices */
  72. self.maxPrebufferedByteCount = 10000000; // 10 MB
  73. #else
  74. self.maxPrebufferedByteCount = 1000000; // 1 MB
  75. #endif
  76. self.userAgent = [NSString stringWithFormat:@"FreeStreamer/%@ (%@)", freeStreamerReleaseVersion(), systemVersion];
  77. self.cacheEnabled = YES;
  78. self.seekingFromCacheEnabled = YES;
  79. self.automaticAudioSessionHandlingEnabled = YES;
  80. self.enableTimeAndPitchConversion = NO;
  81. self.requireStrictContentTypeChecking = YES;
  82. self.maxDiskCacheSize = 256000000; // 256 MB
  83. self.usePrebufferSizeCalculationInSeconds = YES;
  84. self.usePrebufferSizeCalculationInPackets = NO;
  85. self.requiredInitialPrebufferedPacketCount = 32;
  86. self.requiredPrebufferSizeInSeconds = 7;
  87. // With dynamic calculation, these are actually the maximum sizes, the dynamic
  88. // calculation may lower the sizes based on the stream bitrate
  89. self.requiredInitialPrebufferedByteCountForContinuousStream = 256000;
  90. self.requiredInitialPrebufferedByteCountForNonContinuousStream = 256000;
  91. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  92. if ([paths count] > 0) {
  93. self.cacheDirectory = [paths objectAtIndex:0];
  94. }
  95. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  96. AVAudioSession *session = [AVAudioSession sharedInstance];
  97. double sampleRate = session.sampleRate;
  98. if (sampleRate > 0) {
  99. self.outputSampleRate = sampleRate;
  100. }
  101. NSInteger channels = session.outputNumberOfChannels;
  102. if (channels > 0) {
  103. self.outputNumChannels = channels;
  104. }
  105. #endif
  106. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  107. /* iOS */
  108. #else
  109. /* OS X */
  110. self.requiredPrebufferSizeInSeconds = 3;
  111. // No need to be so concervative with the cache sizes
  112. self.maxPrebufferedByteCount = 16000000; // 16 MB
  113. #endif
  114. }
  115. return self;
  116. }
  117. @end
  118. static NSDateFormatter *statisticsDateFormatter = nil;
  119. @implementation FSStreamStatistics
  120. - (NSString *)snapshotTimeFormatted
  121. {
  122. if (!statisticsDateFormatter) {
  123. statisticsDateFormatter = [[NSDateFormatter alloc] init];
  124. [statisticsDateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  125. }
  126. return [statisticsDateFormatter stringFromDate:self.snapshotTime];
  127. }
  128. - (NSString *)description
  129. {
  130. return [[NSString alloc] initWithFormat:@"%@\t%lu\t%lu\t%lu",
  131. self.snapshotTimeFormatted,
  132. (unsigned long)self.audioStreamPacketCount,
  133. (unsigned long)self.audioQueueUsedBufferCount,
  134. (unsigned long)self.audioQueuePCMPacketQueueCount];
  135. }
  136. @end
  137. NSString *freeStreamerReleaseVersion()
  138. {
  139. NSString *version = [NSString stringWithFormat:@"%i.%i.%i",
  140. FREESTREAMER_VERSION_MAJOR,
  141. FREESTREAMER_VERSION_MINOR,
  142. FREESTREAMER_VERSION_REVISION];
  143. return version;
  144. }
  145. NSString* const FSAudioStreamStateChangeNotification = @"FSAudioStreamStateChangeNotification";
  146. NSString* const FSAudioStreamNotificationKey_Stream = @"stream";
  147. NSString* const FSAudioStreamNotificationKey_State = @"state";
  148. NSString* const FSAudioStreamErrorNotification = @"FSAudioStreamErrorNotification";
  149. NSString* const FSAudioStreamNotificationKey_Error = @"error";
  150. NSString* const FSAudioStreamNotificationKey_ErrorDescription = @"errorDescription";
  151. NSString* const FSAudioStreamMetaDataNotification = @"FSAudioStreamMetaDataNotification";
  152. NSString* const FSAudioStreamNotificationKey_MetaData = @"metadata";
  153. class AudioStreamStateObserver : public astreamer::Audio_Stream_Delegate
  154. {
  155. public:
  156. astreamer::Audio_Stream *source;
  157. FSAudioStreamPrivate *priv;
  158. void audioStreamErrorOccurred(int errorCode, CFStringRef errorDescription);
  159. void audioStreamStateChanged(astreamer::Audio_Stream::State state);
  160. void audioStreamMetaDataAvailable(std::map<CFStringRef,CFStringRef> metaData);
  161. void samplesAvailable(AudioBufferList *samples, UInt32 frames, AudioStreamPacketDescription description);
  162. void bitrateAvailable();
  163. };
  164. /*
  165. * ===============================================================
  166. * FSAudioStream private implementation
  167. * ===============================================================
  168. */
  169. @interface FSAudioStreamPrivate : NSObject {
  170. astreamer::Audio_Stream *_audioStream;
  171. NSURL *_url;
  172. AudioStreamStateObserver *_observer;
  173. NSString *_defaultContentType;
  174. Reachability *_reachability;
  175. FSSeekByteOffset _lastSeekByteOffset;
  176. BOOL _wasPaused;
  177. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  178. UIBackgroundTaskIdentifier _backgroundTask;
  179. #endif
  180. }
  181. @property (nonatomic,assign) NSURL *url;
  182. @property (nonatomic,assign) BOOL strictContentTypeChecking;
  183. @property (nonatomic,assign) NSString *defaultContentType;
  184. @property (readonly) NSString *contentType;
  185. @property (readonly) NSString *suggestedFileExtension;
  186. @property (nonatomic, assign) UInt64 defaultContentLength;
  187. @property (readonly) UInt64 contentLength;
  188. @property (nonatomic,assign) NSURL *outputFile;
  189. @property (nonatomic,assign) BOOL wasInterrupted;
  190. @property (nonatomic,assign) BOOL wasDisconnected;
  191. @property (nonatomic,assign) BOOL wasContinuousStream;
  192. @property (nonatomic,assign) BOOL internetConnectionAvailable;
  193. @property (nonatomic,assign) NSUInteger maxRetryCount;
  194. @property (nonatomic,assign) NSUInteger retryCount;
  195. @property (readonly) FSStreamStatistics *statistics;
  196. @property (readonly) FSLevelMeterState levels;
  197. @property (readonly) size_t prebufferedByteCount;
  198. @property (readonly) FSSeekByteOffset currentSeekByteOffset;
  199. @property (readonly) float bitRate;
  200. @property (readonly) FSStreamConfiguration *configuration;
  201. @property (readonly) NSString *formatDescription;
  202. @property (readonly) BOOL cached;
  203. @property (copy) void (^onCompletion)();
  204. @property (copy) void (^onStateChange)(FSAudioStreamState state);
  205. @property (copy) void (^onMetaDataAvailable)(NSDictionary *metaData);
  206. @property (copy) void (^onFailure)(FSAudioStreamError error, NSString *errorDescription);
  207. @property (nonatomic,unsafe_unretained) id<FSPCMAudioStreamDelegate> delegate;
  208. @property (nonatomic,unsafe_unretained) FSAudioStream *stream;
  209. - (AudioStreamStateObserver *)streamStateObserver;
  210. - (void)endBackgroundTask;
  211. - (void)reachabilityChanged:(NSNotification *)note;
  212. - (void)interruptionOccurred:(NSNotification *)notification;
  213. - (void)notifyPlaybackStopped;
  214. - (void)notifyPlaybackBuffering;
  215. - (void)notifyPlaybackPlaying;
  216. - (void)notifyPlaybackPaused;
  217. - (void)notifyPlaybackSeeking;
  218. - (void)notifyPlaybackEndOfFile;
  219. - (void)notifyPlaybackFailed;
  220. - (void)notifyPlaybackCompletion;
  221. - (void)notifyPlaybackUnknownState;
  222. - (void)notifyRetryingStarted;
  223. - (void)notifyRetryingSucceeded;
  224. - (void)notifyRetryingFailed;
  225. - (void)notifyStateChange:(FSAudioStreamState)streamerState;
  226. - (void)attemptRestart;
  227. - (void)expungeCache;
  228. - (void)play;
  229. - (void)playFromURL:(NSURL*)url;
  230. - (void)playFromOffset:(FSSeekByteOffset)offset;
  231. - (void)stop;
  232. - (BOOL)isPlaying;
  233. - (void)pause;
  234. - (void)rewind:(unsigned)seconds;
  235. - (void)seekToOffset:(float)offset;
  236. - (float)currentVolume;
  237. - (unsigned long long)totalCachedObjectsSize;
  238. - (void)setVolume:(float)volume;
  239. - (void)setPlayRate:(float)playRate;
  240. - (astreamer::AS_Playback_Position)playbackPosition;
  241. - (UInt64)audioDataByteCount;
  242. - (float)durationInSeconds;
  243. - (void)bitrateAvailable;
  244. @end
  245. @implementation FSAudioStreamPrivate
  246. -(id)init
  247. {
  248. NSAssert([NSThread isMainThread], @"FSAudioStreamPrivate.init needs to be called in the main thread");
  249. if (self = [super init]) {
  250. _url = nil;
  251. _observer = new AudioStreamStateObserver();
  252. _observer->priv = self;
  253. _audioStream = new astreamer::Audio_Stream();
  254. _observer->source = _audioStream;
  255. _audioStream->m_delegate = _observer;
  256. _reachability = nil;
  257. _delegate = nil;
  258. _maxRetryCount = 3;
  259. [[NSNotificationCenter defaultCenter] addObserver:self
  260. selector:@selector(reachabilityChanged:)
  261. name:kReachabilityChangedNotification
  262. object:nil];
  263. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  264. _backgroundTask = UIBackgroundTaskInvalid;
  265. @synchronized (self) {
  266. if (!fsAudioStreamPrivateActiveSessions) {
  267. fsAudioStreamPrivateActiveSessions = [[NSMutableDictionary alloc] init];
  268. }
  269. }
  270. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  271. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
  272. }
  273. #endif
  274. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  275. [[NSNotificationCenter defaultCenter] addObserver:self
  276. selector:@selector(interruptionOccurred:)
  277. name:AVAudioSessionInterruptionNotification
  278. object:nil];
  279. #endif
  280. }
  281. return self;
  282. }
  283. - (void)dealloc
  284. {
  285. NSAssert([NSThread isMainThread], @"FSAudioStreamPrivate.dealloc needs to be called in the main thread");
  286. [[NSNotificationCenter defaultCenter] removeObserver:self];
  287. [self stop];
  288. _delegate = nil;
  289. delete _audioStream;
  290. _audioStream = nil;
  291. delete _observer;
  292. _observer = nil;
  293. // Clean up the disk cache.
  294. if (!self.configuration.cacheEnabled) {
  295. // Don't clean up if cache not enabled
  296. return;
  297. }
  298. unsigned long long totalCacheSize = 0;
  299. NSMutableArray *cachedFiles = [[NSMutableArray alloc] init];
  300. for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.configuration.cacheDirectory error:nil]) {
  301. if ([file hasPrefix:@"FSCache-"]) {
  302. FSCacheObject *cacheObj = [[FSCacheObject alloc] init];
  303. cacheObj.name = file;
  304. cacheObj.path = [NSString stringWithFormat:@"%@/%@", self.configuration.cacheDirectory, cacheObj.name];
  305. cacheObj.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:cacheObj.path error:nil];
  306. totalCacheSize += [cacheObj fileSize];
  307. if (![cacheObj.name hasSuffix:@".metadata"]) {
  308. [cachedFiles addObject:cacheObj];
  309. }
  310. }
  311. }
  312. // Sort by the modification date.
  313. // In this way the older content will be removed first from the cache.
  314. [cachedFiles sortUsingFunction:sortCacheObjects context:NULL];
  315. for (FSCacheObject *cacheObj in cachedFiles) {
  316. if (totalCacheSize < self.configuration.maxDiskCacheSize) {
  317. break;
  318. }
  319. FSCacheObject *cachedMetaData = [[FSCacheObject alloc] init];
  320. cachedMetaData.name = [NSString stringWithFormat:@"%@.metadata", cacheObj.name];
  321. cachedMetaData.path = [NSString stringWithFormat:@"%@/%@", self.configuration.cacheDirectory, cachedMetaData.name];
  322. cachedMetaData.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:cachedMetaData.path error:nil];
  323. if (![[NSFileManager defaultManager] removeItemAtPath:cachedMetaData.path error:nil]) {
  324. continue;
  325. }
  326. totalCacheSize -= [cachedMetaData fileSize];
  327. if (![[NSFileManager defaultManager] removeItemAtPath:cacheObj.path error:nil]) {
  328. continue;
  329. }
  330. totalCacheSize -= [cacheObj fileSize];
  331. }
  332. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  333. @synchronized (self) {
  334. [fsAudioStreamPrivateActiveSessions removeObjectForKey:[NSNumber numberWithUnsignedLong:(unsigned long)self]];
  335. if ([fsAudioStreamPrivateActiveSessions count] == 0) {
  336. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  337. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  338. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  339. #else
  340. [[AVAudioSession sharedInstance] setActive:NO error:nil];
  341. #endif
  342. }
  343. }
  344. }
  345. #endif
  346. }
  347. - (void)endBackgroundTask
  348. {
  349. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  350. if (_backgroundTask != UIBackgroundTaskInvalid) {
  351. [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
  352. _backgroundTask = UIBackgroundTaskInvalid;
  353. }
  354. #endif
  355. }
  356. - (AudioStreamStateObserver *)streamStateObserver
  357. {
  358. return _observer;
  359. }
  360. - (void)setUrl:(NSURL *)url
  361. {
  362. if ([self isPlaying]) {
  363. [self stop];
  364. }
  365. @synchronized (self) {
  366. if ([url isEqual:_url]) {
  367. return;
  368. }
  369. _url = [url copy];
  370. _audioStream->setUrl((__bridge CFURLRef)_url);
  371. }
  372. if ([self isPlaying]) {
  373. [self play];
  374. }
  375. }
  376. - (NSURL*)url
  377. {
  378. if (!_url) {
  379. return nil;
  380. }
  381. NSURL *copyOfURL = [_url copy];
  382. return copyOfURL;
  383. }
  384. - (void)setStrictContentTypeChecking:(BOOL)strictContentTypeChecking
  385. {
  386. _audioStream->setStrictContentTypeChecking(strictContentTypeChecking);
  387. }
  388. - (BOOL)strictContentTypeChecking
  389. {
  390. return _audioStream->strictContentTypeChecking();
  391. }
  392. - (void)playFromURL:(NSURL*)url
  393. {
  394. [self setUrl:url];
  395. [self play];
  396. }
  397. - (void)playFromOffset:(FSSeekByteOffset)offset
  398. {
  399. _wasPaused = NO;
  400. if (_audioStream->isPreloading()) {
  401. _audioStream->seekToOffset(offset.position);
  402. _audioStream->setPreloading(false);
  403. } else {
  404. astreamer::Input_Stream_Position position;
  405. position.start = offset.start;
  406. position.end = offset.end;
  407. _audioStream->open(&position);
  408. _audioStream->setSeekOffset(offset.position);
  409. _audioStream->setContentLength(offset.end);
  410. }
  411. if (!_reachability) {
  412. _reachability = [Reachability reachabilityForInternetConnection];
  413. [_reachability startNotifier];
  414. }
  415. }
  416. - (void)setDefaultContentType:(NSString *)defaultContentType
  417. {
  418. if (defaultContentType) {
  419. _defaultContentType = [defaultContentType copy];
  420. _audioStream->setDefaultContentType((__bridge CFStringRef)_defaultContentType);
  421. } else {
  422. _audioStream->setDefaultContentType(NULL);
  423. }
  424. }
  425. - (NSString*)defaultContentType
  426. {
  427. if (!_defaultContentType) {
  428. return nil;
  429. }
  430. NSString *copyOfDefaultContentType = [_defaultContentType copy];
  431. return copyOfDefaultContentType;
  432. }
  433. - (NSString*)contentType
  434. {
  435. CFStringRef c = _audioStream->contentType();
  436. if (c) {
  437. return CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, c));
  438. }
  439. return nil;
  440. }
  441. - (NSString*)suggestedFileExtension
  442. {
  443. NSString *contentType = [self contentType];
  444. NSString *suggestedFileExtension = nil;
  445. if ([contentType isEqualToString:@"audio/mpeg"]) {
  446. suggestedFileExtension = @"mp3";
  447. } else if ([contentType isEqualToString:@"audio/x-wav"]) {
  448. suggestedFileExtension = @"wav";
  449. } else if ([contentType isEqualToString:@"audio/x-aifc"]) {
  450. suggestedFileExtension = @"aifc";
  451. } else if ([contentType isEqualToString:@"audio/x-aiff"]) {
  452. suggestedFileExtension = @"aiff";
  453. } else if ([contentType isEqualToString:@"audio/x-m4a"]) {
  454. suggestedFileExtension = @"m4a";
  455. } else if ([contentType isEqualToString:@"audio/mp4"]) {
  456. suggestedFileExtension = @"mp4";
  457. } else if ([contentType isEqualToString:@"audio/x-caf"]) {
  458. suggestedFileExtension = @"caf";
  459. }
  460. else if ([contentType isEqualToString:@"audio/aac"] ||
  461. [contentType isEqualToString:@"audio/aacp"]) {
  462. suggestedFileExtension = @"aac";
  463. }
  464. return suggestedFileExtension;
  465. }
  466. - (UInt64)defaultContentLength
  467. {
  468. return _audioStream->defaultContentLength();
  469. }
  470. - (UInt64)contentLength
  471. {
  472. return _audioStream->contentLength();
  473. }
  474. - (NSURL*)outputFile
  475. {
  476. CFURLRef url = _audioStream->outputFile();
  477. if (url) {
  478. NSURL *u = (__bridge NSURL*)url;
  479. return [u copy];
  480. }
  481. return nil;
  482. }
  483. - (void)setOutputFile:(NSURL *)outputFile
  484. {
  485. if (!outputFile) {
  486. _audioStream->setOutputFile(NULL);
  487. return;
  488. }
  489. NSURL *copyOfURL = [outputFile copy];
  490. _audioStream->setOutputFile((__bridge CFURLRef)copyOfURL);
  491. }
  492. - (FSStreamStatistics *)statistics
  493. {
  494. FSStreamStatistics *stats = [[FSStreamStatistics alloc] init];
  495. stats.snapshotTime = [[NSDate alloc] init];
  496. stats.audioStreamPacketCount = _audioStream->playbackDataCount();
  497. return stats;
  498. }
  499. - (FSLevelMeterState)levels
  500. {
  501. AudioQueueLevelMeterState aqLevels = _audioStream->levels();
  502. FSLevelMeterState l;
  503. l.averagePower = aqLevels.mAveragePower;
  504. l.peakPower = aqLevels.mPeakPower;
  505. return l;
  506. }
  507. - (size_t)prebufferedByteCount
  508. {
  509. return _audioStream->cachedDataSize();
  510. }
  511. - (FSSeekByteOffset)currentSeekByteOffset
  512. {
  513. FSSeekByteOffset offset;
  514. offset.start = 0;
  515. offset.end = 0;
  516. offset.position = 0;
  517. // If continuous
  518. if (!([self durationInSeconds] > 0)) {
  519. return offset;
  520. }
  521. offset.position = _audioStream->playbackPosition().offset;
  522. astreamer::Input_Stream_Position httpStreamPos = _audioStream->streamPositionForOffset(offset.position);
  523. offset.start = httpStreamPos.start;
  524. offset.end = httpStreamPos.end;
  525. return offset;
  526. }
  527. - (float)bitRate
  528. {
  529. return _audioStream->bitrate();
  530. }
  531. - (FSStreamConfiguration *)configuration
  532. {
  533. FSStreamConfiguration *config = [[FSStreamConfiguration alloc] init];
  534. astreamer::Stream_Configuration *c = astreamer::Stream_Configuration::configuration();
  535. config.bufferCount = c->bufferCount;
  536. config.bufferSize = c->bufferSize;
  537. config.maxPacketDescs = c->maxPacketDescs;
  538. config.httpConnectionBufferSize = c->httpConnectionBufferSize;
  539. config.outputSampleRate = c->outputSampleRate;
  540. config.outputNumChannels = c->outputNumChannels;
  541. config.bounceInterval = c->bounceInterval;
  542. config.maxBounceCount = c->maxBounceCount;
  543. config.startupWatchdogPeriod = c->startupWatchdogPeriod;
  544. config.maxPrebufferedByteCount = c->maxPrebufferedByteCount;
  545. config.usePrebufferSizeCalculationInSeconds = c->usePrebufferSizeCalculationInSeconds;
  546. config.usePrebufferSizeCalculationInPackets = c->usePrebufferSizeCalculationInPackets;
  547. config.requiredInitialPrebufferedByteCountForContinuousStream = c->requiredInitialPrebufferedByteCountForContinuousStream;
  548. config.requiredInitialPrebufferedByteCountForNonContinuousStream = c->requiredInitialPrebufferedByteCountForNonContinuousStream;
  549. config.requiredPrebufferSizeInSeconds = c->requiredPrebufferSizeInSeconds;
  550. config.requiredInitialPrebufferedPacketCount = c->requiredInitialPrebufferedPacketCount;
  551. config.cacheEnabled = c->cacheEnabled;
  552. config.seekingFromCacheEnabled = c->seekingFromCacheEnabled;
  553. config.automaticAudioSessionHandlingEnabled = c->automaticAudioSessionHandlingEnabled;
  554. config.enableTimeAndPitchConversion = c->enableTimeAndPitchConversion;
  555. config.requireStrictContentTypeChecking = c->requireStrictContentTypeChecking;
  556. config.maxDiskCacheSize = c->maxDiskCacheSize;
  557. if (c->userAgent) {
  558. // Let the Objective-C side handle the memory for the copy of the original user-agent
  559. config.userAgent = (__bridge_transfer NSString *)CFStringCreateCopy(kCFAllocatorDefault, c->userAgent);
  560. }
  561. if (c->cacheDirectory) {
  562. config.cacheDirectory = (__bridge_transfer NSString *)CFStringCreateCopy(kCFAllocatorDefault, c->cacheDirectory);
  563. }
  564. if (c->predefinedHttpHeaderValues) {
  565. config.predefinedHttpHeaderValues = (__bridge_transfer NSDictionary *)CFDictionaryCreateCopy(kCFAllocatorDefault, c->predefinedHttpHeaderValues);
  566. }
  567. return config;
  568. }
  569. - (NSString *)formatDescription
  570. {
  571. return CFBridgingRelease(_audioStream->sourceFormatDescription());
  572. }
  573. - (BOOL)cached
  574. {
  575. BOOL cachedFileExists = NO;
  576. if (self.url) {
  577. NSString *cacheIdentifier = (NSString*)CFBridgingRelease(_audioStream->createCacheIdentifierForURL((__bridge CFURLRef)self.url));
  578. NSString *fullPath = [NSString stringWithFormat:@"%@/%@.metadata", self.configuration.cacheDirectory, cacheIdentifier];
  579. cachedFileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
  580. }
  581. return cachedFileExists;
  582. }
  583. - (void)reachabilityChanged:(NSNotification *)note
  584. {
  585. NSAssert([NSThread isMainThread], @"FSAudioStreamPrivate.reachabilityChanged needs to be called in the main thread");
  586. Reachability *reach = [note object];
  587. NetworkStatus netStatus = [reach currentReachabilityStatus];
  588. self.internetConnectionAvailable = (netStatus == ReachableViaWiFi || netStatus == ReachableViaWWAN);
  589. if ([self isPlaying] && !self.internetConnectionAvailable) {
  590. self.wasDisconnected = YES;
  591. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  592. NSLog(@"FSAudioStream: Error: Internet connection disconnected while playing a stream.");
  593. #endif
  594. }
  595. if (self.wasDisconnected && self.internetConnectionAvailable) {
  596. self.wasDisconnected = NO;
  597. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  598. NSLog(@"FSAudioStream: Internet connection available again.");
  599. #endif
  600. [self attemptRestart];
  601. }
  602. }
  603. - (void)interruptionOccurred:(NSNotification *)notification
  604. {
  605. NSAssert([NSThread isMainThread], @"FSAudioStreamPrivate.interruptionOccurred needs to be called in the main thread");
  606. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  607. NSNumber *interruptionType = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey];
  608. NSNumber *interruptionResume = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey];
  609. if ([interruptionType intValue] == AVAudioSessionInterruptionTypeBegan) {
  610. if ([self isPlaying] && !_wasPaused) {
  611. self.wasInterrupted = YES;
  612. // Continuous streams do not have a duration.
  613. self.wasContinuousStream = !([self durationInSeconds] > 0);
  614. if (self.wasContinuousStream) {
  615. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  616. NSLog(@"FSAudioStream: Interruption began. Continuous stream. Stopping the stream.");
  617. #endif
  618. [self stop];
  619. } else {
  620. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  621. NSLog(@"FSAudioStream: Interruption began. Non-continuous stream. Stopping the stream and saving the offset.");
  622. #endif
  623. _lastSeekByteOffset = [self currentSeekByteOffset];
  624. [self stop];
  625. }
  626. }
  627. } else if ([interruptionType intValue] == AVAudioSessionInterruptionTypeEnded) {
  628. if (self.wasInterrupted) {
  629. self.wasInterrupted = NO;
  630. if ([interruptionResume intValue] == AVAudioSessionInterruptionOptionShouldResume) {
  631. @synchronized (self) {
  632. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  633. [[AVAudioSession sharedInstance] setActive:YES error:nil];
  634. }
  635. fsAudioStreamPrivateActiveSessions[[NSNumber numberWithUnsignedLong:(unsigned long)self]] = @"";
  636. }
  637. if (self.wasContinuousStream) {
  638. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  639. NSLog(@"FSAudioStream: Interruption ended. Continuous stream. Starting the playback.");
  640. #endif
  641. /*
  642. * Resume playing.
  643. */
  644. [self play];
  645. } else {
  646. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  647. NSLog(@"FSAudioStream: Interruption ended. Continuous stream. Playing from the offset");
  648. #endif
  649. /*
  650. * Resume playing.
  651. */
  652. [self playFromOffset:_lastSeekByteOffset];
  653. }
  654. } else {
  655. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  656. NSLog(@"FSAudioStream: Interruption ended. Continuous stream. Not resuming.");
  657. #endif
  658. }
  659. }
  660. }
  661. #endif
  662. }
  663. - (void)notifyPlaybackStopped
  664. {
  665. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  666. @synchronized (self) {
  667. [fsAudioStreamPrivateActiveSessions removeObjectForKey:[NSNumber numberWithUnsignedLong:(unsigned long)self]];
  668. if ([fsAudioStreamPrivateActiveSessions count] == 0) {
  669. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  670. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  671. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  672. #else
  673. [[AVAudioSession sharedInstance] setActive:NO error:nil];
  674. #endif
  675. }
  676. }
  677. }
  678. #endif
  679. [self notifyStateChange:kFsAudioStreamStopped];
  680. }
  681. - (void)notifyPlaybackBuffering
  682. {
  683. self.internetConnectionAvailable = YES;
  684. [self notifyStateChange:kFsAudioStreamBuffering];
  685. }
  686. - (void)notifyPlaybackPlaying
  687. {
  688. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  689. @synchronized (self) {
  690. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  691. [[AVAudioSession sharedInstance] setActive:YES error:nil];
  692. }
  693. fsAudioStreamPrivateActiveSessions[[NSNumber numberWithUnsignedLong:(unsigned long)self]] = @"";
  694. }
  695. #endif
  696. if (self.retryCount > 0) {
  697. [NSTimer scheduledTimerWithTimeInterval:0.1
  698. target:self
  699. selector:@selector(notifyRetryingSucceeded)
  700. userInfo:nil
  701. repeats:NO];
  702. }
  703. self.retryCount = 0;
  704. [self notifyStateChange:kFsAudioStreamPlaying];
  705. }
  706. - (void)notifyPlaybackPaused
  707. {
  708. [self notifyStateChange:kFsAudioStreamPaused];
  709. }
  710. - (void)notifyPlaybackSeeking
  711. {
  712. [self notifyStateChange:kFsAudioStreamSeeking];
  713. }
  714. - (void)notifyPlaybackEndOfFile
  715. {
  716. [self notifyStateChange:kFSAudioStreamEndOfFile];
  717. }
  718. - (void)notifyPlaybackFailed
  719. {
  720. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  721. @synchronized (self) {
  722. [fsAudioStreamPrivateActiveSessions removeObjectForKey:[NSNumber numberWithUnsignedLong:(unsigned long)self]];
  723. if ([fsAudioStreamPrivateActiveSessions count] == 0) {
  724. if (self.configuration.automaticAudioSessionHandlingEnabled) {
  725. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)
  726. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  727. #else
  728. [[AVAudioSession sharedInstance] setActive:NO error:nil];
  729. #endif
  730. }
  731. }
  732. }
  733. #endif
  734. [self notifyStateChange:kFsAudioStreamFailed];
  735. }
  736. - (void)notifyPlaybackCompletion
  737. {
  738. [self notifyStateChange:kFsAudioStreamPlaybackCompleted];
  739. if (self.onCompletion) {
  740. self.onCompletion();
  741. }
  742. }
  743. - (void)notifyPlaybackUnknownState
  744. {
  745. [self notifyStateChange:kFsAudioStreamUnknownState];
  746. }
  747. - (void)notifyRetryingStarted
  748. {
  749. [self notifyStateChange:kFsAudioStreamRetryingStarted];
  750. }
  751. - (void)notifyRetryingSucceeded
  752. {
  753. [self notifyStateChange:kFsAudioStreamRetryingSucceeded];
  754. }
  755. - (void)notifyRetryingFailed
  756. {
  757. [self notifyStateChange:kFsAudioStreamRetryingFailed];
  758. }
  759. - (void)notifyStateChange:(FSAudioStreamState)streamerState
  760. {
  761. if (self.onStateChange) {
  762. self.onStateChange(streamerState);
  763. }
  764. NSDictionary *userInfo = @{FSAudioStreamNotificationKey_State: [NSNumber numberWithInt:streamerState],
  765. FSAudioStreamNotificationKey_Stream: [NSValue valueWithPointer:_audioStream]};
  766. NSNotification *notification = [NSNotification notificationWithName:FSAudioStreamStateChangeNotification object:self.stream userInfo:userInfo];
  767. [[NSNotificationCenter defaultCenter] postNotification:notification];
  768. }
  769. - (void)preload
  770. {
  771. _audioStream->setPreloading(true);
  772. _audioStream->open();
  773. }
  774. - (void)attemptRestart
  775. {
  776. if (_audioStream->isPreloading()) {
  777. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  778. NSLog(@"FSAudioStream: Stream is preloading. Not attempting a restart");
  779. #endif
  780. return;
  781. }
  782. if (_wasPaused) {
  783. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  784. NSLog(@"FSAudioStream: Stream was paused. Not attempting a restart");
  785. #endif
  786. return;
  787. }
  788. if (!self.internetConnectionAvailable) {
  789. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  790. NSLog(@"FSAudioStream: Internet connection not available. Not attempting a restart");
  791. #endif
  792. return;
  793. }
  794. if (self.retryCount >= self.maxRetryCount) {
  795. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  796. NSLog(@"FSAudioStream: Retry count %lu. Giving up.", (unsigned long)self.retryCount);
  797. #endif
  798. [NSTimer scheduledTimerWithTimeInterval:0.1
  799. target:self
  800. selector:@selector(notifyRetryingFailed)
  801. userInfo:nil
  802. repeats:NO];
  803. return;
  804. }
  805. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  806. NSLog(@"FSAudioStream: Attempting restart.");
  807. #endif
  808. [NSTimer scheduledTimerWithTimeInterval:0.1
  809. target:self
  810. selector:@selector(notifyRetryingStarted)
  811. userInfo:nil
  812. repeats:NO];
  813. [NSTimer scheduledTimerWithTimeInterval:1
  814. target:self
  815. selector:@selector(play)
  816. userInfo:nil
  817. repeats:NO];
  818. self.retryCount++;
  819. }
  820. - (void)expungeCache
  821. {
  822. for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.configuration.cacheDirectory error:nil]) {
  823. NSString *fullPath = [NSString stringWithFormat:@"%@/%@", self.configuration.cacheDirectory, file];
  824. if ([file hasPrefix:@"FSCache-"]) {
  825. if (![[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil]) {
  826. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  827. NSLog(@"Failed expunging %@ from the cache", fullPath);
  828. #endif
  829. }
  830. }
  831. }
  832. }
  833. - (void)play
  834. {
  835. _wasPaused = NO;
  836. if (_audioStream->isPreloading()) {
  837. _audioStream->startCachedDataPlayback();
  838. return;
  839. }
  840. #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
  841. [self endBackgroundTask];
  842. _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
  843. [self endBackgroundTask];
  844. }];
  845. #endif
  846. _audioStream->open();
  847. if (!_reachability) {
  848. _reachability = [Reachability reachabilityForInternetConnection];
  849. [_reachability startNotifier];
  850. }
  851. }
  852. - (void)stop
  853. {
  854. _audioStream->close(true);
  855. [self endBackgroundTask];
  856. [_reachability stopNotifier];
  857. _reachability = nil;
  858. }
  859. - (BOOL)isPlaying
  860. {
  861. const astreamer::Audio_Stream::State currentState = _audioStream->state();
  862. return (currentState == astreamer::Audio_Stream::PLAYING ||
  863. currentState == astreamer::Audio_Stream::END_OF_FILE);
  864. }
  865. - (void)pause
  866. {
  867. _wasPaused = YES;
  868. _audioStream->pause();
  869. }
  870. - (void)rewind:(unsigned int)seconds
  871. {
  872. if (([self durationInSeconds] > 0)) {
  873. // Rewinding only possible for continuous streams
  874. return;
  875. }
  876. const float originalVolume = [self currentVolume];
  877. // Set volume to 0 to avoid glitches
  878. _audioStream->setVolume(0);
  879. _audioStream->rewind(seconds);
  880. __weak FSAudioStreamPrivate *weakSelf = self;
  881. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  882. FSAudioStreamPrivate *strongSelf = weakSelf;
  883. // Return the original volume back
  884. strongSelf->_audioStream->setVolume(originalVolume);
  885. });
  886. }
  887. - (void)seekToOffset:(float)offset
  888. {
  889. _audioStream->seekToOffset(offset);
  890. }
  891. - (float)currentVolume
  892. {
  893. return _audioStream->currentVolume();
  894. }
  895. - (unsigned long long)totalCachedObjectsSize
  896. {
  897. unsigned long long totalCacheSize = 0;
  898. for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.configuration.cacheDirectory error:nil]) {
  899. if ([file hasPrefix:@"FSCache-"]) {
  900. NSString *fullPath = [NSString stringWithFormat:@"%@/%@", self.configuration.cacheDirectory, file];
  901. NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil];
  902. totalCacheSize += [[attributes objectForKey:NSFileSize] longLongValue];
  903. }
  904. }
  905. return totalCacheSize;
  906. }
  907. - (void)setVolume:(float)volume
  908. {
  909. _audioStream->setVolume(volume);
  910. }
  911. - (void)setPlayRate:(float)playRate
  912. {
  913. _audioStream->setPlayRate(playRate);
  914. }
  915. - (astreamer::AS_Playback_Position)playbackPosition
  916. {
  917. return _audioStream->playbackPosition();
  918. }
  919. - (UInt64)audioDataByteCount
  920. {
  921. return _audioStream->audioDataByteCount();
  922. }
  923. - (float)durationInSeconds
  924. {
  925. return _audioStream->durationInSeconds();
  926. }
  927. - (void)bitrateAvailable
  928. {
  929. if (!self.configuration.usePrebufferSizeCalculationInSeconds) {
  930. return;
  931. }
  932. float bitrate = (int)_audioStream->bitrate();
  933. if (!(bitrate > 0)) {
  934. // No bitrate provided, use the defaults
  935. return;
  936. }
  937. const Float64 bufferSizeForSecond = bitrate / 8.0;
  938. int bufferSize = (bufferSizeForSecond * self.configuration.requiredPrebufferSizeInSeconds);
  939. // Check that we still got somewhat sane buffer size
  940. if (bufferSize < 50000) {
  941. bufferSize = 50000;
  942. }
  943. if (!([self durationInSeconds] > 0)) {
  944. // continuous
  945. if (bufferSize > self.configuration.requiredInitialPrebufferedByteCountForContinuousStream) {
  946. bufferSize = self.configuration.requiredInitialPrebufferedByteCountForContinuousStream;
  947. }
  948. } else {
  949. if (bufferSize > self.configuration.requiredInitialPrebufferedByteCountForNonContinuousStream) {
  950. bufferSize = self.configuration.requiredInitialPrebufferedByteCountForNonContinuousStream;
  951. }
  952. }
  953. // Update the configuration
  954. self.configuration.requiredInitialPrebufferedByteCountForContinuousStream = bufferSize;
  955. self.configuration.requiredInitialPrebufferedByteCountForNonContinuousStream = bufferSize;
  956. astreamer::Stream_Configuration *c = astreamer::Stream_Configuration::configuration();
  957. c->requiredInitialPrebufferedByteCountForContinuousStream = bufferSize;
  958. c->requiredInitialPrebufferedByteCountForNonContinuousStream = bufferSize;
  959. }
  960. -(NSString *)description
  961. {
  962. return [NSString stringWithFormat:@"[FreeStreamer %@] URL: %@\nbufferCount: %i\nbufferSize: %i\nmaxPacketDescs: %i\nhttpConnectionBufferSize: %i\noutputSampleRate: %f\noutputNumChannels: %ld\nbounceInterval: %i\nmaxBounceCount: %i\nstartupWatchdogPeriod: %i\nmaxPrebufferedByteCount: %i\nformat: %@\nbit rate: %f\nuserAgent: %@\ncacheDirectory: %@\npredefinedHttpHeaderValues: %@\ncacheEnabled: %@\nseekingFromCacheEnabled: %@\nautomaticAudioSessionHandlingEnabled: %@\nenableTimeAndPitchConversion: %@\nrequireStrictContentTypeChecking: %@\nmaxDiskCacheSize: %i\nusePrebufferSizeCalculationInSeconds: %@\nusePrebufferSizeCalculationInPackets: %@\nrequiredPrebufferSizeInSeconds: %f\nrequiredInitialPrebufferedByteCountForContinuousStream: %i\nrequiredInitialPrebufferedByteCountForNonContinuousStream: %i\nrequiredInitialPrebufferedPacketCount: %i",
  963. freeStreamerReleaseVersion(),
  964. self.url,
  965. self.configuration.bufferCount,
  966. self.configuration.bufferSize,
  967. self.configuration.maxPacketDescs,
  968. self.configuration.httpConnectionBufferSize,
  969. self.configuration.outputSampleRate,
  970. self.configuration.outputNumChannels,
  971. self.configuration.bounceInterval,
  972. self.configuration.maxBounceCount,
  973. self.configuration.startupWatchdogPeriod,
  974. self.configuration.maxPrebufferedByteCount,
  975. self.formatDescription,
  976. self.bitRate,
  977. self.configuration.userAgent,
  978. self.configuration.cacheDirectory,
  979. self.configuration.predefinedHttpHeaderValues,
  980. (self.configuration.cacheEnabled ? @"YES" : @"NO"),
  981. (self.configuration.seekingFromCacheEnabled ? @"YES" : @"NO"),
  982. (self.configuration.automaticAudioSessionHandlingEnabled ? @"YES" : @"NO"),
  983. (self.configuration.enableTimeAndPitchConversion ? @"YES" : @"NO"),
  984. (self.configuration.requireStrictContentTypeChecking ? @"YES" : @"NO"),
  985. self.configuration.maxDiskCacheSize,
  986. (self.configuration.usePrebufferSizeCalculationInSeconds ? @"YES" : @"NO"),
  987. (self.configuration.usePrebufferSizeCalculationInPackets ? @"YES" : @"NO"),
  988. self.configuration.requiredPrebufferSizeInSeconds,
  989. self.configuration.requiredInitialPrebufferedByteCountForContinuousStream,
  990. self.configuration.requiredInitialPrebufferedByteCountForNonContinuousStream,
  991. self.configuration.requiredInitialPrebufferedPacketCount];
  992. }
  993. @end
  994. /*
  995. * ===============================================================
  996. * FSAudioStream public implementation, merely wraps the
  997. * private class.
  998. * ===============================================================
  999. */
  1000. @implementation FSAudioStream
  1001. -(id)init
  1002. {
  1003. NSAssert([NSThread isMainThread], @"FSAudioStream.init needs to be called in the main thread");
  1004. FSStreamConfiguration *defaultConfiguration = [[FSStreamConfiguration alloc] init];
  1005. if (self = [self initWithConfiguration:defaultConfiguration]) {
  1006. }
  1007. return self;
  1008. }
  1009. - (id)initWithUrl:(NSURL *)url
  1010. {
  1011. NSAssert([NSThread isMainThread], @"FSAudioStream.initWithURL needs to be called in the main thread");
  1012. if (self = [self init]) {
  1013. _private.url = url;
  1014. }
  1015. return self;
  1016. }
  1017. - (id)initWithConfiguration:(FSStreamConfiguration *)configuration
  1018. {
  1019. NSAssert([NSThread isMainThread], @"FSAudioStream.initWithConfiguration needs to be called in the main thread");
  1020. if (self = [super init]) {
  1021. astreamer::Stream_Configuration *c = astreamer::Stream_Configuration::configuration();
  1022. c->bufferCount = configuration.bufferCount;
  1023. c->bufferSize = configuration.bufferSize;
  1024. c->maxPacketDescs = configuration.maxPacketDescs;
  1025. c->httpConnectionBufferSize = configuration.httpConnectionBufferSize;
  1026. c->outputSampleRate = configuration.outputSampleRate;
  1027. c->outputNumChannels = configuration.outputNumChannels;
  1028. c->maxBounceCount = configuration.maxBounceCount;
  1029. c->bounceInterval = configuration.bounceInterval;
  1030. c->startupWatchdogPeriod = configuration.startupWatchdogPeriod;
  1031. c->maxPrebufferedByteCount = configuration.maxPrebufferedByteCount;
  1032. c->usePrebufferSizeCalculationInSeconds = configuration.usePrebufferSizeCalculationInSeconds;
  1033. c->usePrebufferSizeCalculationInPackets = configuration.usePrebufferSizeCalculationInPackets;
  1034. c->cacheEnabled = configuration.cacheEnabled;
  1035. c->seekingFromCacheEnabled = configuration.seekingFromCacheEnabled;
  1036. c->automaticAudioSessionHandlingEnabled = configuration.automaticAudioSessionHandlingEnabled;
  1037. c->enableTimeAndPitchConversion = configuration.enableTimeAndPitchConversion;
  1038. c->requireStrictContentTypeChecking = configuration.requireStrictContentTypeChecking;
  1039. c->maxDiskCacheSize = configuration.maxDiskCacheSize;
  1040. c->requiredInitialPrebufferedByteCountForContinuousStream = configuration.requiredInitialPrebufferedByteCountForContinuousStream;
  1041. c->requiredInitialPrebufferedByteCountForNonContinuousStream = configuration.requiredInitialPrebufferedByteCountForNonContinuousStream;
  1042. c->requiredPrebufferSizeInSeconds = configuration.requiredPrebufferSizeInSeconds;
  1043. c->requiredInitialPrebufferedPacketCount = configuration.requiredInitialPrebufferedPacketCount;
  1044. if (c->userAgent) {
  1045. CFRelease(c->userAgent);
  1046. }
  1047. c->userAgent = CFStringCreateCopy(kCFAllocatorDefault, (__bridge CFStringRef)configuration.userAgent);
  1048. if (c->cacheDirectory) {
  1049. CFRelease(c->cacheDirectory);
  1050. }
  1051. if (configuration.cacheDirectory) {
  1052. c->cacheDirectory = CFStringCreateCopy(kCFAllocatorDefault, (__bridge CFStringRef)configuration.cacheDirectory);
  1053. } else {
  1054. c->cacheDirectory = NULL;
  1055. }
  1056. if (c->predefinedHttpHeaderValues) {
  1057. CFRelease(c->predefinedHttpHeaderValues);
  1058. }
  1059. if (configuration.predefinedHttpHeaderValues) {
  1060. c->predefinedHttpHeaderValues = CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)configuration.predefinedHttpHeaderValues);
  1061. } else {
  1062. c->predefinedHttpHeaderValues = NULL;
  1063. }
  1064. _private = [[FSAudioStreamPrivate alloc] init];
  1065. _private.stream = self;
  1066. }
  1067. return self;
  1068. }
  1069. - (void)dealloc
  1070. {
  1071. NSAssert([NSThread isMainThread], @"FSAudioStream.dealloc needs to be called in the main thread");
  1072. AudioStreamStateObserver *observer = [_private streamStateObserver];
  1073. // Break the cyclic loop so that dealloc() may be called
  1074. observer->priv = nil;
  1075. _private.stream = nil;
  1076. _private.delegate = nil;
  1077. _private = nil;
  1078. }
  1079. - (void)setUrl:(NSURL *)url
  1080. {
  1081. NSAssert([NSThread isMainThread], @"FSAudioStream.setUrl needs to be called in the main thread");
  1082. [_private setUrl:url];
  1083. }
  1084. - (NSURL*)url
  1085. {
  1086. NSAssert([NSThread isMainThread], @"FSAudioStream.url needs to be called in the main thread");
  1087. return [_private url];
  1088. }
  1089. - (void)setStrictContentTypeChecking:(BOOL)strictContentTypeChecking
  1090. {
  1091. NSAssert([NSThread isMainThread], @"FSAudioStream.setStrictContentTypeChecking needs to be called in the main thread");
  1092. [_private setStrictContentTypeChecking:strictContentTypeChecking];
  1093. }
  1094. - (BOOL)strictContentTypeChecking
  1095. {
  1096. NSAssert([NSThread isMainThread], @"FSAudioStream.strictContentTypeChecking needs to be called in the main thread");
  1097. return [_private strictContentTypeChecking];
  1098. }
  1099. - (NSURL*)outputFile
  1100. {
  1101. NSAssert([NSThread isMainThread], @"FSAudioStream.outputFile needs to be called in the main thread");
  1102. return [_private outputFile];
  1103. }
  1104. - (void)setOutputFile:(NSURL *)outputFile
  1105. {
  1106. NSAssert([NSThread isMainThread], @"FSAudioStream.setOutputFile needs to be called in the main thread");
  1107. [_private setOutputFile:outputFile];
  1108. }
  1109. - (void)setDefaultContentType:(NSString *)defaultContentType
  1110. {
  1111. NSAssert([NSThread isMainThread], @"FSAudioStream.setDefaultContentType needs to be called in the main thread");
  1112. [_private setDefaultContentType:defaultContentType];
  1113. }
  1114. - (NSString*)defaultContentType
  1115. {
  1116. NSAssert([NSThread isMainThread], @"FSAudioStream.defaultContentType needs to be called in the main thread");
  1117. return [_private defaultContentType];
  1118. }
  1119. - (NSString*)contentType
  1120. {
  1121. NSAssert([NSThread isMainThread], @"FSAudioStream.contentType needs to be called in the main thread");
  1122. return [_private contentType];
  1123. }
  1124. - (NSString*)suggestedFileExtension
  1125. {
  1126. NSAssert([NSThread isMainThread], @"FSAudioStream.suggestedFileExtension needs to be called in the main thread");
  1127. return [_private suggestedFileExtension];
  1128. }
  1129. - (UInt64)defaultContentLength
  1130. {
  1131. NSAssert([NSThread isMainThread], @"FSAudioStream.defaultContentLength needs to be called in the main thread");
  1132. return [_private defaultContentLength];
  1133. }
  1134. - (UInt64)contentLength
  1135. {
  1136. NSAssert([NSThread isMainThread], @"FSAudioStream.contentLength needs to be called in the main thread");
  1137. return [_private contentLength];
  1138. }
  1139. - (UInt64)audioDataByteCount
  1140. {
  1141. NSAssert([NSThread isMainThread], @"FSAudioStream.audioDataByteCount needs to be called in the main thread");
  1142. return [_private audioDataByteCount];
  1143. }
  1144. - (void)preload
  1145. {
  1146. NSAssert([NSThread isMainThread], @"FSAudioStream.preload needs to be called in the main thread");
  1147. [_private preload];
  1148. }
  1149. - (void)play
  1150. {
  1151. NSAssert([NSThread isMainThread], @"FSAudioStream.play needs to be called in the main thread");
  1152. [_private play];
  1153. }
  1154. - (void)playFromURL:(NSURL*)url
  1155. {
  1156. NSAssert([NSThread isMainThread], @"FSAudioStream.playFromURL needs to be called in the main thread");
  1157. [_private playFromURL:url];
  1158. }
  1159. - (void)playFromOffset:(FSSeekByteOffset)offset
  1160. {
  1161. NSAssert([NSThread isMainThread], @"FSAudioStream.playFromOffset needs to be called in the main thread");
  1162. [_private playFromOffset:offset];
  1163. }
  1164. - (void)stop
  1165. {
  1166. NSAssert([NSThread isMainThread], @"FSAudioStream.stop needs to be called in the main thread");
  1167. [_private stop];
  1168. }
  1169. - (void)pause
  1170. {
  1171. NSAssert([NSThread isMainThread], @"FSAudioStream.pause needs to be called in the main thread");
  1172. [_private pause];
  1173. }
  1174. - (void)rewind:(unsigned int)seconds
  1175. {
  1176. NSAssert([NSThread isMainThread], @"FSAudioStream.rewind needs to be called in the main thread");
  1177. [_private rewind:seconds];
  1178. }
  1179. - (void)seekToPosition:(FSStreamPosition)position
  1180. {
  1181. NSAssert([NSThread isMainThread], @"FSAudioStream.seekToPosition needs to be called in the main thread");
  1182. if (!(position.position > 0)) {
  1183. // To retain compatibility with older implementations,
  1184. // fallback to using less accurate position.minute and position.second, if needed
  1185. const float seekTime = position.minute * 60 + position.second;
  1186. position.position = seekTime / [_private durationInSeconds];
  1187. }
  1188. [_private seekToOffset:position.position];
  1189. }
  1190. - (void)setPlayRate:(float)playRate
  1191. {
  1192. NSAssert([NSThread isMainThread], @"FSAudioStream.setPlayRate needs to be called in the main thread");
  1193. [_private setPlayRate:playRate];
  1194. }
  1195. - (BOOL)isPlaying
  1196. {
  1197. NSAssert([NSThread isMainThread], @"FSAudioStream.isPlaying needs to be called in the main thread");
  1198. return [_private isPlaying];
  1199. }
  1200. - (void)expungeCache
  1201. {
  1202. NSAssert([NSThread isMainThread], @"FSAudioStream.expungeCache needs to be called in the main thread");
  1203. [_private expungeCache];
  1204. }
  1205. - (NSUInteger)retryCount
  1206. {
  1207. NSAssert([NSThread isMainThread], @"FSAudioStream.retryCount needs to be called in the main thread");
  1208. return _private.retryCount;
  1209. }
  1210. - (FSStreamStatistics *)statistics
  1211. {
  1212. return _private.statistics;
  1213. }
  1214. - (FSLevelMeterState)levels
  1215. {
  1216. return _private.levels;
  1217. }
  1218. - (FSStreamPosition)currentTimePlayed
  1219. {
  1220. NSAssert([NSThread isMainThread], @"FSAudioStream.currentTimePlayed needs to be called in the main thread");
  1221. FSStreamPosition pos;
  1222. pos.position = 0;
  1223. pos.playbackTimeInSeconds = [_private playbackPosition].timePlayed;
  1224. pos.minute = 0;
  1225. pos.second = 0;
  1226. const float durationInSeconds = [_private durationInSeconds];
  1227. if (durationInSeconds > 0) {
  1228. pos.position = pos.playbackTimeInSeconds / [_private durationInSeconds];
  1229. }
  1230. // Extract the minutes and seconds for convenience
  1231. if (pos.playbackTimeInSeconds > 0) {
  1232. unsigned u = pos.playbackTimeInSeconds;
  1233. unsigned s,m;
  1234. s = u % 60;
  1235. u /= 60;
  1236. m = u;
  1237. pos.minute = m;
  1238. pos.second = s;
  1239. }
  1240. return pos;
  1241. }
  1242. - (FSStreamPosition)duration
  1243. {
  1244. NSAssert([NSThread isMainThread], @"FSAudioStream.duration needs to be called in the main thread");
  1245. FSStreamPosition pos;
  1246. pos.minute = 0;
  1247. pos.second = 0;
  1248. pos.playbackTimeInSeconds = 0;
  1249. pos.position = 0;
  1250. const float durationInSeconds = [_private durationInSeconds];
  1251. if (durationInSeconds > 0) {
  1252. unsigned u = durationInSeconds;
  1253. unsigned s,m;
  1254. s = u % 60;
  1255. u /= 60;
  1256. m = u;
  1257. pos.minute = m;
  1258. pos.second = s;
  1259. }
  1260. pos.playbackTimeInSeconds = durationInSeconds;
  1261. return pos;
  1262. }
  1263. - (FSSeekByteOffset)currentSeekByteOffset
  1264. {
  1265. NSAssert([NSThread isMainThread], @"FSAudioStream.currentSeekByteOffset needs to be called in the main thread");
  1266. return _private.currentSeekByteOffset;
  1267. }
  1268. - (float)bitRate
  1269. {
  1270. NSAssert([NSThread isMainThread], @"FSAudioStream.bitRate needs to be called in the main thread");
  1271. return _private.bitRate;
  1272. }
  1273. - (BOOL)continuous
  1274. {
  1275. NSAssert([NSThread isMainThread], @"FSAudioStream.continuous needs to be called in the main thread");
  1276. return !([_private durationInSeconds] > 0);
  1277. }
  1278. - (BOOL)cached
  1279. {
  1280. NSAssert([NSThread isMainThread], @"FSAudioStream.cached needs to be called in the main thread");
  1281. return _private.cached;
  1282. }
  1283. - (size_t)prebufferedByteCount
  1284. {
  1285. NSAssert([NSThread isMainThread], @"FSAudioStream.prebufferedByteCount needs to be called in the main thread");
  1286. return _private.prebufferedByteCount;
  1287. }
  1288. - (float)volume
  1289. {
  1290. NSAssert([NSThread isMainThread], @"FSAudioStream.volume needs to be called in the main thread");
  1291. return [_private currentVolume];
  1292. }
  1293. - (unsigned long long)totalCachedObjectsSize
  1294. {
  1295. NSAssert([NSThread isMainThread], @"FSAudioStream.totalCachedObjectsSize needs to be called in the main thread");
  1296. return [_private totalCachedObjectsSize];
  1297. }
  1298. - (void)setVolume:(float)volume
  1299. {
  1300. NSAssert([NSThread isMainThread], @"FSAudioStream.setVolume needs to be called in the main thread");
  1301. [_private setVolume:volume];
  1302. }
  1303. - (void (^)())onCompletion
  1304. {
  1305. NSAssert([NSThread isMainThread], @"FSAudioStream.onCompletion needs to be called in the main thread");
  1306. return _private.onCompletion;
  1307. }
  1308. - (void)setOnCompletion:(void (^)())onCompletion
  1309. {
  1310. NSAssert([NSThread isMainThread], @"FSAudioStream.setOnCompletion needs to be called in the main thread");
  1311. _private.onCompletion = onCompletion;
  1312. }
  1313. - (void (^)(FSAudioStreamState state))onStateChange
  1314. {
  1315. NSAssert([NSThread isMainThread], @"FSAudioStream.onStateChange needs to be called in the main thread");
  1316. return _private.onStateChange;
  1317. }
  1318. - (void (^)(NSDictionary *metaData))onMetaDataAvailable
  1319. {
  1320. NSAssert([NSThread isMainThread], @"FSAudioStream.onMetaDataAvailable needs to be called in the main thread");
  1321. return _private.onMetaDataAvailable;
  1322. }
  1323. - (void (^)(FSAudioStreamError error, NSString *errorDescription))onFailure
  1324. {
  1325. NSAssert([NSThread isMainThread], @"FSAudioStream.onFailure needs to be called in the main thread");
  1326. return _private.onFailure;
  1327. }
  1328. - (void)setOnStateChange:(void (^)(FSAudioStreamState))onStateChange
  1329. {
  1330. NSAssert([NSThread isMainThread], @"FSAudioStream.setOnStateChange needs to be called in the main thread");
  1331. _private.onStateChange = onStateChange;
  1332. }
  1333. - (void)setOnMetaDataAvailable:(void (^)(NSDictionary *))onMetaDataAvailable
  1334. {
  1335. NSAssert([NSThread isMainThread], @"FSAudioStream.setOnMetaDataAvailable needs to be called in the main thread");
  1336. _private.onMetaDataAvailable = onMetaDataAvailable;
  1337. }
  1338. - (void)setOnFailure:(void (^)(FSAudioStreamError error, NSString *errorDescription))onFailure
  1339. {
  1340. NSAssert([NSThread isMainThread], @"FSAudioStream.setOnFailure needs to be called in the main thread");
  1341. _private.onFailure = onFailure;
  1342. }
  1343. - (FSStreamConfiguration *)configuration
  1344. {
  1345. NSAssert([NSThread isMainThread], @"FSAudioStream.configuration needs to be called in the main thread");
  1346. return _private.configuration;
  1347. }
  1348. - (void)setDelegate:(id<FSPCMAudioStreamDelegate>)delegate
  1349. {
  1350. NSAssert([NSThread isMainThread], @"FSAudioStream.setDelegate needs to be called in the main thread");
  1351. _private.delegate = delegate;
  1352. }
  1353. - (id<FSPCMAudioStreamDelegate>)delegate
  1354. {
  1355. NSAssert([NSThread isMainThread], @"FSAudioStream.delegate needs to be called in the main thread");
  1356. return _private.delegate;
  1357. }
  1358. -(NSString *)description
  1359. {
  1360. NSAssert([NSThread isMainThread], @"FSAudioStream.description needs to be called in the main thread");
  1361. return [_private description];
  1362. }
  1363. -(NSUInteger)maxRetryCount
  1364. {
  1365. NSAssert([NSThread isMainThread], @"FSAudioStream.maxRetryCount needs to be called in the main thread");
  1366. return [_private maxRetryCount];
  1367. }
  1368. -(void)setMaxRetryCount:(NSUInteger)maxRetryCount
  1369. {
  1370. NSAssert([NSThread isMainThread], @"FSAudioStream.setMaxRetryCount needs to be called in the main thread");
  1371. [_private setMaxRetryCount:maxRetryCount];
  1372. }
  1373. @end
  1374. /*
  1375. * ===============================================================
  1376. * AudioStreamStateObserver: listen to the state from the audio stream.
  1377. * ===============================================================
  1378. */
  1379. void AudioStreamStateObserver::audioStreamErrorOccurred(int errorCode, CFStringRef errorDescription)
  1380. {
  1381. FSAudioStreamError error = kFsAudioStreamErrorNone;
  1382. NSString *errorForObjC = @"";
  1383. if (errorDescription) {
  1384. errorForObjC = CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, errorDescription));
  1385. }
  1386. switch (errorCode) {
  1387. case kFsAudioStreamErrorOpen:
  1388. error = kFsAudioStreamErrorOpen;
  1389. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1390. NSLog(@"FSAudioStream: Error opening the stream: %@ %@", errorForObjC, priv);
  1391. #endif
  1392. break;
  1393. case kFsAudioStreamErrorStreamParse:
  1394. error = kFsAudioStreamErrorStreamParse;
  1395. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1396. NSLog(@"FSAudioStream: Error parsing the stream: %@ %@", errorForObjC, priv);
  1397. #endif
  1398. break;
  1399. case kFsAudioStreamErrorNetwork:
  1400. error = kFsAudioStreamErrorNetwork;
  1401. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1402. NSLog(@"FSAudioStream: Network error: %@ %@", errorForObjC, priv);
  1403. #endif
  1404. break;
  1405. case kFsAudioStreamErrorUnsupportedFormat:
  1406. error = kFsAudioStreamErrorUnsupportedFormat;
  1407. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1408. NSLog(@"FSAudioStream: Unsupported format error: %@ %@", errorForObjC, priv);
  1409. #endif
  1410. break;
  1411. case kFsAudioStreamErrorStreamBouncing:
  1412. error = kFsAudioStreamErrorStreamBouncing;
  1413. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1414. NSLog(@"FSAudioStream: Stream bounced: %@ %@", errorForObjC, priv);
  1415. #endif
  1416. break;
  1417. case kFsAudioStreamErrorTerminated:
  1418. error = kFsAudioStreamErrorTerminated;
  1419. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  1420. NSLog(@"FSAudioStream: Stream terminated: %@ %@", errorForObjC, priv);
  1421. #endif
  1422. break;
  1423. default:
  1424. break;
  1425. }
  1426. if (priv.onFailure) {
  1427. priv.onFailure(error, errorForObjC);
  1428. }
  1429. NSDictionary *userInfo = @{FSAudioStreamNotificationKey_Error: @(errorCode),
  1430. FSAudioStreamNotificationKey_ErrorDescription: errorForObjC,
  1431. FSAudioStreamNotificationKey_Stream: [NSValue valueWithPointer:source]};
  1432. NSNotification *notification = [NSNotification notificationWithName:FSAudioStreamErrorNotification object:priv.stream userInfo:userInfo];
  1433. [[NSNotificationCenter defaultCenter] postNotification:notification];
  1434. if (error == kFsAudioStreamErrorNetwork ||
  1435. error == kFsAudioStreamErrorUnsupportedFormat ||
  1436. error == kFsAudioStreamErrorOpen ||
  1437. error == kFsAudioStreamErrorTerminated) {
  1438. if (!source->isPreloading()) {
  1439. [priv attemptRestart];
  1440. }
  1441. }
  1442. }
  1443. void AudioStreamStateObserver::audioStreamStateChanged(astreamer::Audio_Stream::State state)
  1444. {
  1445. SEL notificationHandler;
  1446. switch (state) {
  1447. case astreamer::Audio_Stream::STOPPED:
  1448. notificationHandler = @selector(notifyPlaybackStopped);
  1449. break;
  1450. case astreamer::Audio_Stream::BUFFERING:
  1451. notificationHandler = @selector(notifyPlaybackBuffering);
  1452. break;
  1453. case astreamer::Audio_Stream::PLAYING:
  1454. [priv endBackgroundTask];
  1455. notificationHandler = @selector(notifyPlaybackPlaying);
  1456. break;
  1457. case astreamer::Audio_Stream::PAUSED:
  1458. notificationHandler = @selector(notifyPlaybackPaused);
  1459. break;
  1460. case astreamer::Audio_Stream::SEEKING:
  1461. notificationHandler = @selector(notifyPlaybackSeeking);
  1462. break;
  1463. case astreamer::Audio_Stream::END_OF_FILE:
  1464. notificationHandler = @selector(notifyPlaybackEndOfFile);
  1465. break;
  1466. case astreamer::Audio_Stream::FAILED:
  1467. [priv endBackgroundTask];
  1468. notificationHandler = @selector(notifyPlaybackFailed);
  1469. break;
  1470. case astreamer::Audio_Stream::PLAYBACK_COMPLETED:
  1471. notificationHandler = @selector(notifyPlaybackCompletion);
  1472. break;
  1473. default:
  1474. // Unknown state
  1475. notificationHandler = @selector(notifyPlaybackUnknownState);
  1476. break;
  1477. }
  1478. // Detach from the player so that the event loop can complete its cycle.
  1479. // This ensures that the stream gets closed, if needs to be.
  1480. [NSTimer scheduledTimerWithTimeInterval:0
  1481. target:priv
  1482. selector:notificationHandler
  1483. userInfo:nil
  1484. repeats:NO];
  1485. }
  1486. void AudioStreamStateObserver::audioStreamMetaDataAvailable(std::map<CFStringRef,CFStringRef> metaData)
  1487. {
  1488. NSMutableDictionary *metaDataDictionary = [[NSMutableDictionary alloc] init];
  1489. for (std::map<CFStringRef,CFStringRef>::iterator iter = metaData.begin(); iter != metaData.end(); ++iter) {
  1490. CFStringRef key = iter->first;
  1491. CFStringRef value = iter->second;
  1492. metaDataDictionary[CFBridgingRelease(key)] = CFBridgingRelease(value);
  1493. }
  1494. if (priv.onMetaDataAvailable) {
  1495. priv.onMetaDataAvailable(metaDataDictionary);
  1496. }
  1497. NSDictionary *userInfo = @{FSAudioStreamNotificationKey_MetaData: metaDataDictionary,
  1498. FSAudioStreamNotificationKey_Stream: [NSValue valueWithPointer:source]};
  1499. NSNotification *notification = [NSNotification notificationWithName:FSAudioStreamMetaDataNotification object:priv.stream userInfo:userInfo];
  1500. [[NSNotificationCenter defaultCenter] postNotification:notification];
  1501. }
  1502. void AudioStreamStateObserver::samplesAvailable(AudioBufferList *samples, UInt32 frames, AudioStreamPacketDescription description)
  1503. {
  1504. if ([priv.delegate respondsToSelector:@selector(audioStream:samplesAvailable:frames:description:)]) {
  1505. [priv.delegate audioStream:priv.stream samplesAvailable:samples frames:frames description:description];
  1506. }
  1507. }
  1508. void AudioStreamStateObserver::bitrateAvailable()
  1509. {
  1510. [priv bitrateAvailable];
  1511. }