2007-02-24 20:36:27 +00:00
|
|
|
#import "PluginController.h"
|
|
|
|
#import "Plugin.h"
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
#import "CogDecoderMulti.h"
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
@implementation PluginController
|
|
|
|
|
2008-03-01 15:28:17 +00:00
|
|
|
@synthesize sources;
|
|
|
|
@synthesize containers;
|
|
|
|
@synthesize metadataReaders;
|
|
|
|
|
|
|
|
@synthesize propertiesReadersByExtension;
|
|
|
|
@synthesize propertiesReadersByMimeType;
|
|
|
|
|
|
|
|
@synthesize decodersByExtension;
|
|
|
|
@synthesize decodersByMimeType;
|
|
|
|
|
|
|
|
@synthesize configured;
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
static PluginController *sharedPluginController = nil;
|
|
|
|
|
2007-10-20 15:53:52 +00:00
|
|
|
+ (id<CogPluginController>)sharedPluginController
|
2007-02-24 20:36:27 +00:00
|
|
|
{
|
|
|
|
@synchronized(self) {
|
|
|
|
if (sharedPluginController == nil) {
|
2008-03-03 02:18:27 +00:00
|
|
|
sharedPluginController = [[self alloc] init];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
}
|
2008-03-03 02:18:27 +00:00
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
return sharedPluginController;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (id)init {
|
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
2008-03-01 15:32:28 +00:00
|
|
|
self.sources = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
self.containers = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
|
|
|
|
self.metadataReaders = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
|
|
|
|
self.propertiesReadersByExtension = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
self.propertiesReadersByMimeType = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
|
|
|
|
self.decodersByExtension = [[[NSMutableDictionary alloc] init] autorelease];
|
|
|
|
self.decodersByMimeType = [[[NSMutableDictionary alloc] init] autorelease];
|
2007-10-20 15:53:52 +00:00
|
|
|
|
2008-03-01 15:28:17 +00:00
|
|
|
[self setup];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setup
|
|
|
|
{
|
2008-03-01 15:28:17 +00:00
|
|
|
if (self.configured == NO) {
|
2008-03-01 15:32:28 +00:00
|
|
|
self.configured = YES;
|
2007-10-20 15:46:39 +00:00
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bundleDidLoad:) name:NSBundleDidLoadNotification object:nil];
|
|
|
|
|
|
|
|
[self loadPlugins];
|
|
|
|
[self printPluginInfo];
|
|
|
|
}
|
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2007-10-20 15:46:39 +00:00
|
|
|
- (void)bundleDidLoad:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
NSArray *classNames = [[notification userInfo] objectForKey:@"NSLoadedClasses"];
|
2008-03-01 15:04:46 +00:00
|
|
|
for (NSString *className in classNames)
|
2007-10-20 15:46:39 +00:00
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Class loaded: %@", className);
|
2007-10-20 15:46:39 +00:00
|
|
|
Class bundleClass = NSClassFromString(className);
|
|
|
|
if ([bundleClass conformsToProtocol:@protocol(CogContainer)]) {
|
|
|
|
[self setupContainer:className];
|
|
|
|
}
|
2009-03-01 04:12:47 +00:00
|
|
|
if ([bundleClass conformsToProtocol:@protocol(CogDecoder)]) {
|
2007-10-20 15:46:39 +00:00
|
|
|
[self setupDecoder:className];
|
|
|
|
}
|
2009-03-01 04:12:47 +00:00
|
|
|
if ([bundleClass conformsToProtocol:@protocol(CogMetadataReader)]) {
|
2007-10-20 15:46:39 +00:00
|
|
|
[self setupMetadataReader:className];
|
|
|
|
}
|
2009-03-01 04:12:47 +00:00
|
|
|
if ([bundleClass conformsToProtocol:@protocol(CogPropertiesReader)]) {
|
2007-10-20 15:46:39 +00:00
|
|
|
[self setupPropertiesReader:className];
|
|
|
|
}
|
2009-03-01 04:12:47 +00:00
|
|
|
if ([bundleClass conformsToProtocol:@protocol(CogSource)]) {
|
2007-10-20 15:46:39 +00:00
|
|
|
[self setupSource:className];
|
|
|
|
}
|
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)loadPluginsAtPath:(NSString *)path
|
|
|
|
{
|
|
|
|
|
2013-10-03 08:00:58 +00:00
|
|
|
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2008-03-01 15:28:17 +00:00
|
|
|
for (NSString *pname in dirContents)
|
2007-02-24 20:36:27 +00:00
|
|
|
{
|
|
|
|
NSString *ppath;
|
|
|
|
ppath = [NSString pathWithComponents:[NSArray arrayWithObjects:path,pname,nil]];
|
|
|
|
|
|
|
|
if ([[pname pathExtension] isEqualToString:@"bundle"])
|
|
|
|
{
|
|
|
|
NSBundle *b = [NSBundle bundleWithPath:ppath];
|
2007-10-20 15:46:39 +00:00
|
|
|
[b load];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)loadPlugins
|
|
|
|
{
|
|
|
|
[self loadPluginsAtPath:[[NSBundle mainBundle] builtInPlugInsPath]];
|
|
|
|
[self loadPluginsAtPath:[@"~/Library/Application Support/Cog/Plugins" stringByExpandingTildeInPath]];
|
|
|
|
}
|
|
|
|
|
2007-10-09 01:20:46 +00:00
|
|
|
- (void)setupContainer:(NSString *)className
|
|
|
|
{
|
|
|
|
Class container = NSClassFromString(className);
|
|
|
|
if (container && [container respondsToSelector:@selector(fileTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id fileType in [container fileTypes])
|
2007-10-09 01:20:46 +00:00
|
|
|
{
|
|
|
|
[containers setObject:className forKey:[fileType lowercaseString]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-02 01:36:52 +00:00
|
|
|
- (void)setupDecoder:(NSString *)className
|
2007-02-24 20:36:27 +00:00
|
|
|
{
|
2007-03-02 01:36:52 +00:00
|
|
|
Class decoder = NSClassFromString(className);
|
|
|
|
if (decoder && [decoder respondsToSelector:@selector(fileTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id fileType in [decoder fileTypes])
|
2007-03-02 01:36:52 +00:00
|
|
|
{
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
NSString *ext = [fileType lowercaseString];
|
|
|
|
NSMutableArray *decoders;
|
|
|
|
if (![decodersByExtension objectForKey:ext])
|
|
|
|
{
|
|
|
|
decoders = [[[NSMutableArray alloc] init] autorelease];
|
|
|
|
[decodersByExtension setObject:decoders forKey:ext];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
decoders = [decodersByExtension objectForKey:ext];
|
|
|
|
[decoders addObject:className];
|
2007-10-14 18:12:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (decoder && [decoder respondsToSelector:@selector(mimeTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id mimeType in [decoder mimeTypes])
|
2007-10-14 18:12:15 +00:00
|
|
|
{
|
|
|
|
[decodersByMimeType setObject:className forKey:[mimeType lowercaseString]];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
2007-03-02 01:36:52 +00:00
|
|
|
}
|
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2007-03-02 01:36:52 +00:00
|
|
|
- (void)setupMetadataReader:(NSString *)className
|
|
|
|
{
|
|
|
|
Class metadataReader = NSClassFromString(className);
|
|
|
|
if (metadataReader && [metadataReader respondsToSelector:@selector(fileTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id fileType in [metadataReader fileTypes])
|
2007-03-02 01:36:52 +00:00
|
|
|
{
|
2007-06-10 22:05:49 +00:00
|
|
|
[metadataReaders setObject:className forKey:[fileType lowercaseString]];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
2007-03-02 01:36:52 +00:00
|
|
|
}
|
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2007-03-02 01:36:52 +00:00
|
|
|
- (void)setupPropertiesReader:(NSString *)className
|
|
|
|
{
|
|
|
|
Class propertiesReader = NSClassFromString(className);
|
|
|
|
if (propertiesReader && [propertiesReader respondsToSelector:@selector(fileTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id fileType in [propertiesReader fileTypes])
|
2007-03-02 01:36:52 +00:00
|
|
|
{
|
2007-10-14 18:12:15 +00:00
|
|
|
[propertiesReadersByExtension setObject:className forKey:[fileType lowercaseString]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (propertiesReader && [propertiesReader respondsToSelector:@selector(mimeTypes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id mimeType in [propertiesReader mimeTypes])
|
2007-10-14 18:12:15 +00:00
|
|
|
{
|
|
|
|
[propertiesReadersByMimeType setObject:className forKey:[mimeType lowercaseString]];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-02 01:36:52 +00:00
|
|
|
- (void)setupSource:(NSString *)className
|
|
|
|
{
|
|
|
|
Class source = NSClassFromString(className);
|
|
|
|
if (source && [source respondsToSelector:@selector(schemes)]) {
|
2008-03-01 15:28:17 +00:00
|
|
|
for (id scheme in [source schemes])
|
2007-03-02 01:36:52 +00:00
|
|
|
{
|
|
|
|
[sources setObject:className forKey:scheme];
|
|
|
|
}
|
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)printPluginInfo
|
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Sources: %@", self.sources);
|
|
|
|
ALog(@"Containers: %@", self.containers);
|
|
|
|
ALog(@"Metadata Readers: %@", self.metadataReaders);
|
2007-10-14 18:12:15 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Properties Readers By Extension: %@", self.propertiesReadersByExtension);
|
|
|
|
ALog(@"Properties Readers By Mime Type: %@", self.propertiesReadersByMimeType);
|
2007-10-14 18:12:15 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Decoders by Extension: %@", self.decodersByExtension);
|
|
|
|
ALog(@"Decoders by Mime Type: %@", self.decodersByMimeType);
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
2007-10-09 01:20:46 +00:00
|
|
|
- (id<CogSource>) audioSourceForURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
NSString *scheme = [url scheme];
|
|
|
|
|
|
|
|
Class source = NSClassFromString([sources objectForKey:scheme]);
|
|
|
|
|
|
|
|
return [[[source alloc] init] autorelease];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) urlsForContainerURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
NSString *ext = [[url path] pathExtension];
|
|
|
|
|
|
|
|
Class container = NSClassFromString([containers objectForKey:[ext lowercaseString]]);
|
|
|
|
|
|
|
|
return [container urlsForContainerURL:url];
|
|
|
|
}
|
|
|
|
|
2007-10-14 18:12:15 +00:00
|
|
|
//Note: Source is assumed to already be opened.
|
|
|
|
- (id<CogDecoder>) audioDecoderForSource:(id <CogSource>)source
|
2007-10-09 01:20:46 +00:00
|
|
|
{
|
2007-10-14 18:12:15 +00:00
|
|
|
NSString *ext = [[[source url] path] pathExtension];
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
NSArray *decoders = [decodersByExtension objectForKey:[ext lowercaseString]];
|
|
|
|
NSString *classString;
|
|
|
|
if (decoders) {
|
|
|
|
if ( [decoders count] > 1 ) {
|
|
|
|
return [[[CogDecoderMulti alloc] initWithDecoders:decoders] autorelease];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
classString = [decoders objectAtIndex:0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2007-10-14 18:12:15 +00:00
|
|
|
classString = [decodersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
|
|
|
}
|
|
|
|
|
|
|
|
Class decoder = NSClassFromString(classString);
|
2007-10-09 01:20:46 +00:00
|
|
|
|
|
|
|
return [[[decoder alloc] init] autorelease];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *)metadataForURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
NSString *ext = [[url path] pathExtension];
|
|
|
|
|
|
|
|
Class metadataReader = NSClassFromString([metadataReaders objectForKey:[ext lowercaseString]]);
|
|
|
|
|
|
|
|
return [metadataReader metadataForURL:url];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-10-14 17:31:20 +00:00
|
|
|
|
|
|
|
//If no properties reader is defined, use the decoder's properties.
|
2007-10-09 01:20:46 +00:00
|
|
|
- (NSDictionary *)propertiesForURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
NSString *ext = [[url path] pathExtension];
|
|
|
|
|
|
|
|
id<CogSource> source = [self audioSourceForURL:url];
|
|
|
|
if (![source open:url])
|
|
|
|
return nil;
|
|
|
|
|
2007-10-14 18:12:15 +00:00
|
|
|
NSString *classString = [propertiesReadersByExtension objectForKey:[ext lowercaseString]];
|
|
|
|
if (!classString) {
|
|
|
|
classString = [propertiesReadersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
|
|
|
}
|
|
|
|
|
2007-10-14 17:31:20 +00:00
|
|
|
if (classString)
|
|
|
|
{
|
|
|
|
Class propertiesReader = NSClassFromString(classString);
|
2007-03-02 01:36:52 +00:00
|
|
|
|
2007-10-14 17:31:20 +00:00
|
|
|
return [propertiesReader propertiesForSource:source];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-10-14 18:12:15 +00:00
|
|
|
|
|
|
|
id<CogDecoder> decoder = [self audioDecoderForSource:source];
|
2007-10-14 17:31:20 +00:00
|
|
|
if (![decoder open:source])
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSDictionary *properties = [decoder properties];
|
|
|
|
|
|
|
|
[decoder close];
|
|
|
|
|
|
|
|
return properties;
|
|
|
|
}
|
2007-10-09 01:20:46 +00:00
|
|
|
}
|
2007-03-02 01:36:52 +00:00
|
|
|
|
2013-10-03 08:00:58 +00:00
|
|
|
- (int)putMetadataInURL:(NSURL *)url
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
@end
|
|
|
|
|