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 "MediaDebug.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::GetStreamers(entry_ref* outRefs, int32* outCount, 208 int32 maxCount) 209 { 210 BAutolock locker(fLock); 211 RegisterAddOns(); 212 213 int32 count = 0; 214 streamer_info* info; 215 for (fStreamerList.Rewind(); fStreamerList.GetNext(&info);) { 216 if (count == maxCount) 217 break; 218 219 *outRefs = info->ref; 220 outRefs++; 221 count++; 222 } 223 224 *outCount = count; 225 return B_OK; 226 } 227 228 229 status_t 230 AddOnManager::GetEncoder(entry_ref* _encoderRef, int32 id) 231 { 232 BAutolock locker(fLock); 233 RegisterAddOns(); 234 235 encoder_info* info; 236 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { 237 // check if the encoder matches the supplied format 238 if (info->internalID == (uint32)id) { 239 *_encoderRef = info->ref; 240 return B_OK; 241 } 242 } 243 244 return B_ENTRY_NOT_FOUND; 245 } 246 247 248 status_t 249 AddOnManager::GetWriter(entry_ref* _ref, uint32 internalID) 250 { 251 BAutolock locker(fLock); 252 RegisterAddOns(); 253 254 writer_info* info; 255 for (fWriterList.Rewind(); fWriterList.GetNext(&info);) { 256 if (info->internalID == internalID) { 257 *_ref = info->ref; 258 return B_OK; 259 } 260 } 261 262 return B_ERROR; 263 } 264 265 266 status_t 267 AddOnManager::GetFileFormat(media_file_format* _fileFormat, int32 cookie) 268 { 269 BAutolock locker(fLock); 270 RegisterAddOns(); 271 272 media_file_format* fileFormat; 273 if (fWriterFileFormats.Get(cookie, &fileFormat)) { 274 *_fileFormat = *fileFormat; 275 return B_OK; 276 } 277 278 return B_BAD_INDEX; 279 } 280 281 282 status_t 283 AddOnManager::GetCodecInfo(media_codec_info* _codecInfo, 284 media_format_family* _formatFamily, 285 media_format* _inputFormat, media_format* _outputFormat, int32 cookie) 286 { 287 BAutolock locker(fLock); 288 RegisterAddOns(); 289 290 encoder_info* info; 291 if (fEncoderList.Get(cookie, &info)) { 292 *_codecInfo = info->codecInfo; 293 *_formatFamily = info->formatFamily; 294 *_inputFormat = info->intputFormat; 295 *_outputFormat = info->outputFormat; 296 return B_OK; 297 } 298 299 return B_BAD_INDEX; 300 } 301 302 303 // #pragma mark - 304 305 306 void 307 AddOnManager::RegisterAddOns() 308 { 309 // Check if add-ons are already registered. 310 if (!fReaderList.IsEmpty() || !fWriterList.IsEmpty() 311 || !fDecoderList.IsEmpty() || !fEncoderList.IsEmpty()) { 312 return; 313 } 314 315 char** directories = NULL; 316 size_t directoryCount = 0; 317 318 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY, 319 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories, 320 &directoryCount) != B_OK) { 321 return; 322 } 323 324 MemoryDeleter directoriesDeleter(directories); 325 326 BPath path; 327 for (uint i = 0; i < directoryCount; i++) { 328 BDirectory directory; 329 if (directory.SetTo(directories[i]) == B_OK) { 330 entry_ref ref; 331 while(directory.GetNextRef(&ref) == B_OK) 332 _RegisterAddOn(ref); 333 } 334 } 335 } 336 337 338 status_t 339 AddOnManager::_RegisterAddOn(const entry_ref& ref) 340 { 341 BPath path(&ref); 342 343 ImageLoader loader(path); 344 status_t status = loader.InitCheck(); 345 if (status != B_OK) 346 return status; 347 348 MediaPlugin* (*instantiate_plugin_func)(); 349 350 if (get_image_symbol(loader.Image(), "instantiate_plugin", 351 B_SYMBOL_TYPE_TEXT, (void**)&instantiate_plugin_func) < B_OK) { 352 printf("AddOnManager::_RegisterAddOn(): can't find instantiate_plugin " 353 "in \"%s\"\n", path.Path()); 354 return B_BAD_TYPE; 355 } 356 357 MediaPlugin* plugin = (*instantiate_plugin_func)(); 358 if (plugin == NULL) { 359 printf("AddOnManager::_RegisterAddOn(): instantiate_plugin in \"%s\" " 360 "returned NULL\n", path.Path()); 361 return B_ERROR; 362 } 363 364 ReaderPlugin* reader = dynamic_cast<ReaderPlugin*>(plugin); 365 if (reader != NULL) 366 _RegisterReader(reader, ref); 367 368 DecoderPlugin* decoder = dynamic_cast<DecoderPlugin*>(plugin); 369 if (decoder != NULL) 370 _RegisterDecoder(decoder, ref); 371 372 WriterPlugin* writer = dynamic_cast<WriterPlugin*>(plugin); 373 if (writer != NULL) 374 _RegisterWriter(writer, ref); 375 376 EncoderPlugin* encoder = dynamic_cast<EncoderPlugin*>(plugin); 377 if (encoder != NULL) 378 _RegisterEncoder(encoder, ref); 379 380 StreamerPlugin* streamer = dynamic_cast<StreamerPlugin*>(plugin); 381 if (streamer != NULL) 382 _RegisterStreamer(streamer, ref); 383 384 delete plugin; 385 386 return B_OK; 387 } 388 389 390 status_t 391 AddOnManager::_UnregisterAddOn(const entry_ref& ref) 392 { 393 BAutolock locker(fLock); 394 395 // Remove any Readers exported by this add-on 396 reader_info* readerInfo; 397 for (fReaderList.Rewind(); fReaderList.GetNext(&readerInfo);) { 398 if (readerInfo->ref == ref) { 399 fReaderList.RemoveCurrent(); 400 break; 401 } 402 } 403 404 // Remove any Decoders exported by this add-on 405 decoder_info* decoderInfo; 406 for (fDecoderList.Rewind(); fDecoderList.GetNext(&decoderInfo);) { 407 if (decoderInfo->ref == ref) { 408 media_format* format; 409 for (decoderInfo->formats.Rewind(); 410 decoderInfo->formats.GetNext(&format);) { 411 FormatManager::GetInstance()->RemoveFormat(*format); 412 } 413 fDecoderList.RemoveCurrent(); 414 break; 415 } 416 } 417 418 // Remove any Writers exported by this add-on 419 writer_info* writerInfo; 420 for (fWriterList.Rewind(); fWriterList.GetNext(&writerInfo);) { 421 if (writerInfo->ref == ref) { 422 // Remove any formats from this writer 423 media_file_format* writerFormat; 424 for (fWriterFileFormats.Rewind(); 425 fWriterFileFormats.GetNext(&writerFormat);) { 426 if (writerFormat->id.internal_id == writerInfo->internalID) 427 fWriterFileFormats.RemoveCurrent(); 428 } 429 fWriterList.RemoveCurrent(); 430 break; 431 } 432 } 433 434 encoder_info* encoderInfo; 435 for (fEncoderList.Rewind(); fEncoderList.GetNext(&encoderInfo);) { 436 if (encoderInfo->ref == ref) { 437 fEncoderList.RemoveCurrent(); 438 // Keep going, since we add multiple encoder infos per add-on. 439 } 440 } 441 442 return B_OK; 443 } 444 445 446 void 447 AddOnManager::_RegisterReader(ReaderPlugin* reader, const entry_ref& ref) 448 { 449 BAutolock locker(fLock); 450 451 reader_info* pinfo; 452 for (fReaderList.Rewind(); fReaderList.GetNext(&pinfo);) { 453 if (!strcmp(pinfo->ref.name, ref.name)) { 454 // we already know this reader 455 return; 456 } 457 } 458 459 reader_info info; 460 info.ref = ref; 461 462 fReaderList.Insert(info); 463 } 464 465 466 void 467 AddOnManager::_RegisterDecoder(DecoderPlugin* plugin, const entry_ref& ref) 468 { 469 BAutolock locker(fLock); 470 471 decoder_info* pinfo; 472 for (fDecoderList.Rewind(); fDecoderList.GetNext(&pinfo);) { 473 if (!strcmp(pinfo->ref.name, ref.name)) { 474 // we already know this decoder 475 return; 476 } 477 } 478 479 decoder_info info; 480 info.ref = ref; 481 482 media_format* formats = 0; 483 size_t count = 0; 484 if (plugin->GetSupportedFormats(&formats, &count) != B_OK) { 485 printf("AddOnManager::_RegisterDecoder(): plugin->GetSupportedFormats" 486 "(...) failed!\n"); 487 return; 488 } 489 for (uint i = 0 ; i < count ; i++) 490 info.formats.Insert(formats[i]); 491 492 fDecoderList.Insert(info); 493 } 494 495 496 void 497 AddOnManager::_RegisterWriter(WriterPlugin* writer, const entry_ref& ref) 498 { 499 BAutolock locker(fLock); 500 501 writer_info* pinfo; 502 for (fWriterList.Rewind(); fWriterList.GetNext(&pinfo);) { 503 if (!strcmp(pinfo->ref.name, ref.name)) { 504 // we already know this writer 505 return; 506 } 507 } 508 509 writer_info info; 510 info.ref = ref; 511 info.internalID = fNextWriterFormatFamilyID++; 512 513 // Get list of support media_file_formats... 514 const media_file_format* fileFormats = NULL; 515 size_t count = 0; 516 if (writer->GetSupportedFileFormats(&fileFormats, &count) != B_OK) { 517 printf("AddOnManager::_RegisterWriter(): " 518 "plugin->GetSupportedFileFormats(...) failed!\n"); 519 return; 520 } 521 for (uint i = 0 ; i < count ; i++) { 522 media_file_format fileFormat = fileFormats[i]; 523 // Ignore non-writable formats 524 if ((fileFormat.capabilities & media_file_format::B_WRITABLE) == 0) 525 continue; 526 527 // Generate a proper ID before inserting this format, this encodes 528 // the specific plugin in the media_file_format. 529 fileFormat.id.node = ref.directory; 530 fileFormat.id.device = ref.device; 531 fileFormat.id.internal_id = info.internalID; 532 533 fWriterFileFormats.Insert(fileFormat); 534 } 535 536 fWriterList.Insert(info); 537 } 538 539 540 void 541 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) 542 { 543 BAutolock locker(fLock); 544 545 encoder_info* pinfo; 546 for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) { 547 if (!strcmp(pinfo->ref.name, ref.name)) { 548 // We already know this encoder. When we reject encoders with 549 // the same name, we allow the user to overwrite system encoders 550 // in her home folder. 551 return; 552 } 553 } 554 555 // Get list of supported encoders... 556 557 encoder_info info; 558 info.ref = ref; 559 info.internalID = fNextEncoderCodecInfoID++; 560 561 int32 cookie = 0; 562 563 while (true) { 564 memset(&info.codecInfo, 0, sizeof(media_codec_info)); 565 info.intputFormat.Clear(); 566 info.outputFormat.Clear(); 567 if (plugin->RegisterNextEncoder(&cookie, 568 &info.codecInfo, &info.formatFamily, &info.intputFormat, 569 &info.outputFormat) != B_OK) { 570 break; 571 } 572 info.codecInfo.id = info.internalID; 573 // NOTE: info.codecInfo.sub_id is for private use by the Encoder, 574 // we don't touch it, but it is maintained and passed back to the 575 // EncoderPlugin in NewEncoder(media_codec_info). 576 577 if (!fEncoderList.Insert(info)) 578 break; 579 } 580 } 581 582 583 void 584 AddOnManager::_RegisterStreamer(StreamerPlugin* streamer, const entry_ref& ref) 585 { 586 BAutolock locker(fLock); 587 588 streamer_info* pInfo; 589 for (fStreamerList.Rewind(); fStreamerList.GetNext(&pInfo);) { 590 if (!strcmp(pInfo->ref.name, ref.name)) { 591 // We already know this streamer 592 return; 593 } 594 } 595 596 streamer_info info; 597 info.ref = ref; 598 fStreamerList.Insert(info); 599 } 600 601 602 bool 603 AddOnManager::_FindDecoder(const media_format& format, const BPath& path, 604 entry_ref* _decoderRef) 605 { 606 node_ref nref; 607 BDirectory directory; 608 if (directory.SetTo(path.Path()) != B_OK 609 || directory.GetNodeRef(&nref) != B_OK) { 610 return false; 611 } 612 613 decoder_info* info; 614 for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) { 615 if (info->ref.directory != nref.node) 616 continue; 617 618 media_format* decoderFormat; 619 for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) { 620 // check if the decoder matches the supplied format 621 if (!decoderFormat->Matches(&format)) 622 continue; 623 624 *_decoderRef = info->ref; 625 return true; 626 } 627 } 628 return false; 629 } 630 631 632 bool 633 AddOnManager::_FindEncoder(const media_format& format, const BPath& path, 634 entry_ref* _encoderRef) 635 { 636 node_ref nref; 637 BDirectory directory; 638 if (directory.SetTo(path.Path()) != B_OK 639 || directory.GetNodeRef(&nref) != B_OK) { 640 return false; 641 } 642 643 encoder_info* info; 644 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { 645 if (info->ref.directory != nref.node) 646 continue; 647 648 // check if the encoder matches the supplied format 649 if (info->outputFormat.Matches(&format)) { 650 *_encoderRef = info->ref; 651 return true; 652 } 653 continue; 654 } 655 return false; 656 } 657 658 659 void 660 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs, 661 int32* outCount, int32 maxCount) 662 { 663 node_ref nref; 664 BDirectory directory; 665 if (directory.SetTo(path.Path()) != B_OK 666 || directory.GetNodeRef(&nref) != B_OK) { 667 return; 668 } 669 670 reader_info* info; 671 for (fReaderList.Rewind(); fReaderList.GetNext(&info) 672 && *outCount < maxCount;) { 673 if (info->ref.directory != nref.node) 674 continue; 675 676 outRefs[*outCount] = info->ref; 677 (*outCount)++; 678 } 679 } 680 681 682 } // namespace media 683 } // namespace BPrivate 684