1 /* 2 * Copyright 2004-2010, Marcus Overhagen. All rights reserved. 3 * Copyright 2016, Dario Casalinuovo. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <AdapterIO.h> 9 #include <AutoDeleter.h> 10 #include <Autolock.h> 11 #include <BufferIO.h> 12 #include <DataIO.h> 13 #include <image.h> 14 #include <Path.h> 15 16 #include <string.h> 17 18 #include "AddOnManager.h" 19 #include "PluginManager.h" 20 #include "DataExchange.h" 21 #include "MediaDebug.h" 22 23 24 PluginManager gPluginManager; 25 26 #define BLOCK_SIZE 4096 27 #define MAX_STREAMERS 40 28 29 30 class DataIOAdapter : public BAdapterIO { 31 public: 32 DataIOAdapter(BDataIO* dataIO) 33 : 34 BAdapterIO(B_MEDIA_SEEK_BACKWARD | B_MEDIA_MUTABLE_SIZE, 35 B_INFINITE_TIMEOUT), 36 fDataIO(dataIO) 37 { 38 fDataInputAdapter = BuildInputAdapter(); 39 } 40 41 virtual ~DataIOAdapter() 42 { 43 } 44 45 virtual ssize_t ReadAt(off_t position, void* buffer, 46 size_t size) 47 { 48 if (position == Position()) { 49 ssize_t ret = fDataIO->Read(buffer, size); 50 fDataInputAdapter->Write(buffer, ret); 51 return ret; 52 } 53 54 off_t totalSize = 0; 55 if (GetSize(&totalSize) != B_OK) 56 return B_UNSUPPORTED; 57 58 if (position+size < (size_t)totalSize) 59 return ReadAt(position, buffer, size); 60 61 return B_NOT_SUPPORTED; 62 } 63 64 virtual ssize_t WriteAt(off_t position, const void* buffer, 65 size_t size) 66 { 67 if (position == Position()) { 68 ssize_t ret = fDataIO->Write(buffer, size); 69 fDataInputAdapter->Write(buffer, ret); 70 return ret; 71 } 72 73 return B_NOT_SUPPORTED; 74 } 75 76 private: 77 BDataIO* fDataIO; 78 BInputAdapter* fDataInputAdapter; 79 }; 80 81 82 class BMediaIOWrapper : public BMediaIO { 83 public: 84 BMediaIOWrapper(BDataIO* source) 85 : 86 fData(NULL), 87 fPosition(NULL), 88 fMedia(NULL), 89 fDataIOAdapter(NULL), 90 fErr(B_NO_ERROR) 91 { 92 CALLED(); 93 94 fPosition = dynamic_cast<BPositionIO*>(source); 95 fMedia = dynamic_cast<BMediaIO*>(source); 96 fData = source; 97 98 if (!IsPosition()) { 99 // In this case we have to supply our own form 100 // of pseudo-seekable object from a non-seekable 101 // BDataIO. 102 fDataIOAdapter = new DataIOAdapter(source); 103 fMedia = dynamic_cast<BMediaIO*>(fDataIOAdapter); 104 fPosition = dynamic_cast<BPositionIO*>(fDataIOAdapter); 105 fData = dynamic_cast<BDataIO*>(fDataIOAdapter); 106 TRACE("Unable to improve performance with a BufferIO\n"); 107 } 108 109 if (IsMedia()) 110 fMedia->GetFlags(&fFlags); 111 else if (IsPosition()) 112 fFlags = B_MEDIA_SEEKABLE; 113 } 114 115 virtual ~BMediaIOWrapper() 116 { 117 if (fDataIOAdapter != NULL) 118 delete fDataIOAdapter; 119 } 120 121 status_t InitCheck() const 122 { 123 return fErr; 124 } 125 126 // BMediaIO interface 127 128 virtual void GetFlags(int32* flags) const 129 { 130 *flags = fFlags; 131 } 132 133 // BPositionIO interface 134 135 virtual ssize_t ReadAt(off_t position, void* buffer, 136 size_t size) 137 { 138 CALLED(); 139 140 return fPosition->ReadAt(position, buffer, size); 141 } 142 143 virtual ssize_t WriteAt(off_t position, const void* buffer, 144 size_t size) 145 { 146 CALLED(); 147 148 return fPosition->WriteAt(position, buffer, size); 149 } 150 151 virtual off_t Seek(off_t position, uint32 seekMode) 152 { 153 CALLED(); 154 155 return fPosition->Seek(position, seekMode); 156 157 } 158 159 virtual off_t Position() const 160 { 161 CALLED(); 162 163 return fPosition->Position(); 164 } 165 166 virtual status_t SetSize(off_t size) 167 { 168 CALLED(); 169 170 return fPosition->SetSize(size); 171 } 172 173 virtual status_t GetSize(off_t* size) const 174 { 175 CALLED(); 176 177 return fPosition->GetSize(size); 178 } 179 180 protected: 181 182 bool IsMedia() const 183 { 184 return fMedia != NULL; 185 } 186 187 bool IsPosition() const 188 { 189 return fPosition != NULL; 190 } 191 192 private: 193 BDataIO* fData; 194 BPositionIO* fPosition; 195 BMediaIO* fMedia; 196 DataIOAdapter* fDataIOAdapter; 197 198 int32 fFlags; 199 200 status_t fErr; 201 }; 202 203 204 // #pragma mark - Readers/Decoders 205 206 207 status_t 208 PluginManager::CreateReader(Reader** reader, int32* streamCount, 209 media_file_format* mff, BDataIO* source) 210 { 211 TRACE("PluginManager::CreateReader enter\n"); 212 213 // The wrapper class will present our source in a more useful 214 // way, we create an instance which is buffering our reads and 215 // writes. 216 BMediaIOWrapper* buffered_source = new BMediaIOWrapper(source); 217 ObjectDeleter<BMediaIOWrapper> ioDeleter(buffered_source); 218 219 status_t ret = buffered_source->InitCheck(); 220 if (ret != B_OK) 221 return ret; 222 223 // get list of available readers from the server 224 entry_ref refs[MAX_READERS]; 225 int32 count; 226 227 ret = AddOnManager::GetInstance()->GetReaders(refs, &count, 228 MAX_READERS); 229 if (ret != B_OK) { 230 printf("PluginManager::CreateReader: can't get list of readers: %s\n", 231 strerror(ret)); 232 return ret; 233 } 234 235 // try each reader by calling it's Sniff function... 236 for (int32 i = 0; i < count; i++) { 237 entry_ref ref = refs[i]; 238 MediaPlugin* plugin = GetPlugin(ref); 239 if (plugin == NULL) { 240 printf("PluginManager::CreateReader: GetPlugin failed\n"); 241 return B_ERROR; 242 } 243 244 ReaderPlugin* readerPlugin = dynamic_cast<ReaderPlugin*>(plugin); 245 if (readerPlugin == NULL) { 246 printf("PluginManager::CreateReader: dynamic_cast failed\n"); 247 PutPlugin(plugin); 248 return B_ERROR; 249 } 250 251 *reader = readerPlugin->NewReader(); 252 if (*reader == NULL) { 253 printf("PluginManager::CreateReader: NewReader failed\n"); 254 PutPlugin(plugin); 255 return B_ERROR; 256 } 257 258 buffered_source->Seek(0, SEEK_SET); 259 (*reader)->Setup(buffered_source); 260 (*reader)->fMediaPlugin = plugin; 261 262 if ((*reader)->Sniff(streamCount) == B_OK) { 263 TRACE("PluginManager::CreateReader: Sniff success " 264 "(%" B_PRId32 " stream(s))\n", *streamCount); 265 (*reader)->GetFileFormatInfo(mff); 266 ioDeleter.Detach(); 267 return B_OK; 268 } 269 270 DestroyReader(*reader); 271 *reader = NULL; 272 } 273 274 TRACE("PluginManager::CreateReader leave\n"); 275 return B_MEDIA_NO_HANDLER; 276 } 277 278 279 void 280 PluginManager::DestroyReader(Reader* reader) 281 { 282 if (reader != NULL) { 283 TRACE("PluginManager::DestroyReader(%p (plugin: %p))\n", reader, 284 reader->fMediaPlugin); 285 // NOTE: We have to put the plug-in after deleting the reader, 286 // since otherwise we may actually unload the code for the 287 // destructor... 288 MediaPlugin* plugin = reader->fMediaPlugin; 289 delete reader; 290 PutPlugin(plugin); 291 } 292 } 293 294 295 status_t 296 PluginManager::CreateDecoder(Decoder** _decoder, const media_format& format) 297 { 298 TRACE("PluginManager::CreateDecoder enter\n"); 299 300 // get decoder for this format 301 entry_ref ref; 302 status_t ret = AddOnManager::GetInstance()->GetDecoderForFormat( 303 &ref, format); 304 if (ret != B_OK) { 305 printf("PluginManager::CreateDecoder: can't get decoder for format: " 306 "%s\n", strerror(ret)); 307 return ret; 308 } 309 310 MediaPlugin* plugin = GetPlugin(ref); 311 if (plugin == NULL) { 312 printf("PluginManager::CreateDecoder: GetPlugin failed\n"); 313 return B_ERROR; 314 } 315 316 DecoderPlugin* decoderPlugin = dynamic_cast<DecoderPlugin*>(plugin); 317 if (decoderPlugin == NULL) { 318 printf("PluginManager::CreateDecoder: dynamic_cast failed\n"); 319 PutPlugin(plugin); 320 return B_ERROR; 321 } 322 323 // TODO: In theory, one DecoderPlugin could support multiple Decoders, 324 // but this is not yet handled (passing "0" as index/ID). 325 *_decoder = decoderPlugin->NewDecoder(0); 326 if (*_decoder == NULL) { 327 printf("PluginManager::CreateDecoder: NewDecoder() failed\n"); 328 PutPlugin(plugin); 329 return B_ERROR; 330 } 331 TRACE(" created decoder: %p\n", *_decoder); 332 (*_decoder)->fMediaPlugin = plugin; 333 334 TRACE("PluginManager::CreateDecoder leave\n"); 335 336 return B_OK; 337 } 338 339 340 status_t 341 PluginManager::CreateDecoder(Decoder** decoder, const media_codec_info& mci) 342 { 343 TRACE("PluginManager::CreateDecoder enter\n"); 344 entry_ref ref; 345 status_t status = AddOnManager::GetInstance()->GetEncoder(&ref, mci.id); 346 if (status != B_OK) 347 return status; 348 349 MediaPlugin* plugin = GetPlugin(ref); 350 if (plugin == NULL) { 351 ERROR("PluginManager::CreateDecoder: GetPlugin failed\n"); 352 return B_ERROR; 353 } 354 355 DecoderPlugin* decoderPlugin = dynamic_cast<DecoderPlugin*>(plugin); 356 if (decoderPlugin == NULL) { 357 ERROR("PluginManager::CreateDecoder: dynamic_cast failed\n"); 358 PutPlugin(plugin); 359 return B_ERROR; 360 } 361 362 // TODO: In theory, one DecoderPlugin could support multiple Decoders, 363 // but this is not yet handled (passing "0" as index/ID). 364 *decoder = decoderPlugin->NewDecoder(0); 365 if (*decoder == NULL) { 366 ERROR("PluginManager::CreateDecoder: NewDecoder() failed\n"); 367 PutPlugin(plugin); 368 return B_ERROR; 369 } 370 TRACE(" created decoder: %p\n", *decoder); 371 (*decoder)->fMediaPlugin = plugin; 372 373 TRACE("PluginManager::CreateDecoder leave\n"); 374 375 return B_OK; 376 377 } 378 379 380 status_t 381 PluginManager::GetDecoderInfo(Decoder* decoder, media_codec_info* _info) const 382 { 383 if (decoder == NULL) 384 return B_BAD_VALUE; 385 386 decoder->GetCodecInfo(_info); 387 // TODO: 388 // out_info->id = 389 // out_info->sub_id = 390 return B_OK; 391 } 392 393 394 void 395 PluginManager::DestroyDecoder(Decoder* decoder) 396 { 397 if (decoder != NULL) { 398 TRACE("PluginManager::DestroyDecoder(%p, plugin: %p)\n", decoder, 399 decoder->fMediaPlugin); 400 // NOTE: We have to put the plug-in after deleting the decoder, 401 // since otherwise we may actually unload the code for the 402 // destructor... 403 MediaPlugin* plugin = decoder->fMediaPlugin; 404 delete decoder; 405 PutPlugin(plugin); 406 } 407 } 408 409 410 // #pragma mark - Writers/Encoders 411 412 413 status_t 414 PluginManager::CreateWriter(Writer** writer, const media_file_format& mff, 415 BDataIO* target) 416 { 417 TRACE("PluginManager::CreateWriter enter\n"); 418 419 // Get the Writer responsible for this media_file_format from the server. 420 entry_ref ref; 421 status_t ret = AddOnManager::GetInstance()->GetWriter(&ref, 422 mff.id.internal_id); 423 if (ret != B_OK) { 424 printf("PluginManager::CreateWriter: can't get writer for file " 425 "family: %s\n", strerror(ret)); 426 return ret; 427 } 428 429 MediaPlugin* plugin = GetPlugin(ref); 430 if (plugin == NULL) { 431 printf("PluginManager::CreateWriter: GetPlugin failed\n"); 432 return B_ERROR; 433 } 434 435 WriterPlugin* writerPlugin = dynamic_cast<WriterPlugin*>(plugin); 436 if (writerPlugin == NULL) { 437 printf("PluginManager::CreateWriter: dynamic_cast failed\n"); 438 PutPlugin(plugin); 439 return B_ERROR; 440 } 441 442 *writer = writerPlugin->NewWriter(); 443 if (*writer == NULL) { 444 printf("PluginManager::CreateWriter: NewWriter failed\n"); 445 PutPlugin(plugin); 446 return B_ERROR; 447 } 448 449 (*writer)->Setup(target); 450 (*writer)->fMediaPlugin = plugin; 451 452 TRACE("PluginManager::CreateWriter leave\n"); 453 return B_OK; 454 } 455 456 457 void 458 PluginManager::DestroyWriter(Writer* writer) 459 { 460 if (writer != NULL) { 461 TRACE("PluginManager::DestroyWriter(%p (plugin: %p))\n", writer, 462 writer->fMediaPlugin); 463 // NOTE: We have to put the plug-in after deleting the writer, 464 // since otherwise we may actually unload the code for the 465 // destructor... 466 MediaPlugin* plugin = writer->fMediaPlugin; 467 delete writer; 468 PutPlugin(plugin); 469 } 470 } 471 472 473 status_t 474 PluginManager::CreateEncoder(Encoder** _encoder, 475 const media_codec_info* codecInfo, uint32 flags) 476 { 477 TRACE("PluginManager::CreateEncoder enter\n"); 478 479 // Get encoder for this codec info from the server 480 entry_ref ref; 481 status_t ret = AddOnManager::GetInstance()->GetEncoder(&ref, 482 codecInfo->id); 483 if (ret != B_OK) { 484 printf("PluginManager::CreateEncoder: can't get encoder for codec %s: " 485 "%s\n", codecInfo->pretty_name, strerror(ret)); 486 return ret; 487 } 488 489 MediaPlugin* plugin = GetPlugin(ref); 490 if (!plugin) { 491 printf("PluginManager::CreateEncoder: GetPlugin failed\n"); 492 return B_ERROR; 493 } 494 495 EncoderPlugin* encoderPlugin = dynamic_cast<EncoderPlugin*>(plugin); 496 if (encoderPlugin == NULL) { 497 printf("PluginManager::CreateEncoder: dynamic_cast failed\n"); 498 PutPlugin(plugin); 499 return B_ERROR; 500 } 501 502 *_encoder = encoderPlugin->NewEncoder(*codecInfo); 503 if (*_encoder == NULL) { 504 printf("PluginManager::CreateEncoder: NewEncoder() failed\n"); 505 PutPlugin(plugin); 506 return B_ERROR; 507 } 508 TRACE(" created encoder: %p\n", *_encoder); 509 (*_encoder)->fMediaPlugin = plugin; 510 511 TRACE("PluginManager::CreateEncoder leave\n"); 512 513 return B_OK; 514 } 515 516 517 status_t 518 PluginManager::CreateEncoder(Encoder** encoder, const media_format& format) 519 { 520 TRACE("PluginManager::CreateEncoder enter nr2\n"); 521 522 entry_ref ref; 523 524 status_t ret = AddOnManager::GetInstance()->GetEncoderForFormat( 525 &ref, format); 526 527 if (ret != B_OK) { 528 ERROR("PluginManager::CreateEncoder: can't get decoder for format: " 529 "%s\n", strerror(ret)); 530 return ret; 531 } 532 533 MediaPlugin* plugin = GetPlugin(ref); 534 if (plugin == NULL) { 535 ERROR("PluginManager::CreateEncoder: GetPlugin failed\n"); 536 return B_ERROR; 537 } 538 539 EncoderPlugin* encoderPlugin = dynamic_cast<EncoderPlugin*>(plugin); 540 if (encoderPlugin == NULL) { 541 ERROR("PluginManager::CreateEncoder: dynamic_cast failed\n"); 542 PutPlugin(plugin); 543 return B_ERROR; 544 } 545 546 547 *encoder = encoderPlugin->NewEncoder(format); 548 if (*encoder == NULL) { 549 ERROR("PluginManager::CreateEncoder: NewEncoder() failed\n"); 550 PutPlugin(plugin); 551 return B_ERROR; 552 } 553 TRACE(" created encoder: %p\n", *encoder); 554 (*encoder)->fMediaPlugin = plugin; 555 556 TRACE("PluginManager::CreateEncoder leave nr2\n"); 557 558 return B_OK; 559 } 560 561 562 void 563 PluginManager::DestroyEncoder(Encoder* encoder) 564 { 565 if (encoder != NULL) { 566 TRACE("PluginManager::DestroyEncoder(%p, plugin: %p)\n", encoder, 567 encoder->fMediaPlugin); 568 // NOTE: We have to put the plug-in after deleting the encoder, 569 // since otherwise we may actually unload the code for the 570 // destructor... 571 MediaPlugin* plugin = encoder->fMediaPlugin; 572 delete encoder; 573 PutPlugin(plugin); 574 } 575 } 576 577 578 status_t 579 PluginManager::CreateStreamer(Streamer** streamer, BUrl url, BDataIO** source) 580 { 581 BAutolock _(fLocker); 582 583 TRACE("PluginManager::CreateStreamer enter\n"); 584 585 entry_ref refs[MAX_STREAMERS]; 586 int32 count; 587 588 status_t ret = AddOnManager::GetInstance()->GetStreamers(refs, &count, 589 MAX_STREAMERS); 590 if (ret != B_OK) { 591 printf("PluginManager::CreateStreamer: can't get list of streamers:" 592 " %s\n", strerror(ret)); 593 return ret; 594 } 595 596 // try each reader by calling it's Sniff function... 597 for (int32 i = 0; i < count; i++) { 598 entry_ref ref = refs[i]; 599 MediaPlugin* plugin = GetPlugin(ref); 600 if (plugin == NULL) { 601 printf("PluginManager::CreateStreamer: GetPlugin failed\n"); 602 return B_ERROR; 603 } 604 605 StreamerPlugin* streamerPlugin = dynamic_cast<StreamerPlugin*>(plugin); 606 if (streamerPlugin == NULL) { 607 printf("PluginManager::CreateStreamer: dynamic_cast failed\n"); 608 PutPlugin(plugin); 609 return B_ERROR; 610 } 611 612 *streamer = streamerPlugin->NewStreamer(); 613 if (*streamer == NULL) { 614 printf("PluginManager::CreateStreamer: NewReader failed\n"); 615 PutPlugin(plugin); 616 return B_ERROR; 617 } 618 619 (*streamer)->fMediaPlugin = plugin; 620 plugin->fRefCount++; 621 622 BDataIO* streamSource = NULL; 623 if ((*streamer)->Sniff(url, &streamSource) == B_OK) { 624 TRACE("PluginManager::CreateStreamer: Sniff success\n"); 625 *source = streamSource; 626 return B_OK; 627 } 628 629 DestroyStreamer(*streamer); 630 *streamer = NULL; 631 } 632 633 TRACE("PluginManager::CreateStreamer leave\n"); 634 return B_MEDIA_NO_HANDLER; 635 } 636 637 638 void 639 PluginManager::DestroyStreamer(Streamer* streamer) 640 { 641 BAutolock _(fLocker); 642 643 if (streamer != NULL) { 644 TRACE("PluginManager::DestroyStreamer(%p, plugin: %p)\n", streamer, 645 streamer->fMediaPlugin); 646 647 // NOTE: We have to put the plug-in after deleting the streamer, 648 // since otherwise we may actually unload the code for the 649 // destructor... 650 MediaPlugin* plugin = streamer->fMediaPlugin; 651 delete streamer; 652 653 // Delete the plugin only when every reference is released 654 if (plugin->fRefCount == 1) { 655 plugin->fRefCount = 0; 656 PutPlugin(plugin); 657 } else 658 plugin->fRefCount--; 659 } 660 } 661 662 663 // #pragma mark - 664 665 666 PluginManager::PluginManager() 667 : 668 fPluginList(), 669 fLocker("media plugin manager") 670 { 671 CALLED(); 672 } 673 674 675 PluginManager::~PluginManager() 676 { 677 CALLED(); 678 for (int i = fPluginList.CountItems() - 1; i >= 0; i--) { 679 plugin_info* info = NULL; 680 fPluginList.Get(i, &info); 681 TRACE("PluginManager: Error, unloading PlugIn %s with usecount " 682 "%d\n", info->name, info->usecount); 683 delete info->plugin; 684 unload_add_on(info->image); 685 } 686 } 687 688 689 MediaPlugin* 690 PluginManager::GetPlugin(const entry_ref& ref) 691 { 692 TRACE("PluginManager::GetPlugin(%s)\n", ref.name); 693 fLocker.Lock(); 694 695 MediaPlugin* plugin; 696 plugin_info* pinfo; 697 plugin_info info; 698 699 for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) { 700 if (0 == strcmp(ref.name, pinfo->name)) { 701 plugin = pinfo->plugin; 702 pinfo->usecount++; 703 TRACE(" found existing plugin: %p\n", pinfo->plugin); 704 fLocker.Unlock(); 705 return plugin; 706 } 707 } 708 709 if (_LoadPlugin(ref, &info.plugin, &info.image) < B_OK) { 710 printf("PluginManager: Error, loading PlugIn %s failed\n", ref.name); 711 fLocker.Unlock(); 712 return NULL; 713 } 714 715 strcpy(info.name, ref.name); 716 info.usecount = 1; 717 fPluginList.Insert(info); 718 719 TRACE("PluginManager: PlugIn %s loaded\n", ref.name); 720 721 plugin = info.plugin; 722 TRACE(" loaded plugin: %p\n", plugin); 723 724 fLocker.Unlock(); 725 return plugin; 726 } 727 728 729 void 730 PluginManager::PutPlugin(MediaPlugin* plugin) 731 { 732 TRACE("PluginManager::PutPlugin()\n"); 733 fLocker.Lock(); 734 735 plugin_info* pinfo; 736 737 for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) { 738 if (plugin == pinfo->plugin) { 739 pinfo->usecount--; 740 if (pinfo->usecount == 0) { 741 TRACE(" deleting %p\n", pinfo->plugin); 742 delete pinfo->plugin; 743 TRACE(" unloading add-on: %" B_PRId32 "\n\n", pinfo->image); 744 unload_add_on(pinfo->image); 745 fPluginList.RemoveCurrent(); 746 } 747 fLocker.Unlock(); 748 return; 749 } 750 } 751 752 printf("PluginManager: Error, can't put PlugIn %p\n", plugin); 753 754 fLocker.Unlock(); 755 } 756 757 758 status_t 759 PluginManager::_LoadPlugin(const entry_ref& ref, MediaPlugin** plugin, 760 image_id* image) 761 { 762 BPath p(&ref); 763 764 TRACE("PluginManager: _LoadPlugin trying to load %s\n", p.Path()); 765 766 image_id id; 767 id = load_add_on(p.Path()); 768 if (id < 0) { 769 printf("PluginManager: Error, load_add_on(): %s\n", strerror(id)); 770 return B_ERROR; 771 } 772 773 MediaPlugin* (*instantiate_plugin_func)(); 774 775 if (get_image_symbol(id, "instantiate_plugin", B_SYMBOL_TYPE_TEXT, 776 (void**)&instantiate_plugin_func) < B_OK) { 777 printf("PluginManager: Error, _LoadPlugin can't find " 778 "instantiate_plugin in %s\n", p.Path()); 779 unload_add_on(id); 780 return B_ERROR; 781 } 782 783 MediaPlugin *pl; 784 785 pl = (*instantiate_plugin_func)(); 786 if (pl == NULL) { 787 printf("PluginManager: Error, _LoadPlugin instantiate_plugin in %s " 788 "returned NULL\n", p.Path()); 789 unload_add_on(id); 790 return B_ERROR; 791 } 792 793 *plugin = pl; 794 *image = id; 795 return B_OK; 796 } 797