xref: /haiku/src/servers/media/NodeManager.cpp (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 #include <OS.h>
31 #include <Entry.h>
32 #include <Message.h>
33 #include <Locker.h>
34 #include <Autolock.h>
35 #include <Path.h>
36 #include <Messenger.h>
37 #include <MediaDefs.h>
38 #include <MediaAddOn.h>
39 #include "debug.h"
40 #include "NodeManager.h"
41 #include "DefaultManager.h"
42 #include "AppManager.h"
43 #include "MediaMisc.h"
44 
45 extern AppManager *gAppManager;
46 
47 const char *get_node_type(node_type t);
48 
49 NodeManager::NodeManager() :
50 	fNextAddOnID(1),
51 	fNextNodeID(1),
52 	fLocker(new BLocker("node manager locker")),
53 	fDormantAddonFlavorList(new List<dormant_addon_flavor_info>),
54 	fAddonPathMap(new Map<media_addon_id, entry_ref>),
55 	fRegisteredNodeMap(new Map<media_node_id, registered_node>),
56 	fDefaultManager(new DefaultManager)
57 {
58 }
59 
60 
61 NodeManager::~NodeManager()
62 {
63 	delete fLocker;
64 	delete fDormantAddonFlavorList;
65 	delete fAddonPathMap;
66 	delete fRegisteredNodeMap;
67 	delete fDefaultManager;
68 }
69 
70 /**********************************************************************
71  * Live node management
72  **********************************************************************/
73 
74 status_t
75 NodeManager::RegisterNode(media_node_id *nodeid, media_addon_id addon_id, int32 addon_flavor_id, const char *name, uint64 kinds, port_id port, team_id team)
76 {
77 	BAutolock lock(fLocker);
78 	bool b;
79 	registered_node rn;
80 	rn.nodeid = fNextNodeID;
81 	rn.addon_id = addon_id;
82 	rn.addon_flavor_id = addon_flavor_id;
83 	strcpy(rn.name, name);
84 	rn.kinds = kinds;
85 	rn.port = port;
86 	rn.team = team;
87 	rn.creator = -1; // will be set later
88 	rn.globalrefcount = 1;
89 	rn.teamrefcount.Insert(team, 1);
90 
91 	b = fRegisteredNodeMap->Insert(fNextNodeID, rn);
92 	ASSERT(b);
93 	*nodeid = fNextNodeID;
94 	fNextNodeID += 1;
95 	TRACE("NodeManager::RegisterNode: node %ld, addon_id %ld, flavor_id %ld, name \"%s\", kinds %#Lx, port %ld, team %ld\n", *nodeid, addon_id, addon_flavor_id, name, kinds, port, team);
96 	return B_OK;
97 }
98 
99 
100 status_t
101 NodeManager::UnregisterNode(media_addon_id *addonid, int32 *flavorid, media_node_id nodeid, team_id team)
102 {
103 	BAutolock lock(fLocker);
104 	bool b;
105 	registered_node *rn;
106 	TRACE("NodeManager::UnregisterNode enter: node %ld, team %ld\n", nodeid, team);
107 	b = fRegisteredNodeMap->Get(nodeid, &rn);
108 	if (!b) {
109 		ERROR("NodeManager::UnregisterNode: couldn't find node %ld (team %ld)\n", nodeid, team);
110 		return B_ERROR;
111 	}
112 	if (rn->team != team) {
113 		ERROR("NodeManager::UnregisterNode: team %ld tried to unregister node %ld, but it was instantiated by team %ld\n", team, nodeid, rn->team);
114 		return B_ERROR;
115 	}
116 	if (rn->globalrefcount != 1) {
117 		ERROR("NodeManager::UnregisterNode: node %ld, team %ld has globalrefcount %ld (should be 1)\n", nodeid, team, rn->globalrefcount);
118 		//return B_ERROR;
119 	}
120 	*addonid = rn->addon_id;
121 	*flavorid = rn->addon_flavor_id;
122 	b = fRegisteredNodeMap->Remove(nodeid);
123 	ASSERT(b);
124 	TRACE("NodeManager::UnregisterNode leave: node %ld, addon_id %ld, flavor_id %ld team %ld\n", nodeid, *addonid, *flavorid, team);
125 	return B_OK;
126 }
127 
128 
129 status_t
130 NodeManager::IncrementGlobalRefCount(media_node_id nodeid, team_id team)
131 {
132 	BAutolock lock(fLocker);
133 	registered_node *rn;
134 	bool b;
135 	TRACE("NodeManager::IncrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team);
136 	b = fRegisteredNodeMap->Get(nodeid, &rn);
137 	if (!b) {
138 		ERROR("NodeManager::IncrementGlobalRefCount: node %ld not found\n", nodeid);
139 		return B_ERROR;
140 	}
141 	int32 *count;
142 	int32 debug_count;
143 	b = rn->teamrefcount.Get(team, &count);
144 	if (b) {
145 		*count += 1;
146 		debug_count = *count;
147 	} else {
148 		b = rn->teamrefcount.Insert(team, 1);
149 		ASSERT(b);
150 		debug_count = 1;
151 	}
152 	rn->globalrefcount += 1;
153 	TRACE("NodeManager::IncrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount);
154 	return B_OK;
155 }
156 
157 
158 status_t
159 NodeManager::DecrementGlobalRefCount(media_node_id nodeid, team_id team)
160 {
161 	BAutolock lock(fLocker);
162 	registered_node *rn;
163 	bool b;
164 	TRACE("NodeManager::DecrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team);
165 	b = fRegisteredNodeMap->Get(nodeid, &rn);
166 	if (!b) {
167 		ERROR("NodeManager::DecrementGlobalRefCount: node %ld not found\n", nodeid);
168 		return B_ERROR;
169 	}
170 	int32 *count;
171 	b = rn->teamrefcount.Get(team, &count);
172 	if (!b) {
173 		// Normally it is an error to release a node in another team. But we make one
174 		// exception. If the node is global, and the creator team tries to release it,
175 		// we will release it in the the media_addon_server.
176 		team_id addon_server_team;
177 		addon_server_team = gAppManager->AddonServerTeam();
178 		if (rn->creator == team && rn->teamrefcount.Get(addon_server_team, &count)) {
179 			printf("!!! NodeManager::DecrementGlobalRefCount doing global release!\n");
180 			rn->creator = -1; //invalidate!
181 			team = addon_server_team; //redirect!
182 			// the count variable was already redirected in if() statement above.
183 		} else {
184 			ERROR("NodeManager::DecrementGlobalRefCount: node %ld has no team %ld references\n", nodeid, team);
185 			return B_ERROR;
186 		}
187 	}
188 	*count -= 1;
189 	#if DEBUG >= 2
190 		int32 debug_count = *count;
191 	#endif
192 	if (*count == 0) {
193 		b = rn->teamrefcount.Remove(team);
194 		ASSERT(b);
195 	}
196 	rn->globalrefcount -= 1;
197 
198 	if (rn->globalrefcount == 0) {
199 		printf("NodeManager::DecrementGlobalRefCount: detected released node is now unused, node %ld\n", nodeid);
200 		FinalReleaseNode(nodeid);
201 	}
202 
203 	TRACE("NodeManager::DecrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount);
204 	return B_OK;
205 }
206 
207 status_t
208 NodeManager::SetNodeCreator(media_node_id nodeid, team_id creator)
209 {
210 	BAutolock lock(fLocker);
211 	registered_node *rn;
212 	bool b;
213 
214 	TRACE("NodeManager::SetNodeCreator node %ld, creator %ld\n", nodeid, creator);
215 
216 	b = fRegisteredNodeMap->Get(nodeid, &rn);
217 	if (!b) {
218 		ERROR("NodeManager::SetNodeCreator: node %ld not found\n", nodeid);
219 		return B_ERROR;
220 	}
221 
222 	if (rn->creator != -1) {
223 		ERROR("NodeManager::SetNodeCreator: node %ld is already assigned creator %ld\n", nodeid, rn->creator);
224 		return B_ERROR;
225 	}
226 
227 	rn->creator = creator;
228 	return B_OK;
229 }
230 
231 void
232 NodeManager::FinalReleaseNode(media_node_id nodeid)
233 {
234 	BAutolock lock(fLocker);
235 	registered_node *rn;
236 	bool b;
237 	status_t rv;
238 
239 	TRACE("NodeManager::FinalReleaseNode enter: node %ld\n", nodeid);
240 	b = fRegisteredNodeMap->Get(nodeid, &rn);
241 	if (!b) {
242 		ERROR("NodeManager::FinalReleaseNode: node %ld not found\n", nodeid);
243 		return;
244 	}
245 
246 	node_final_release_command cmd;
247 	rv = SendToPort(rn->port, NODE_FINAL_RELEASE, &cmd, sizeof(cmd));
248 	if (rv != B_OK) {
249 		ERROR("NodeManager::FinalReleaseNode: can't send command to node %ld\n", nodeid);
250 		return;
251 	}
252 }
253 
254 
255 status_t
256 NodeManager::GetCloneForId(media_node *node, media_node_id nodeid, team_id team)
257 {
258 	BAutolock lock(fLocker);
259 	registered_node *rn;
260 	bool b;
261 	TRACE("NodeManager::GetCloneForId enter: node %ld team %ld\n", nodeid, team);
262 
263 	if (B_OK != IncrementGlobalRefCount(nodeid, team)) {
264 		ERROR("NodeManager::GetCloneForId: couldn't increment ref count, node %ld team %ld\n", nodeid, team);
265 		return B_ERROR;
266 	}
267 
268 	b = fRegisteredNodeMap->Get(nodeid, &rn);
269 	if (!b) {
270 		ERROR("NodeManager::GetCloneForId: node %ld not found\n", nodeid);
271 		DecrementGlobalRefCount(nodeid, team);
272 		return B_ERROR;
273 	}
274 
275 	node->node = rn->nodeid;
276 	node->port = rn->port;
277 	node->kind = rn->kinds;
278 
279 	TRACE("NodeManager::GetCloneForId leave: node %ld team %ld\n", nodeid, team);
280 	return B_OK;
281 }
282 
283 
284 /* This function locates the default "node" for the requested "type" and returnes a clone.
285  * If the requested type is AUDIO_OUTPUT_EX, also "input_name" and "input_id" need to be set and returned,
286  * as this is required by BMediaRoster::GetAudioOutput(media_node *out_node, int32 *out_input_id, BString *out_input_name)
287  */
288 status_t
289 NodeManager::GetClone(media_node *node, char *input_name, int32 *input_id, node_type type, team_id team)
290 {
291 	BAutolock lock(fLocker);
292 	status_t status;
293 	media_node_id id;
294 
295 	TRACE("NodeManager::GetClone enter: team %ld, type %d (%s)\n", team, type, get_node_type(type));
296 
297 	status = GetDefaultNode(&id, input_name, input_id, type);
298 	if (status != B_OK) {
299 		ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %ld, type %d (%s)\n", team, type, get_node_type(type));
300 		*node = media_node::null;
301 		return status;
302 	}
303 	ASSERT(id > 0);
304 
305 	status = GetCloneForId(node, id, team);
306 	if (status != B_OK) {
307 		ERROR("NodeManager::GetClone: couldn't GetCloneForId, id %ld, team %ld, type %d (%s)\n", id, team, type, get_node_type(type));
308 		*node = media_node::null;
309 		return status;
310 	}
311 	ASSERT(id == node->node);
312 
313 	TRACE("NodeManager::GetClone leave: node id %ld, node port %ld, node kind %#lx\n", node->node, node->port, node->kind);
314 
315 	return B_OK;
316 }
317 
318 
319 status_t
320 NodeManager::ReleaseNode(const media_node &node, team_id team)
321 {
322 	BAutolock lock(fLocker);
323 	TRACE("NodeManager::ReleaseNode enter: node %ld team %ld\n", node.node, team);
324 	if (B_OK != DecrementGlobalRefCount(node.node, team)) {
325 		ERROR("NodeManager::ReleaseNode: couldn't decrement node %ld team %ld ref count\n", node.node, team);
326 	}
327 	TRACE("NodeManager::ReleaseNode leave: node %ld team %ld\n", node.node, team);
328 	return B_OK;
329 }
330 
331 
332 status_t
333 NodeManager::PublishInputs(const media_node &node, const media_input *inputs, int32 count)
334 {
335 	BAutolock lock(fLocker);
336 	registered_node *rn;
337 	bool b;
338 	b = fRegisteredNodeMap->Get(node.node, &rn);
339 	if (!b) {
340 		ERROR("NodeManager::PublishInputs: node %ld not found\n", node.node);
341 		return B_ERROR;
342 	}
343 	rn->inputlist.MakeEmpty();
344 	for (int32 i = 0; i < count; i++)
345 		rn->inputlist.Insert(inputs[i]);
346 	return B_OK;
347 }
348 
349 
350 status_t
351 NodeManager::PublishOutputs(const media_node &node, const media_output *outputs, int32 count)
352 {
353 	BAutolock lock(fLocker);
354 	registered_node *rn;
355 	bool b;
356 	b = fRegisteredNodeMap->Get(node.node, &rn);
357 	if (!b) {
358 		ERROR("NodeManager::PublishOutputs: node %ld not found\n", node.node);
359 		return B_ERROR;
360 	}
361 	rn->outputlist.MakeEmpty();
362 	for (int32 i = 0; i < count; i++)
363 		rn->outputlist.Insert(outputs[i]);
364 	return B_OK;
365 }
366 
367 
368 status_t
369 NodeManager::FindNodeId(media_node_id *nodeid, port_id port)
370 {
371 	BAutolock lock(fLocker);
372 	registered_node *rn;
373 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
374 		if (rn->port == port) {
375 			*nodeid = rn->nodeid;
376 			TRACE("NodeManager::FindNodeId found port %ld, node %ld\n", port, *nodeid);
377 			return B_OK;
378 		}
379 		media_output *output;
380 		for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) {
381 			if (output->source.port == port) {
382 				*nodeid = rn->nodeid;
383 				TRACE("NodeManager::FindNodeId found output port %ld, node %ld\n", port, *nodeid);
384 				return B_OK;
385 			}
386 		}
387 		media_input *input;
388 		for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) {
389 			if (input->destination.port == port) {
390 				*nodeid = rn->nodeid;
391 				TRACE("NodeManager::FindNodeId found input port %ld, node %ld\n", port, *nodeid);
392 				return B_OK;
393 			}
394 		}
395 	}
396 	ERROR("NodeManager::FindNodeId failed, port %ld\n", port);
397 	return B_ERROR;
398 }
399 
400 status_t
401 NodeManager::GetDormantNodeInfo(dormant_node_info *node_info, const media_node &node)
402 {
403 	BAutolock lock(fLocker);
404 	// XXX not sure if this is correct
405 	registered_node *rn;
406 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
407 		if (rn->nodeid == node.node) {
408 			if (rn->addon_id == -1 && node.node != NODE_SYSTEM_TIMESOURCE_ID) { // This function must return an error if the node is application owned
409 				TRACE("NodeManager::GetDormantNodeInfo NODE IS APPLICATION OWNED! node %ld, addon_id %ld, addon_flavor_id %ld, name \"%s\"\n", node.node, rn->addon_id, rn->addon_flavor_id, rn->name);
410 				return B_ERROR;
411 			}
412 			ASSERT(node.port == rn->port);
413 			ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK));
414 			node_info->addon = rn->addon_id;
415 			node_info->flavor_id = rn->addon_flavor_id;
416 			strcpy(node_info->name, rn->name);
417 			TRACE("NodeManager::GetDormantNodeInfo node %ld, addon_id %ld, addon_flavor_id %ld, name \"%s\"\n", node.node, rn->addon_id, rn->addon_flavor_id, rn->name);
418 			return B_OK;
419 		}
420 	}
421 	ERROR("NodeManager::GetDormantNodeInfo failed, node %ld\n", node.node);
422 	return B_ERROR;
423 }
424 
425 status_t
426 NodeManager::GetLiveNodeInfo(live_node_info *live_info, const media_node &node)
427 {
428 	BAutolock lock(fLocker);
429 	registered_node *rn;
430 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
431 		if (rn->nodeid == node.node) {
432 			ASSERT(node.port == rn->port);
433 			ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK));
434 			live_info->node = node;
435 			live_info->hint_point = BPoint(0, 0);
436 			strcpy(live_info->name, rn->name);
437 			TRACE("NodeManager::GetLiveNodeInfo node %ld, name = \"%s\"\n", node.node, rn->name);
438 			return B_OK;
439 		}
440 	}
441 	ERROR("NodeManager::GetLiveNodeInfo failed, node %ld\n", node.node);
442 	return B_ERROR;
443 }
444 
445 
446 status_t
447 NodeManager::GetInstances(media_node_id *node_ids, int32* count, int32 maxcount, media_addon_id addon_id, int32 addon_flavor_id)
448 {
449 	BAutolock lock(fLocker);
450 	registered_node *rn;
451 	*count = 0;
452 	for (fRegisteredNodeMap->Rewind(); (maxcount > 0) &&  fRegisteredNodeMap->GetNext(&rn); ) {
453 		if (rn->addon_id == addon_id && rn->addon_flavor_id == addon_flavor_id) {
454 			node_ids[*count] = rn->nodeid;
455 			*count += 1;
456 			maxcount -= 1;
457 		}
458 	}
459 	TRACE("NodeManager::GetInstances found %ld instances for addon_id %ld, addon_flavor_id %ld\n", *count, addon_id, addon_flavor_id);
460 	return B_OK;
461 }
462 
463 
464 status_t
465 NodeManager::GetLiveNodes(Stack<live_node_info> *livenodes,	int32 maxcount, const media_format *inputformat /* = NULL */, const media_format *outputformat /* = NULL */, const char* name /* = NULL */, uint64 require_kinds /* = 0 */)
466 {
467 	BAutolock lock(fLocker);
468 	registered_node *rn;
469 	int namelen;
470 
471 	TRACE("NodeManager::GetLiveNodes: maxcount %ld, in-format %p, out-format %p, name %s, require_kinds 0x%Lx\n",
472 		  maxcount, inputformat, outputformat, (name ? name : "NULL"), require_kinds);
473 
474 	// determine the count of byte to compare when checking for a name with(out) wildcard
475 	if (name) {
476 		namelen = strlen(name);
477 		if (namelen > 0 && name[namelen - 1] == '*')
478 			namelen--; // compares without the '*'
479 	} else {
480 		namelen = 0;
481 	}
482 
483 	for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) {
484 		if ((rn->kinds & require_kinds) != require_kinds)
485 			continue;
486 		if (namelen) {
487 			if (0 != strncmp(name, rn->name, namelen))
488 				continue;
489 		}
490 		if (inputformat) {
491 			bool hasit = false;
492 			media_input *input;
493 			for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) {
494 				if (format_is_compatible(*inputformat, input->format)) {
495 					hasit = true;
496 					break;
497 				}
498 			}
499 			if (!hasit)
500 				continue;
501 		}
502 		if (outputformat) {
503 			bool hasit = false;
504 			media_output *output;
505 			for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) {
506 				if (format_is_compatible(*outputformat, output->format)) {
507 					hasit = true;
508 					break;
509 				}
510 			}
511 			if (!hasit)
512 				continue;
513 		}
514 
515 		live_node_info lni;
516 		lni.node.node = rn->nodeid;
517 		lni.node.port = rn->port;
518 		lni.node.kind = rn->kinds;
519 		lni.hint_point = BPoint(0, 0);
520 		strcpy(lni.name, rn->name);
521 		livenodes->Push(lni);
522 		maxcount -= 1;
523 	}
524 
525 	TRACE("NodeManager::GetLiveNodes found %ld\n", livenodes->CountItems());
526 	return B_OK;
527 }
528 
529 /* Add media_node_id of all live nodes to the message
530  * int32 "media_node_id" (multiple items)
531  */
532 status_t
533 NodeManager::GetLiveNodes(BMessage *msg)
534 {
535 	BAutolock lock(fLocker);
536 	registered_node *rn;
537 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
538 		msg->AddInt32("media_node_id", rn->nodeid);
539 	}
540 	return B_OK;
541 }
542 
543 /**********************************************************************
544  * Registration of BMediaAddOns
545  **********************************************************************/
546 
547 void
548 NodeManager::RegisterAddon(const entry_ref &ref, media_addon_id *newid)
549 {
550 	BAutolock lock(fLocker);
551 	media_addon_id id;
552 	id = fNextAddOnID;
553 	fNextAddOnID += 1;
554 
555 	printf("NodeManager::RegisterAddon: ref-name \"%s\", assigning id %ld\n", ref.name, id);
556 
557 	fAddonPathMap->Insert(id, ref);
558 	*newid = id;
559 }
560 
561 void
562 NodeManager::UnregisterAddon(media_addon_id id)
563 {
564 	BAutolock lock(fLocker);
565 
566 	printf("NodeManager::UnregisterAddon: id %ld\n", id);
567 
568 	RemoveDormantFlavorInfo(id);
569 	fAddonPathMap->Remove(id);
570 }
571 
572 status_t
573 NodeManager::GetAddonRef(entry_ref *ref, media_addon_id id)
574 {
575 	BAutolock lock(fLocker);
576 	entry_ref *tempref;
577 
578 	if (fAddonPathMap->Get(id, &tempref)) {
579 		*ref = *tempref;
580 		return B_OK;
581 	}
582 
583 	return B_ERROR;
584 }
585 
586 /**********************************************************************
587  * Registration of node flavors, published by BMediaAddOns
588  **********************************************************************/
589 
590 // this function is only called (indirectly) by the media_addon_server
591 void
592 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info &dfi)
593 {
594 	BAutolock lock(fLocker);
595 
596 	printf("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dfi.node_info.addon, dfi.node_info.flavor_id, dfi.node_info.name, dfi.name, dfi.info);
597 
598 	// Try to find the addon-id/flavor-id in the list.
599 	// If it already exists, update the Info, but don't
600 	// change the GlobalInstancesCount
601 	dormant_addon_flavor_info *dafi;
602 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
603 		if (dafi->AddonID != dfi.node_info.addon || dafi->AddonFlavorID != dfi.node_info.flavor_id)
604 			continue;
605 		if (dafi->InfoValid) {
606 			ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld does already exist\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id);
607 		}
608 		TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, flavor-id %ld\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id);
609 		dafi->MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff;
610 		// do NOT modify dafi.GlobalInstancesCount
611 		dafi->InfoValid = true;
612 		dafi->Info = dfi;
613 		return;
614 	}
615 
616 	// Insert information into the list
617 	{
618 		dormant_addon_flavor_info dafi;
619 		dafi.AddonID = dfi.node_info.addon;
620 		dafi.AddonFlavorID = dfi.node_info.flavor_id;
621 		dafi.MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff;
622 		dafi.GlobalInstancesCount = 0;
623 		dafi.InfoValid = true;
624 		dafi.Info = dfi;
625 		fDormantAddonFlavorList->Insert(dafi);
626 	}
627 }
628 
629 // this function is only called (indirectly) by the media_addon_server
630 void
631 NodeManager::InvalidateDormantFlavorInfo(media_addon_id id)
632 {
633 	BAutolock lock(fLocker);
634 	dormant_addon_flavor_info *dafi;
635 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
636 		if (dafi->AddonID == id && dafi->InfoValid == true) {
637 			printf("NodeManager::InvalidateDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name, dafi->Info.name, dafi->Info.info);
638 			dormant_flavor_info dfi_null;
639 			dafi->Info = dfi_null;
640 			dafi->InfoValid = false;
641 		}
642 	}
643 }
644 
645 // this function is only called by clean up after gone add-ons
646 void
647 NodeManager::RemoveDormantFlavorInfo(media_addon_id id)
648 {
649 	BAutolock lock(fLocker);
650 	dormant_addon_flavor_info *dafi;
651 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
652 		if (dafi->AddonID == id) {
653 			printf("NodeManager::RemoveDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name, dafi->Info.name, dafi->Info.info);
654 			fDormantAddonFlavorList->RemoveCurrent();
655 		}
656 	}
657 }
658 
659 status_t
660 NodeManager::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team)
661 {
662 	BAutolock lock(fLocker);
663 
664 	dormant_addon_flavor_info *dafi;
665 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
666 		if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid)
667 			continue;
668 
669 		if (dafi->GlobalInstancesCount >= dafi->MaxInstancesCount) {
670 			ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld maximum (or more) instances already exist\n", addonid, flavorid);
671 			return B_ERROR; // maximum (or more) instances already exist
672 		}
673 
674 		bool b;
675 		int32 *count;
676 		b = dafi->TeamInstancesCount.Get(team, &count);
677 		if (b) {
678 			*count += 1;
679 		} else {
680 			b = dafi->TeamInstancesCount.Insert(team, 1);
681 			ASSERT(b);
682 		}
683 		dafi->GlobalInstancesCount += 1;
684 		return B_OK;
685 	}
686 	ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid);
687 	return B_ERROR;
688 }
689 
690 status_t
691 NodeManager::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team)
692 {
693 	BAutolock lock(fLocker);
694 
695 	dormant_addon_flavor_info *dafi;
696 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
697 		if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid)
698 			continue;
699 
700 		bool b;
701 		int32 *count;
702 		b = dafi->TeamInstancesCount.Get(team, &count);
703 		if (!b) {
704 			ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld team %ld has no references\n", addonid, flavorid, team);
705 			return B_ERROR;
706 		}
707 		*count -= 1;
708 		if (*count == 0) {
709 			b = dafi->TeamInstancesCount.Remove(team);
710 			ASSERT(b);
711 		}
712 		if (dafi->GlobalInstancesCount > 0)		// avoid underflow
713 			dafi->GlobalInstancesCount -= 1;
714 		return B_OK;
715 	}
716 	ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid);
717 	return B_ERROR;
718 }
719 
720 // this function is called when the media_addon_server has crashed
721 void
722 NodeManager::CleanupDormantFlavorInfos()
723 {
724 	BAutolock lock(fLocker);
725 	printf("NodeManager::CleanupDormantFlavorInfos\n");
726 	fDormantAddonFlavorList->MakeEmpty();
727 	printf("NodeManager::CleanupDormantFlavorInfos done\n");
728 	// XXX FlavorsChanged(media_addon_id addonid, int32 newcount, int32 gonecount)
729 }
730 
731 status_t
732 NodeManager::GetDormantNodes(dormant_node_info * out_info,
733 							  int32 * io_count,
734 							  const media_format * has_input /* = NULL */,
735 							  const media_format * has_output /* = NULL */,
736 							  const char * name /* = NULL */,
737 							  uint64 require_kinds /* = NULL */,
738 							  uint64 deny_kinds /* = NULL */)
739 {
740 	BAutolock lock(fLocker);
741 	dormant_addon_flavor_info *dafi;
742 	int32 maxcount;
743 	int namelen;
744 
745 	// determine the count of byte to compare when checking for a name with(out) wildcard
746 	if (name) {
747 		namelen = strlen(name);
748 		if (namelen > 0 && name[namelen - 1] == '*')
749 			namelen--; // compares without the '*'
750 	} else {
751 		namelen = 0;
752 	}
753 
754 	maxcount = *io_count;
755 	*io_count = 0;
756 	for (fDormantAddonFlavorList->Rewind(); (*io_count < maxcount) && fDormantAddonFlavorList->GetNext(&dafi); ) {
757 		if (!dafi->InfoValid)
758 			continue;
759 
760 		dormant_flavor_info *dfi;
761 		dfi = &dafi->Info;
762 
763 		if ((dfi->kinds & require_kinds) != require_kinds)
764 			continue;
765 		if ((dfi->kinds & deny_kinds) != 0)
766 			continue;
767 		if (namelen) {
768 			if (0 != strncmp(name, dfi->name, namelen))
769 				continue;
770 		}
771 		if (has_input) {
772 			bool hasit = false;
773 			for (int32 i = 0; i < dfi->in_format_count; i++)
774 				if (format_is_compatible(*has_input, dfi->in_formats[i])) {
775 					hasit = true;
776 					break;
777 				}
778 			if (!hasit)
779 				continue;
780 		}
781 		if (has_output) {
782 			bool hasit = false;
783 			for (int32 i = 0; i < dfi->out_format_count; i++)
784 				if (format_is_compatible(*has_output, dfi->out_formats[i])) {
785 					hasit = true;
786 					break;
787 				}
788 			if (!hasit)
789 				continue;
790 		}
791 
792 		out_info[*io_count] = dfi->node_info;
793 		*io_count += 1;
794 	}
795 
796 	return B_OK;
797 }
798 
799 status_t
800 NodeManager::GetDormantFlavorInfoFor(media_addon_id addon,
801 									 int32 flavor_id,
802 									 dormant_flavor_info *outFlavor)
803 {
804 	BAutolock lock(fLocker);
805 	dormant_addon_flavor_info *dafi;
806 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
807 		if (dafi->AddonID == addon && dafi->AddonFlavorID == flavor_id && dafi->InfoValid == true) {
808 			*outFlavor = dafi->Info;
809 			return B_OK;
810 		}
811 	}
812 	return B_ERROR;
813 }
814 
815 /**********************************************************************
816  * Default node management
817  **********************************************************************/
818 
819 status_t
820 NodeManager::SetDefaultNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input)
821 {
822 	BAutolock lock(fLocker);
823 	status_t err = B_BAD_VALUE;
824 	if (node)
825 		err = fDefaultManager->Set(node->node, NULL, 0, type);
826 	else if (input)
827 		err = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type);
828 	else if (info) {
829 		media_node_id node_id;
830 		int32 count = 1;
831 		if ((err=GetInstances(&node_id, &count, count, info->addon, info->flavor_id))!=B_OK)
832 			return err;
833 		err = fDefaultManager->Set(node_id, NULL, 0, type);
834 	}
835 	if(err==B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) {
836 		fDefaultManager->SaveState(this);
837 		Dump();
838 	}
839 	return err;
840 }
841 
842 status_t
843 NodeManager::GetDefaultNode(media_node_id *nodeid, char *input_name, int32 *input_id, node_type type)
844 {
845 	BAutolock lock(fLocker);
846 	return fDefaultManager->Get(nodeid, input_name, input_id, type);
847 }
848 
849 status_t
850 NodeManager::RescanDefaultNodes()
851 {
852 	BAutolock lock(fLocker);
853 	return fDefaultManager->Rescan();
854 }
855 
856 /**********************************************************************
857  * Cleanup of dead teams
858  **********************************************************************/
859 
860 void
861 NodeManager::CleanupTeam(team_id team)
862 {
863 	BAutolock lock(fLocker);
864 
865 	fDefaultManager->CleanupTeam(team);
866 
867 	PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team);
868 
869 	// XXX send notifications after removing nodes
870 
871 	// Cleanup node references
872 
873 	registered_node *rn;
874 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
875 		// if the gone team was the creator of some global dormant node instance, we now invalidate that
876 		// we may want to remove that global node, but I'm not sure
877 		if (rn->creator == team) {
878 			rn->creator = -1;
879 			// fall through
880 		}
881 		// if the team hosting this node is gone, remove node from database
882 		if (rn->team == team) {
883 			PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team %ld\n", rn->nodeid, team);
884 			fRegisteredNodeMap->RemoveCurrent();
885 			continue;
886 		}
887 		// check the list of teams that have references to this node, and remove the team
888 		team_id *pteam;
889 		int32 *prefcount;
890 		for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&prefcount); ) {
891 			rn->teamrefcount.GetCurrentKey(&pteam);
892 			if (*pteam == team) {
893 				PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node id %ld, team %ld\n", *prefcount, rn->nodeid, team);
894 				rn->teamrefcount.RemoveCurrent();
895 				break;
896 			}
897 		}
898 		// if the team refcount is now empty, also remove the node
899 		if (rn->teamrefcount.IsEmpty()) {
900 			PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that has no teams\n", rn->nodeid);
901 			fRegisteredNodeMap->RemoveCurrent();
902 		}
903 	}
904 
905 	// Cleanup addon references
906 	dormant_addon_flavor_info *dafi;
907 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
908 		bool b;
909 		int32 *count;
910 		b = dafi->TeamInstancesCount.Get(team, &count);
911 		if (b) {
912 			PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from addon %ld, flavor %ld\n", *count, dafi->AddonID, dafi->AddonFlavorID);
913 			dafi->GlobalInstancesCount -= *count;
914 			if (dafi->GlobalInstancesCount < 0)		// avoid underflow
915 				dafi->GlobalInstancesCount = 0;
916 			b = dafi->TeamInstancesCount.Remove(team);
917 			ASSERT(b);
918 		}
919 	}
920 }
921 
922 /**********************************************************************
923  * State saving/loading
924  **********************************************************************/
925 
926 status_t
927 NodeManager::LoadState()
928 {
929 	BAutolock lock(fLocker);
930 	return fDefaultManager->LoadState();
931 }
932 
933 status_t
934 NodeManager::SaveState()
935 {
936 	BAutolock lock(fLocker);
937 	return fDefaultManager->SaveState(this);
938 }
939 
940 /**********************************************************************
941  * Debugging
942  **********************************************************************/
943 
944 void
945 NodeManager::Dump()
946 {
947 	BAutolock lock(fLocker);
948 	printf("\n");
949 
950 	/* for each addon-id, the addon path map contains an entry_ref
951 	 */
952 	printf("NodeManager: addon path map follows:\n");
953 	entry_ref *ref;
954 	media_addon_id *id;
955 	for (fAddonPathMap->Rewind(); fAddonPathMap->GetNext(&ref); ) {
956 		fAddonPathMap->GetCurrentKey(&id);
957 		BPath path(ref);
958 		printf(" addon-id %ld, ref-name \"%s\", path \"%s\"\n", *id, ref->name, (path.InitCheck() == B_OK) ? path.Path() : "INVALID");
959 	}
960 	printf("NodeManager: list end\n");
961 	printf("\n");
962 
963 	/* for each node-id, the registered node map contians information about source of the node, users, etc.
964 	 */
965 	printf("NodeManager: registered nodes map follows:\n");
966 	registered_node *rn;
967 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
968 		printf("  node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n",
969 			rn->nodeid, rn->addon_id, rn->addon_flavor_id, rn->port, rn->creator, rn->team, rn->kinds, rn->name);
970 		printf("    teams (refcount): ");
971 		team_id *team;
972 		int32 *refcount;
973 		for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&refcount); ) {
974 			rn->teamrefcount.GetCurrentKey(&team);
975 			printf("%ld (%ld), ", *team, *refcount);
976 		}
977 		printf("\n");
978 		media_input *input;
979 		for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) {
980 			printf("    media_input: node-id %ld, node-port %ld, source-port %ld, source-id  %ld, dest-port %ld, dest-id %ld, name \"%s\"\n",
981 				input->node.node, input->node.port, input->source.port, input->source.id, input->destination.port, input->destination.id, input->name);
982 		}
983 		if (rn->inputlist.IsEmpty())
984 			printf("    media_input: none\n");
985 		media_output *output;
986 		for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) {
987 			printf("    media_output: node-id %ld, node-port %ld, source-port %ld, source-id  %ld, dest-port %ld, dest-id %ld, name \"%s\"\n",
988 				output->node.node, output->node.port, output->source.port, output->source.id, output->destination.port, output->destination.id, output->name);
989 		}
990 		if (rn->outputlist.IsEmpty())
991 			printf("    media_output: none\n");
992 	}
993 	printf("NodeManager: list end\n");
994 	printf("\n");
995 
996 	/*
997 	 */
998 	printf("NodeManager: dormant flavor list follows:\n");
999 	dormant_addon_flavor_info *dafi;
1000 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
1001 		printf("  AddonID %ld, AddonFlavorID %ld, MaxInstancesCount %ld, GlobalInstancesCount %ld, InfoValid %s\n",
1002 			dafi->AddonID, dafi->AddonFlavorID, dafi->MaxInstancesCount, dafi->GlobalInstancesCount, dafi->InfoValid ? "yes" : "no");
1003 		if (!dafi->InfoValid)
1004 			continue;
1005 		printf("    addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n",
1006 			dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name);
1007 		printf("    flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, possible_count %ld, in_format_count %ld, out_format_count %ld\n",
1008 			 dafi->Info.kinds, dafi->Info.flavor_flags, dafi->Info.internal_id, dafi->Info.possible_count, dafi->Info.in_format_count, dafi->Info.out_format_count);
1009 		printf("    flavor-name \"%s\"\n", dafi->Info.name);
1010 		printf("    flavor-info \"%s\"\n", dafi->Info.info);
1011 	}
1012 	printf("NodeManager: list end\n");
1013 	fDefaultManager->Dump();
1014 }
1015 
1016 const char *
1017 get_node_type(node_type t)
1018 {
1019 	switch (t) {
1020 		#define CASE(c) case c: return #c;
1021 		CASE(VIDEO_INPUT)
1022 		CASE(AUDIO_INPUT)
1023 		CASE(VIDEO_OUTPUT)
1024 		CASE(AUDIO_MIXER)
1025 		CASE(AUDIO_OUTPUT)
1026 		CASE(AUDIO_OUTPUT_EX)
1027 		CASE(TIME_SOURCE)
1028 		CASE(SYSTEM_TIME_SOURCE)
1029 		default: return "unknown";
1030 	}
1031 };
1032 
1033