123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /* vim: set ft=objc fenc=utf-8 sw=2 ts=2 et: */
- /*
- * DOUAudioStreamer - A Core Audio based streaming audio player for iOS/Mac:
- *
- * https://github.com/douban/DOUAudioStreamer
- *
- * Copyright 2013-2016 Douban Inc. All rights reserved.
- *
- * Use and distribution licensed under the BSD license. See
- * the LICENSE file for full text.
- *
- * Authors:
- * Chongyu Zhu <i@lembacon.com>
- *
- */
- #import "DOUAudioPlaybackItem.h"
- #import "DOUAudioFileProvider.h"
- #import "DOUAudioFilePreprocessor.h"
- @interface DOUAudioPlaybackItem () {
- @private
- DOUAudioFileProvider *_fileProvider;
- DOUAudioFilePreprocessor *_filePreprocessor;
- AudioFileID _fileID;
- AudioStreamBasicDescription _fileFormat;
- NSUInteger _bitRate;
- NSUInteger _dataOffset;
- NSUInteger _estimatedDuration;
- }
- @end
- @implementation DOUAudioPlaybackItem
- @synthesize fileProvider = _fileProvider;
- @synthesize filePreprocessor = _filePreprocessor;
- @synthesize fileID = _fileID;
- @synthesize fileFormat = _fileFormat;
- @synthesize bitRate = _bitRate;
- @synthesize dataOffset = _dataOffset;
- @synthesize estimatedDuration = _estimatedDuration;
- - (id <DOUAudioFile>)audioFile
- {
- return [_fileProvider audioFile];
- }
- - (NSURL *)cachedURL
- {
- return [_fileProvider cachedURL];
- }
- - (NSData *)mappedData
- {
- return [_fileProvider mappedData];
- }
- - (BOOL)isOpened
- {
- return _fileID != NULL;
- }
- static OSStatus audio_file_read(void *inClientData,
- SInt64 inPosition,
- UInt32 requestCount,
- void *buffer,
- UInt32 *actualCount)
- {
- __unsafe_unretained DOUAudioPlaybackItem *item = (__bridge DOUAudioPlaybackItem *)inClientData;
- if (inPosition + requestCount > [[item mappedData] length]) {
- if (inPosition >= [[item mappedData] length]) {
- *actualCount = 0;
- }
- else {
- *actualCount = (UInt32)((SInt64)[[item mappedData] length] - inPosition);
- }
- }
- else {
- *actualCount = requestCount;
- }
- if (*actualCount == 0) {
- return noErr;
- }
- if ([item filePreprocessor] == nil) {
- memcpy(buffer, (uint8_t *)[[item mappedData] bytes] + inPosition, *actualCount);
- }
- else {
- NSData *input = [NSData dataWithBytesNoCopy:(uint8_t *)[[item mappedData] bytes] + inPosition
- length:*actualCount
- freeWhenDone:NO];
- NSData *output = [[item filePreprocessor] handleData:input offset:(NSUInteger)inPosition];
- memcpy(buffer, [output bytes], [output length]);
- }
- return noErr;
- }
- static SInt64 audio_file_get_size(void *inClientData)
- {
- __unsafe_unretained DOUAudioPlaybackItem *item = (__bridge DOUAudioPlaybackItem *)inClientData;
- return (SInt64)[[item mappedData] length];
- }
- - (BOOL)_openWithFileTypeHint:(AudioFileTypeID)fileTypeHint
- {
- OSStatus status;
- status = AudioFileOpenWithCallbacks((__bridge void *)self,
- audio_file_read,
- NULL,
- audio_file_get_size,
- NULL,
- fileTypeHint,
- &_fileID);
- return status == noErr;
- }
- - (BOOL)_openWithFallbacks
- {
- NSArray *fallbackTypeIDs = [self _fallbackTypeIDs];
- for (NSNumber *typeIDNumber in fallbackTypeIDs) {
- AudioFileTypeID typeID = (AudioFileTypeID)[typeIDNumber unsignedLongValue];
- if ([self _openWithFileTypeHint:typeID]) {
- return YES;
- }
- }
- return NO;
- }
- - (NSArray *)_fallbackTypeIDs
- {
- NSMutableArray *fallbackTypeIDs = [NSMutableArray array];
- NSMutableSet *fallbackTypeIDSet = [NSMutableSet set];
- struct {
- CFStringRef specifier;
- AudioFilePropertyID propertyID;
- } properties[] = {
- { (__bridge CFStringRef)[_fileProvider mimeType], kAudioFileGlobalInfo_TypesForMIMEType },
- { (__bridge CFStringRef)[_fileProvider fileExtension], kAudioFileGlobalInfo_TypesForExtension }
- };
- const size_t numberOfProperties = sizeof(properties) / sizeof(properties[0]);
- for (size_t i = 0; i < numberOfProperties; ++i) {
- if (properties[i].specifier == NULL) {
- continue;
- }
- UInt32 outSize = 0;
- OSStatus status;
- status = AudioFileGetGlobalInfoSize(properties[i].propertyID,
- sizeof(properties[i].specifier),
- &properties[i].specifier,
- &outSize);
- if (status != noErr) {
- continue;
- }
- size_t count = outSize / sizeof(AudioFileTypeID);
- AudioFileTypeID *buffer = (AudioFileTypeID *)malloc(outSize);
- if (buffer == NULL) {
- continue;
- }
- status = AudioFileGetGlobalInfo(properties[i].propertyID,
- sizeof(properties[i].specifier),
- &properties[i].specifier,
- &outSize,
- buffer);
- if (status != noErr) {
- free(buffer);
- continue;
- }
- for (size_t j = 0; j < count; ++j) {
- NSNumber *tid = [NSNumber numberWithUnsignedLong:buffer[j]];
- if ([fallbackTypeIDSet containsObject:tid]) {
- continue;
- }
- [fallbackTypeIDs addObject:tid];
- [fallbackTypeIDSet addObject:tid];
- }
- free(buffer);
- }
- return fallbackTypeIDs;
- }
- - (BOOL)open
- {
- if ([self isOpened]) {
- return YES;
- }
- if (![self _openWithFileTypeHint:0] &&
- ![self _openWithFallbacks]) {
- _fileID = NULL;
- return NO;
- }
- if (![self _fillFileFormat] ||
- ![self _fillMiscProperties]) {
- AudioFileClose(_fileID);
- _fileID = NULL;
- return NO;
- }
- return YES;
- }
- - (BOOL)_fillFileFormat
- {
- UInt32 size;
- OSStatus status;
- status = AudioFileGetPropertyInfo(_fileID, kAudioFilePropertyFormatList, &size, NULL);
- if (status != noErr) {
- return NO;
- }
- UInt32 numFormats = size / sizeof(AudioFormatListItem);
- AudioFormatListItem *formatList = (AudioFormatListItem *)malloc(size);
- status = AudioFileGetProperty(_fileID, kAudioFilePropertyFormatList, &size, formatList);
- if (status != noErr) {
- free(formatList);
- return NO;
- }
- if (numFormats == 1) {
- _fileFormat = formatList[0].mASBD;
- }
- else {
- status = AudioFormatGetPropertyInfo(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size);
- if (status != noErr) {
- free(formatList);
- return NO;
- }
- UInt32 numDecoders = size / sizeof(OSType);
- OSType *decoderIDS = (OSType *)malloc(size);
- status = AudioFormatGetProperty(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size, decoderIDS);
- if (status != noErr) {
- free(formatList);
- free(decoderIDS);
- return NO;
- }
- UInt32 i;
- for (i = 0; i < numFormats; ++i) {
- OSType decoderID = formatList[i].mASBD.mFormatID;
- BOOL found = NO;
- for (UInt32 j = 0; j < numDecoders; ++j) {
- if (decoderID == decoderIDS[j]) {
- found = YES;
- break;
- }
- }
- if (found) {
- break;
- }
- }
- free(decoderIDS);
- if (i >= numFormats) {
- free(formatList);
- return NO;
- }
- _fileFormat = formatList[i].mASBD;
- }
- free(formatList);
- return YES;
- }
- - (BOOL)_fillMiscProperties
- {
- UInt32 size;
- OSStatus status;
- UInt32 bitRate = 0;
- size = sizeof(bitRate);
- status = AudioFileGetProperty(_fileID, kAudioFilePropertyBitRate, &size, &bitRate);
- if (status != noErr) {
- return NO;
- }
- _bitRate = bitRate;
- SInt64 dataOffset = 0;
- size = sizeof(dataOffset);
- status = AudioFileGetProperty(_fileID, kAudioFilePropertyDataOffset, &size, &dataOffset);
- if (status != noErr) {
- return NO;
- }
- _dataOffset = (NSUInteger)dataOffset;
- Float64 estimatedDuration = 0.0;
- size = sizeof(estimatedDuration);
- status = AudioFileGetProperty(_fileID, kAudioFilePropertyEstimatedDuration, &size, &estimatedDuration);
- if (status != noErr) {
- return NO;
- }
- _estimatedDuration = estimatedDuration * 1000.0;
- return YES;
- }
- - (void)close
- {
- if (![self isOpened]) {
- return;
- }
- AudioFileClose(_fileID);
- _fileID = NULL;
- }
- + (instancetype)playbackItemWithFileProvider:(DOUAudioFileProvider *)fileProvider
- {
- return [[[self class] alloc] initWithFileProvider:fileProvider];
- }
- - (instancetype)initWithFileProvider:(DOUAudioFileProvider *)fileProvider
- {
- self = [super init];
- if (self) {
- _fileProvider = fileProvider;
- if ([[self audioFile] respondsToSelector:@selector(audioFilePreprocessor)]) {
- _filePreprocessor = [[self audioFile] audioFilePreprocessor];
- }
- }
- return self;
- }
- - (void)dealloc
- {
- if ([self isOpened]) {
- [self close];
- }
- }
- @end
|