1 /* 2 * Copyright 2004-2010, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Marcus Overhagen 7 * Axel Dörfler 8 * Stephan Aßmus <superstippi@gmx.de> 9 */ 10 11 12 #include "AddOnManager.h" 13 14 #include <stdio.h> 15 #include <string.h> 16 17 #include <Architecture.h> 18 #include <Autolock.h> 19 #include <Directory.h> 20 #include <Entry.h> 21 #include <FindDirectory.h> 22 #include <image.h> 23 #include <Path.h> 24 25 #include "debug.h" 26 27 #include "FormatManager.h" 28 #include "MetaFormat.h" 29 30 31 // #pragma mark - ImageLoader 32 33 /*! The ImageLoader class is a convenience class to temporarily load 34 an image file, and unload it on deconstruction automatically. 35 */ 36 class ImageLoader { 37 public: 38 ImageLoader(BPath& path) 39 { 40 fImage = load_add_on(path.Path()); 41 } 42 43 ~ImageLoader() 44 { 45 if (fImage >= B_OK) 46 unload_add_on(fImage); 47 } 48 49 status_t InitCheck() const { return fImage >= 0 ? B_OK : fImage; } 50 image_id Image() const { return fImage; } 51 52 private: 53 image_id fImage; 54 }; 55 56 57 // #pragma mark - 58 59 60 AddOnManager::AddOnManager() 61 : 62 fLock("add-on manager"), 63 fNextWriterFormatFamilyID(0), 64 fNextEncoderCodecInfoID(0) 65 { 66 } 67 68 69 AddOnManager::~AddOnManager() 70 { 71 } 72 73 74 AddOnManager AddOnManager::sInstance; 75 76 77 /* static */ AddOnManager* 78 AddOnManager::GetInstance() 79 { 80 return &sInstance; 81 } 82 83 84 status_t 85 AddOnManager::GetDecoderForFormat(entry_ref* _decoderRef, 86 const media_format& format) 87 { 88 if ((format.type == B_MEDIA_ENCODED_VIDEO 89 || format.type == B_MEDIA_ENCODED_AUDIO 90 || format.type == B_MEDIA_MULTISTREAM) 91 && format.Encoding() == 0) { 92 return B_MEDIA_BAD_FORMAT; 93 } 94 if (format.type == B_MEDIA_NO_TYPE || format.type == B_MEDIA_UNKNOWN_TYPE) 95 return B_MEDIA_BAD_FORMAT; 96 97 BAutolock locker(fLock); 98 RegisterAddOns(); 99 100 // Since the list of decoders is unsorted, we need to search for 101 // a decoder by add-on directory, in order to maintain the shadowing 102 // of system add-ons by user add-ons, in case they offer decoders 103 // for the same format. 104 105 char** directories = NULL; 106 size_t directoryCount = 0; 107 108 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, 109 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, 110 &directoryCount) != B_OK) { 111 printf("AddOnManager::GetDecoderForFormat: failed to locate plugins\n"); 112 return B_ENTRY_NOT_FOUND; 113 } 114 115 BPath path; 116 for (uint i = 0; i < directoryCount; i++) { 117 path.SetTo(directories[i]); 118 if (_FindDecoder(format, path, _decoderRef)) 119 return B_OK; 120 } 121 122 return B_ENTRY_NOT_FOUND; 123 } 124 125 126 status_t 127 AddOnManager::GetReaders(entry_ref* outRefs, int32* outCount, 128 int32 maxCount) 129 { 130 BAutolock locker(fLock); 131 RegisterAddOns(); 132 133 *outCount = 0; 134 135 // See GetDecoderForFormat() for why we need to scan the list by path. 136 137 char** directories = NULL; 138 size_t directoryCount = 0; 139 140 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, 141 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, 142 &directoryCount) != B_OK) { 143 printf("AddOnManager::GetReaders: failed to locate plugins\n"); 144 return B_ENTRY_NOT_FOUND; 145 } 146 147 BPath path; 148 for (uint i = 0; i < directoryCount; i++) { 149 path.SetTo(directories[i]); 150 _GetReaders(path, outRefs, outCount, maxCount); 151 } 152 153 return B_OK; 154 } 155 156 157 status_t 158 AddOnManager::GetEncoder(entry_ref* _encoderRef, int32 id) 159 { 160 BAutolock locker(fLock); 161 RegisterAddOns(); 162 163 encoder_info* info; 164 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { 165 // check if the encoder matches the supplied format 166 if (info->internalID == (uint32)id) { 167 *_encoderRef = info->ref; 168 return B_OK; 169 } 170 } 171 172 return B_ENTRY_NOT_FOUND; 173 } 174 175 176 status_t 177 AddOnManager::GetWriter(entry_ref* _ref, uint32 internalID) 178 { 179 BAutolock locker(fLock); 180 RegisterAddOns(); 181 182 writer_info* info; 183 for (fWriterList.Rewind(); fWriterList.GetNext(&info);) { 184 if (info->internalID == internalID) { 185 *_ref = info->ref; 186 return B_OK; 187 } 188 } 189 190 return B_ERROR; 191 } 192 193 194 status_t 195 AddOnManager::GetFileFormat(media_file_format* _fileFormat, int32 cookie) 196 { 197 BAutolock locker(fLock); 198 RegisterAddOns(); 199 200 media_file_format* fileFormat; 201 if (fWriterFileFormats.Get(cookie, &fileFormat)) { 202 *_fileFormat = *fileFormat; 203 return B_OK; 204 } 205 206 return B_BAD_INDEX; 207 } 208 209 210 status_t 211 AddOnManager::GetCodecInfo(media_codec_info* _codecInfo, 212 media_format_family* _formatFamily, 213 media_format* _inputFormat, media_format* _outputFormat, int32 cookie) 214 { 215 BAutolock locker(fLock); 216 RegisterAddOns(); 217 218 encoder_info* info; 219 if (fEncoderList.Get(cookie, &info)) { 220 *_codecInfo = info->codecInfo; 221 *_formatFamily = info->formatFamily; 222 *_inputFormat = info->intputFormat; 223 *_outputFormat = info->outputFormat; 224 return B_OK; 225 } 226 227 return B_BAD_INDEX; 228 } 229 230 231 // #pragma mark - 232 233 234 void 235 AddOnManager::RegisterAddOns() 236 { 237 // Check if add-ons are already registered. 238 if (!fReaderList.IsEmpty() || !fWriterList.IsEmpty() 239 || !fDecoderList.IsEmpty() || !fEncoderList.IsEmpty()) { 240 return; 241 } 242 243 char** directories = NULL; 244 size_t directoryCount = 0; 245 246 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, 247 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, 248 &directoryCount) != B_OK) { 249 return; 250 } 251 252 BPath path; 253 for (uint i = 0; i < directoryCount; i++) { 254 BDirectory directory; 255 if (directory.SetTo(directories[i]) == B_OK) { 256 entry_ref ref; 257 while(directory.GetNextRef(&ref) == B_OK) 258 _RegisterAddOn(ref); 259 } 260 } 261 } 262 263 264 status_t 265 AddOnManager::_RegisterAddOn(const entry_ref& ref) 266 { 267 BPath path(&ref); 268 269 ImageLoader loader(path); 270 status_t status = loader.InitCheck(); 271 if (status != B_OK) 272 return status; 273 274 MediaPlugin* (*instantiate_plugin_func)(); 275 276 if (get_image_symbol(loader.Image(), "instantiate_plugin", 277 B_SYMBOL_TYPE_TEXT, (void**)&instantiate_plugin_func) < B_OK) { 278 printf("AddOnManager::_RegisterAddOn(): can't find instantiate_plugin " 279 "in \"%s\"\n", path.Path()); 280 return B_BAD_TYPE; 281 } 282 283 MediaPlugin* plugin = (*instantiate_plugin_func)(); 284 if (plugin == NULL) { 285 printf("AddOnManager::_RegisterAddOn(): instantiate_plugin in \"%s\" " 286 "returned NULL\n", path.Path()); 287 return B_ERROR; 288 } 289 290 ReaderPlugin* reader = dynamic_cast<ReaderPlugin*>(plugin); 291 if (reader != NULL) 292 _RegisterReader(reader, ref); 293 294 DecoderPlugin* decoder = dynamic_cast<DecoderPlugin*>(plugin); 295 if (decoder != NULL) 296 _RegisterDecoder(decoder, ref); 297 298 WriterPlugin* writer = dynamic_cast<WriterPlugin*>(plugin); 299 if (writer != NULL) 300 _RegisterWriter(writer, ref); 301 302 EncoderPlugin* encoder = dynamic_cast<EncoderPlugin*>(plugin); 303 if (encoder != NULL) 304 _RegisterEncoder(encoder, ref); 305 306 delete plugin; 307 308 return B_OK; 309 } 310 311 312 status_t 313 AddOnManager::_UnregisterAddOn(const entry_ref& ref) 314 { 315 BAutolock locker(fLock); 316 317 // Remove any Readers exported by this add-on 318 reader_info* readerInfo; 319 for (fReaderList.Rewind(); fReaderList.GetNext(&readerInfo);) { 320 if (readerInfo->ref == ref) { 321 fReaderList.RemoveCurrent(); 322 break; 323 } 324 } 325 326 // Remove any Decoders exported by this add-on 327 decoder_info* decoderInfo; 328 for (fDecoderList.Rewind(); fDecoderList.GetNext(&decoderInfo);) { 329 if (decoderInfo->ref == ref) { 330 media_format* format; 331 for (decoderInfo->formats.Rewind(); 332 decoderInfo->formats.GetNext(&format);) { 333 FormatManager::GetInstance()->RemoveFormat(*format); 334 } 335 fDecoderList.RemoveCurrent(); 336 break; 337 } 338 } 339 340 // Remove any Writers exported by this add-on 341 writer_info* writerInfo; 342 for (fWriterList.Rewind(); fWriterList.GetNext(&writerInfo);) { 343 if (writerInfo->ref == ref) { 344 // Remove any formats from this writer 345 media_file_format* writerFormat; 346 for (fWriterFileFormats.Rewind(); 347 fWriterFileFormats.GetNext(&writerFormat);) { 348 if (writerFormat->id.internal_id == writerInfo->internalID) 349 fWriterFileFormats.RemoveCurrent(); 350 } 351 fWriterList.RemoveCurrent(); 352 break; 353 } 354 } 355 356 encoder_info* encoderInfo; 357 for (fEncoderList.Rewind(); fEncoderList.GetNext(&encoderInfo);) { 358 if (encoderInfo->ref == ref) { 359 fEncoderList.RemoveCurrent(); 360 // Keep going, since we add multiple encoder infos per add-on. 361 } 362 } 363 364 return B_OK; 365 } 366 367 368 void 369 AddOnManager::_RegisterReader(ReaderPlugin* reader, const entry_ref& ref) 370 { 371 BAutolock locker(fLock); 372 373 reader_info* pinfo; 374 for (fReaderList.Rewind(); fReaderList.GetNext(&pinfo);) { 375 if (!strcmp(pinfo->ref.name, ref.name)) { 376 // we already know this reader 377 return; 378 } 379 } 380 381 reader_info info; 382 info.ref = ref; 383 384 fReaderList.Insert(info); 385 } 386 387 388 void 389 AddOnManager::_RegisterDecoder(DecoderPlugin* plugin, const entry_ref& ref) 390 { 391 BAutolock locker(fLock); 392 393 decoder_info* pinfo; 394 for (fDecoderList.Rewind(); fDecoderList.GetNext(&pinfo);) { 395 if (!strcmp(pinfo->ref.name, ref.name)) { 396 // we already know this decoder 397 return; 398 } 399 } 400 401 decoder_info info; 402 info.ref = ref; 403 404 media_format* formats = 0; 405 size_t count = 0; 406 if (plugin->GetSupportedFormats(&formats, &count) != B_OK) { 407 printf("AddOnManager::_RegisterDecoder(): plugin->GetSupportedFormats" 408 "(...) failed!\n"); 409 return; 410 } 411 for (uint i = 0 ; i < count ; i++) 412 info.formats.Insert(formats[i]); 413 414 fDecoderList.Insert(info); 415 } 416 417 418 void 419 AddOnManager::_RegisterWriter(WriterPlugin* writer, const entry_ref& ref) 420 { 421 BAutolock locker(fLock); 422 423 writer_info* pinfo; 424 for (fWriterList.Rewind(); fWriterList.GetNext(&pinfo);) { 425 if (!strcmp(pinfo->ref.name, ref.name)) { 426 // we already know this writer 427 return; 428 } 429 } 430 431 writer_info info; 432 info.ref = ref; 433 info.internalID = fNextWriterFormatFamilyID++; 434 435 // Get list of support media_file_formats... 436 const media_file_format* fileFormats = NULL; 437 size_t count = 0; 438 if (writer->GetSupportedFileFormats(&fileFormats, &count) != B_OK) { 439 printf("AddOnManager::_RegisterWriter(): " 440 "plugin->GetSupportedFileFormats(...) failed!\n"); 441 return; 442 } 443 for (uint i = 0 ; i < count ; i++) { 444 // Generate a proper ID before inserting this format, this encodes 445 // the specific plugin in the media_file_format. 446 media_file_format fileFormat = fileFormats[i]; 447 fileFormat.id.node = ref.directory; 448 fileFormat.id.device = ref.device; 449 fileFormat.id.internal_id = info.internalID; 450 451 fWriterFileFormats.Insert(fileFormat); 452 } 453 454 fWriterList.Insert(info); 455 } 456 457 458 void 459 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) 460 { 461 BAutolock locker(fLock); 462 463 encoder_info* pinfo; 464 for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) { 465 if (!strcmp(pinfo->ref.name, ref.name)) { 466 // We already know this encoder. When we reject encoders with 467 // the same name, we allow the user to overwrite system encoders 468 // in her home folder. 469 return; 470 } 471 } 472 473 // Get list of supported encoders... 474 475 encoder_info info; 476 info.ref = ref; 477 info.internalID = fNextEncoderCodecInfoID++; 478 479 int32 cookie = 0; 480 481 while (true) { 482 memset(&info.codecInfo, 0, sizeof(media_codec_info)); 483 memset(&info.intputFormat, 0, sizeof(media_format)); 484 memset(&info.outputFormat, 0, sizeof(media_format)); 485 if (plugin->RegisterNextEncoder(&cookie, 486 &info.codecInfo, &info.formatFamily, &info.intputFormat, 487 &info.outputFormat) != B_OK) { 488 break; 489 } 490 info.codecInfo.id = info.internalID; 491 // NOTE: info.codecInfo.sub_id is for private use by the Encoder, 492 // we don't touch it, but it is maintained and passed back to the 493 // EncoderPlugin in NewEncoder(media_codec_info). 494 495 if (!fEncoderList.Insert(info)) 496 break; 497 } 498 } 499 500 501 bool 502 AddOnManager::_FindDecoder(const media_format& format, const BPath& path, 503 entry_ref* _decoderRef) 504 { 505 node_ref nref; 506 BDirectory directory; 507 if (directory.SetTo(path.Path()) != B_OK 508 || directory.GetNodeRef(&nref) != B_OK) { 509 return false; 510 } 511 512 decoder_info* info; 513 for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) { 514 if (info->ref.directory != nref.node) 515 continue; 516 517 media_format* decoderFormat; 518 for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) { 519 // check if the decoder matches the supplied format 520 if (!decoderFormat->Matches(&format)) 521 continue; 522 523 *_decoderRef = info->ref; 524 return true; 525 } 526 } 527 return false; 528 } 529 530 531 void 532 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs, 533 int32* outCount, int32 maxCount) 534 { 535 node_ref nref; 536 BDirectory directory; 537 if (directory.SetTo(path.Path()) != B_OK 538 || directory.GetNodeRef(&nref) != B_OK) { 539 return; 540 } 541 542 reader_info* info; 543 for (fReaderList.Rewind(); fReaderList.GetNext(&info) 544 && *outCount < maxCount;) { 545 if (info->ref.directory != nref.node) 546 continue; 547 548 outRefs[*outCount] = info->ref; 549 (*outCount)++; 550 } 551 } 552