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