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
66typedef std::vector<media_node> NodeVector;
67
68
69struct 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
81class MediaAddonServer : BApplication {
82public:
83								MediaAddonServer(const char* signature);
84	virtual						~MediaAddonServer();
85	virtual	void				ReadyToRun();
86	virtual bool				QuitRequested();
87	virtual void				MessageReceived(BMessage* message);
88
89private:
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
110private:
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
128class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler {
129public:
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
135private:
136			MediaAddonServer*	fServer;
137};
138
139
140#if DEBUG >= 2
141static void
142DumpFlavorInfo(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
172MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server)
173{
174	fServer = server;
175}
176
177
178void
179MediaAddonServer::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
194void
195MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info)
196{
197	fServer->_AddOnRemoved(info->nref.node);
198}
199
200
201// #pragma mark -
202
203
204MediaAddonServer::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
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	delete fMonitorHandler;
235	delete fPulseRunner;
236}
237
238
239void
240MediaAddonServer::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
316bool
317MediaAddonServer::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
345void
346MediaAddonServer::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
372void
373MediaAddonServer::_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
426status_t
427MediaAddonServer::_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
442void
443MediaAddonServer::_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
522void
523MediaAddonServer::_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
601void
602MediaAddonServer::_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
695void
696MediaAddonServer::_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
705void
706MediaAddonServer::_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
743void
744MediaAddonServer::_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
782void
783MediaAddonServer::_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
828int
829main()
830{
831	new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE);
832	be_app->Run();
833	delete be_app;
834	return 0;
835}
836