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
32namespace BPrivate {
33namespace 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*/
41class ImageLoader {
42public:
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
57private:
58	image_id	fImage;
59};
60
61
62//	#pragma mark -
63
64
65AddOnManager::AddOnManager()
66	:
67	fLock("add-on manager"),
68	fNextWriterFormatFamilyID(0),
69	fNextEncoderCodecInfoID(0)
70{
71}
72
73
74AddOnManager::~AddOnManager()
75{
76}
77
78
79AddOnManager AddOnManager::sInstance;
80
81
82/* static */ AddOnManager*
83AddOnManager::GetInstance()
84{
85	return &sInstance;
86}
87
88
89status_t
90AddOnManager::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
133status_t
134AddOnManager::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
173status_t
174AddOnManager::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
206status_t
207AddOnManager::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
229status_t
230AddOnManager::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
248status_t
249AddOnManager::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
266status_t
267AddOnManager::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
282status_t
283AddOnManager::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
306void
307AddOnManager::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
338status_t
339AddOnManager::_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
390status_t
391AddOnManager::_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
446void
447AddOnManager::_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
466void
467AddOnManager::_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
496void
497AddOnManager::_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
536void
537AddOnManager::_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
579void
580AddOnManager::_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
598bool
599AddOnManager::_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
628bool
629AddOnManager::_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
655void
656AddOnManager::_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