xref: /haiku/src/kits/media/PluginManager.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 		const 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