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