xref: /haiku/src/servers/media_addon/MediaAddonServer.cpp (revision a6e73cb9e8addfe832c064bfcb68067f1c2fa3eb)
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 
70 typedef std::vector<media_node> NodeVector;
71 
72 
73 struct 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 
85 class MediaAddonServer : BApplication {
86 public:
87 								MediaAddonServer(const char* signature);
88 	virtual						~MediaAddonServer();
89 	virtual	void				ReadyToRun();
90 	virtual bool				QuitRequested();
91 	virtual void				MessageReceived(BMessage* message);
92 
93 private:
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 
114 private:
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 
131 class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler {
132 public:
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 
138 private:
139 	MediaAddonServer* fServer;
140 };
141 
142 
143 #if DEBUG >= 2
144 static void
145 DumpFlavorInfo(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 
175 MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server)
176 {
177 	fServer = server;
178 }
179 
180 
181 void
182 MediaAddonServer::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 
197 void
198 MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info)
199 {
200 	fServer->_AddOnRemoved(info->nref.node);
201 }
202 
203 
204 // #pragma mark -
205 
206 
207 MediaAddonServer::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 
222 MediaAddonServer::~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 
238 void
239 MediaAddonServer::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_NONPACKAGED_ADDONS_DIRECTORY,
298 		B_USER_ADDONS_DIRECTORY,
299 		B_COMMON_NONPACKAGED_ADDONS_DIRECTORY,
300 		B_COMMON_ADDONS_DIRECTORY,
301 		B_SYSTEM_ADDONS_DIRECTORY
302 	};
303 
304 	// when safemode, only B_SYSTEM_ADDONS_DIRECTORY is used
305 	for (uint32 i = safeMode ? 4 : 0;
306 			i < sizeof(directories) / sizeof(directory_which); i++) {
307 		BDirectory directory;
308 		node_ref nodeRef;
309 		BPath path;
310 		if (find_directory(directories[i], &path) == B_OK
311 			&& path.Append("media") == B_OK
312 			&& directory.SetTo(path.Path()) == B_OK
313 			&& directory.GetNodeRef(&nodeRef) == B_OK)
314 			fMonitorHandler->AddDirectory(&nodeRef);
315 	}
316 
317 #ifdef USER_ADDON_PATH
318 	node_ref nodeRef;
319 	if (entry.SetTo(USER_ADDON_PATH) == B_OK
320 		&& entry.GetNodeRef(&nodeRef) == B_OK)
321 		fMonitorHandler->AddDirectory(&nodeRef);
322 #endif
323 
324 	fStartup = false;
325 
326 	InfoMap::iterator iterator = fInfoMap.begin();
327 	for (; iterator != fInfoMap.end(); iterator++)
328 		_InstantiatePhysicalInputsAndOutputs(iterator->second);
329 
330 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
331 		_InstantiateAutostartFlavors(iterator->second);
332 
333 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
334 		_PutAddonIfPossible(iterator->second);
335 
336 	server_rescan_defaults_command cmd;
337 	SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
338 }
339 
340 
341 bool
342 MediaAddonServer::QuitRequested()
343 {
344 	CALLED();
345 
346 	InfoMap::iterator iterator = fInfoMap.begin();
347 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
348 		_DestroyInstantiatedFlavors(iterator->second);
349 
350 	BMediaRoster::CurrentRoster()->Lock();
351 	BMediaRoster::CurrentRoster()->Quit();
352 
353 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
354 		_PutAddonIfPossible(iterator->second);
355 
356 	return true;
357 }
358 
359 
360 void
361 MediaAddonServer::MessageReceived(BMessage* message)
362 {
363 	switch (message->what) {
364 		case MEDIA_ADD_ON_SERVER_PLAY_MEDIA:
365 		{
366 			const char* name;
367 			const char* type;
368 			if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK
369 				|| message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) {
370 				message->SendReply(B_ERROR);
371 				break;
372 			}
373 
374 			PlayMediaFile(type, name);
375 			message->SendReply((uint32)B_OK);
376 				// TODO: don't know which reply is expected
377 			return;
378 		}
379 
380 		default:
381 			BApplication::MessageReceived(message);
382 			break;
383 	}
384 }
385 
386 
387 void
388 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size)
389 {
390 	switch (code) {
391 		case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE:
392 		{
393 			const add_on_server_instantiate_dormant_node_request* request
394 				= static_cast<
395 					const add_on_server_instantiate_dormant_node_request*>(
396 						data);
397 			add_on_server_instantiate_dormant_node_reply reply;
398 
399 			status_t status
400 				= MediaRosterEx(fMediaRoster)->InstantiateDormantNode(
401 					request->add_on_id, request->flavor_id,
402 					request->creator_team, &reply.node);
403 			request->SendReply(status, &reply, sizeof(reply));
404 			break;
405 		}
406 
407 		case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS:
408 		{
409 			const add_on_server_rescan_flavors_command* command = static_cast<
410 				const add_on_server_rescan_flavors_command*>(data);
411 			BMediaAddOn* addOn
412 				= gDormantNodeManager->GetAddOn(command->add_on_id);
413 			if (addOn == NULL) {
414 				ERROR("rescan flavors: Can't find a addon object for id %d\n",
415 					(int)command->add_on_id);
416 				break;
417 			}
418 			_ScanAddOnFlavors(addOn);
419 			gDormantNodeManager->PutAddOn(command->add_on_id);
420 			break;
421 		}
422 
423 		case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY:
424 			if (fStartupSound) {
425 				system_beep(MEDIA_SOUNDS_STARTUP);
426 				fStartupSound = false;
427 			}
428 			break;
429 
430 		default:
431 			ERROR("media_addon_server: received unknown message code %#08lx\n",
432 				code);
433 			break;
434 	}
435 }
436 
437 
438 status_t
439 MediaAddonServer::_ControlThread(void* _server)
440 {
441 	MediaAddonServer* server = (MediaAddonServer*)_server;
442 
443 	char data[B_MEDIA_MESSAGE_SIZE];
444 	ssize_t size;
445 	int32 code;
446 	while ((size = read_port_etc(server->_ControlPort(), &code, data,
447 			sizeof(data), 0, 0)) > 0)
448 		server->_HandleMessage(code, data, size);
449 
450 	return B_OK;
451 }
452 
453 
454 void
455 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon)
456 {
457 	ASSERT(addon->AddonID() > 0);
458 
459 	TRACE("MediaAddonServer::_ScanAddOnFlavors: id %ld\n", addon->AddonID());
460 
461 	// cache the media_addon_id in a local variable to avoid
462 	// calling BMediaAddOn::AddonID() too often
463 	media_addon_id addonID = addon->AddonID();
464 
465 	// update the cached flavor count, get oldflavorcount and newflavorcount
466 
467 	InfoMap::iterator found = fInfoMap.find(addonID);
468 	ASSERT(found != fInfoMap.end());
469 
470 	AddOnInfo& info = found->second;
471 	int32 oldFlavorCount = info.flavor_count;
472 	int32 newFlavorCount = addon->CountFlavors();
473 	info.flavor_count = newFlavorCount;
474 
475 	TRACE("%ld old flavors, %ld new flavors\n", oldflavorcount, newFlavorCount);
476 
477 	// during the first update (i == 0), the server removes old dormant_flavor_infos
478 	for (int i = 0; i < newFlavorCount; i++) {
479 		const flavor_info* flavorInfo;
480 		TRACE("flavor %d:\n", i);
481 		if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) {
482 			ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for "
483 				"index %d!\n", i);
484 			continue;
485 		}
486 
487 #if DEBUG >= 2
488 		DumpFlavorInfo(flavorInfo);
489 #endif
490 
491 		dormant_flavor_info dormantFlavorInfo;
492 		dormantFlavorInfo = *flavorInfo;
493 		dormantFlavorInfo.node_info.addon = addonID;
494 		dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id;
495 		strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name,
496 			B_MEDIA_NAME_LENGTH);
497 
498 		size_t flattenedSize = dormantFlavorInfo.FlattenedSize();
499 		size_t messageSize = flattenedSize
500 			+ sizeof(server_register_dormant_node_command);
501 		server_register_dormant_node_command* message
502 			= (server_register_dormant_node_command*)malloc(messageSize);
503 		if (message == NULL)
504 			break;
505 
506 		// The server should remove previously registered "dormant_flavor_info"s
507 		// during the first update, but after  the first iteration, we don't
508 		// want the server to anymore remove old dormant_flavor_infos
509 		message->purge_id = i == 0 ? addonID : 0;
510 
511 		message->type = dormantFlavorInfo.TypeCode();
512 		message->flattened_size = flattenedSize;
513 		dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize);
514 
515 		status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE,
516 			message, messageSize);
517 		if (status != B_OK) {
518 			ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register "
519 				"dormant node: %s\n", strerror(status));
520 		}
521 
522 		free(message);
523 	}
524 
525 	// TODO: we currently pretend that all old flavors have been removed, this
526 	// could probably be done in a smarter way
527 	BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount,
528 		oldFlavorCount);
529 }
530 
531 
532 void
533 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode)
534 {
535 	TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path);
536 
537 	media_addon_id id = gDormantNodeManager->RegisterAddOn(path);
538 	if (id <= 0) {
539 		ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n",
540 			path);
541 		return;
542 	}
543 
544 	TRACE("MediaAddonServer::_AddOnAdded: loading addon %ld now...\n", id);
545 
546 	BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id);
547 	if (addon == NULL) {
548 		ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", path);
549 		gDormantNodeManager->UnregisterAddOn(id);
550 		return;
551 	}
552 
553 	TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %ld\n", id);
554 
555 	try {
556 		// put file's inode and addon's id into map
557 		fFileMap.insert(std::make_pair(fileNode, id));
558 
559 		AddOnInfo info;
560 		fInfoMap.insert(std::make_pair(id, info));
561 	} catch (std::bad_alloc& exception) {
562 		fFileMap.erase(fileNode);
563 		return;
564 	}
565 
566 	InfoMap::iterator found = fInfoMap.find(id);
567 	AddOnInfo& info = found->second;
568 
569 	info.id = id;
570 	info.wants_autostart = false; // temporary default
571 	info.flavor_count = 0;
572 	info.addon = addon;
573 
574 	// scan the flavors
575 	_ScanAddOnFlavors(addon);
576 
577 	// need to call BMediaNode::WantsAutoStart()
578 	// after the flavors have been scanned
579 	info.wants_autostart = addon->WantsAutoStart();
580 
581 	if (info.wants_autostart)
582 		TRACE("add-on %ld WantsAutoStart!\n", id);
583 
584 	// During startup, first all add-ons are loaded, then all
585 	// nodes (flavors) representing physical inputs and outputs
586 	// are instantiated. Next, all add-ons that need autostart
587 	// will be autostarted. Finally, add-ons that don't have
588 	// any active nodes (flavors) will be unloaded.
589 
590 	// After startup is done, we simply do it for each new
591 	// loaded add-on, too.
592 	if (!fStartup) {
593 		_InstantiatePhysicalInputsAndOutputs(info);
594 		_InstantiateAutostartFlavors(info);
595 		_PutAddonIfPossible(info);
596 
597 		// since something might have changed
598 		server_rescan_defaults_command cmd;
599 		SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
600 	}
601 
602 	// we do not call gDormantNodeManager->PutAddOn(id)
603 	// since it is done by _PutAddonIfPossible()
604 }
605 
606 
607 void
608 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info)
609 {
610 	printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %ld\n", info.id);
611 
612 	NodeVector::iterator iterator = info.active_flavors.begin();
613 	for (; iterator != info.active_flavors.end(); iterator++) {
614 		media_node& node = *iterator;
615 
616 		printf("node %ld\n", node.node);
617 
618 		if ((node.kind & B_TIME_SOURCE) != 0
619 			&& (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) {
620 			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
621 				"timesource\n");
622 			continue;
623 		}
624 
625 		if (fMediaRoster->StopNode(node, 0, true) != B_OK) {
626 			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
627 				"node\n");
628 			continue;
629 		}
630 
631 		if ((node.kind & B_BUFFER_CONSUMER) != 0) {
632 			media_input inputs[16];
633 			int32 count = 0;
634 			if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count)
635 					!= B_OK) {
636 				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
637 					"get connected inputs\n");
638 				continue;
639 			}
640 
641 			for (int32 i = 0; i < count; i++) {
642 				media_node_id sourceNode;
643 				if ((sourceNode = fMediaRoster->NodeIDFor(
644 						inputs[i].source.port)) < 0) {
645 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
646 						"couldn't get source node id\n");
647 					continue;
648 				}
649 
650 				if (fMediaRoster->Disconnect(sourceNode, inputs[i].source,
651 						node.node, inputs[i].destination) != B_OK) {
652 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
653 						"couldn't disconnect input\n");
654 					continue;
655 				}
656 			}
657 		}
658 
659 		if ((node.kind & B_BUFFER_PRODUCER) != 0) {
660 			media_output outputs[16];
661 			int32 count = 0;
662 			if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16,
663 					&count) != B_OK) {
664 				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
665 					"get connected outputs\n");
666 				continue;
667 			}
668 
669 			for (int32 i = 0; i < count; i++) {
670 				media_node_id destNode;
671 				if ((destNode = fMediaRoster->NodeIDFor(
672 						outputs[i].destination.port)) < 0) {
673 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
674 						"couldn't get destination node id\n");
675 					continue;
676 				}
677 
678 				if (fMediaRoster->Disconnect(node.node, outputs[i].source,
679 						destNode, outputs[i].destination) != B_OK) {
680 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
681 						"couldn't disconnect output\n");
682 					continue;
683 				}
684 			}
685 		}
686 
687 		MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node);
688 	}
689 
690 	info.active_flavors.clear();
691 }
692 
693 
694 void
695 MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info)
696 {
697 	if (info.addon && info.active_flavors.empty()) {
698 		gDormantNodeManager->PutAddOn(info.id);
699 		info.addon = NULL;
700 	}
701 }
702 
703 
704 void
705 MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info)
706 {
707 	CALLED();
708 	int32 count = info.addon->CountFlavors();
709 
710 	for (int32 i = 0; i < count; i++) {
711 		const flavor_info* flavorinfo;
712 		if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) {
713 			ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
714 				"GetFlavorAt failed for index %ld!\n", i);
715 			continue;
716 		}
717 		if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) {
718 			media_node node;
719 
720 			dormant_node_info dormantNodeInfo;
721 			dormantNodeInfo.addon = info.id;
722 			dormantNodeInfo.flavor_id = flavorinfo->internal_id;
723 			strcpy(dormantNodeInfo.name, flavorinfo->name);
724 
725 			PRINT("MediaAddonServer::InstantiatePhysialInputsAndOutputs: "
726 				"\"%s\" is a physical input/output\n", flavorinfo->name);
727 			status_t status = fMediaRoster->InstantiateDormantNode(
728 				dormantNodeInfo, &node);
729 			if (status != B_OK) {
730 				ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
731 					"Couldn't instantiate node flavor, internal_id %ld, "
732 					"name %s\n", flavorinfo->internal_id, flavorinfo->name);
733 			} else {
734 				PRINT("Node created!\n");
735 				info.active_flavors.push_back(node);
736 			}
737 		}
738 	}
739 }
740 
741 
742 void
743 MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info)
744 {
745 	if (!info.wants_autostart)
746 		return;
747 
748 	for (int32 index = 0;; index++) {
749 		PRINT("trying autostart of node %ld, index %ld\n", info.id, index);
750 
751 		BMediaNode* node;
752 		int32 internalID;
753 		bool hasMore;
754 		status_t status = info.addon->AutoStart(index, &node, &internalID,
755 			&hasMore);
756 		if (status == B_MEDIA_ADDON_FAILED && hasMore)
757 			continue;
758 		else if (status != B_OK)
759 			break;
760 
761 		printf("started node %ld\n", index);
762 
763 		status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id,
764 			internalID);
765 		if (status != B_OK) {
766 			ERROR("failed to register node %ld\n", index);
767 			node->Release();
768 		} else {
769 			MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount(
770 				info.id, internalID);
771 			info.active_flavors.push_back(node->Node());
772 		}
773 
774 		if (!hasMore)
775 			return;
776 	}
777 }
778 
779 
780 void
781 MediaAddonServer::_AddOnRemoved(ino_t fileNode)
782 {
783 	// TODO: locking?
784 
785 	FileMap::iterator foundFile = fFileMap.find(fileNode);
786 	if (foundFile == fFileMap.end()) {
787 		ERROR("MediaAddonServer::_AddOnRemoved: inode %Ld removed, but no "
788 			"media add-on found\n", fileNode);
789 		return;
790 	}
791 
792 	media_addon_id id = foundFile->second;
793 	fFileMap.erase(foundFile);
794 
795 	int32 oldFlavorCount;
796 	InfoMap::iterator foundInfo = fInfoMap.find(id);
797 
798 	if (foundInfo == fInfoMap.end()) {
799 		ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for "
800 			"add-on %ld\n", id);
801 		oldFlavorCount = 1000;
802 	} else {
803 		AddOnInfo& info = foundInfo->second;
804 		oldFlavorCount = info.flavor_count;
805 
806 		_DestroyInstantiatedFlavors(info);
807 		_PutAddonIfPossible(info);
808 
809 		if (info.addon) {
810 			ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon "
811 				"%ld since flavors are in use\n", id);
812 		}
813 
814 		fInfoMap.erase(foundInfo);
815 	}
816 
817 	gDormantNodeManager->UnregisterAddOn(id);
818 
819 	BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount);
820 }
821 
822 
823 //	#pragma mark -
824 
825 
826 int
827 main()
828 {
829 	new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE);
830 	be_app->Run();
831 	delete be_app;
832 	return 0;
833 }
834 
835