diff --git a/src/CoreAudio/AudioToolbox/AudioFile.cpp b/src/CoreAudio/AudioToolbox/AudioFile.cpp
index 6d34d85d1..0b155ebfd 100644
--- a/src/CoreAudio/AudioToolbox/AudioFile.cpp
+++ b/src/CoreAudio/AudioToolbox/AudioFile.cpp
@@ -19,6 +19,7 @@ along with Darling. If not, see .
#include
#include
+#include "AudioFileFormatManager.h"
OSStatus AudioFileCreateWithURL (CFURLRef inFileRef,
AudioFileTypeID inFileType,
@@ -193,7 +194,34 @@ OSStatus AudioFileGetGlobalInfoSize( AudioFilePropertyID inPropertyID,
void * _Nullable inSpecifier,
UInt32 *outDataSize)
{
- return unimpErr;
+ return AudioFileGetGlobalInfo(inPropertyID, inSpecifierSize, inSpecifier, outDataSize, nullptr);
+}
+
+static void writeUInt32Set(const std::set& set, void *outPropertyData)
+{
+ if (outPropertyData)
+ {
+ UInt32* out = static_cast(outPropertyData);
+ int i = 0;
+ for (UInt32 v : set)
+ out[i++] = v;
+ }
+}
+
+template
+CFArrayRef stringContainerToArray(const ContainerType& t)
+{
+ std::unique_ptr ptrs(new CFStringRef[t.size()]);
+ int i = 0;
+
+ for (const std::string& str : t)
+ ptrs[i++] = CFStringCreateWithCString(nullptr, str.c_str(), kCFStringEncodingUTF8);
+
+ CFArrayRef rv = CFArrayCreate(nullptr, (const void**) ptrs.get(), t.size(), &kCFTypeArrayCallBacks);
+
+ for (int i = 0; i < t.size(); i++)
+ CFRelease(ptrs[i]);
+ return rv;
}
OSStatus AudioFileGetGlobalInfo( AudioFilePropertyID inPropertyID,
@@ -202,6 +230,142 @@ OSStatus AudioFileGetGlobalInfo( AudioFilePropertyID inPropertyID,
UInt32 *ioDataSize,
void *outPropertyData)
{
+ switch (inPropertyID)
+ {
+ case kAudioFileGlobalInfo_ReadableTypes:
+ case kAudioFileGlobalInfo_WritableTypes:
+ {
+ std::set types = AudioFileFormatManager::instance()->types(inPropertyID == kAudioFileGlobalInfo_WritableTypes);
+
+ *ioDataSize = types.size() * sizeof(UInt32);
+ writeUInt32Set(types, outPropertyData);
+
+ return noErr;
+ }
+ case kAudioFileGlobalInfo_FileTypeName:
+ {
+ const UInt32* fileType = static_cast(inSpecifier);
+ if (inSpecifierSize != sizeof(*fileType))
+ return kAudioFileBadPropertySizeError;
+
+ const AudioFileFormatManager::ComponentInfo* ci;
+
+ ci = AudioFileFormatManager::instance()->fileType(*fileType);
+ if (!ci)
+ return kAudioFileUnsupportedFileTypeError;
+
+ CFStringRef* out = static_cast(outPropertyData);
+ *ioDataSize = sizeof(*out);
+
+ if (out)
+ *out = CFStringCreateWithCString(nullptr, ci->name.c_str(), kCFStringEncodingUTF8);
+
+ return noErr;
+ }
+ case kAudioFileGlobalInfo_AvailableStreamDescriptionsForFormat:
+ {
+ const AudioFileTypeAndFormatID* spec = static_cast(inSpecifier);
+ if (inSpecifierSize != sizeof(*spec))
+ return kAudioFileBadPropertySizeError;
+ break;
+ }
+ case kAudioFileGlobalInfo_AvailableFormatIDs:
+ {
+ const UInt32* fileType = static_cast(inSpecifier);
+ if (inSpecifierSize != sizeof(*fileType))
+ return kAudioFileBadPropertySizeError;
+
+ auto set = AudioFileFormatManager::instance()->availableFormatIDs(*fileType);
+ *ioDataSize = set.size() * sizeof(UInt32);
+
+ writeUInt32Set(set, outPropertyData);
+
+ return noErr;
+ }
+ case kAudioFileGlobalInfo_AllHFSTypeCodes:
+ case kAudioFileGlobalInfo_HFSTypeCodesForType:
+ case kAudioFileGlobalInfo_TypesForHFSTypeCode:
+ *ioDataSize = 0;
+ return noErr;
+ case kAudioFileGlobalInfo_AllExtensions:
+ case kAudioFileGlobalInfo_AllUTIs:
+ case kAudioFileGlobalInfo_AllMIMETypes:
+ {
+ *ioDataSize = sizeof(CFArrayRef);
+
+ if (outPropertyData)
+ {
+ std::set set;
+ AudioFileFormatManager* mgr = AudioFileFormatManager::instance();
+
+ if (inPropertyID == kAudioFileGlobalInfo_AllExtensions)
+ set = mgr->allExtensions();
+ else if (inPropertyID == kAudioFileGlobalInfo_AllUTIs)
+ set = mgr->allUTIs();
+ else if (inPropertyID == kAudioFileGlobalInfo_AllMIMETypes)
+ set = mgr->allMIMEs();
+
+ *((CFArrayRef*)outPropertyData) = stringContainerToArray(set);
+ }
+
+ return noErr;
+ }
+ case kAudioFileGlobalInfo_ExtensionsForType:
+ case kAudioFileGlobalInfo_UTIsForType:
+ case kAudioFileGlobalInfo_MIMETypesForType:
+ {
+ const UInt32* fileType = static_cast(inSpecifier);
+ if (inSpecifierSize != sizeof(*fileType))
+ return kAudioFileBadPropertySizeError;
+
+ *ioDataSize = sizeof(CFArrayRef);
+
+ if (outPropertyData)
+ {
+ const std::vector* vector;
+ const AudioFileFormatManager::ComponentInfo* ci;
+
+ ci = AudioFileFormatManager::instance()->fileType(*fileType);
+ if (!ci)
+ return kAudioFileUnsupportedFileTypeError;
+
+ if (inPropertyID == kAudioFileGlobalInfo_ExtensionsForType)
+ vector = &ci->extensions;
+ else if (inPropertyID == kAudioFileGlobalInfo_UTIsForType)
+ vector = &ci->utis;
+ else if (inPropertyID == kAudioFileGlobalInfo_MIMETypesForType)
+ vector = &ci->mimeTypes;
+
+ *((CFArrayRef*)outPropertyData) = stringContainerToArray(*vector);
+ }
+
+ return noErr;
+ }
+ case kAudioFileGlobalInfo_TypesForMIMEType:
+ case kAudioFileGlobalInfo_TypesForUTI:
+ case kAudioFileGlobalInfo_TypesForExtension:
+ {
+ CFStringRef str = static_cast(inSpecifier);
+ if (inSpecifierSize != sizeof(str))
+ return kAudioFileBadPropertySizeError;
+
+ AudioFileFormatManager* mgr = AudioFileFormatManager::instance();
+ const char* cstr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
+ std::set set;
+
+ if (inPropertyID == kAudioFileGlobalInfo_TypesForMIMEType)
+ set = mgr->typesForMIME(cstr);
+ else if (inPropertyID == kAudioFileGlobalInfo_TypesForUTI)
+ set = mgr->typesForUTI(cstr);
+ else if (inPropertyID == kAudioFileGlobalInfo_TypesForExtension)
+ set = mgr->typesForExtension(cstr);
+
+ *ioDataSize = set.size() * sizeof(UInt32);
+ writeUInt32Set(set, outPropertyData);
+
+ return noErr;
+ }
+ }
return unimpErr;
}
diff --git a/src/CoreAudio/AudioToolbox/AudioFileFormatManager.cpp b/src/CoreAudio/AudioToolbox/AudioFileFormatManager.cpp
index 4b1cb5f54..d6ff9a3fd 100644
--- a/src/CoreAudio/AudioToolbox/AudioFileFormatManager.cpp
+++ b/src/CoreAudio/AudioToolbox/AudioFileFormatManager.cpp
@@ -44,15 +44,126 @@ void AudioFileFormatManager::buildDatabase()
}
}
+static std::vector cfArrayToVector(CFArrayRef array)
+{
+ std::vector rv;
+ const CFIndex count = CFArrayGetCount(array);
+
+ rv.reserve(count);
+
+ for (CFIndex i = 0; i < count; i++)
+ {
+ CFStringRef str = (CFStringRef) CFArrayGetValueAtIndex(array, i);
+ rv.push_back(CFStringGetCStringPtr(str, kCFStringEncodingUTF8));
+ }
+
+ return rv;
+}
+
void AudioFileFormatManager::analyzeAudioFileComponent(AudioFileComponent component)
{
- /*
- kAudioFileComponent_FileTypeName = 'ftnm',
- kAudioFileComponent_UTIsForType = 'futi',
- kAudioFileComponent_MIMETypesForType = 'fmim',
- kAudioFileComponent_ExtensionsForType = 'fext',
- kAudioFileComponent_AvailableFormatIDs = 'fmid',
- */
+ ComponentInfo ci;
+ UInt32 propSize;
+
+ ::AudioComponentGetDescription(AudioComponent(component), &ci.acd);
+
+ ci.fileType = ci.acd.componentSubType; // TODO: Is this correct?
+
+ {
+ CFStringRef name = nullptr;
+ propSize = sizeof(name);
+
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_FileTypeName, 0, nullptr, &propSize, &name) == noErr && name)
+ {
+ ci.name = CFStringGetCStringPtr(name, kCFStringEncodingUTF8);
+ CFRelease(name);
+ }
+ }
+ {
+ UInt32 can;
+
+ propSize = sizeof(can);
+ ci.canRead = ci.canWrite = false;
+
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_CanRead, 0, nullptr, &propSize, &can) == noErr && can)
+ ci.canRead = true;
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_CanWrite, 0, nullptr, &propSize, &can) == noErr && can)
+ ci.canWrite = true;
+ }
+
+ CFArrayRef array = nullptr;
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_UTIsForType, 0, nullptr, &propSize, &array) == noErr && array)
+ {
+ ci.utis = cfArrayToVector(array);
+ CFRelease(array);
+ }
+
+ array = nullptr;
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_MIMETypesForType, 0, nullptr, &propSize, &array) == noErr && array)
+ {
+ ci.mimeTypes = cfArrayToVector(array);
+ CFRelease(array);
+ }
+
+ array = nullptr;
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_ExtensionsForType, 0, nullptr, &propSize, &array) == noErr && array)
+ {
+ ci.extensions = cfArrayToVector(array);
+ CFRelease(array);
+ }
+
+ if (::AudioFileComponentGetGlobalInfoSize(component, kAudioFileComponent_AvailableFormatIDs, 0, nullptr, &propSize) == noErr)
+ {
+ const size_t formatCount = propSize / sizeof(UInt32);
+ std::unique_ptr formatIds(new UInt32[formatCount]);
+
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_AvailableFormatIDs, 0, nullptr, &propSize, formatIds.get()) == noErr)
+ {
+ for (size_t i = 0; i < formatCount; i++)
+ {
+ UInt32 format = formatIds[i];
+ std::vector asbds;
+
+ if (::AudioFileComponentGetGlobalInfoSize(component, kAudioFileComponent_AvailableStreamDescriptionsForFormat, sizeof(UInt32), &format, &propSize) == noErr)
+ {
+ asbds.resize(propSize / sizeof(AudioStreamBasicDescription));
+
+ if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_AvailableStreamDescriptionsForFormat, sizeof(UInt32), &format, &propSize, asbds.data()) != noErr)
+ asbds.resize(0);
+
+ ci.formatDescriptions.emplace(format, std::move(asbds));
+ }
+ else
+ ci.formatDescriptions.emplace(format, asbds);
+ }
+ }
+ }
+
+ registerComponent(ci);
+}
+
+void AudioFileFormatManager::addToMap(std::unordered_map>& map, const std::string& key, ComponentInfo* ci)
+{
+ auto it = map.find(key);
+ if (it == map.end())
+ it = map.emplace(key, std::vector()).first;
+ it->second.push_back(ci);
+}
+
+void AudioFileFormatManager::registerComponent(const ComponentInfo& ci)
+{
+ m_components.push_back(ci);
+
+ // Add format into various maps
+ ComponentInfo* pci = &m_components.back();
+ m_fileTypeMap.emplace(ci.fileType, pci);
+
+ for (const std::string& uti : ci.utis)
+ addToMap(m_utiMap, uti, pci);
+ for (const std::string& mime : ci.mimeTypes)
+ addToMap(m_mimeMap, mime, pci);
+ for (const std::string& ext : ci.extensions)
+ addToMap(m_extensionMap, ext, pci);
}
AudioFileFormatManager* AudioFileFormatManager::instance()
@@ -60,3 +171,85 @@ AudioFileFormatManager* AudioFileFormatManager::instance()
static AudioFileFormatManager inst;
return &inst;
}
+
+std::set AudioFileFormatManager::availableFormatIDs(UInt32 fileType) const
+{
+ std::set rv;
+
+ auto it = m_fileTypeMap.find(fileType);
+ if (it == m_fileTypeMap.end())
+ return rv;
+
+ const ComponentInfo* ci = it->second;
+
+ for (auto const& [key, value] : ci->formatDescriptions)
+ rv.insert(key);
+ return rv;
+}
+
+const AudioFileFormatManager::ComponentInfo* AudioFileFormatManager::fileType(UInt32 fileType) const
+{
+ auto it = m_fileTypeMap.find(fileType);
+ if (it == m_fileTypeMap.end())
+ return nullptr;
+ return it->second;
+}
+
+std::set AudioFileFormatManager::types(bool writableTypes) const
+{
+ std::set rv;
+
+ for (const ComponentInfo& ci : m_components)
+ {
+ if (writableTypes && ci.canWrite)
+ rv.insert(ci.fileType);
+ else if (!writableTypes && ci.canRead)
+ rv.insert(ci.fileType);
+ }
+
+ return rv;
+}
+
+std::set AudioFileFormatManager::findTypes(const std::unordered_map>& map, const char* key)
+{
+ std::set rv;
+
+ auto it = map.find(key);
+ if (it == map.end())
+ return rv;
+
+ for (const ComponentInfo* ci : it->second)
+ rv.insert(ci->fileType);
+
+ return rv;
+}
+
+std::set AudioFileFormatManager::typesForMIME(const char* mime) const
+{
+ return findTypes(m_mimeMap, mime);
+}
+
+std::set AudioFileFormatManager::typesForUTI(const char* uti) const
+{
+ return findTypes(m_utiMap, uti);
+}
+
+std::set AudioFileFormatManager::typesForExtension(const char* ext) const
+{
+ return findTypes(m_extensionMap, ext);
+}
+
+std::set AudioFileFormatManager::allMIMEs() const
+{
+ return allKeys(m_mimeMap);
+}
+
+std::set AudioFileFormatManager::allUTIs() const
+{
+ return allKeys(m_utiMap);
+}
+
+std::set AudioFileFormatManager::allExtensions() const
+{
+ return allKeys(m_extensionMap);
+}
diff --git a/src/CoreAudio/AudioToolbox/AudioFileFormatManager.h b/src/CoreAudio/AudioToolbox/AudioFileFormatManager.h
index 596d89702..f937f6fa9 100644
--- a/src/CoreAudio/AudioToolbox/AudioFileFormatManager.h
+++ b/src/CoreAudio/AudioToolbox/AudioFileFormatManager.h
@@ -22,15 +22,60 @@ along with Darling. If not, see .
#define _AUDIO_FILE_FORMAT_MANAGER_H
#include
#include
+#include
+#include
+#include
+#include
+#include
class __attribute__((visibility("hidden"))) AudioFileFormatManager
{
+public:
+ static AudioFileFormatManager* instance();
+
+ std::set availableFormatIDs(UInt32 fileType) const;
+
+ struct ComponentInfo
+ {
+ AudioComponentDescription acd;
+ UInt32 fileType; // container type
+ std::string name;
+ std::vector utis, extensions, mimeTypes;
+ // codec type
+ std::unordered_map> formatDescriptions;
+ bool canRead, canWrite;
+ };
+
+ const ComponentInfo* fileType(UInt32 type) const;
+ std::set types(bool writableTypes) const;
+
+ std::set typesForMIME(const char* mime) const;
+ std::set typesForUTI(const char* uti) const;
+ std::set typesForExtension(const char* ext) const;
+
+ std::set allMIMEs() const;
+ std::set allUTIs() const;
+ std::set allExtensions() const;
private:
AudioFileFormatManager();
void buildDatabase();
void analyzeAudioFileComponent(AudioFileComponent component);
-public:
- static AudioFileFormatManager* instance();
+ void registerComponent(const ComponentInfo& ci);
+ static void addToMap(std::unordered_map>& map, const std::string& key, ComponentInfo* ci);
+ static std::set findTypes(const std::unordered_map>& map, const char* key);
+
+ template
+ std::set allKeys(const std::unordered_map& map) const
+ {
+ std::set rv;
+ for (auto const& [k, v] : map)
+ rv.insert(k);
+ return rv;
+ }
+private:
+ std::list m_components;
+ std::unordered_map m_fileTypeMap;
+ std::unordered_map> m_mimeMap, m_utiMap, m_extensionMap;
};
#endif