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