xref: /haiku/src/kits/media/AddOnManager.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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:
43 	ImageLoader(BPath& path)
44 	{
45 		fImage = load_add_on(path.Path());
46 	}
47 
48 	~ImageLoader()
49 	{
50 		if (fImage >= B_OK)
51 			unload_add_on(fImage);
52 	}
53 
54 	status_t InitCheck() const { return fImage >= 0 ? B_OK : fImage; }
55 	image_id Image() const { return fImage; }
56 
57 private:
58 	image_id	fImage;
59 };
60 
61 
62 //	#pragma mark -
63 
64 
65 AddOnManager::AddOnManager()
66 	:
67 	fLock("add-on manager"),
68 	fNextWriterFormatFamilyID(0),
69 	fNextEncoderCodecInfoID(0)
70 {
71 }
72 
73 
74 AddOnManager::~AddOnManager()
75 {
76 }
77 
78 
79 AddOnManager AddOnManager::sInstance;
80 
81 
82 /* static */ AddOnManager*
83 AddOnManager::GetInstance()
84 {
85 	return &sInstance;
86 }
87 
88 
89 status_t
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
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
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
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
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
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
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
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
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
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
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
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
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
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 		media_file_format fileFormat = fileFormats[i];
523 		// Ignore non-writable formats
524 		if ((fileFormat.capabilities & media_file_format::B_WRITABLE) == 0)
525 			continue;
526 
527 		// Generate a proper ID before inserting this format, this encodes
528 		// the specific plugin in the media_file_format.
529 		fileFormat.id.node = ref.directory;
530 		fileFormat.id.device = ref.device;
531 		fileFormat.id.internal_id = info.internalID;
532 
533 		fWriterFileFormats.Insert(fileFormat);
534 	}
535 
536 	fWriterList.Insert(info);
537 }
538 
539 
540 void
541 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref)
542 {
543 	BAutolock locker(fLock);
544 
545 	encoder_info* pinfo;
546 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) {
547 		if (!strcmp(pinfo->ref.name, ref.name)) {
548 			// We already know this encoder. When we reject encoders with
549 			// the same name, we allow the user to overwrite system encoders
550 			// in her home folder.
551 			return;
552 		}
553 	}
554 
555 	// Get list of supported encoders...
556 
557 	encoder_info info;
558 	info.ref = ref;
559 	info.internalID = fNextEncoderCodecInfoID++;
560 
561 	int32 cookie = 0;
562 
563 	while (true) {
564 		memset(&info.codecInfo, 0, sizeof(media_codec_info));
565 		info.intputFormat.Clear();
566 		info.outputFormat.Clear();
567 		if (plugin->RegisterNextEncoder(&cookie,
568 			&info.codecInfo, &info.formatFamily, &info.intputFormat,
569 			&info.outputFormat) != B_OK) {
570 			break;
571 		}
572 		info.codecInfo.id = info.internalID;
573 		// NOTE: info.codecInfo.sub_id is for private use by the Encoder,
574 		// we don't touch it, but it is maintained and passed back to the
575 		// EncoderPlugin in NewEncoder(media_codec_info).
576 
577 		if (!fEncoderList.Insert(info))
578 			break;
579 	}
580 }
581 
582 
583 void
584 AddOnManager::_RegisterStreamer(StreamerPlugin* streamer, const entry_ref& ref)
585 {
586 	BAutolock locker(fLock);
587 
588 	streamer_info* pInfo;
589 	for (fStreamerList.Rewind(); fStreamerList.GetNext(&pInfo);) {
590 		if (!strcmp(pInfo->ref.name, ref.name)) {
591 			// We already know this streamer
592 			return;
593 		}
594 	}
595 
596 	streamer_info info;
597 	info.ref = ref;
598 	fStreamerList.Insert(info);
599 }
600 
601 
602 bool
603 AddOnManager::_FindDecoder(const media_format& format, const BPath& path,
604 	entry_ref* _decoderRef)
605 {
606 	node_ref nref;
607 	BDirectory directory;
608 	if (directory.SetTo(path.Path()) != B_OK
609 		|| directory.GetNodeRef(&nref) != B_OK) {
610 		return false;
611 	}
612 
613 	decoder_info* info;
614 	for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) {
615 		if (info->ref.directory != nref.node)
616 			continue;
617 
618 		media_format* decoderFormat;
619 		for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) {
620 			// check if the decoder matches the supplied format
621 			if (!decoderFormat->Matches(&format))
622 				continue;
623 
624 			*_decoderRef = info->ref;
625 			return true;
626 		}
627 	}
628 	return false;
629 }
630 
631 
632 bool
633 AddOnManager::_FindEncoder(const media_format& format, const BPath& path,
634 	entry_ref* _encoderRef)
635 {
636 	node_ref nref;
637 	BDirectory directory;
638 	if (directory.SetTo(path.Path()) != B_OK
639 		|| directory.GetNodeRef(&nref) != B_OK) {
640 		return false;
641 	}
642 
643 	encoder_info* info;
644 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) {
645 		if (info->ref.directory != nref.node)
646 			continue;
647 
648 		// check if the encoder matches the supplied format
649 		if (info->outputFormat.Matches(&format)) {
650 			*_encoderRef = info->ref;
651 			return true;
652 		}
653 		continue;
654 	}
655 	return false;
656 }
657 
658 
659 void
660 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs,
661 	int32* outCount, int32 maxCount)
662 {
663 	node_ref nref;
664 	BDirectory directory;
665 	if (directory.SetTo(path.Path()) != B_OK
666 		|| directory.GetNodeRef(&nref) != B_OK) {
667 		return;
668 	}
669 
670 	reader_info* info;
671 	for (fReaderList.Rewind(); fReaderList.GetNext(&info)
672 		&& *outCount < maxCount;) {
673 		if (info->ref.directory != nref.node)
674 			continue;
675 
676 		outRefs[*outCount] = info->ref;
677 		(*outCount)++;
678 	}
679 }
680 
681 
682 } // namespace media
683 } // namespace BPrivate
684