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