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