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