DOUAudioPlaybackItem.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* vim: set ft=objc fenc=utf-8 sw=2 ts=2 et: */
  2. /*
  3. * DOUAudioStreamer - A Core Audio based streaming audio player for iOS/Mac:
  4. *
  5. * https://github.com/douban/DOUAudioStreamer
  6. *
  7. * Copyright 2013-2016 Douban Inc. All rights reserved.
  8. *
  9. * Use and distribution licensed under the BSD license. See
  10. * the LICENSE file for full text.
  11. *
  12. * Authors:
  13. * Chongyu Zhu <i@lembacon.com>
  14. *
  15. */
  16. #import "DOUAudioPlaybackItem.h"
  17. #import "DOUAudioFileProvider.h"
  18. #import "DOUAudioFilePreprocessor.h"
  19. @interface DOUAudioPlaybackItem () {
  20. @private
  21. DOUAudioFileProvider *_fileProvider;
  22. DOUAudioFilePreprocessor *_filePreprocessor;
  23. AudioFileID _fileID;
  24. AudioStreamBasicDescription _fileFormat;
  25. NSUInteger _bitRate;
  26. NSUInteger _dataOffset;
  27. NSUInteger _estimatedDuration;
  28. }
  29. @end
  30. @implementation DOUAudioPlaybackItem
  31. @synthesize fileProvider = _fileProvider;
  32. @synthesize filePreprocessor = _filePreprocessor;
  33. @synthesize fileID = _fileID;
  34. @synthesize fileFormat = _fileFormat;
  35. @synthesize bitRate = _bitRate;
  36. @synthesize dataOffset = _dataOffset;
  37. @synthesize estimatedDuration = _estimatedDuration;
  38. - (id <DOUAudioFile>)audioFile
  39. {
  40. return [_fileProvider audioFile];
  41. }
  42. - (NSURL *)cachedURL
  43. {
  44. return [_fileProvider cachedURL];
  45. }
  46. - (NSData *)mappedData
  47. {
  48. return [_fileProvider mappedData];
  49. }
  50. - (BOOL)isOpened
  51. {
  52. return _fileID != NULL;
  53. }
  54. static OSStatus audio_file_read(void *inClientData,
  55. SInt64 inPosition,
  56. UInt32 requestCount,
  57. void *buffer,
  58. UInt32 *actualCount)
  59. {
  60. __unsafe_unretained DOUAudioPlaybackItem *item = (__bridge DOUAudioPlaybackItem *)inClientData;
  61. if (inPosition + requestCount > [[item mappedData] length]) {
  62. if (inPosition >= [[item mappedData] length]) {
  63. *actualCount = 0;
  64. }
  65. else {
  66. *actualCount = (UInt32)((SInt64)[[item mappedData] length] - inPosition);
  67. }
  68. }
  69. else {
  70. *actualCount = requestCount;
  71. }
  72. if (*actualCount == 0) {
  73. return noErr;
  74. }
  75. if ([item filePreprocessor] == nil) {
  76. memcpy(buffer, (uint8_t *)[[item mappedData] bytes] + inPosition, *actualCount);
  77. }
  78. else {
  79. NSData *input = [NSData dataWithBytesNoCopy:(uint8_t *)[[item mappedData] bytes] + inPosition
  80. length:*actualCount
  81. freeWhenDone:NO];
  82. NSData *output = [[item filePreprocessor] handleData:input offset:(NSUInteger)inPosition];
  83. memcpy(buffer, [output bytes], [output length]);
  84. }
  85. return noErr;
  86. }
  87. static SInt64 audio_file_get_size(void *inClientData)
  88. {
  89. __unsafe_unretained DOUAudioPlaybackItem *item = (__bridge DOUAudioPlaybackItem *)inClientData;
  90. return (SInt64)[[item mappedData] length];
  91. }
  92. - (BOOL)_openWithFileTypeHint:(AudioFileTypeID)fileTypeHint
  93. {
  94. OSStatus status;
  95. status = AudioFileOpenWithCallbacks((__bridge void *)self,
  96. audio_file_read,
  97. NULL,
  98. audio_file_get_size,
  99. NULL,
  100. fileTypeHint,
  101. &_fileID);
  102. return status == noErr;
  103. }
  104. - (BOOL)_openWithFallbacks
  105. {
  106. NSArray *fallbackTypeIDs = [self _fallbackTypeIDs];
  107. for (NSNumber *typeIDNumber in fallbackTypeIDs) {
  108. AudioFileTypeID typeID = (AudioFileTypeID)[typeIDNumber unsignedLongValue];
  109. if ([self _openWithFileTypeHint:typeID]) {
  110. return YES;
  111. }
  112. }
  113. return NO;
  114. }
  115. - (NSArray *)_fallbackTypeIDs
  116. {
  117. NSMutableArray *fallbackTypeIDs = [NSMutableArray array];
  118. NSMutableSet *fallbackTypeIDSet = [NSMutableSet set];
  119. struct {
  120. CFStringRef specifier;
  121. AudioFilePropertyID propertyID;
  122. } properties[] = {
  123. { (__bridge CFStringRef)[_fileProvider mimeType], kAudioFileGlobalInfo_TypesForMIMEType },
  124. { (__bridge CFStringRef)[_fileProvider fileExtension], kAudioFileGlobalInfo_TypesForExtension }
  125. };
  126. const size_t numberOfProperties = sizeof(properties) / sizeof(properties[0]);
  127. for (size_t i = 0; i < numberOfProperties; ++i) {
  128. if (properties[i].specifier == NULL) {
  129. continue;
  130. }
  131. UInt32 outSize = 0;
  132. OSStatus status;
  133. status = AudioFileGetGlobalInfoSize(properties[i].propertyID,
  134. sizeof(properties[i].specifier),
  135. &properties[i].specifier,
  136. &outSize);
  137. if (status != noErr) {
  138. continue;
  139. }
  140. size_t count = outSize / sizeof(AudioFileTypeID);
  141. AudioFileTypeID *buffer = (AudioFileTypeID *)malloc(outSize);
  142. if (buffer == NULL) {
  143. continue;
  144. }
  145. status = AudioFileGetGlobalInfo(properties[i].propertyID,
  146. sizeof(properties[i].specifier),
  147. &properties[i].specifier,
  148. &outSize,
  149. buffer);
  150. if (status != noErr) {
  151. free(buffer);
  152. continue;
  153. }
  154. for (size_t j = 0; j < count; ++j) {
  155. NSNumber *tid = [NSNumber numberWithUnsignedLong:buffer[j]];
  156. if ([fallbackTypeIDSet containsObject:tid]) {
  157. continue;
  158. }
  159. [fallbackTypeIDs addObject:tid];
  160. [fallbackTypeIDSet addObject:tid];
  161. }
  162. free(buffer);
  163. }
  164. return fallbackTypeIDs;
  165. }
  166. - (BOOL)open
  167. {
  168. if ([self isOpened]) {
  169. return YES;
  170. }
  171. if (![self _openWithFileTypeHint:0] &&
  172. ![self _openWithFallbacks]) {
  173. _fileID = NULL;
  174. return NO;
  175. }
  176. if (![self _fillFileFormat] ||
  177. ![self _fillMiscProperties]) {
  178. AudioFileClose(_fileID);
  179. _fileID = NULL;
  180. return NO;
  181. }
  182. return YES;
  183. }
  184. - (BOOL)_fillFileFormat
  185. {
  186. UInt32 size;
  187. OSStatus status;
  188. status = AudioFileGetPropertyInfo(_fileID, kAudioFilePropertyFormatList, &size, NULL);
  189. if (status != noErr) {
  190. return NO;
  191. }
  192. UInt32 numFormats = size / sizeof(AudioFormatListItem);
  193. AudioFormatListItem *formatList = (AudioFormatListItem *)malloc(size);
  194. status = AudioFileGetProperty(_fileID, kAudioFilePropertyFormatList, &size, formatList);
  195. if (status != noErr) {
  196. free(formatList);
  197. return NO;
  198. }
  199. if (numFormats == 1) {
  200. _fileFormat = formatList[0].mASBD;
  201. }
  202. else {
  203. status = AudioFormatGetPropertyInfo(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size);
  204. if (status != noErr) {
  205. free(formatList);
  206. return NO;
  207. }
  208. UInt32 numDecoders = size / sizeof(OSType);
  209. OSType *decoderIDS = (OSType *)malloc(size);
  210. status = AudioFormatGetProperty(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size, decoderIDS);
  211. if (status != noErr) {
  212. free(formatList);
  213. free(decoderIDS);
  214. return NO;
  215. }
  216. UInt32 i;
  217. for (i = 0; i < numFormats; ++i) {
  218. OSType decoderID = formatList[i].mASBD.mFormatID;
  219. BOOL found = NO;
  220. for (UInt32 j = 0; j < numDecoders; ++j) {
  221. if (decoderID == decoderIDS[j]) {
  222. found = YES;
  223. break;
  224. }
  225. }
  226. if (found) {
  227. break;
  228. }
  229. }
  230. free(decoderIDS);
  231. if (i >= numFormats) {
  232. free(formatList);
  233. return NO;
  234. }
  235. _fileFormat = formatList[i].mASBD;
  236. }
  237. free(formatList);
  238. return YES;
  239. }
  240. - (BOOL)_fillMiscProperties
  241. {
  242. UInt32 size;
  243. OSStatus status;
  244. UInt32 bitRate = 0;
  245. size = sizeof(bitRate);
  246. status = AudioFileGetProperty(_fileID, kAudioFilePropertyBitRate, &size, &bitRate);
  247. if (status != noErr) {
  248. return NO;
  249. }
  250. _bitRate = bitRate;
  251. SInt64 dataOffset = 0;
  252. size = sizeof(dataOffset);
  253. status = AudioFileGetProperty(_fileID, kAudioFilePropertyDataOffset, &size, &dataOffset);
  254. if (status != noErr) {
  255. return NO;
  256. }
  257. _dataOffset = (NSUInteger)dataOffset;
  258. Float64 estimatedDuration = 0.0;
  259. size = sizeof(estimatedDuration);
  260. status = AudioFileGetProperty(_fileID, kAudioFilePropertyEstimatedDuration, &size, &estimatedDuration);
  261. if (status != noErr) {
  262. return NO;
  263. }
  264. _estimatedDuration = estimatedDuration * 1000.0;
  265. return YES;
  266. }
  267. - (void)close
  268. {
  269. if (![self isOpened]) {
  270. return;
  271. }
  272. AudioFileClose(_fileID);
  273. _fileID = NULL;
  274. }
  275. + (instancetype)playbackItemWithFileProvider:(DOUAudioFileProvider *)fileProvider
  276. {
  277. return [[[self class] alloc] initWithFileProvider:fileProvider];
  278. }
  279. - (instancetype)initWithFileProvider:(DOUAudioFileProvider *)fileProvider
  280. {
  281. self = [super init];
  282. if (self) {
  283. _fileProvider = fileProvider;
  284. if ([[self audioFile] respondsToSelector:@selector(audioFilePreprocessor)]) {
  285. _filePreprocessor = [[self audioFile] audioFilePreprocessor];
  286. }
  287. }
  288. return self;
  289. }
  290. - (void)dealloc
  291. {
  292. if ([self isOpened]) {
  293. [self close];
  294. }
  295. }
  296. @end