1 /*
2 * Copyright 2004-2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 * Marcus Overhagen
7 * Axel Dörfler
8 * Stephan Aßmus <superstippi@gmx.de>
9 */
10
11
12 #include "AddOnManager.h"
13
14 #include <stdio.h>
15 #include <string.h>
16
17 #include <Architecture.h>
18 #include <AutoDeleter.h>
19 #include <Autolock.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <FindDirectory.h>
23 #include <image.h>
24 #include <Path.h>
25
26 #include "MediaDebug.h"
27
28 #include "FormatManager.h"
29 #include "MetaFormat.h"
30
31
32 namespace BPrivate {
33 namespace media {
34
35
36 // #pragma mark - ImageLoader
37
38 /*! The ImageLoader class is a convenience class to temporarily load
39 an image file, and unload it on deconstruction automatically.
40 */
41 class ImageLoader {
42 public:
ImageLoader(BPath & path)43 ImageLoader(BPath& path)
44 {
45 fImage = load_add_on(path.Path());
46 }
47
~ImageLoader()48 ~ImageLoader()
49 {
50 if (fImage >= B_OK)
51 unload_add_on(fImage);
52 }
53
InitCheck() const54 status_t InitCheck() const { return fImage >= 0 ? B_OK : fImage; }
Image() const55 image_id Image() const { return fImage; }
56
57 private:
58 image_id fImage;
59 };
60
61
62 // #pragma mark -
63
64
AddOnManager()65 AddOnManager::AddOnManager()
66 :
67 fLock("add-on manager"),
68 fNextWriterFormatFamilyID(0),
69 fNextEncoderCodecInfoID(0)
70 {
71 }
72
73
~AddOnManager()74 AddOnManager::~AddOnManager()
75 {
76 }
77
78
79 AddOnManager AddOnManager::sInstance;
80
81
82 /* static */ AddOnManager*
GetInstance()83 AddOnManager::GetInstance()
84 {
85 return &sInstance;
86 }
87
88
89 status_t
GetDecoderForFormat(entry_ref * _decoderRef,const media_format & format)90 AddOnManager::GetDecoderForFormat(entry_ref* _decoderRef,
91 const media_format& format)
92 {
93 if ((format.type == B_MEDIA_ENCODED_VIDEO
94 || format.type == B_MEDIA_ENCODED_AUDIO
95 || format.type == B_MEDIA_MULTISTREAM)
96 && format.Encoding() == 0) {
97 return B_MEDIA_BAD_FORMAT;
98 }
99 if (format.type == B_MEDIA_NO_TYPE || format.type == B_MEDIA_UNKNOWN_TYPE)
100 return B_MEDIA_BAD_FORMAT;
101
102 BAutolock locker(fLock);
103 RegisterAddOns();
104
105 // Since the list of decoders is unsorted, we need to search for
106 // a decoder by add-on directory, in order to maintain the shadowing
107 // of system add-ons by user add-ons, in case they offer decoders
108 // for the same format.
109
110 char** directories = NULL;
111 size_t directoryCount = 0;
112
113 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY,
114 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories,
115 &directoryCount) != B_OK) {
116 printf("AddOnManager::GetDecoderForFormat: failed to locate plugins\n");
117 return B_ENTRY_NOT_FOUND;
118 }
119
120 MemoryDeleter directoriesDeleter(directories);
121
122 BPath path;
123 for (uint i = 0; i < directoryCount; i++) {
124 path.SetTo(directories[i]);
125 if (_FindDecoder(format, path, _decoderRef))
126 return B_OK;
127 }
128
129 return B_ENTRY_NOT_FOUND;
130 }
131
132
133 status_t
GetEncoderForFormat(entry_ref * _encoderRef,const media_format & outputFormat)134 AddOnManager::GetEncoderForFormat(entry_ref* _encoderRef,
135 const media_format& outputFormat)
136 {
137 if ((outputFormat.type == B_MEDIA_RAW_VIDEO
138 || outputFormat.type == B_MEDIA_RAW_AUDIO)) {
139 return B_MEDIA_BAD_FORMAT;
140 }
141
142 if (outputFormat.type == B_MEDIA_NO_TYPE
143 || outputFormat.type == B_MEDIA_UNKNOWN_TYPE) {
144 return B_MEDIA_BAD_FORMAT;
145 }
146
147 BAutolock locker(fLock);
148 RegisterAddOns();
149
150 char** directories = NULL;
151 size_t directoryCount = 0;
152
153 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY,
154 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories,
155 &directoryCount) != B_OK) {
156 printf("AddOnManager::GetDecoderForFormat: failed to locate plugins\n");
157 return B_ENTRY_NOT_FOUND;
158 }
159
160 MemoryDeleter directoriesDeleter(directories);
161
162 BPath path;
163 for (uint i = 0; i < directoryCount; i++) {
164 path.SetTo(directories[i]);
165 if (_FindEncoder(outputFormat, path, _encoderRef))
166 return B_OK;
167 }
168
169 return B_ENTRY_NOT_FOUND;
170 }
171
172
173 status_t
GetReaders(entry_ref * outRefs,int32 * outCount,int32 maxCount)174 AddOnManager::GetReaders(entry_ref* outRefs, int32* outCount,
175 int32 maxCount)
176 {
177 BAutolock locker(fLock);
178 RegisterAddOns();
179
180 *outCount = 0;
181
182 // See GetDecoderForFormat() for why we need to scan the list by path.
183
184 char** directories = NULL;
185 size_t directoryCount = 0;
186
187 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY,
188 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories,
189 &directoryCount) != B_OK) {
190 printf("AddOnManager::GetReaders: failed to locate plugins\n");
191 return B_ENTRY_NOT_FOUND;
192 }
193
194 MemoryDeleter directoriesDeleter(directories);
195
196 BPath path;
197 for (uint i = 0; i < directoryCount; i++) {
198 path.SetTo(directories[i]);
199 _GetReaders(path, outRefs, outCount, maxCount);
200 }
201
202 return B_OK;
203 }
204
205
206 status_t
GetStreamers(entry_ref * outRefs,int32 * outCount,int32 maxCount)207 AddOnManager::GetStreamers(entry_ref* outRefs, int32* outCount,
208 int32 maxCount)
209 {
210 BAutolock locker(fLock);
211 RegisterAddOns();
212
213 int32 count = 0;
214 streamer_info* info;
215 for (fStreamerList.Rewind(); fStreamerList.GetNext(&info);) {
216 if (count == maxCount)
217 break;
218
219 *outRefs = info->ref;
220 outRefs++;
221 count++;
222 }
223
224 *outCount = count;
225 return B_OK;
226 }
227
228
229 status_t
GetEncoder(entry_ref * _encoderRef,int32 id)230 AddOnManager::GetEncoder(entry_ref* _encoderRef, int32 id)
231 {
232 BAutolock locker(fLock);
233 RegisterAddOns();
234
235 encoder_info* info;
236 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) {
237 // check if the encoder matches the supplied format
238 if (info->internalID == (uint32)id) {
239 *_encoderRef = info->ref;
240 return B_OK;
241 }
242 }
243
244 return B_ENTRY_NOT_FOUND;
245 }
246
247
248 status_t
GetWriter(entry_ref * _ref,uint32 internalID)249 AddOnManager::GetWriter(entry_ref* _ref, uint32 internalID)
250 {
251 BAutolock locker(fLock);
252 RegisterAddOns();
253
254 writer_info* info;
255 for (fWriterList.Rewind(); fWriterList.GetNext(&info);) {
256 if (info->internalID == internalID) {
257 *_ref = info->ref;
258 return B_OK;
259 }
260 }
261
262 return B_ERROR;
263 }
264
265
266 status_t
GetFileFormat(media_file_format * _fileFormat,int32 cookie)267 AddOnManager::GetFileFormat(media_file_format* _fileFormat, int32 cookie)
268 {
269 BAutolock locker(fLock);
270 RegisterAddOns();
271
272 media_file_format* fileFormat;
273 if (fWriterFileFormats.Get(cookie, &fileFormat)) {
274 *_fileFormat = *fileFormat;
275 return B_OK;
276 }
277
278 return B_BAD_INDEX;
279 }
280
281
282 status_t
GetCodecInfo(media_codec_info * _codecInfo,media_format_family * _formatFamily,media_format * _inputFormat,media_format * _outputFormat,int32 cookie)283 AddOnManager::GetCodecInfo(media_codec_info* _codecInfo,
284 media_format_family* _formatFamily,
285 media_format* _inputFormat, media_format* _outputFormat, int32 cookie)
286 {
287 BAutolock locker(fLock);
288 RegisterAddOns();
289
290 encoder_info* info;
291 if (fEncoderList.Get(cookie, &info)) {
292 *_codecInfo = info->codecInfo;
293 *_formatFamily = info->formatFamily;
294 *_inputFormat = info->intputFormat;
295 *_outputFormat = info->outputFormat;
296 return B_OK;
297 }
298
299 return B_BAD_INDEX;
300 }
301
302
303 // #pragma mark -
304
305
306 void
RegisterAddOns()307 AddOnManager::RegisterAddOns()
308 {
309 // Check if add-ons are already registered.
310 if (!fReaderList.IsEmpty() || !fWriterList.IsEmpty()
311 || !fDecoderList.IsEmpty() || !fEncoderList.IsEmpty()) {
312 return;
313 }
314
315 char** directories = NULL;
316 size_t directoryCount = 0;
317
318 if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY,
319 "media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories,
320 &directoryCount) != B_OK) {
321 return;
322 }
323
324 MemoryDeleter directoriesDeleter(directories);
325
326 BPath path;
327 for (uint i = 0; i < directoryCount; i++) {
328 BDirectory directory;
329 if (directory.SetTo(directories[i]) == B_OK) {
330 entry_ref ref;
331 while(directory.GetNextRef(&ref) == B_OK)
332 _RegisterAddOn(ref);
333 }
334 }
335 }
336
337
338 status_t
_RegisterAddOn(const entry_ref & ref)339 AddOnManager::_RegisterAddOn(const entry_ref& ref)
340 {
341 BPath path(&ref);
342
343 ImageLoader loader(path);
344 status_t status = loader.InitCheck();
345 if (status != B_OK)
346 return status;
347
348 MediaPlugin* (*instantiate_plugin_func)();
349
350 if (get_image_symbol(loader.Image(), "instantiate_plugin",
351 B_SYMBOL_TYPE_TEXT, (void**)&instantiate_plugin_func) < B_OK) {
352 printf("AddOnManager::_RegisterAddOn(): can't find instantiate_plugin "
353 "in \"%s\"\n", path.Path());
354 return B_BAD_TYPE;
355 }
356
357 MediaPlugin* plugin = (*instantiate_plugin_func)();
358 if (plugin == NULL) {
359 printf("AddOnManager::_RegisterAddOn(): instantiate_plugin in \"%s\" "
360 "returned NULL\n", path.Path());
361 return B_ERROR;
362 }
363
364 ReaderPlugin* reader = dynamic_cast<ReaderPlugin*>(plugin);
365 if (reader != NULL)
366 _RegisterReader(reader, ref);
367
368 DecoderPlugin* decoder = dynamic_cast<DecoderPlugin*>(plugin);
369 if (decoder != NULL)
370 _RegisterDecoder(decoder, ref);
371
372 WriterPlugin* writer = dynamic_cast<WriterPlugin*>(plugin);
373 if (writer != NULL)
374 _RegisterWriter(writer, ref);
375
376 EncoderPlugin* encoder = dynamic_cast<EncoderPlugin*>(plugin);
377 if (encoder != NULL)
378 _RegisterEncoder(encoder, ref);
379
380 StreamerPlugin* streamer = dynamic_cast<StreamerPlugin*>(plugin);
381 if (streamer != NULL)
382 _RegisterStreamer(streamer, ref);
383
384 delete plugin;
385
386 return B_OK;
387 }
388
389
390 status_t
_UnregisterAddOn(const entry_ref & ref)391 AddOnManager::_UnregisterAddOn(const entry_ref& ref)
392 {
393 BAutolock locker(fLock);
394
395 // Remove any Readers exported by this add-on
396 reader_info* readerInfo;
397 for (fReaderList.Rewind(); fReaderList.GetNext(&readerInfo);) {
398 if (readerInfo->ref == ref) {
399 fReaderList.RemoveCurrent();
400 break;
401 }
402 }
403
404 // Remove any Decoders exported by this add-on
405 decoder_info* decoderInfo;
406 for (fDecoderList.Rewind(); fDecoderList.GetNext(&decoderInfo);) {
407 if (decoderInfo->ref == ref) {
408 media_format* format;
409 for (decoderInfo->formats.Rewind();
410 decoderInfo->formats.GetNext(&format);) {
411 FormatManager::GetInstance()->RemoveFormat(*format);
412 }
413 fDecoderList.RemoveCurrent();
414 break;
415 }
416 }
417
418 // Remove any Writers exported by this add-on
419 writer_info* writerInfo;
420 for (fWriterList.Rewind(); fWriterList.GetNext(&writerInfo);) {
421 if (writerInfo->ref == ref) {
422 // Remove any formats from this writer
423 media_file_format* writerFormat;
424 for (fWriterFileFormats.Rewind();
425 fWriterFileFormats.GetNext(&writerFormat);) {
426 if (writerFormat->id.internal_id == writerInfo->internalID)
427 fWriterFileFormats.RemoveCurrent();
428 }
429 fWriterList.RemoveCurrent();
430 break;
431 }
432 }
433
434 encoder_info* encoderInfo;
435 for (fEncoderList.Rewind(); fEncoderList.GetNext(&encoderInfo);) {
436 if (encoderInfo->ref == ref) {
437 fEncoderList.RemoveCurrent();
438 // Keep going, since we add multiple encoder infos per add-on.
439 }
440 }
441
442 return B_OK;
443 }
444
445
446 void
_RegisterReader(ReaderPlugin * reader,const entry_ref & ref)447 AddOnManager::_RegisterReader(ReaderPlugin* reader, const entry_ref& ref)
448 {
449 BAutolock locker(fLock);
450
451 reader_info* pinfo;
452 for (fReaderList.Rewind(); fReaderList.GetNext(&pinfo);) {
453 if (!strcmp(pinfo->ref.name, ref.name)) {
454 // we already know this reader
455 return;
456 }
457 }
458
459 reader_info info;
460 info.ref = ref;
461
462 fReaderList.Insert(info);
463 }
464
465
466 void
_RegisterDecoder(DecoderPlugin * plugin,const entry_ref & ref)467 AddOnManager::_RegisterDecoder(DecoderPlugin* plugin, const entry_ref& ref)
468 {
469 BAutolock locker(fLock);
470
471 decoder_info* pinfo;
472 for (fDecoderList.Rewind(); fDecoderList.GetNext(&pinfo);) {
473 if (!strcmp(pinfo->ref.name, ref.name)) {
474 // we already know this decoder
475 return;
476 }
477 }
478
479 decoder_info info;
480 info.ref = ref;
481
482 media_format* formats = 0;
483 size_t count = 0;
484 if (plugin->GetSupportedFormats(&formats, &count) != B_OK) {
485 printf("AddOnManager::_RegisterDecoder(): plugin->GetSupportedFormats"
486 "(...) failed!\n");
487 return;
488 }
489 for (uint i = 0 ; i < count ; i++)
490 info.formats.Insert(formats[i]);
491
492 fDecoderList.Insert(info);
493 }
494
495
496 void
_RegisterWriter(WriterPlugin * writer,const entry_ref & ref)497 AddOnManager::_RegisterWriter(WriterPlugin* writer, const entry_ref& ref)
498 {
499 BAutolock locker(fLock);
500
501 writer_info* pinfo;
502 for (fWriterList.Rewind(); fWriterList.GetNext(&pinfo);) {
503 if (!strcmp(pinfo->ref.name, ref.name)) {
504 // we already know this writer
505 return;
506 }
507 }
508
509 writer_info info;
510 info.ref = ref;
511 info.internalID = fNextWriterFormatFamilyID++;
512
513 // Get list of support media_file_formats...
514 const media_file_format* fileFormats = NULL;
515 size_t count = 0;
516 if (writer->GetSupportedFileFormats(&fileFormats, &count) != B_OK) {
517 printf("AddOnManager::_RegisterWriter(): "
518 "plugin->GetSupportedFileFormats(...) failed!\n");
519 return;
520 }
521 for (uint i = 0 ; i < count ; i++) {
522 // Generate a proper ID before inserting this format, this encodes
523 // the specific plugin in the media_file_format.
524 media_file_format fileFormat = fileFormats[i];
525 fileFormat.id.node = ref.directory;
526 fileFormat.id.device = ref.device;
527 fileFormat.id.internal_id = info.internalID;
528
529 fWriterFileFormats.Insert(fileFormat);
530 }
531
532 fWriterList.Insert(info);
533 }
534
535
536 void
_RegisterEncoder(EncoderPlugin * plugin,const entry_ref & ref)537 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref)
538 {
539 BAutolock locker(fLock);
540
541 encoder_info* pinfo;
542 for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) {
543 if (!strcmp(pinfo->ref.name, ref.name)) {
544 // We already know this encoder. When we reject encoders with
545 // the same name, we allow the user to overwrite system encoders
546 // in her home folder.
547 return;
548 }
549 }
550
551 // Get list of supported encoders...
552
553 encoder_info info;
554 info.ref = ref;
555 info.internalID = fNextEncoderCodecInfoID++;
556
557 int32 cookie = 0;
558
559 while (true) {
560 memset(&info.codecInfo, 0, sizeof(media_codec_info));
561 info.intputFormat.Clear();
562 info.outputFormat.Clear();
563 if (plugin->RegisterNextEncoder(&cookie,
564 &info.codecInfo, &info.formatFamily, &info.intputFormat,
565 &info.outputFormat) != B_OK) {
566 break;
567 }
568 info.codecInfo.id = info.internalID;
569 // NOTE: info.codecInfo.sub_id is for private use by the Encoder,
570 // we don't touch it, but it is maintained and passed back to the
571 // EncoderPlugin in NewEncoder(media_codec_info).
572
573 if (!fEncoderList.Insert(info))
574 break;
575 }
576 }
577
578
579 void
_RegisterStreamer(StreamerPlugin * streamer,const entry_ref & ref)580 AddOnManager::_RegisterStreamer(StreamerPlugin* streamer, const entry_ref& ref)
581 {
582 BAutolock locker(fLock);
583
584 streamer_info* pInfo;
585 for (fStreamerList.Rewind(); fStreamerList.GetNext(&pInfo);) {
586 if (!strcmp(pInfo->ref.name, ref.name)) {
587 // We already know this streamer
588 return;
589 }
590 }
591
592 streamer_info info;
593 info.ref = ref;
594 fStreamerList.Insert(info);
595 }
596
597
598 bool
_FindDecoder(const media_format & format,const BPath & path,entry_ref * _decoderRef)599 AddOnManager::_FindDecoder(const media_format& format, const BPath& path,
600 entry_ref* _decoderRef)
601 {
602 node_ref nref;
603 BDirectory directory;
604 if (directory.SetTo(path.Path()) != B_OK
605 || directory.GetNodeRef(&nref) != B_OK) {
606 return false;
607 }
608
609 decoder_info* info;
610 for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) {
611 if (info->ref.directory != nref.node)
612 continue;
613
614 media_format* decoderFormat;
615 for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) {
616 // check if the decoder matches the supplied format
617 if (!decoderFormat->Matches(&format))
618 continue;
619
620 *_decoderRef = info->ref;
621 return true;
622 }
623 }
624 return false;
625 }
626
627
628 bool
_FindEncoder(const media_format & format,const BPath & path,entry_ref * _encoderRef)629 AddOnManager::_FindEncoder(const media_format& format, const BPath& path,
630 entry_ref* _encoderRef)
631 {
632 node_ref nref;
633 BDirectory directory;
634 if (directory.SetTo(path.Path()) != B_OK
635 || directory.GetNodeRef(&nref) != B_OK) {
636 return false;
637 }
638
639 encoder_info* info;
640 for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) {
641 if (info->ref.directory != nref.node)
642 continue;
643
644 // check if the encoder matches the supplied format
645 if (info->outputFormat.Matches(&format)) {
646 *_encoderRef = info->ref;
647 return true;
648 }
649 continue;
650 }
651 return false;
652 }
653
654
655 void
_GetReaders(const BPath & path,entry_ref * outRefs,int32 * outCount,int32 maxCount)656 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs,
657 int32* outCount, int32 maxCount)
658 {
659 node_ref nref;
660 BDirectory directory;
661 if (directory.SetTo(path.Path()) != B_OK
662 || directory.GetNodeRef(&nref) != B_OK) {
663 return;
664 }
665
666 reader_info* info;
667 for (fReaderList.Rewind(); fReaderList.GetNext(&info)
668 && *outCount < maxCount;) {
669 if (info->ref.directory != nref.node)
670 continue;
671
672 outRefs[*outCount] = info->ref;
673 (*outCount)++;
674 }
675 }
676
677
678 } // namespace media
679 } // namespace BPrivate
680