xref: /haiku/src/servers/media_addon/MediaAddonServer.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 <DataExchange.h>
51 #include <DormantNodeManager.h>
52 #include <MediaDebug.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 			// if != NULL, need to call gDormantNodeManager->PutAddOn(id)
77 			BMediaAddOn*		addon;
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 			SystemTimeSource*	fSystemTimeSource;
125 };
126 
127 
128 class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler {
129 public:
130 								MonitorHandler(MediaAddonServer* server);
131 
132 	virtual void				AddOnEnabled(const add_on_entry_info* info);
133 	virtual void				AddOnDisabled(const add_on_entry_info* info);
134 
135 private:
136 			MediaAddonServer*	fServer;
137 };
138 
139 
140 #if DEBUG >= 2
141 static void
142 DumpFlavorInfo(const flavor_info* info)
143 {
144 	printf("  name = %s\n", info->name);
145 	printf("  info = %s\n", info->info);
146 	printf("  internal_id = %" B_PRId32 "\n", info->internal_id);
147 	printf("  possible_count = %" B_PRId32 "\n", info->possible_count);
148 	printf("  flavor_flags = 0x%" B_PRIx32, info->flavor_flags);
149 	if (info->flavor_flags & B_FLAVOR_IS_GLOBAL) printf(" B_FLAVOR_IS_GLOBAL");
150 	if (info->flavor_flags & B_FLAVOR_IS_LOCAL) printf(" B_FLAVOR_IS_LOCAL");
151 	printf("\n");
152 	printf("  kinds = 0x%" B_PRIx64, info->kinds);
153 	if (info->kinds & B_BUFFER_PRODUCER) printf(" B_BUFFER_PRODUCER");
154 	if (info->kinds & B_BUFFER_CONSUMER) printf(" B_BUFFER_CONSUMER");
155 	if (info->kinds & B_TIME_SOURCE) printf(" B_TIME_SOURCE");
156 	if (info->kinds & B_CONTROLLABLE) printf(" B_CONTROLLABLE");
157 	if (info->kinds & B_FILE_INTERFACE) printf(" B_FILE_INTERFACE");
158 	if (info->kinds & B_ENTITY_INTERFACE) printf(" B_ENTITY_INTERFACE");
159 	if (info->kinds & B_PHYSICAL_INPUT) printf(" B_PHYSICAL_INPUT");
160 	if (info->kinds & B_PHYSICAL_OUTPUT) printf(" B_PHYSICAL_OUTPUT");
161 	if (info->kinds & B_SYSTEM_MIXER) printf(" B_SYSTEM_MIXER");
162 	printf("\n");
163 	printf("  in_format_count = %" B_PRId32 "\n", info->in_format_count);
164 	printf("  out_format_count = %" B_PRId32 "\n", info->out_format_count);
165 }
166 #endif
167 
168 
169 // #pragma mark -
170 
171 
172 MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server)
173 {
174 	fServer = server;
175 }
176 
177 
178 void
179 MediaAddonServer::MonitorHandler::AddOnEnabled(const add_on_entry_info* info)
180 {
181 	entry_ref ref;
182 	make_entry_ref(info->dir_nref.device, info->dir_nref.node,
183 		info->name, &ref);
184 	BEntry entry(&ref, true);
185 	if (!entry.IsFile())
186 		return;
187 
188 	BPath path(&ref);
189 	if (path.InitCheck() == B_OK)
190 		fServer->_AddOnAdded(path.Path(), info->nref.node);
191 }
192 
193 
194 void
195 MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info)
196 {
197 	fServer->_AddOnRemoved(info->nref.node);
198 }
199 
200 
201 // #pragma mark -
202 
203 
204 MediaAddonServer::MediaAddonServer(const char* signature)
205 	:
206 	BApplication(signature),
207 	fMonitorHandler(NULL),
208 	fPulseRunner(NULL),
209 	fStartup(true),
210 	fStartupSound(true),
211 	fSystemTimeSource(NULL)
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 	delete fMonitorHandler;
235 	delete fPulseRunner;
236 }
237 
238 
239 void
240 MediaAddonServer::ReadyToRun()
241 {
242 	if (!be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)) {
243 		// the media server is not running, let's quit
244 		fprintf(stderr, "The media_server is not running!\n");
245 		Quit();
246 		return;
247 	}
248 
249 	// the control thread is already running at this point,
250 	// so we can talk to the media server and also receive
251 	// commands for instantiation
252 
253 	ASSERT(fStartup == true);
254 
255 	// The very first thing to do is to create the system time source,
256 	// register it with the server, and make it the default SYSTEM_TIME_SOURCE
257 	fSystemTimeSource = new SystemTimeSource();
258 	status_t result = fMediaRoster->RegisterNode(fSystemTimeSource);
259 	if (result != B_OK) {
260 		fprintf(stderr, "Can't register system time source : %s\n",
261 			strerror(result));
262 		ERROR("Can't register system time source");
263 	}
264 
265 	if (fSystemTimeSource->ID() != NODE_SYSTEM_TIMESOURCE_ID)
266 		ERROR("System time source got wrong node ID");
267 	media_node node = fSystemTimeSource->Node();
268 	result = MediaRosterEx(fMediaRoster)->SetNode(SYSTEM_TIME_SOURCE, &node);
269 	if (result != B_OK)
270 		ERROR("Can't setup system time source as default");
271 
272 	// During startup, first all add-ons are loaded, then all
273 	// nodes (flavors) representing physical inputs and outputs
274 	// are instantiated. Next, all add-ons that need autostart
275 	// will be autostarted. Finally, add-ons that don't have
276 	// any active nodes (flavors) will be unloaded.
277 
278 	fMonitorHandler = new MonitorHandler(this);
279 	AddHandler(fMonitorHandler);
280 
281 	BMessage pulse(B_PULSE);
282 	// the monitor handler needs a pulse to check if add-ons are ready
283 	fPulseRunner = new BMessageRunner(fMonitorHandler, &pulse, 1000000LL);
284 
285 	result = fPulseRunner->InitCheck();
286 	if (result != B_OK)
287 		ERROR("Can't create the pulse runner");
288 
289 	fMonitorHandler->AddAddOnDirectories("media");
290 
291 #ifdef USER_ADDON_PATH
292 	node_ref nodeRef;
293 	if (entry.SetTo(USER_ADDON_PATH) == B_OK
294 		&& entry.GetNodeRef(&nodeRef) == B_OK) {
295 		fMonitorHandler->AddDirectory(&nodeRef);
296 	}
297 #endif
298 
299 	fStartup = false;
300 
301 	InfoMap::iterator iterator = fInfoMap.begin();
302 	for (; iterator != fInfoMap.end(); iterator++)
303 		_InstantiatePhysicalInputsAndOutputs(iterator->second);
304 
305 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
306 		_InstantiateAutostartFlavors(iterator->second);
307 
308 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
309 		_PutAddonIfPossible(iterator->second);
310 
311 	server_rescan_defaults_command cmd;
312 	SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
313 }
314 
315 
316 bool
317 MediaAddonServer::QuitRequested()
318 {
319 	CALLED();
320 
321 	InfoMap::iterator iterator = fInfoMap.begin();
322 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
323 		_DestroyInstantiatedFlavors(iterator->second);
324 
325 	// the System timesource should be removed before we quit the roster
326 	if (fSystemTimeSource != NULL &&
327 		be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)) {
328 		status_t result = fMediaRoster->UnregisterNode(fSystemTimeSource);
329 		if (result != B_OK) {
330 			fprintf(stderr, "Error removing the system time source : %s\n",
331 				strerror(result));
332 			ERROR("Can't remove the system time source");
333 		}
334 		fSystemTimeSource->Release();
335 		fSystemTimeSource = NULL;
336 	}
337 
338 	for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++)
339 		_PutAddonIfPossible(iterator->second);
340 
341 	return true;
342 }
343 
344 
345 void
346 MediaAddonServer::MessageReceived(BMessage* message)
347 {
348 	switch (message->what) {
349 		case MEDIA_ADD_ON_SERVER_PLAY_MEDIA:
350 		{
351 			const char* name;
352 			const char* type;
353 			if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK
354 				|| message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) {
355 				message->SendReply(B_ERROR);
356 				break;
357 			}
358 
359 			PlayMediaFile(type, name);
360 			message->SendReply((uint32)B_OK);
361 			// TODO: don't know which reply is expected
362 			return;
363 		}
364 
365 		default:
366 			BApplication::MessageReceived(message);
367 			break;
368 	}
369 }
370 
371 
372 void
373 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size)
374 {
375 	switch (code) {
376 		case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE:
377 		{
378 			const add_on_server_instantiate_dormant_node_request* request
379 				= static_cast<
380 					const add_on_server_instantiate_dormant_node_request*>(
381 						data);
382 			add_on_server_instantiate_dormant_node_reply reply;
383 
384 			status_t status
385 				= MediaRosterEx(fMediaRoster)->InstantiateDormantNode(
386 					request->add_on_id, request->flavor_id,
387 					request->creator_team, &reply.node);
388 			request->SendReply(status, &reply, sizeof(reply));
389 			break;
390 		}
391 
392 		case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS:
393 		{
394 			const add_on_server_rescan_flavors_command* command = static_cast<
395 				const add_on_server_rescan_flavors_command*>(data);
396 			BMediaAddOn* addOn
397 				= gDormantNodeManager->GetAddOn(command->add_on_id);
398 			if (addOn == NULL) {
399 				ERROR("rescan flavors: Can't find a addon object for id %d\n",
400 					(int)command->add_on_id);
401 				break;
402 			}
403 			_ScanAddOnFlavors(addOn);
404 			gDormantNodeManager->PutAddOn(command->add_on_id);
405 			break;
406 		}
407 
408 		case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY:
409 			if (fStartupSound) {
410 				BMessage msg(MEDIA_ADD_ON_SERVER_PLAY_MEDIA);
411 				msg.AddString(MEDIA_NAME_KEY, MEDIA_SOUNDS_STARTUP);
412 				msg.AddString(MEDIA_TYPE_KEY, MEDIA_TYPE_SOUNDS);
413 				BMessageRunner::StartSending(this, &msg, 2000000, 1);
414 				fStartupSound = false;
415 			}
416 			break;
417 
418 		default:
419 			ERROR("media_addon_server: received unknown message code %#08"
420 				B_PRIx32 "\n", code);
421 			break;
422 	}
423 }
424 
425 
426 status_t
427 MediaAddonServer::_ControlThread(void* _server)
428 {
429 	MediaAddonServer* server = (MediaAddonServer*)_server;
430 
431 	char data[B_MEDIA_MESSAGE_SIZE];
432 	ssize_t size;
433 	int32 code;
434 	while ((size = read_port_etc(server->_ControlPort(), &code, data,
435 			sizeof(data), 0, 0)) > 0)
436 		server->_HandleMessage(code, data, size);
437 
438 	return B_OK;
439 }
440 
441 
442 void
443 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon)
444 {
445 	ASSERT(addon->AddonID() > 0);
446 
447 	TRACE("MediaAddonServer::_ScanAddOnFlavors: id %" B_PRId32 "\n",
448 		addon->AddonID());
449 
450 	// cache the media_addon_id in a local variable to avoid
451 	// calling BMediaAddOn::AddonID() too often
452 	media_addon_id addonID = addon->AddonID();
453 
454 	// update the cached flavor count, get oldflavorcount and newflavorcount
455 
456 	InfoMap::iterator found = fInfoMap.find(addonID);
457 	ASSERT(found != fInfoMap.end());
458 
459 	AddOnInfo& info = found->second;
460 	int32 oldFlavorCount = info.flavor_count;
461 	int32 newFlavorCount = addon->CountFlavors();
462 	info.flavor_count = newFlavorCount;
463 
464 	TRACE("%" B_PRId32 " old flavors, %" B_PRId32 " new flavors\n",
465 		oldFlavorCount, newFlavorCount);
466 
467 	// during the first update (i == 0), the server removes old dormant_flavor_infos
468 	for (int i = 0; i < newFlavorCount; i++) {
469 		const flavor_info* flavorInfo;
470 		TRACE("flavor %d:\n", i);
471 		if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) {
472 			ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for "
473 				"index %d!\n", i);
474 			continue;
475 		}
476 
477 #if DEBUG >= 2
478 		DumpFlavorInfo(flavorInfo);
479 #endif
480 
481 		dormant_flavor_info dormantFlavorInfo;
482 		dormantFlavorInfo = *flavorInfo;
483 		dormantFlavorInfo.node_info.addon = addonID;
484 		dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id;
485 		strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name,
486 			B_MEDIA_NAME_LENGTH);
487 
488 		size_t flattenedSize = dormantFlavorInfo.FlattenedSize();
489 		size_t messageSize = flattenedSize
490 			+ sizeof(server_register_dormant_node_command);
491 		server_register_dormant_node_command* message
492 			= (server_register_dormant_node_command*)malloc(messageSize);
493 		if (message == NULL)
494 			break;
495 
496 		// The server should remove previously registered "dormant_flavor_info"s
497 		// during the first update, but after  the first iteration, we don't
498 		// want the server to anymore remove old dormant_flavor_infos
499 		message->purge_id = i == 0 ? addonID : 0;
500 
501 		message->type = dormantFlavorInfo.TypeCode();
502 		message->flattened_size = flattenedSize;
503 		dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize);
504 
505 		status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE,
506 			message, messageSize);
507 		if (status != B_OK) {
508 			ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register "
509 				"dormant node: %s\n", strerror(status));
510 		}
511 
512 		free(message);
513 	}
514 
515 	// TODO: we currently pretend that all old flavors have been removed, this
516 	// could probably be done in a smarter way
517 	BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount,
518 		oldFlavorCount);
519 }
520 
521 
522 void
523 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode)
524 {
525 	TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path);
526 
527 	media_addon_id id = gDormantNodeManager->RegisterAddOn(path);
528 	if (id <= 0) {
529 		ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n",
530 			path);
531 		return;
532 	}
533 
534 	TRACE("MediaAddonServer::_AddOnAdded: loading addon %" B_PRId32 " now..."
535 		"\n", id);
536 
537 	BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id);
538 	if (addon == NULL) {
539 		ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n",
540 			path);
541 		gDormantNodeManager->UnregisterAddOn(id);
542 		return;
543 	}
544 
545 	TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %" B_PRId32
546 		"\n", id);
547 
548 	try {
549 		// put file's inode and addon's id into map
550 		fFileMap.insert(std::make_pair(fileNode, id));
551 
552 		AddOnInfo info = {};
553 		fInfoMap.insert(std::make_pair(id, info));
554 	} catch (std::bad_alloc& exception) {
555 		fFileMap.erase(fileNode);
556 		return;
557 	}
558 
559 	InfoMap::iterator found = fInfoMap.find(id);
560 	AddOnInfo& info = found->second;
561 
562 	info.id = id;
563 	// temporary default
564 	info.wants_autostart = false;
565 	info.flavor_count = 0;
566 	info.addon = addon;
567 
568 	// scan the flavors
569 	_ScanAddOnFlavors(addon);
570 
571 	// need to call BMediaNode::WantsAutoStart()
572 	// after the flavors have been scanned
573 	info.wants_autostart = addon->WantsAutoStart();
574 
575 	if (info.wants_autostart)
576 		TRACE("add-on %" B_PRId32 " WantsAutoStart!\n", id);
577 
578 	// During startup, first all add-ons are loaded, then all
579 	// nodes (flavors) representing physical inputs and outputs
580 	// are instantiated. Next, all add-ons that need autostart
581 	// will be autostarted. Finally, add-ons that don't have
582 	// any active nodes (flavors) will be unloaded.
583 
584 	// After startup is done, we simply do it for each new
585 	// loaded add-on, too.
586 	if (!fStartup) {
587 		_InstantiatePhysicalInputsAndOutputs(info);
588 		_InstantiateAutostartFlavors(info);
589 		_PutAddonIfPossible(info);
590 
591 		// since something might have changed
592 		server_rescan_defaults_command cmd;
593 		SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd));
594 	}
595 
596 	// we do not call gDormantNodeManager->PutAddOn(id)
597 	// since it is done by _PutAddonIfPossible()
598 }
599 
600 
601 void
602 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info)
603 {
604 	printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %" B_PRId32
605 		"\n", info.id);
606 
607 	NodeVector::iterator iterator = info.active_flavors.begin();
608 	for (; iterator != info.active_flavors.end(); iterator++) {
609 		media_node& node = *iterator;
610 
611 		printf("node %" B_PRId32 "\n", node.node);
612 
613 		if ((node.kind & B_TIME_SOURCE) != 0
614 			&& (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) {
615 			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
616 				"timesource\n");
617 			continue;
618 		}
619 
620 		if (fMediaRoster->StopNode(node, 0, true) != B_OK) {
621 			printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop "
622 				"node\n");
623 			continue;
624 		}
625 
626 		if ((node.kind & B_BUFFER_CONSUMER) != 0) {
627 			media_input inputs[16];
628 			int32 count = 0;
629 			if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count)
630 					!= B_OK) {
631 				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
632 					"get connected inputs\n");
633 				continue;
634 			}
635 
636 			for (int32 i = 0; i < count; i++) {
637 				media_node_id sourceNode;
638 				if ((sourceNode = fMediaRoster->NodeIDFor(
639 						inputs[i].source.port)) < 0) {
640 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
641 						"couldn't get source node id\n");
642 					continue;
643 				}
644 
645 				if (fMediaRoster->Disconnect(sourceNode, inputs[i].source,
646 						node.node, inputs[i].destination) != B_OK) {
647 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
648 						"couldn't disconnect input\n");
649 					continue;
650 				}
651 			}
652 		}
653 
654 		if ((node.kind & B_BUFFER_PRODUCER) != 0) {
655 			media_output outputs[16];
656 			int32 count = 0;
657 			if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16,
658 					&count) != B_OK) {
659 				printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't "
660 					"get connected outputs\n");
661 				continue;
662 			}
663 
664 			for (int32 i = 0; i < count; i++) {
665 				media_node_id destNode;
666 				if ((destNode = fMediaRoster->NodeIDFor(
667 						outputs[i].destination.port)) < 0) {
668 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
669 						"couldn't get destination node id\n");
670 					continue;
671 				}
672 
673 				if (fMediaRoster->Disconnect(node.node, outputs[i].source,
674 						destNode, outputs[i].destination) != B_OK) {
675 					printf("MediaAddonServer::_DestroyInstantiatedFlavors "
676 						"couldn't disconnect output\n");
677 					continue;
678 				}
679 			}
680 		}
681 
682 		if (MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node) != B_OK) {
683 			printf("MediaAddonServer::_DestroyInstantiatedFlavors "
684 						"couldn't release node\n");
685 		}
686 
687 		// wait a bit to let the node clean up
688 		snooze(50000);
689 	}
690 
691 	info.active_flavors.clear();
692 }
693 
694 
695 void
696 MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info)
697 {
698 	if (info.addon && info.active_flavors.empty()) {
699 		gDormantNodeManager->PutAddOn(info.id);
700 		info.addon = NULL;
701 	}
702 }
703 
704 
705 void
706 MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info)
707 {
708 	CALLED();
709 	int32 count = info.addon->CountFlavors();
710 
711 	for (int32 i = 0; i < count; i++) {
712 		const flavor_info* flavorinfo;
713 		if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) {
714 			ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
715 				"GetFlavorAt failed for index %" B_PRId32 "!\n", i);
716 			continue;
717 		}
718 		if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) {
719 			media_node node;
720 
721 			dormant_node_info dormantNodeInfo;
722 			dormantNodeInfo.addon = info.id;
723 			dormantNodeInfo.flavor_id = flavorinfo->internal_id;
724 			strcpy(dormantNodeInfo.name, flavorinfo->name);
725 
726 			TRACE("MediaAddonServer::InstantiatePhysialInputsAndOutputs: "
727 				"\"%s\" is a physical input/output\n", flavorinfo->name);
728 			status_t status = fMediaRoster->InstantiateDormantNode(
729 				dormantNodeInfo, &node);
730 			if (status != B_OK) {
731 				ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs "
732 					"Couldn't instantiate node flavor, internal_id %" B_PRId32
733 					", name %s\n", flavorinfo->internal_id, flavorinfo->name);
734 			} else {
735 				TRACE("Node created!\n");
736 				info.active_flavors.push_back(node);
737 			}
738 		}
739 	}
740 }
741 
742 
743 void
744 MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info)
745 {
746 	if (!info.wants_autostart)
747 		return;
748 
749 	for (int32 index = 0;; index++) {
750 		TRACE("trying autostart of node %" B_PRId32 ", index %" B_PRId32 "\n",
751 			info.id, index);
752 
753 		BMediaNode* node;
754 		int32 internalID;
755 		bool hasMore;
756 		status_t status = info.addon->AutoStart(index, &node, &internalID,
757 			&hasMore);
758 		if (status == B_MEDIA_ADDON_FAILED && hasMore)
759 			continue;
760 		else if (status != B_OK)
761 			break;
762 
763 		printf("started node %" B_PRId32 "\n", index);
764 
765 		status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id,
766 			internalID);
767 		if (status != B_OK) {
768 			ERROR("failed to register node %" B_PRId32 "\n", index);
769 			node->Release();
770 		} else {
771 			MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount(
772 				info.id, internalID);
773 			info.active_flavors.push_back(node->Node());
774 		}
775 
776 		if (!hasMore)
777 			return;
778 	}
779 }
780 
781 
782 void
783 MediaAddonServer::_AddOnRemoved(ino_t fileNode)
784 {
785 	// TODO: locking?
786 
787 	FileMap::iterator foundFile = fFileMap.find(fileNode);
788 	if (foundFile == fFileMap.end()) {
789 		ERROR("MediaAddonServer::_AddOnRemoved: inode %" B_PRIdINO " removed, but no "
790 			"media add-on found\n", fileNode);
791 		return;
792 	}
793 
794 	media_addon_id id = foundFile->second;
795 	fFileMap.erase(foundFile);
796 
797 	int32 oldFlavorCount;
798 	InfoMap::iterator foundInfo = fInfoMap.find(id);
799 
800 	if (foundInfo == fInfoMap.end()) {
801 		ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for "
802 			"add-on %" B_PRId32 "\n", id);
803 		oldFlavorCount = 1000;
804 	} else {
805 		AddOnInfo& info = foundInfo->second;
806 		oldFlavorCount = info.flavor_count;
807 
808 		_DestroyInstantiatedFlavors(info);
809 		_PutAddonIfPossible(info);
810 
811 		if (info.addon) {
812 			ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon "
813 				"%" B_PRId32 " since flavors are in use\n", id);
814 		}
815 
816 		fInfoMap.erase(foundInfo);
817 	}
818 
819 	gDormantNodeManager->UnregisterAddOn(id);
820 
821 	BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount);
822 }
823 
824 
825 //	#pragma mark -
826 
827 
828 int
829 main()
830 {
831 	new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE);
832 	be_app->Run();
833 	delete be_app;
834 	return 0;
835 }
836