/* * Copyright 2004-2010, Haiku. All rights reserved. * Distributed under the terms of the MIT license. * * Authors: * Marcus Overhagen * Axel Dörfler * Stephan Aßmus */ #include "AddOnManager.h" #include #include #include #include #include #include #include #include #include #include #include "MediaDebug.h" #include "FormatManager.h" #include "MetaFormat.h" namespace BPrivate { namespace media { // #pragma mark - ImageLoader /*! The ImageLoader class is a convenience class to temporarily load an image file, and unload it on deconstruction automatically. */ class ImageLoader { public: ImageLoader(BPath& path) { fImage = load_add_on(path.Path()); } ~ImageLoader() { if (fImage >= B_OK) unload_add_on(fImage); } status_t InitCheck() const { return fImage >= 0 ? B_OK : fImage; } image_id Image() const { return fImage; } private: image_id fImage; }; // #pragma mark - AddOnManager::AddOnManager() : fLock("add-on manager"), fNextWriterFormatFamilyID(0), fNextEncoderCodecInfoID(0) { } AddOnManager::~AddOnManager() { } AddOnManager AddOnManager::sInstance; /* static */ AddOnManager* AddOnManager::GetInstance() { return &sInstance; } status_t AddOnManager::GetDecoderForFormat(entry_ref* _decoderRef, const media_format& format) { if ((format.type == B_MEDIA_ENCODED_VIDEO || format.type == B_MEDIA_ENCODED_AUDIO || format.type == B_MEDIA_MULTISTREAM) && format.Encoding() == 0) { return B_MEDIA_BAD_FORMAT; } if (format.type == B_MEDIA_NO_TYPE || format.type == B_MEDIA_UNKNOWN_TYPE) return B_MEDIA_BAD_FORMAT; BAutolock locker(fLock); RegisterAddOns(); // Since the list of decoders is unsorted, we need to search for // a decoder by add-on directory, in order to maintain the shadowing // of system add-ons by user add-ons, in case they offer decoders // for the same format. char** directories = NULL; size_t directoryCount = 0; if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, &directoryCount) != B_OK) { printf("AddOnManager::GetDecoderForFormat: failed to locate plugins\n"); return B_ENTRY_NOT_FOUND; } MemoryDeleter directoriesDeleter(directories); BPath path; for (uint i = 0; i < directoryCount; i++) { path.SetTo(directories[i]); if (_FindDecoder(format, path, _decoderRef)) return B_OK; } return B_ENTRY_NOT_FOUND; } status_t AddOnManager::GetEncoderForFormat(entry_ref* _encoderRef, const media_format& outputFormat) { if ((outputFormat.type == B_MEDIA_RAW_VIDEO || outputFormat.type == B_MEDIA_RAW_AUDIO)) { return B_MEDIA_BAD_FORMAT; } if (outputFormat.type == B_MEDIA_NO_TYPE || outputFormat.type == B_MEDIA_UNKNOWN_TYPE) { return B_MEDIA_BAD_FORMAT; } BAutolock locker(fLock); RegisterAddOns(); char** directories = NULL; size_t directoryCount = 0; if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, &directoryCount) != B_OK) { printf("AddOnManager::GetDecoderForFormat: failed to locate plugins\n"); return B_ENTRY_NOT_FOUND; } MemoryDeleter directoriesDeleter(directories); BPath path; for (uint i = 0; i < directoryCount; i++) { path.SetTo(directories[i]); if (_FindEncoder(outputFormat, path, _encoderRef)) return B_OK; } return B_ENTRY_NOT_FOUND; } status_t AddOnManager::GetReaders(entry_ref* outRefs, int32* outCount, int32 maxCount) { BAutolock locker(fLock); RegisterAddOns(); *outCount = 0; // See GetDecoderForFormat() for why we need to scan the list by path. char** directories = NULL; size_t directoryCount = 0; if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, &directoryCount) != B_OK) { printf("AddOnManager::GetReaders: failed to locate plugins\n"); return B_ENTRY_NOT_FOUND; } MemoryDeleter directoriesDeleter(directories); BPath path; for (uint i = 0; i < directoryCount; i++) { path.SetTo(directories[i]); _GetReaders(path, outRefs, outCount, maxCount); } return B_OK; } status_t AddOnManager::GetStreamers(entry_ref* outRefs, int32* outCount, int32 maxCount) { BAutolock locker(fLock); RegisterAddOns(); int32 count = 0; streamer_info* info; for (fStreamerList.Rewind(); fStreamerList.GetNext(&info);) { if (count == maxCount) break; *outRefs = info->ref; outRefs++; count++; } *outCount = count; return B_OK; } status_t AddOnManager::GetEncoder(entry_ref* _encoderRef, int32 id) { BAutolock locker(fLock); RegisterAddOns(); encoder_info* info; for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { // check if the encoder matches the supplied format if (info->internalID == (uint32)id) { *_encoderRef = info->ref; return B_OK; } } return B_ENTRY_NOT_FOUND; } status_t AddOnManager::GetWriter(entry_ref* _ref, uint32 internalID) { BAutolock locker(fLock); RegisterAddOns(); writer_info* info; for (fWriterList.Rewind(); fWriterList.GetNext(&info);) { if (info->internalID == internalID) { *_ref = info->ref; return B_OK; } } return B_ERROR; } status_t AddOnManager::GetFileFormat(media_file_format* _fileFormat, int32 cookie) { BAutolock locker(fLock); RegisterAddOns(); media_file_format* fileFormat; if (fWriterFileFormats.Get(cookie, &fileFormat)) { *_fileFormat = *fileFormat; return B_OK; } return B_BAD_INDEX; } status_t AddOnManager::GetCodecInfo(media_codec_info* _codecInfo, media_format_family* _formatFamily, media_format* _inputFormat, media_format* _outputFormat, int32 cookie) { BAutolock locker(fLock); RegisterAddOns(); encoder_info* info; if (fEncoderList.Get(cookie, &info)) { *_codecInfo = info->codecInfo; *_formatFamily = info->formatFamily; *_inputFormat = info->intputFormat; *_outputFormat = info->outputFormat; return B_OK; } return B_BAD_INDEX; } // #pragma mark - void AddOnManager::RegisterAddOns() { // Check if add-ons are already registered. if (!fReaderList.IsEmpty() || !fWriterList.IsEmpty() || !fDecoderList.IsEmpty() || !fEncoderList.IsEmpty()) { return; } char** directories = NULL; size_t directoryCount = 0; if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, &directoryCount) != B_OK) { return; } MemoryDeleter directoriesDeleter(directories); BPath path; for (uint i = 0; i < directoryCount; i++) { BDirectory directory; if (directory.SetTo(directories[i]) == B_OK) { entry_ref ref; while(directory.GetNextRef(&ref) == B_OK) _RegisterAddOn(ref); } } } status_t AddOnManager::_RegisterAddOn(const entry_ref& ref) { BPath path(&ref); ImageLoader loader(path); status_t status = loader.InitCheck(); if (status != B_OK) return status; MediaPlugin* (*instantiate_plugin_func)(); if (get_image_symbol(loader.Image(), "instantiate_plugin", B_SYMBOL_TYPE_TEXT, (void**)&instantiate_plugin_func) < B_OK) { printf("AddOnManager::_RegisterAddOn(): can't find instantiate_plugin " "in \"%s\"\n", path.Path()); return B_BAD_TYPE; } MediaPlugin* plugin = (*instantiate_plugin_func)(); if (plugin == NULL) { printf("AddOnManager::_RegisterAddOn(): instantiate_plugin in \"%s\" " "returned NULL\n", path.Path()); return B_ERROR; } ReaderPlugin* reader = dynamic_cast(plugin); if (reader != NULL) _RegisterReader(reader, ref); DecoderPlugin* decoder = dynamic_cast(plugin); if (decoder != NULL) _RegisterDecoder(decoder, ref); WriterPlugin* writer = dynamic_cast(plugin); if (writer != NULL) _RegisterWriter(writer, ref); EncoderPlugin* encoder = dynamic_cast(plugin); if (encoder != NULL) _RegisterEncoder(encoder, ref); StreamerPlugin* streamer = dynamic_cast(plugin); if (streamer != NULL) _RegisterStreamer(streamer, ref); delete plugin; return B_OK; } status_t AddOnManager::_UnregisterAddOn(const entry_ref& ref) { BAutolock locker(fLock); // Remove any Readers exported by this add-on reader_info* readerInfo; for (fReaderList.Rewind(); fReaderList.GetNext(&readerInfo);) { if (readerInfo->ref == ref) { fReaderList.RemoveCurrent(); break; } } // Remove any Decoders exported by this add-on decoder_info* decoderInfo; for (fDecoderList.Rewind(); fDecoderList.GetNext(&decoderInfo);) { if (decoderInfo->ref == ref) { media_format* format; for (decoderInfo->formats.Rewind(); decoderInfo->formats.GetNext(&format);) { FormatManager::GetInstance()->RemoveFormat(*format); } fDecoderList.RemoveCurrent(); break; } } // Remove any Writers exported by this add-on writer_info* writerInfo; for (fWriterList.Rewind(); fWriterList.GetNext(&writerInfo);) { if (writerInfo->ref == ref) { // Remove any formats from this writer media_file_format* writerFormat; for (fWriterFileFormats.Rewind(); fWriterFileFormats.GetNext(&writerFormat);) { if (writerFormat->id.internal_id == writerInfo->internalID) fWriterFileFormats.RemoveCurrent(); } fWriterList.RemoveCurrent(); break; } } encoder_info* encoderInfo; for (fEncoderList.Rewind(); fEncoderList.GetNext(&encoderInfo);) { if (encoderInfo->ref == ref) { fEncoderList.RemoveCurrent(); // Keep going, since we add multiple encoder infos per add-on. } } return B_OK; } void AddOnManager::_RegisterReader(ReaderPlugin* reader, const entry_ref& ref) { BAutolock locker(fLock); reader_info* pinfo; for (fReaderList.Rewind(); fReaderList.GetNext(&pinfo);) { if (!strcmp(pinfo->ref.name, ref.name)) { // we already know this reader return; } } reader_info info; info.ref = ref; fReaderList.Insert(info); } void AddOnManager::_RegisterDecoder(DecoderPlugin* plugin, const entry_ref& ref) { BAutolock locker(fLock); decoder_info* pinfo; for (fDecoderList.Rewind(); fDecoderList.GetNext(&pinfo);) { if (!strcmp(pinfo->ref.name, ref.name)) { // we already know this decoder return; } } decoder_info info; info.ref = ref; media_format* formats = 0; size_t count = 0; if (plugin->GetSupportedFormats(&formats, &count) != B_OK) { printf("AddOnManager::_RegisterDecoder(): plugin->GetSupportedFormats" "(...) failed!\n"); return; } for (uint i = 0 ; i < count ; i++) info.formats.Insert(formats[i]); fDecoderList.Insert(info); } void AddOnManager::_RegisterWriter(WriterPlugin* writer, const entry_ref& ref) { BAutolock locker(fLock); writer_info* pinfo; for (fWriterList.Rewind(); fWriterList.GetNext(&pinfo);) { if (!strcmp(pinfo->ref.name, ref.name)) { // we already know this writer return; } } writer_info info; info.ref = ref; info.internalID = fNextWriterFormatFamilyID++; // Get list of support media_file_formats... const media_file_format* fileFormats = NULL; size_t count = 0; if (writer->GetSupportedFileFormats(&fileFormats, &count) != B_OK) { printf("AddOnManager::_RegisterWriter(): " "plugin->GetSupportedFileFormats(...) failed!\n"); return; } for (uint i = 0 ; i < count ; i++) { // Generate a proper ID before inserting this format, this encodes // the specific plugin in the media_file_format. media_file_format fileFormat = fileFormats[i]; fileFormat.id.node = ref.directory; fileFormat.id.device = ref.device; fileFormat.id.internal_id = info.internalID; fWriterFileFormats.Insert(fileFormat); } fWriterList.Insert(info); } void AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) { BAutolock locker(fLock); encoder_info* pinfo; for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) { if (!strcmp(pinfo->ref.name, ref.name)) { // We already know this encoder. When we reject encoders with // the same name, we allow the user to overwrite system encoders // in her home folder. return; } } // Get list of supported encoders... encoder_info info; info.ref = ref; info.internalID = fNextEncoderCodecInfoID++; int32 cookie = 0; while (true) { memset(&info.codecInfo, 0, sizeof(media_codec_info)); info.intputFormat.Clear(); info.outputFormat.Clear(); if (plugin->RegisterNextEncoder(&cookie, &info.codecInfo, &info.formatFamily, &info.intputFormat, &info.outputFormat) != B_OK) { break; } info.codecInfo.id = info.internalID; // NOTE: info.codecInfo.sub_id is for private use by the Encoder, // we don't touch it, but it is maintained and passed back to the // EncoderPlugin in NewEncoder(media_codec_info). if (!fEncoderList.Insert(info)) break; } } void AddOnManager::_RegisterStreamer(StreamerPlugin* streamer, const entry_ref& ref) { BAutolock locker(fLock); streamer_info* pInfo; for (fStreamerList.Rewind(); fStreamerList.GetNext(&pInfo);) { if (!strcmp(pInfo->ref.name, ref.name)) { // We already know this streamer return; } } streamer_info info; info.ref = ref; fStreamerList.Insert(info); } bool AddOnManager::_FindDecoder(const media_format& format, const BPath& path, entry_ref* _decoderRef) { node_ref nref; BDirectory directory; if (directory.SetTo(path.Path()) != B_OK || directory.GetNodeRef(&nref) != B_OK) { return false; } decoder_info* info; for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) { if (info->ref.directory != nref.node) continue; media_format* decoderFormat; for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) { // check if the decoder matches the supplied format if (!decoderFormat->Matches(&format)) continue; *_decoderRef = info->ref; return true; } } return false; } bool AddOnManager::_FindEncoder(const media_format& format, const BPath& path, entry_ref* _encoderRef) { node_ref nref; BDirectory directory; if (directory.SetTo(path.Path()) != B_OK || directory.GetNodeRef(&nref) != B_OK) { return false; } encoder_info* info; for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { if (info->ref.directory != nref.node) continue; // check if the encoder matches the supplied format if (info->outputFormat.Matches(&format)) { *_encoderRef = info->ref; return true; } continue; } return false; } void AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs, int32* outCount, int32 maxCount) { node_ref nref; BDirectory directory; if (directory.SetTo(path.Path()) != B_OK || directory.GetNodeRef(&nref) != B_OK) { return; } reader_info* info; for (fReaderList.Rewind(); fReaderList.GetNext(&info) && *outCount < maxCount;) { if (info->ref.directory != nref.node) continue; outRefs[*outCount] = info->ref; (*outCount)++; } } } // namespace media } // namespace BPrivate