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