// // CogPluginMulti.m // CogAudio // // Created by Christopher Snowhill on 10/21/13. // // #import "CogPluginMulti.h" NSArray *sortClassesByPriority(NSArray *theClasses) { NSMutableArray *sortedClasses = [NSMutableArray arrayWithArray:theClasses]; [sortedClasses sortUsingComparator: ^NSComparisonResult(id obj1, id obj2) { NSString *classString1 = (NSString *)obj1; NSString *classString2 = (NSString *)obj2; Class class1 = NSClassFromString(classString1); Class class2 = NSClassFromString(classString2); float priority1 = [class1 priority]; float priority2 = [class2 priority]; if(priority1 == priority2) return NSOrderedSame; else if(priority1 > priority2) return NSOrderedAscending; else return NSOrderedDescending; }]; return sortedClasses; } @interface CogDecoderMulti (Private) - (void)registerObservers; - (void)removeObservers; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; @end @implementation CogDecoderMulti static void *kCogDecoderMultiContext = &kCogDecoderMultiContext; + (NSArray *)mimeTypes { return nil; } + (NSArray *)fileTypes { return nil; } + (float)priority { return -1.0; } + (NSArray *)fileTypeAssociations { return nil; } - (id)initWithDecoders:(NSArray *)decoders { self = [super init]; if(self) { theDecoders = sortClassesByPriority(decoders); theDecoder = nil; } return self; } - (NSDictionary *)properties { if(theDecoder != nil) return [theDecoder properties]; return nil; } - (NSDictionary *)metadata { if(theDecoder != nil) return [theDecoder metadata]; return @{}; } - (int)readAudio:(void *)buffer frames:(UInt32)frames { if(theDecoder != nil) return [theDecoder readAudio:buffer frames:frames]; return 0; } - (BOOL)open:(id)source { for(NSString *classString in theDecoders) { Class decoder = NSClassFromString(classString); theDecoder = [[decoder alloc] init]; [self registerObservers]; if([theDecoder open:source]) return YES; [self removeObservers]; // HTTP reader supports limited rewinding [source seek:0 whence:SEEK_SET]; } theDecoder = nil; return NO; } - (long)seek:(long)frame { if(theDecoder != nil) return [theDecoder seek:frame]; return -1; } - (void)close { if(theDecoder != nil) { [self removeObservers]; [theDecoder close]; theDecoder = nil; } } - (void)dealloc { [self close]; } - (void)registerObservers { if(!observersAdded) { [theDecoder addObserver:self forKeyPath:@"properties" options:(NSKeyValueObservingOptionNew) context:kCogDecoderMultiContext]; [theDecoder addObserver:self forKeyPath:@"metadata" options:(NSKeyValueObservingOptionNew) context:kCogDecoderMultiContext]; observersAdded = YES; } } - (void)removeObservers { if(observersAdded) { observersAdded = NO; [theDecoder removeObserver:self forKeyPath:@"properties" context:kCogDecoderMultiContext]; [theDecoder removeObserver:self forKeyPath:@"metadata" context:kCogDecoderMultiContext]; } } - (BOOL)setTrack:(NSURL *)track { if(theDecoder != nil && [theDecoder respondsToSelector:@selector(setTrack:)]) return [theDecoder setTrack:track]; return NO; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if(context == kCogDecoderMultiContext) { [self willChangeValueForKey:keyPath]; [self didChangeValueForKey:keyPath]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } @end @implementation CogContainerMulti + (NSArray *)urlsForContainerURL:(NSURL *)url containers:(NSArray *)containers { NSArray *sortedContainers = sortClassesByPriority(containers); for(NSString *classString in sortedContainers) { Class container = NSClassFromString(classString); NSArray *urls = [container urlsForContainerURL:url]; if([urls count]) return urls; } return nil; } @end @implementation CogMetadataReaderMulti + (NSDictionary *)metadataForURL:(NSURL *)url readers:(NSArray *)readers { NSArray *sortedReaders = sortClassesByPriority(readers); for(NSString *classString in sortedReaders) { Class reader = NSClassFromString(classString); NSDictionary *data = [reader metadataForURL:url]; if([data count]) return data; } return nil; } @end @implementation CogPropertiesReaderMulti + (NSDictionary *)propertiesForSource:(id)source readers:(NSArray *)readers { NSArray *sortedReaders = sortClassesByPriority(readers); for(NSString *classString in sortedReaders) { Class reader = NSClassFromString(classString); NSDictionary *data = [reader propertiesForSource:source]; if([data count]) return data; if([source seekable]) [source seek:0 whence:SEEK_SET]; } return nil; } @end