headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / media / PluginManager.cpp
blobc7fa5cf1ea66d1819b48059650a92e78ce9bba89
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 */
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>
16 #include <string.h>
18 #include "AddOnManager.h"
19 #include "PluginManager.h"
20 #include "DataExchange.h"
21 #include "debug.h"
24 PluginManager gPluginManager;
26 #define BLOCK_SIZE 4096
27 #define MAX_STREAMERS 40
30 class DataIOAdapter : public BAdapterIO {
31 public:
32 DataIOAdapter(BDataIO* dataIO)
34 BAdapterIO(B_MEDIA_SEEK_BACKWARD | B_MEDIA_MUTABLE_SIZE,
35 B_INFINITE_TIMEOUT),
36 fDataIO(dataIO)
38 fDataInputAdapter = BuildInputAdapter();
41 virtual ~DataIOAdapter()
45 virtual ssize_t ReadAt(off_t position, void* buffer,
46 size_t size)
48 if (position == Position()) {
49 ssize_t ret = fDataIO->Read(buffer, size);
50 fDataInputAdapter->Write(buffer, ret);
51 return ret;
54 off_t totalSize = 0;
55 if (GetSize(&totalSize) != B_OK)
56 return B_UNSUPPORTED;
58 if (position+size < (size_t)totalSize)
59 return ReadAt(position, buffer, size);
61 return B_NOT_SUPPORTED;
64 virtual ssize_t WriteAt(off_t position, const void* buffer,
65 size_t size)
67 if (position == Position()) {
68 ssize_t ret = fDataIO->Write(buffer, size);
69 fDataInputAdapter->Write(buffer, ret);
70 return ret;
73 return B_NOT_SUPPORTED;
76 private:
77 BDataIO* fDataIO;
78 BInputAdapter* fDataInputAdapter;
82 class BMediaIOWrapper : public BMediaIO {
83 public:
84 BMediaIOWrapper(BDataIO* source)
86 fData(NULL),
87 fPosition(NULL),
88 fMedia(NULL),
89 fBufferIO(NULL),
90 fDataIOAdapter(NULL),
91 fErr(B_NO_ERROR)
93 CALLED();
95 fPosition = dynamic_cast<BPositionIO*>(source);
96 fMedia = dynamic_cast<BMediaIO*>(source);
97 fBufferIO = dynamic_cast<BBufferIO *>(source);
98 fData = source;
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;
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");
125 if (IsMedia())
126 fMedia->GetFlags(&fFlags);
127 else if (IsPosition())
128 fFlags = B_MEDIA_SEEKABLE;
131 virtual ~BMediaIOWrapper()
133 if (fBufferIO != NULL)
134 delete fBufferIO;
136 if (fDataIOAdapter != NULL)
137 delete fDataIOAdapter;
140 status_t InitCheck() const
142 return fErr;
145 // BMediaIO interface
147 virtual void GetFlags(int32* flags) const
149 *flags = fFlags;
152 // BPositionIO interface
154 virtual ssize_t ReadAt(off_t position, void* buffer,
155 size_t size)
157 CALLED();
159 return fPosition->ReadAt(position, buffer, size);
162 virtual ssize_t WriteAt(off_t position, const void* buffer,
163 size_t size)
165 CALLED();
167 return fPosition->WriteAt(position, buffer, size);
170 virtual off_t Seek(off_t position, uint32 seekMode)
172 CALLED();
174 return fPosition->Seek(position, seekMode);
178 virtual off_t Position() const
180 CALLED();
182 return fPosition->Position();
185 virtual status_t SetSize(off_t size)
187 CALLED();
189 return fPosition->SetSize(size);
192 virtual status_t GetSize(off_t* size) const
194 CALLED();
196 return fPosition->GetSize(size);
199 protected:
201 bool IsMedia() const
203 return fMedia != NULL;
206 bool IsPosition() const
208 return fPosition != NULL;
211 private:
212 BDataIO* fData;
213 BPositionIO* fPosition;
214 BMediaIO* fMedia;
215 BBufferIO* fBufferIO;
216 DataIOAdapter* fDataIOAdapter;
218 int32 fFlags;
220 status_t fErr;
224 // #pragma mark - Readers/Decoders
227 status_t
228 PluginManager::CreateReader(Reader** reader, int32* streamCount,
229 media_file_format* mff, BDataIO* source)
231 TRACE("PluginManager::CreateReader enter\n");
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);
239 status_t ret = buffered_source->InitCheck();
240 if (ret != B_OK)
241 return ret;
243 // get list of available readers from the server
244 entry_ref refs[MAX_READERS];
245 int32 count;
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;
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;
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;
271 *reader = readerPlugin->NewReader();
272 if (*reader == NULL) {
273 printf("PluginManager::CreateReader: NewReader failed\n");
274 PutPlugin(plugin);
275 return B_ERROR;
278 buffered_source->Seek(0, SEEK_SET);
279 (*reader)->Setup(buffered_source);
280 (*reader)->fMediaPlugin = plugin;
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;
290 DestroyReader(*reader);
291 *reader = NULL;
294 TRACE("PluginManager::CreateReader leave\n");
295 return B_MEDIA_NO_HANDLER;
299 void
300 PluginManager::DestroyReader(Reader* reader)
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);
315 status_t
316 PluginManager::CreateDecoder(Decoder** _decoder, const media_format& format)
318 TRACE("PluginManager::CreateDecoder enter\n");
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;
330 MediaPlugin* plugin = GetPlugin(ref);
331 if (plugin == NULL) {
332 printf("PluginManager::CreateDecoder: GetPlugin failed\n");
333 return B_ERROR;
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;
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;
351 TRACE(" created decoder: %p\n", *_decoder);
352 (*_decoder)->fMediaPlugin = plugin;
354 TRACE("PluginManager::CreateDecoder leave\n");
356 return B_OK;
360 status_t
361 PluginManager::CreateDecoder(Decoder** decoder, const media_codec_info& mci)
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;
369 MediaPlugin* plugin = GetPlugin(ref);
370 if (plugin == NULL) {
371 ERROR("PluginManager::CreateDecoder: GetPlugin failed\n");
372 return B_ERROR;
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;
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;
390 TRACE(" created decoder: %p\n", *decoder);
391 (*decoder)->fMediaPlugin = plugin;
393 TRACE("PluginManager::CreateDecoder leave\n");
395 return B_OK;
400 status_t
401 PluginManager::GetDecoderInfo(Decoder* decoder, media_codec_info* _info) const
403 if (decoder == NULL)
404 return B_BAD_VALUE;
406 decoder->GetCodecInfo(_info);
407 // TODO:
408 // out_info->id =
409 // out_info->sub_id =
410 return B_OK;
414 void
415 PluginManager::DestroyDecoder(Decoder* decoder)
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);
430 // #pragma mark - Writers/Encoders
433 status_t
434 PluginManager::CreateWriter(Writer** writer, const media_file_format& mff,
435 BDataIO* target)
437 TRACE("PluginManager::CreateWriter enter\n");
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;
449 MediaPlugin* plugin = GetPlugin(ref);
450 if (plugin == NULL) {
451 printf("PluginManager::CreateWriter: GetPlugin failed\n");
452 return B_ERROR;
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;
462 *writer = writerPlugin->NewWriter();
463 if (*writer == NULL) {
464 printf("PluginManager::CreateWriter: NewWriter failed\n");
465 PutPlugin(plugin);
466 return B_ERROR;
469 (*writer)->Setup(target);
470 (*writer)->fMediaPlugin = plugin;
472 TRACE("PluginManager::CreateWriter leave\n");
473 return B_OK;
477 void
478 PluginManager::DestroyWriter(Writer* writer)
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);
493 status_t
494 PluginManager::CreateEncoder(Encoder** _encoder,
495 const media_codec_info* codecInfo, uint32 flags)
497 TRACE("PluginManager::CreateEncoder enter\n");
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;
509 MediaPlugin* plugin = GetPlugin(ref);
510 if (!plugin) {
511 printf("PluginManager::CreateEncoder: GetPlugin failed\n");
512 return B_ERROR;
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;
522 *_encoder = encoderPlugin->NewEncoder(*codecInfo);
523 if (*_encoder == NULL) {
524 printf("PluginManager::CreateEncoder: NewEncoder() failed\n");
525 PutPlugin(plugin);
526 return B_ERROR;
528 TRACE(" created encoder: %p\n", *_encoder);
529 (*_encoder)->fMediaPlugin = plugin;
531 TRACE("PluginManager::CreateEncoder leave\n");
533 return B_OK;
537 status_t
538 PluginManager::CreateEncoder(Encoder** encoder, const media_format& format)
540 TRACE("PluginManager::CreateEncoder enter nr2\n");
542 entry_ref ref;
544 status_t ret = AddOnManager::GetInstance()->GetEncoderForFormat(
545 &ref, format);
547 if (ret != B_OK) {
548 ERROR("PluginManager::CreateEncoder: can't get decoder for format: "
549 "%s\n", strerror(ret));
550 return ret;
553 MediaPlugin* plugin = GetPlugin(ref);
554 if (plugin == NULL) {
555 ERROR("PluginManager::CreateEncoder: GetPlugin failed\n");
556 return B_ERROR;
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;
567 *encoder = encoderPlugin->NewEncoder(format);
568 if (*encoder == NULL) {
569 ERROR("PluginManager::CreateEncoder: NewEncoder() failed\n");
570 PutPlugin(plugin);
571 return B_ERROR;
573 TRACE(" created encoder: %p\n", *encoder);
574 (*encoder)->fMediaPlugin = plugin;
576 TRACE("PluginManager::CreateEncoder leave nr2\n");
578 return B_OK;
582 void
583 PluginManager::DestroyEncoder(Encoder* encoder)
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);
598 status_t
599 PluginManager::CreateStreamer(Streamer** streamer, BUrl url, BDataIO** source)
601 BAutolock _(fLocker);
603 TRACE("PluginManager::CreateStreamer enter\n");
605 entry_ref refs[MAX_STREAMERS];
606 int32 count;
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;
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;
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;
632 *streamer = streamerPlugin->NewStreamer();
633 if (*streamer == NULL) {
634 printf("PluginManager::CreateStreamer: NewReader failed\n");
635 PutPlugin(plugin);
636 return B_ERROR;
639 (*streamer)->fMediaPlugin = plugin;
640 plugin->fRefCount++;
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;
649 DestroyStreamer(*streamer);
650 *streamer = NULL;
653 TRACE("PluginManager::CreateStreamer leave\n");
654 return B_MEDIA_NO_HANDLER;
658 void
659 PluginManager::DestroyStreamer(Streamer* streamer)
661 BAutolock _(fLocker);
663 if (streamer != NULL) {
664 TRACE("PluginManager::DestroyStreamer(%p, plugin: %p)\n", streamer,
665 streamer->fMediaPlugin);
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;
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--;
683 // #pragma mark -
686 PluginManager::PluginManager()
688 fPluginList(),
689 fLocker("media plugin manager")
691 CALLED();
695 PluginManager::~PluginManager()
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);
709 MediaPlugin*
710 PluginManager::GetPlugin(const entry_ref& ref)
712 TRACE("PluginManager::GetPlugin(%s)\n", ref.name);
713 fLocker.Lock();
715 MediaPlugin* plugin;
716 plugin_info* pinfo;
717 plugin_info info;
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;
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;
735 strcpy(info.name, ref.name);
736 info.usecount = 1;
737 fPluginList.Insert(info);
739 TRACE("PluginManager: PlugIn %s loaded\n", ref.name);
741 plugin = info.plugin;
742 TRACE(" loaded plugin: %p\n", plugin);
744 fLocker.Unlock();
745 return plugin;
749 void
750 PluginManager::PutPlugin(MediaPlugin* plugin)
752 TRACE("PluginManager::PutPlugin()\n");
753 fLocker.Lock();
755 plugin_info* pinfo;
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();
767 fLocker.Unlock();
768 return;
772 printf("PluginManager: Error, can't put PlugIn %p\n", plugin);
774 fLocker.Unlock();
778 status_t
779 PluginManager::_LoadPlugin(const entry_ref& ref, MediaPlugin** plugin,
780 image_id* image)
782 BPath p(&ref);
784 TRACE("PluginManager: _LoadPlugin trying to load %s\n", p.Path());
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;
793 MediaPlugin* (*instantiate_plugin_func)();
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;
803 MediaPlugin *pl;
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;
813 *plugin = pl;
814 *image = id;
815 return B_OK;