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