1/*
2 * Copyright 2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*
7 * Copyright (c) 2002-2004, Marcus Overhagen <marcus@overhagen.de>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without modification,
11 * are permitted provided that the following conditions are met:
12 *
13 *  * Redistributions of source code must retain the above copyright notice,
14 *    this list of conditions and the following disclaimer.
15 *  * Redistributions in binary form must reproduce the above copyright notice,
16 *    this list of conditions and the following disclaimer in the documentation
17 *    and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32#include <map>
33#include <stdio.h>
34#include <vector>
35
36#include <Alert.h>
37#include <Application.h>
38#include <Beep.h>
39#include <Directory.h>
40#include <driver_settings.h>
41#include <Entry.h>
42#include <FindDirectory.h>
43#include <MediaAddOn.h>
44#include <MediaRoster.h>
45#include <MessageRunner.h>
46#include <Path.h>
47#include <Roster.h>
48#include <String.h>
49
50#include <safemode_defs.h>
51#include <syscalls.h>
52
53#include <AddOnMonitorHandler.h>
54#include <debug.h>
55#include <DataExchange.h>
56#include <DormantNodeManager.h>
57#include <MediaMisc.h>
58#include <MediaRosterEx.h>
59#include <MediaSounds.h>
60#include <Notifications.h>
61#include <ServerInterface.h>
62
63#include "MediaFilePlayer.h"
64#include "SystemTimeSource.h"
65
66
67//#define USER_ADDON_PATH "../add-ons/media"
68
69
70typedef std::vector<media_node> NodeVector;
71
72
73struct AddOnInfo {
74	media_addon_id		id;
75	bool				wants_autostart;
76	int32				flavor_count;
77
78	NodeVector			active_flavors;
79
80	BMediaAddOn*		addon;
81		// if != NULL, need to call gDormantNodeManager->PutAddOn(id)
82};
83
84
85class MediaAddonServer : BApplication {
86public:
87								MediaAddonServer(const char* signature);
88	virtual						~MediaAddonServer();
89	virtual	void				ReadyToRun();
90	virtual bool				QuitRequested();
91	virtual void				MessageReceived(BMessage* message);
92
93private:
94	class MonitorHandler;
95	friend class MonitorHandler;
96
97			void				_AddOnAdded(const char* path, ino_t fileNode);
98			void				_AddOnRemoved(ino_t fileNode);
99			void				_HandleMessage(int32 code, const void* data,
100									size_t size);
101
102			void				_PutAddonIfPossible(AddOnInfo& info);
103			void				_InstantiatePhysicalInputsAndOutputs(
104									AddOnInfo& info);
105			void				_InstantiateAutostartFlavors(AddOnInfo& info);
106			void				_DestroyInstantiatedFlavors(AddOnInfo& info);
107
108			void				_ScanAddOnFlavors(BMediaAddOn* addOn);
109
110			port_id				_ControlPort() const { return fControlPort; }
111
112	static	status_t			_ControlThread(void* arg);
113
114private:
115	typedef std::map<ino_t, media_addon_id> FileMap;
116	typedef std::map<media_addon_id, AddOnInfo> InfoMap;
117
118			FileMap				fFileMap;
119			InfoMap				fInfoMap;
120
121			BMediaRoster*		fMediaRoster;
122			MonitorHandler*		fMonitorHandler;
123			BMessageRunner*		fPulseRunner;
124			port_id				fControlPort;
125			thread_id			fControlThread;
126			bool				fStartup;
127			bool				fStartupSound;
128};
129
130
131class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler {
132public:
133							MonitorHandler(MediaAddonServer* server);
134
135	virtual void			AddOnEnabled(const add_on_entry_info* info);
136	virtual void			AddOnDisabled(const add_on_entry_info* info);
137
138private:
139	MediaAddonServer* fServer;
140};
141
142
143#if DEBUG >= 2
144static void
145DumpFlavorInfo(const flavor_info* info)
146{
147	printf("  name = %s\n", info->name);
148	printf("  info = %s\n", info->info);
149	printf("  internal_id = %ld\n", info->internal_id);
150	printf("  possible_count = %ld\n", info->possible_count);
151	printf("  flavor_flags = 0x%lx", info->flavor_flags);
152	if (info->flavor_flags & B_FLAVOR_IS_GLOBAL) printf(" B_FLAVOR_IS_GLOBAL");
153	if (info->flavor_flags & B_FLAVOR_IS_LOCAL) printf(" B_FLAVOR_IS_LOCAL");
154	printf("\n");
155	printf("  kinds = 0x%Lx", info->kinds);
156	if (info->kinds & B_BUFFER_PRODUCER) printf(" B_BUFFER_PRODUCER");
157	if (info->kinds & B_BUFFER_CONSUMER) printf(" B_BUFFER_CONSUMER");
158	if (info->kinds & B_TIME_SOURCE) printf(" B_TIME_SOURCE");
159	if (info->kinds & B_CONTROLLABLE) printf(" B_CONTROLLABLE");
160	if (info->kinds & B_FILE_INTERFACE) printf(" B_FILE_INTERFACE");
161	if (info->kinds & B_ENTITY_INTERFACE) printf(" B_ENTITY_INTERFACE");
162	if (info->kinds & B_PHYSICAL_INPUT) printf(" B_PHYSICAL_INPUT");
163	if (info->kinds & B_PHYSICAL_OUTPUT) printf(" B_PHYSICAL_OUTPUT");
164	if (info->kinds & B_SYSTEM_MIXER) printf(" B_SYSTEM_MIXER");
165	printf("\n");
166	printf("  in_format_count = %ld\n", info->in_format_count);
167	printf("  out_format_count = %ld\n", info->out_format_count);
168}
169#endif
170
171
172// #pragma mark -
173
174
175MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server)
176{
177	fServer = server;
178}
179
180
181void
182MediaAddonServer::MonitorHandler::AddOnEnabled(const add_on_entry_info* info)
183{
184	entry_ref ref;
185	make_entry_ref(info->dir_nref.device, info->dir_nref.node,
186		info->name, &ref);
187	BEntry entry(&ref, true);
188	if (!entry.IsFile())
189		return;
190
191	BPath path(&ref);
192	if (path.InitCheck() == B_OK)
193		fServer->_AddOnAdded(path.Path(), info->nref.node);
194}
195
196
197void
198MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info)
199{
200	fServer->_AddOnRemoved(info->nref.node);
201}
202
203
204// #pragma mark -
205
206
207MediaAddonServer::MediaAddonServer(const char* signature)
208	:
209	BApplication(signature),
210	fStartup(true),
211	fStartupSound(true)
212{
213	CALLED();
214	fMediaRoster = BMediaRoster::Roster();
215	fControlPort = create_port(64, MEDIA_ADDON_SERVER_PORT_NAME);
216	fControlThread = spawn_thread(_ControlThread, "media_addon_server control",
217		B_NORMAL_PRIORITY + 2, this);
218	resume_thread(fControlThread);
219}
220
221
222MediaAddonServer::~MediaAddonServer()
223{
224	CALLED();
225
226	delete_port(fControlPort);
227	wait_for_thread(fControlThread, NULL);
228
229	// unregister all media add-ons
230	FileMap::iterator iterator = fFileMap.begin();
231	for (; iterator != fFileMap.end(); iterator++)
232		gDormantNodeManager->UnregisterAddOn(iterator->second);
233
234	// TODO: unregister system time source
235}
236
237
238void
239MediaAddonServer::ReadyToRun()
240{
241	if (!be_roster->IsRunning("application/x-vnd.Be.media-server")) {
242		// the media server is not running, let's quit
243		fprintf(stderr, "The media_server is not running!\n");
244		Quit();
245		return;
246	}
247
248	// the control thread is already running at this point,
249	// so we can talk to the media server and also receive
250	// commands for instantiation
251
252	ASSERT(fStartup == true);
253
254	// The very first thing to do is to create the system time source,
255	// register it with the server, and make it the default SYSTEM_TIME_SOURCE
256	BMediaNode *timeSource = new SystemTimeSource;
257	status_t result = fMediaRoster->RegisterNode(timeSource);
258	if (result != B_OK) {
259		fprintf(stderr, "Can't register system time source : %s\n",
260			strerror(result));
261		debugger("Can't register system time source");
262	}
263
264	if (timeSource->ID() != NODE_SYSTEM_TIMESOURCE_ID)
265		debugger("System time source got wrong node ID");
266	media_node node = timeSource->Node();
267	result = MediaRosterEx(fMediaRoster)->SetNode(SYSTEM_TIME_SOURCE, &node);
268	if (result != B_OK)
269		debugger("Can't setup system time source as default");
270
271	// During startup, first all add-ons are loaded, then all
272	// nodes (flavors) representing physical inputs and outputs
273	// are instantiated. Next, all add-ons that need autostart
274	// will be autostarted. Finally, add-ons that don't have
275	// any active nodes (flavors) will be unloaded.
276
277	char parameter[32];
278	size_t parameterLength = sizeof(parameter);
279	bool safeMode = false;
280	if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
281			&parameterLength) == B_OK) {
282		if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
283			|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
284			|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
285			safeMode = true;
286	}
287
288	fMonitorHandler = new MonitorHandler(this);
289	AddHandler(fMonitorHandler);
290
291	BMessage pulse(B_PULSE);
292	fPulseRunner = new BMessageRunner(fMonitorHandler, &pulse, 1000000LL);
293		// the monitor handler needs a pulse to check if add-ons are ready
294
295	// load dormant media nodes
296	const directory_which directories[] = {
297		B_USER_ADDONS_DIRECTORY,
298		B_COMMON_ADDONS_DIRECTORY,
299		B_SYSTEM_ADDONS_DIRECTORY
300	};
301
302	// when safemode, only B_SYSTEM_ADDONS_DIRECTORY is used
303	for (uint32 i = safeMode ? 2 : 0;
304			i < sizeof(directories) / sizeof(directory_which); i++) {
305		BDirectory directory;
306		node_ref nodeRef;
307		BPath path;
308		if (find_directory(directories[i], &path) == B_OK
309			&& path.Append("media") == B_OK
310			&& directory.SetTo(path.Path()) == B_OK
311			&& directory.GetNodeRef(&nodeRef) == B_OK)
312			fMonitorHandler->AddDirectory(&nodeRef);
313	}
314
315#ifdef USER_ADDON_PATH
316	node_ref nodeRef;
317	if (entry.SetTo(USER_ADDON_PATH) == B_OK
318		&& entry.GetNodeRef(&nodeRef) == B_OK)
319		fMonitorHandler->AddDirectory(&nodeRef);
320#endif
321
322	fStartup = false;
323
324	InfoMap::iterator iterator = fInfoMap.begin();
325	for (; iterator != fInfoMap.end(); iterator++)
326		_InstantiatePhysicalInputsAndOutputs(iterator->second);
327
328	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
329		_InstantiateAutostartFlavors(iterator->second);
330
331	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
332		_PutAddonIfPossible(iterator->second);
333
334	server_rescan_defaults_command cmd;
335	SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
336}
337
338
339bool
340MediaAddonServer::QuitRequested()
341{
342	CALLED();
343
344	InfoMap::iterator iterator = fInfoMap.begin();
345	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
346		_DestroyInstantiatedFlavors(iterator->second);
347
348	BMediaRoster::CurrentRoster()->Lock();
349	BMediaRoster::CurrentRoster()->Quit();
350
351	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
352		_PutAddonIfPossible(iterator->second);
353
354	return true;
355}
356
357
358void
359MediaAddonServer::MessageReceived(BMessage* message)
360{
361	switch (message->what) {
362		case MEDIA_ADD_ON_SERVER_PLAY_MEDIA:
363		{
364			const char* name;
365			const char* type;
366			if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK
367				|| message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) {
368				message->SendReply(B_ERROR);
369				break;
370			}
371
372			PlayMediaFile(type, name);
373			message->SendReply((uint32)B_OK);
374				// TODO: don't know which reply is expected
375			return;
376		}
377
378		default:
379			BApplication::MessageReceived(message);
380			break;
381	}
382}
383
384
385void
386MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size)
387{
388	switch (code) {
389		case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE:
390		{
391			const add_on_server_instantiate_dormant_node_request* request
392				= static_cast<
393					const add_on_server_instantiate_dormant_node_request*>(
394						data);
395			add_on_server_instantiate_dormant_node_reply reply;
396
397			status_t status
398				= MediaRosterEx(fMediaRoster)->InstantiateDormantNode(
399					request->add_on_id, request->flavor_id,
400					request->creator_team, &reply.node);
401			request->SendReply(status, &reply, sizeof(reply));
402			break;
403		}
404
405		case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS:
406		{
407			const add_on_server_rescan_flavors_command* command = static_cast<
408				const add_on_server_rescan_flavors_command*>(data);
409			BMediaAddOn* addOn
410				= gDormantNodeManager->GetAddOn(command->add_on_id);
411			if (addOn == NULL) {
412				ERROR("rescan flavors: Can't find a addon object for id %d\n",
413					(int)command->add_on_id);
414				break;
415			}
416			_ScanAddOnFlavors(addOn);
417			gDormantNodeManager->PutAddOn(command->add_on_id);
418			break;
419		}
420
421		case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY:
422			if (fStartupSound) {
423				system_beep(MEDIA_SOUNDS_STARTUP);
424				fStartupSound = false;
425			}
426			break;
427
428		default:
429			ERROR("media_addon_server: received unknown message code %#08lx\n",
430				code);
431			break;
432	}
433}
434
435
436status_t
437MediaAddonServer::_ControlThread(void* _server)
438{
439	MediaAddonServer* server = (MediaAddonServer*)_server;
440
441	char data[B_MEDIA_MESSAGE_SIZE];
442	ssize_t size;
443	int32 code;
444	while ((size = read_port_etc(server->_ControlPort(), &code, data,
445			sizeof(data), 0, 0)) > 0)
446		server->_HandleMessage(code, data, size);
447
448	return B_OK;
449}
450
451
452void
453MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon)
454{
455	ASSERT(addon->AddonID() > 0);
456
457	TRACE("MediaAddonServer::_ScanAddOnFlavors: id %ld\n", addon->AddonID());
458
459	// cache the media_addon_id in a local variable to avoid
460	// calling BMediaAddOn::AddonID() too often
461	media_addon_id addonID = addon->AddonID();
462
463	// update the cached flavor count, get oldflavorcount and newflavorcount
464
465	InfoMap::iterator found = fInfoMap.find(addonID);
466	ASSERT(found != fInfoMap.end());
467
468	AddOnInfo& info = found->second;
469	int32 oldFlavorCount = info.flavor_count;
470	int32 newFlavorCount = addon->CountFlavors();
471	info.flavor_count = newFlavorCount;
472
473	TRACE("%ld old flavors, %ld new flavors\n", oldFlavorCount, newFlavorCount);
474
475	// during the first update (i == 0), the server removes old dormant_flavor_infos
476	for (int i = 0; i < newFlavorCount; i++) {
477		const flavor_info* flavorInfo;
478		TRACE("flavor %d:\n", i);
479		if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) {
480			ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for "
481				"index %d!\n", i);
482			continue;
483		}
484
485#if DEBUG >= 2
486		DumpFlavorInfo(flavorInfo);
487#endif
488
489		dormant_flavor_info dormantFlavorInfo;
490		dormantFlavorInfo = *flavorInfo;
491		dormantFlavorInfo.node_info.addon = addonID;
492		dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id;
493		strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name,
494			B_MEDIA_NAME_LENGTH);
495
496		size_t flattenedSize = dormantFlavorInfo.FlattenedSize();
497		size_t messageSize = flattenedSize
498			+ sizeof(server_register_dormant_node_command);
499		server_register_dormant_node_command* message
500			= (server_register_dormant_node_command*)malloc(messageSize);
501		if (message == NULL)
502			break;
503
504		// The server should remove previously registered "dormant_flavor_info"s
505		// during the first update, but after  the first iteration, we don't
506		// want the server to anymore remove old dormant_flavor_infos
507		message->purge_id = i == 0 ? addonID : 0;
508
509		message->type = dormantFlavorInfo.TypeCode();
510		message->flattened_size = flattenedSize;
511		dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize);
512
513		status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE,
514			message, messageSize);
515		if (status != B_OK) {
516			ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register "
517				"dormant node: %s\n", strerror(status));
518		}
519
520		free(message);
521	}
522
523	// TODO: we currently pretend that all old flavors have been removed, this
524	// could probably be done in a smarter way
525	BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount,
526		oldFlavorCount);
527}
528
529
530void
531MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode)
532{
533	TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path);
534
535	media_addon_id id = gDormantNodeManager->RegisterAddOn(path);
536	if (id <= 0) {
537		ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n",
538			path);
539		return;
540	}
541
542	TRACE("MediaAddonServer::_AddOnAdded: loading addon %ld now...\n", id);
543
544	BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id);
545	if (addon == NULL) {
546		ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", path);
547		gDormantNodeManager->UnregisterAddOn(id);
548		return;
549	}
550
551	TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %ld\n", id);
552
553	try {
554		// put file's inode and addon's id into map
555		fFileMap.insert(std::make_pair(fileNode, id));
556
557		AddOnInfo info;
558		fInfoMap.insert(std::make_pair(id, info));
559	} catch (std::bad_alloc& exception) {
560		fFileMap.erase(fileNode);
561		return;
562	}
563
564	InfoMap::iterator found = fInfoMap.find(id);
565	AddOnInfo& info = found->second;
566
567	info.id = id;
568	info.wants_autostart = false; // temporary default
569	info.flavor_count = 0;
570	info.addon = addon;
571
572	// scan the flavors
573	_ScanAddOnFlavors(addon);
574
575	// need to call BMediaNode::WantsAutoStart()
576	// after the flavors have been scanned
577	info.wants_autostart = addon->WantsAutoStart();
578
579	if (info.wants_autostart)
580		TRACE("add-on %ld WantsAutoStart!\n", id);
581
582	// During startup, first all add-ons are loaded, then all
583	// nodes (flavors) representing physical inputs and outputs
584	// are instantiated. Next, all add-ons that need autostart
585	// will be autostarted. Finally, add-ons that don't have
586	// any active nodes (flavors) will be unloaded.
587
588	// After startup is done, we simply do it for each new
589	// loaded add-on, too.
590	if (!fStartup) {
591		_InstantiatePhysicalInputsAndOutputs(info);
592		_InstantiateAutostartFlavors(info);
593		_PutAddonIfPossible(info);
594
595		// since something might have changed
596		server_rescan_defaults_command cmd;
597		SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
598	}
599
600	// we do not call gDormantNodeManager->PutAddOn(id)
601	// since it is done by _PutAddonIfPossible()
602}
603
604
605void
606MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info)
607{
608	printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %ld\n", info.id);
609
610	NodeVector::iterator iterator = info.active_flavors.begin();
611	for (; iterator != info.active_flavors.end(); iterator++) {
612		media_node& node = *iterator;
613
614		printf("node %ld\n", node.node);
615
616		if ((node.kind & B_TIME_SOURCE) != 0
617			&& (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) {
618			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
619				"timesource\n");
620			continue;
621		}
622
623		if (fMediaRoster->StopNode(node, 0, true) != B_OK) {
624			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
625				"node\n");
626			continue;
627		}
628
629		if ((node.kind & B_BUFFER_CONSUMER) != 0) {
630			media_input inputs[16];
631			int32 count = 0;
632			if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count)
633					!= B_OK) {
634				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
635					"get connected inputs\n");
636				continue;
637			}
638
639			for (int32 i = 0; i < count; i++) {
640				media_node_id sourceNode;
641				if ((sourceNode = fMediaRoster->NodeIDFor(
642						inputs[i].source.port)) < 0) {
643					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
644						"couldn't get source node id\n");
645					continue;
646				}
647
648				if (fMediaRoster->Disconnect(sourceNode, inputs[i].source,
649						node.node, inputs[i].destination) != B_OK) {
650					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
651						"couldn't disconnect input\n");
652					continue;
653				}
654			}
655		}
656
657		if ((node.kind & B_BUFFER_PRODUCER) != 0) {
658			media_output outputs[16];
659			int32 count = 0;
660			if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16,
661					&count) != B_OK) {
662				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
663					"get connected outputs\n");
664				continue;
665			}
666
667			for (int32 i = 0; i < count; i++) {
668				media_node_id destNode;
669				if ((destNode = fMediaRoster->NodeIDFor(
670						outputs[i].destination.port)) < 0) {
671					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
672						"couldn't get destination node id\n");
673					continue;
674				}
675
676				if (fMediaRoster->Disconnect(node.node, outputs[i].source,
677						destNode, outputs[i].destination) != B_OK) {
678					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
679						"couldn't disconnect output\n");
680					continue;
681				}
682			}
683		}
684
685		MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node);
686	}
687
688	info.active_flavors.clear();
689}
690
691
692void
693MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info)
694{
695	if (info.addon && info.active_flavors.empty()) {
696		gDormantNodeManager->PutAddOn(info.id);
697		info.addon = NULL;
698	}
699}
700
701
702void
703MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info)
704{
705	CALLED();
706	int32 count = info.addon->CountFlavors();
707
708	for (int32 i = 0; i < count; i++) {
709		const flavor_info* flavorinfo;
710		if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) {
711			ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
712				"GetFlavorAt failed for index %ld!\n", i);
713			continue;
714		}
715		if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) {
716			media_node node;
717
718			dormant_node_info dormantNodeInfo;
719			dormantNodeInfo.addon = info.id;
720			dormantNodeInfo.flavor_id = flavorinfo->internal_id;
721			strcpy(dormantNodeInfo.name, flavorinfo->name);
722
723			TRACE("MediaAddonServer::InstantiatePhysialInputsAndOutputs: "
724				"\"%s\" is a physical input/output\n", flavorinfo->name);
725			status_t status = fMediaRoster->InstantiateDormantNode(
726				dormantNodeInfo, &node);
727			if (status != B_OK) {
728				ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
729					"Couldn't instantiate node flavor, internal_id %ld, "
730					"name %s\n", flavorinfo->internal_id, flavorinfo->name);
731			} else {
732				TRACE("Node created!\n");
733				info.active_flavors.push_back(node);
734			}
735		}
736	}
737}
738
739
740void
741MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info)
742{
743	if (!info.wants_autostart)
744		return;
745
746	for (int32 index = 0;; index++) {
747		TRACE("trying autostart of node %ld, index %ld\n", info.id, index);
748
749		BMediaNode* node;
750		int32 internalID;
751		bool hasMore;
752		status_t status = info.addon->AutoStart(index, &node, &internalID,
753			&hasMore);
754		if (status == B_MEDIA_ADDON_FAILED && hasMore)
755			continue;
756		else if (status != B_OK)
757			break;
758
759		printf("started node %ld\n", index);
760
761		status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id,
762			internalID);
763		if (status != B_OK) {
764			ERROR("failed to register node %ld\n", index);
765			node->Release();
766		} else {
767			MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount(
768				info.id, internalID);
769			info.active_flavors.push_back(node->Node());
770		}
771
772		if (!hasMore)
773			return;
774	}
775}
776
777
778void
779MediaAddonServer::_AddOnRemoved(ino_t fileNode)
780{
781	// TODO: locking?
782
783	FileMap::iterator foundFile = fFileMap.find(fileNode);
784	if (foundFile == fFileMap.end()) {
785		ERROR("MediaAddonServer::_AddOnRemoved: inode %Ld removed, but no "
786			"media add-on found\n", fileNode);
787		return;
788	}
789
790	media_addon_id id = foundFile->second;
791	fFileMap.erase(foundFile);
792
793	int32 oldFlavorCount;
794	InfoMap::iterator foundInfo = fInfoMap.find(id);
795
796	if (foundInfo == fInfoMap.end()) {
797		ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for "
798			"add-on %ld\n", id);
799		oldFlavorCount = 1000;
800	} else {
801		AddOnInfo& info = foundInfo->second;
802		oldFlavorCount = info.flavor_count;
803
804		_DestroyInstantiatedFlavors(info);
805		_PutAddonIfPossible(info);
806
807		if (info.addon) {
808			ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon "
809				"%ld since flavors are in use\n", id);
810		}
811
812		fInfoMap.erase(foundInfo);
813	}
814
815	gDormantNodeManager->UnregisterAddOn(id);
816
817	BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount);
818}
819
820
821//	#pragma mark -
822
823
824int
825main()
826{
827	new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE);
828	be_app->Run();
829	delete be_app;
830	return 0;
831}
832
833