xref: /haiku/src/kits/media/AddOnManager.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 "debug.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::GetEncoder(entry_ref* _encoderRef, int32 id)
208 {
209 	BAutolock locker(fLock);
210 	RegisterAddOns();
211 
212 	encoder_info* info;
213 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) {
214 		// check if the encoder matches the supplied format
215 		if (info->internalID == (uint32)id) {
216 			*_encoderRef = info->ref;
217 			return B_OK;
218 		}
219 	}
220 
221 	return B_ENTRY_NOT_FOUND;
222 }
223 
224 
225 status_t
226 AddOnManager::GetWriter(entry_ref* _ref, uint32 internalID)
227 {
228 	BAutolock locker(fLock);
229 	RegisterAddOns();
230 
231 	writer_info* info;
232 	for (fWriterList.Rewind(); fWriterList.GetNext(&info);) {
233 		if (info->internalID == internalID) {
234 			*_ref = info->ref;
235 			return B_OK;
236 		}
237 	}
238 
239 	return B_ERROR;
240 }
241 
242 
243 status_t
244 AddOnManager::GetFileFormat(media_file_format* _fileFormat, int32 cookie)
245 {
246 	BAutolock locker(fLock);
247 	RegisterAddOns();
248 
249 	media_file_format* fileFormat;
250 	if (fWriterFileFormats.Get(cookie, &fileFormat)) {
251 		*_fileFormat = *fileFormat;
252 		return B_OK;
253 	}
254 
255 	return B_BAD_INDEX;
256 }
257 
258 
259 status_t
260 AddOnManager::GetCodecInfo(media_codec_info* _codecInfo,
261 	media_format_family* _formatFamily,
262 	media_format* _inputFormat, media_format* _outputFormat, int32 cookie)
263 {
264 	BAutolock locker(fLock);
265 	RegisterAddOns();
266 
267 	encoder_info* info;
268 	if (fEncoderList.Get(cookie, &info)) {
269 		*_codecInfo = info->codecInfo;
270 		*_formatFamily = info->formatFamily;
271 		*_inputFormat = info->intputFormat;
272 		*_outputFormat = info->outputFormat;
273 		return B_OK;
274 	}
275 
276 	return B_BAD_INDEX;
277 }
278 
279 
280 // #pragma mark -
281 
282 
283 void
284 AddOnManager::RegisterAddOns()
285 {
286 	// Check if add-ons are already registered.
287 	if (!fReaderList.IsEmpty() || !fWriterList.IsEmpty()
288 		|| !fDecoderList.IsEmpty() || !fEncoderList.IsEmpty()) {
289 		return;
290 	}
291 
292 	char** directories = NULL;
293 	size_t directoryCount = 0;
294 
295 	if (find_paths_etc(get_architecture(), B_FIND_PATH_ADD_ONS_DIRECTORY,
296 			"media/plugins", B_FIND_PATH_EXISTING_ONLY, &directories,
297 			&directoryCount) != B_OK) {
298 		return;
299 	}
300 
301 	MemoryDeleter directoriesDeleter(directories);
302 
303 	BPath path;
304 	for (uint i = 0; i < directoryCount; i++) {
305 		BDirectory directory;
306 		if (directory.SetTo(directories[i]) == B_OK) {
307 			entry_ref ref;
308 			while(directory.GetNextRef(&ref) == B_OK)
309 				_RegisterAddOn(ref);
310 		}
311 	}
312 }
313 
314 
315 status_t
316 AddOnManager::_RegisterAddOn(const entry_ref& ref)
317 {
318 	BPath path(&ref);
319 
320 	ImageLoader loader(path);
321 	status_t status = loader.InitCheck();
322 	if (status != B_OK)
323 		return status;
324 
325 	MediaPlugin* (*instantiate_plugin_func)();
326 
327 	if (get_image_symbol(loader.Image(), "instantiate_plugin",
328 			B_SYMBOL_TYPE_TEXT, (void**)&instantiate_plugin_func) < B_OK) {
329 		printf("AddOnManager::_RegisterAddOn(): can't find instantiate_plugin "
330 			"in \"%s\"\n", path.Path());
331 		return B_BAD_TYPE;
332 	}
333 
334 	MediaPlugin* plugin = (*instantiate_plugin_func)();
335 	if (plugin == NULL) {
336 		printf("AddOnManager::_RegisterAddOn(): instantiate_plugin in \"%s\" "
337 			"returned NULL\n", path.Path());
338 		return B_ERROR;
339 	}
340 
341 	ReaderPlugin* reader = dynamic_cast<ReaderPlugin*>(plugin);
342 	if (reader != NULL)
343 		_RegisterReader(reader, ref);
344 
345 	DecoderPlugin* decoder = dynamic_cast<DecoderPlugin*>(plugin);
346 	if (decoder != NULL)
347 		_RegisterDecoder(decoder, ref);
348 
349 	WriterPlugin* writer = dynamic_cast<WriterPlugin*>(plugin);
350 	if (writer != NULL)
351 		_RegisterWriter(writer, ref);
352 
353 	EncoderPlugin* encoder = dynamic_cast<EncoderPlugin*>(plugin);
354 	if (encoder != NULL)
355 		_RegisterEncoder(encoder, ref);
356 
357 	delete plugin;
358 
359 	return B_OK;
360 }
361 
362 
363 status_t
364 AddOnManager::_UnregisterAddOn(const entry_ref& ref)
365 {
366 	BAutolock locker(fLock);
367 
368 	// Remove any Readers exported by this add-on
369 	reader_info* readerInfo;
370 	for (fReaderList.Rewind(); fReaderList.GetNext(&readerInfo);) {
371 		if (readerInfo->ref == ref) {
372 			fReaderList.RemoveCurrent();
373 			break;
374 		}
375 	}
376 
377 	// Remove any Decoders exported by this add-on
378 	decoder_info* decoderInfo;
379 	for (fDecoderList.Rewind(); fDecoderList.GetNext(&decoderInfo);) {
380 		if (decoderInfo->ref == ref) {
381 			media_format* format;
382 			for (decoderInfo->formats.Rewind();
383 					decoderInfo->formats.GetNext(&format);) {
384 				FormatManager::GetInstance()->RemoveFormat(*format);
385 			}
386 			fDecoderList.RemoveCurrent();
387 			break;
388 		}
389 	}
390 
391 	// Remove any Writers exported by this add-on
392 	writer_info* writerInfo;
393 	for (fWriterList.Rewind(); fWriterList.GetNext(&writerInfo);) {
394 		if (writerInfo->ref == ref) {
395 			// Remove any formats from this writer
396 			media_file_format* writerFormat;
397 			for (fWriterFileFormats.Rewind();
398 				fWriterFileFormats.GetNext(&writerFormat);) {
399 				if (writerFormat->id.internal_id == writerInfo->internalID)
400 					fWriterFileFormats.RemoveCurrent();
401 			}
402 			fWriterList.RemoveCurrent();
403 			break;
404 		}
405 	}
406 
407 	encoder_info* encoderInfo;
408 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&encoderInfo);) {
409 		if (encoderInfo->ref == ref) {
410 			fEncoderList.RemoveCurrent();
411 			// Keep going, since we add multiple encoder infos per add-on.
412 		}
413 	}
414 
415 	return B_OK;
416 }
417 
418 
419 void
420 AddOnManager::_RegisterReader(ReaderPlugin* reader, const entry_ref& ref)
421 {
422 	BAutolock locker(fLock);
423 
424 	reader_info* pinfo;
425 	for (fReaderList.Rewind(); fReaderList.GetNext(&pinfo);) {
426 		if (!strcmp(pinfo->ref.name, ref.name)) {
427 			// we already know this reader
428 			return;
429 		}
430 	}
431 
432 	reader_info info;
433 	info.ref = ref;
434 
435 	fReaderList.Insert(info);
436 }
437 
438 
439 void
440 AddOnManager::_RegisterDecoder(DecoderPlugin* plugin, const entry_ref& ref)
441 {
442 	BAutolock locker(fLock);
443 
444 	decoder_info* pinfo;
445 	for (fDecoderList.Rewind(); fDecoderList.GetNext(&pinfo);) {
446 		if (!strcmp(pinfo->ref.name, ref.name)) {
447 			// we already know this decoder
448 			return;
449 		}
450 	}
451 
452 	decoder_info info;
453 	info.ref = ref;
454 
455 	media_format* formats = 0;
456 	size_t count = 0;
457 	if (plugin->GetSupportedFormats(&formats, &count) != B_OK) {
458 		printf("AddOnManager::_RegisterDecoder(): plugin->GetSupportedFormats"
459 			"(...) failed!\n");
460 		return;
461 	}
462 	for (uint i = 0 ; i < count ; i++)
463 		info.formats.Insert(formats[i]);
464 
465 	fDecoderList.Insert(info);
466 }
467 
468 
469 void
470 AddOnManager::_RegisterWriter(WriterPlugin* writer, const entry_ref& ref)
471 {
472 	BAutolock locker(fLock);
473 
474 	writer_info* pinfo;
475 	for (fWriterList.Rewind(); fWriterList.GetNext(&pinfo);) {
476 		if (!strcmp(pinfo->ref.name, ref.name)) {
477 			// we already know this writer
478 			return;
479 		}
480 	}
481 
482 	writer_info info;
483 	info.ref = ref;
484 	info.internalID = fNextWriterFormatFamilyID++;
485 
486 	// Get list of support media_file_formats...
487 	const media_file_format* fileFormats = NULL;
488 	size_t count = 0;
489 	if (writer->GetSupportedFileFormats(&fileFormats, &count) != B_OK) {
490 		printf("AddOnManager::_RegisterWriter(): "
491 			"plugin->GetSupportedFileFormats(...) failed!\n");
492 		return;
493 	}
494 	for (uint i = 0 ; i < count ; i++) {
495 		// Generate a proper ID before inserting this format, this encodes
496 		// the specific plugin in the media_file_format.
497 		media_file_format fileFormat = fileFormats[i];
498 		fileFormat.id.node = ref.directory;
499 		fileFormat.id.device = ref.device;
500 		fileFormat.id.internal_id = info.internalID;
501 
502 		fWriterFileFormats.Insert(fileFormat);
503 	}
504 
505 	fWriterList.Insert(info);
506 }
507 
508 
509 void
510 AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref)
511 {
512 	BAutolock locker(fLock);
513 
514 	encoder_info* pinfo;
515 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&pinfo);) {
516 		if (!strcmp(pinfo->ref.name, ref.name)) {
517 			// We already know this encoder. When we reject encoders with
518 			// the same name, we allow the user to overwrite system encoders
519 			// in her home folder.
520 			return;
521 		}
522 	}
523 
524 	// Get list of supported encoders...
525 
526 	encoder_info info;
527 	info.ref = ref;
528 	info.internalID = fNextEncoderCodecInfoID++;
529 
530 	int32 cookie = 0;
531 
532 	while (true) {
533 		memset(&info.codecInfo, 0, sizeof(media_codec_info));
534 		memset(&info.intputFormat, 0, sizeof(media_format));
535 		memset(&info.outputFormat, 0, sizeof(media_format));
536 		if (plugin->RegisterNextEncoder(&cookie,
537 			&info.codecInfo, &info.formatFamily, &info.intputFormat,
538 			&info.outputFormat) != B_OK) {
539 			break;
540 		}
541 		info.codecInfo.id = info.internalID;
542 		// NOTE: info.codecInfo.sub_id is for private use by the Encoder,
543 		// we don't touch it, but it is maintained and passed back to the
544 		// EncoderPlugin in NewEncoder(media_codec_info).
545 
546 		if (!fEncoderList.Insert(info))
547 			break;
548 	}
549 }
550 
551 
552 bool
553 AddOnManager::_FindDecoder(const media_format& format, const BPath& path,
554 	entry_ref* _decoderRef)
555 {
556 	node_ref nref;
557 	BDirectory directory;
558 	if (directory.SetTo(path.Path()) != B_OK
559 		|| directory.GetNodeRef(&nref) != B_OK) {
560 		return false;
561 	}
562 
563 	decoder_info* info;
564 	for (fDecoderList.Rewind(); fDecoderList.GetNext(&info);) {
565 		if (info->ref.directory != nref.node)
566 			continue;
567 
568 		media_format* decoderFormat;
569 		for (info->formats.Rewind(); info->formats.GetNext(&decoderFormat);) {
570 			// check if the decoder matches the supplied format
571 			if (!decoderFormat->Matches(&format))
572 				continue;
573 
574 			*_decoderRef = info->ref;
575 			return true;
576 		}
577 	}
578 	return false;
579 }
580 
581 
582 bool
583 AddOnManager::_FindEncoder(const media_format& format, const BPath& path,
584 	entry_ref* _encoderRef)
585 {
586 	node_ref nref;
587 	BDirectory directory;
588 	if (directory.SetTo(path.Path()) != B_OK
589 		|| directory.GetNodeRef(&nref) != B_OK) {
590 		return false;
591 	}
592 
593 	encoder_info* info;
594 	for (fEncoderList.Rewind(); fEncoderList.GetNext(&info);) {
595 		if (info->ref.directory != nref.node)
596 			continue;
597 
598 		// check if the encoder matches the supplied format
599 		if (info->outputFormat.Matches(&format)) {
600 			*_encoderRef = info->ref;
601 			return true;
602 		}
603 		continue;
604 	}
605 	return false;
606 }
607 
608 
609 void
610 AddOnManager::_GetReaders(const BPath& path, entry_ref* outRefs,
611 	int32* outCount, int32 maxCount)
612 {
613 	node_ref nref;
614 	BDirectory directory;
615 	if (directory.SetTo(path.Path()) != B_OK
616 		|| directory.GetNodeRef(&nref) != B_OK) {
617 		return;
618 	}
619 
620 	reader_info* info;
621 	for (fReaderList.Rewind(); fReaderList.GetNext(&info)
622 		&& *outCount < maxCount;) {
623 		if (info->ref.directory != nref.node)
624 			continue;
625 
626 		outRefs[*outCount] = info->ref;
627 		(*outCount)++;
628 	}
629 }
630 
631 
632 } // namespace media
633 } // namespace BPrivate
634