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