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 // Generate a proper ID before inserting this format, this encodes 523 // the specific plugin in the media_file_format. 524 media_file_format fileFormat = fileFormats[i]; 525 fileFormat.id.node = ref.directory; 526 fileFormat.id.device = ref.device; 527 fileFormat.id.internal_id = info.internalID; 528 529 fWriterFileFormats.Insert(fileFormat); 530 } 531 532 fWriterList.Insert(info); 533 } 534 535 536 void 537 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) 538 { 539 BAutolock locker(fLock); 540 541 encoder_info* pinfo; 542 for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) { 543 if (!strcmp(pinfo->ref.name, ref.name)) { 544 // We already know this encoder. When we reject encoders with 545 // the same name, we allow the user to overwrite system encoders 546 // in her home folder. 547 return; 548 } 549 } 550 551 // Get list of supported encoders... 552 553 encoder_info info; 554 info.ref = ref; 555 info.internalID = fNextEncoderCodecInfoID++; 556 557 int32 cookie = 0; 558 559 while (true) { 560 memset(&info.codecInfo, 0, sizeof(media_codec_info)); 561 info.intputFormat.Clear(); 562 info.outputFormat.Clear(); 563 if (plugin->RegisterNextEncoder(&cookie, 564 &info.codecInfo, &info.formatFamily, &info.intputFormat, 565 &info.outputFormat) != B_OK) { 566 break; 567 } 568 info.codecInfo.id = info.internalID; 569 // NOTE: info.codecInfo.sub_id is for private use by the Encoder, 570 // we don't touch it, but it is maintained and passed back to the 571 // EncoderPlugin in NewEncoder(media_codec_info). 572 573 if (!fEncoderList.Insert(info)) 574 break; 575 } 576 } 577 578 579 void 580 AddOnManager::_RegisterStreamer(StreamerPlugin* streamer, const entry_ref& ref) 581 { 582 BAutolock locker(fLock); 583 584 streamer_info* pInfo; 585 for (fStreamerList.Rewind(); fStreamerList.GetNext(&pInfo);) { 586 if (!strcmp(pInfo->ref.name, ref.name)) { 587 // We already know this streamer 588 return; 589 } 590 } 591 592 streamer_info info; 593 info.ref = ref; 594 fStreamerList.Insert(info); 595 } 596 597 598 bool 599 AddOnManager::_FindDecoder(const media_format& format, const BPath& path, 600 entry_ref* _decoderRef) 601 { 602 node_ref nref; 603 BDirectory directory; 604 if (directory.SetTo(path.Path()) != B_OK 605 || directory.GetNodeRef(&nref) != B_OK) { 606 return false; 607 } 608 609 decoder_info* info; 610 for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) { 611 if (info->ref.directory != nref.node) 612 continue; 613 614 media_format* decoderFormat; 615 for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) { 616 // check if the decoder matches the supplied format 617 if (!decoderFormat->Matches(&format)) 618 continue; 619 620 *_decoderRef = info->ref; 621 return true; 622 } 623 } 624 return false; 625 } 626 627 628 bool 629 AddOnManager::_FindEncoder(const media_format& format, const BPath& path, 630 entry_ref* _encoderRef) 631 { 632 node_ref nref; 633 BDirectory directory; 634 if (directory.SetTo(path.Path()) != B_OK 635 || directory.GetNodeRef(&nref) != B_OK) { 636 return false; 637 } 638 639 encoder_info* info; 640 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) { 641 if (info->ref.directory != nref.node) 642 continue; 643 644 // check if the encoder matches the supplied format 645 if (info->outputFormat.Matches(&format)) { 646 *_encoderRef = info->ref; 647 return true; 648 } 649 continue; 650 } 651 return false; 652 } 653 654 655 void 656 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs, 657 int32* outCount, int32 maxCount) 658 { 659 node_ref nref; 660 BDirectory directory; 661 if (directory.SetTo(path.Path()) != B_OK 662 || directory.GetNodeRef(&nref) != B_OK) { 663 return; 664 } 665 666 reader_info* info; 667 for (fReaderList.Rewind(); fReaderList.GetNext(&info) 668 && *outCount < maxCount;) { 669 if (info->ref.directory != nref.node) 670 continue; 671 672 outRefs[*outCount] = info->ref; 673 (*outCount)++; 674 } 675 } 676 677 678 } // namespace media 679 } // namespace BPrivate 680