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