xref: /haiku/src/servers/media/NodeManager.cpp (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
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 finde 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->AddonServer();
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 (name[namelen] == '*')
478 			namelen--; // compares without the '*'
479 		else
480 			namelen++; // also compares the terminating NULL
481 	} else
482 		namelen = 0;
483 
484 	for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) {
485 		if ((rn->kinds & require_kinds) != require_kinds)
486 			continue;
487 		if (namelen) {
488 			if (0 != memcmp(name, rn->name, namelen))
489 				continue;
490 		}
491 		if (inputformat) {
492 			bool hasit = false;
493 			media_input *input;
494 			for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) {
495 				if (format_is_compatible(*inputformat, input->format)) {
496 					hasit = true;
497 					break;
498 				}
499 			}
500 			if (!hasit)
501 				continue;
502 		}
503 		if (outputformat) {
504 			bool hasit = false;
505 			media_output *output;
506 			for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) {
507 				if (format_is_compatible(*outputformat, output->format)) {
508 					hasit = true;
509 					break;
510 				}
511 			}
512 			if (!hasit)
513 				continue;
514 		}
515 
516 		live_node_info lni;
517 		lni.node.node = rn->nodeid;
518 		lni.node.port = rn->port;
519 		lni.node.kind = rn->kinds;
520 		lni.hint_point = BPoint(0, 0);
521 		strcpy(lni.name, rn->name);
522 		livenodes->Push(lni);
523 		maxcount -= 1;
524 	}
525 
526 	TRACE("NodeManager::GetLiveNodes found %ld\n", livenodes->CountItems());
527 	return B_OK;
528 }
529 
530 /* Add media_node_id of all live nodes to the message
531  * int32 "media_node_id" (multiple items)
532  */
533 status_t
534 NodeManager::GetLiveNodes(BMessage *msg)
535 {
536 	BAutolock lock(fLocker);
537 	registered_node *rn;
538 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
539 		msg->AddInt32("media_node_id", rn->nodeid);
540 	}
541 	return B_OK;
542 }
543 
544 /**********************************************************************
545  * Registration of BMediaAddOns
546  **********************************************************************/
547 
548 void
549 NodeManager::RegisterAddon(const entry_ref &ref, media_addon_id *newid)
550 {
551 	BAutolock lock(fLocker);
552 	media_addon_id id;
553 	id = fNextAddOnID;
554 	fNextAddOnID += 1;
555 
556 	printf("NodeManager::RegisterAddon: ref-name \"%s\", assigning id %ld\n", ref.name, id);
557 
558 	fAddonPathMap->Insert(id, ref);
559 	*newid = id;
560 }
561 
562 void
563 NodeManager::UnregisterAddon(media_addon_id id)
564 {
565 	BAutolock lock(fLocker);
566 
567 	printf("NodeManager::UnregisterAddon: id %ld\n", id);
568 
569 	RemoveDormantFlavorInfo(id);
570 	fAddonPathMap->Remove(id);
571 }
572 
573 status_t
574 NodeManager::GetAddonRef(entry_ref *ref, media_addon_id id)
575 {
576 	BAutolock lock(fLocker);
577 	entry_ref *tempref;
578 
579 	if (fAddonPathMap->Get(id, &tempref)) {
580 		*ref = *tempref;
581 		return B_OK;
582 	}
583 
584 	return B_ERROR;
585 }
586 
587 /**********************************************************************
588  * Registration of node flavors, published by BMediaAddOns
589  **********************************************************************/
590 
591 // this function is only called (indirectly) by the media_addon_server
592 void
593 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info &dfi)
594 {
595 	BAutolock lock(fLocker);
596 
597 	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);
598 
599 	// Try to find the addon-id/flavor-id in the list.
600 	// If it already exists, update the Info, but don't
601 	// change the GlobalInstancesCount
602 	dormant_addon_flavor_info *dafi;
603 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
604 		if (dafi->AddonID != dfi.node_info.addon || dafi->AddonFlavorID != dfi.node_info.flavor_id)
605 			continue;
606 		if (dafi->InfoValid) {
607 			ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld does already exist\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id);
608 		}
609 		TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, flavor-id %ld\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id);
610 		dafi->MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff;
611 		// do NOT modify dafi.GlobalInstancesCount
612 		dafi->InfoValid = true;
613 		dafi->Info = dfi;
614 		return;
615 	}
616 
617 	// Insert information into the list
618 	{
619 		dormant_addon_flavor_info dafi;
620 		dafi.AddonID = dfi.node_info.addon;
621 		dafi.AddonFlavorID = dfi.node_info.flavor_id;
622 		dafi.MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff;
623 		dafi.GlobalInstancesCount = 0;
624 		dafi.InfoValid = true;
625 		dafi.Info = dfi;
626 		fDormantAddonFlavorList->Insert(dafi);
627 	}
628 }
629 
630 // this function is only called (indirectly) by the media_addon_server
631 void
632 NodeManager::InvalidateDormantFlavorInfo(media_addon_id id)
633 {
634 	BAutolock lock(fLocker);
635 	dormant_addon_flavor_info *dafi;
636 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
637 		if (dafi->AddonID == id && dafi->InfoValid == true) {
638 			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);
639 			dormant_flavor_info dfi_null;
640 			dafi->Info = dfi_null;
641 			dafi->InfoValid = false;
642 		}
643 	}
644 }
645 
646 // this function is only called by clean up after gone add-ons
647 void
648 NodeManager::RemoveDormantFlavorInfo(media_addon_id id)
649 {
650 	BAutolock lock(fLocker);
651 	dormant_addon_flavor_info *dafi;
652 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
653 		if (dafi->AddonID == id) {
654 			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);
655 			fDormantAddonFlavorList->RemoveCurrent();
656 		}
657 	}
658 }
659 
660 status_t
661 NodeManager::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team)
662 {
663 	BAutolock lock(fLocker);
664 
665 	dormant_addon_flavor_info *dafi;
666 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
667 		if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid)
668 			continue;
669 
670 		if (dafi->GlobalInstancesCount >= dafi->MaxInstancesCount) {
671 			ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld maximum (or more) instances already exist\n", addonid, flavorid);
672 			return B_ERROR; // maximum (or more) instances already exist
673 		}
674 
675 		bool b;
676 		int32 *count;
677 		b = dafi->TeamInstancesCount.Get(team, &count);
678 		if (b) {
679 			*count += 1;
680 		} else {
681 			b = dafi->TeamInstancesCount.Insert(team, 1);
682 			ASSERT(b);
683 		}
684 		dafi->GlobalInstancesCount += 1;
685 		return B_OK;
686 	}
687 	ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid);
688 	return B_ERROR;
689 }
690 
691 status_t
692 NodeManager::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team)
693 {
694 	BAutolock lock(fLocker);
695 
696 	dormant_addon_flavor_info *dafi;
697 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
698 		if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid)
699 			continue;
700 
701 		bool b;
702 		int32 *count;
703 		b = dafi->TeamInstancesCount.Get(team, &count);
704 		if (!b) {
705 			ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld team %ld has no references\n", addonid, flavorid, team);
706 			return B_ERROR;
707 		}
708 		*count -= 1;
709 		if (*count == 0) {
710 			b = dafi->TeamInstancesCount.Remove(team);
711 			ASSERT(b);
712 		}
713 		if (dafi->GlobalInstancesCount > 0)		// avoid underflow
714 			dafi->GlobalInstancesCount -= 1;
715 		return B_OK;
716 	}
717 	ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid);
718 	return B_ERROR;
719 }
720 
721 // this function is called when the media_addon_server has crashed
722 void
723 NodeManager::CleanupDormantFlavorInfos()
724 {
725 	BAutolock lock(fLocker);
726 	printf("NodeManager::CleanupDormantFlavorInfos\n");
727 	fDormantAddonFlavorList->MakeEmpty();
728 	printf("NodeManager::CleanupDormantFlavorInfos done\n");
729 	// XXX FlavorsChanged(media_addon_id addonid, int32 newcount, int32 gonecount)
730 }
731 
732 status_t
733 NodeManager::GetDormantNodes(dormant_node_info * out_info,
734 							  int32 * io_count,
735 							  const media_format * has_input /* = NULL */,
736 							  const media_format * has_output /* = NULL */,
737 							  const char * name /* = NULL */,
738 							  uint64 require_kinds /* = NULL */,
739 							  uint64 deny_kinds /* = NULL */)
740 {
741 	BAutolock lock(fLocker);
742 	dormant_addon_flavor_info *dafi;
743 	int32 maxcount;
744 	int namelen;
745 
746 	// determine the count of byte to compare when checking for a name with(out) wildcard
747 	if (name) {
748 		namelen = strlen(name);
749 		if (name[namelen] == '*')
750 			namelen--; // compares without the '*'
751 		else
752 			namelen++; // also compares the terminating NULL
753 	} else
754 		namelen = 0;
755 
756 	maxcount = *io_count;
757 	*io_count = 0;
758 	for (fDormantAddonFlavorList->Rewind(); (*io_count < maxcount) && fDormantAddonFlavorList->GetNext(&dafi); ) {
759 		if (!dafi->InfoValid)
760 			continue;
761 
762 		dormant_flavor_info *dfi;
763 		dfi = &dafi->Info;
764 
765 		if ((dfi->kinds & require_kinds) != require_kinds)
766 			continue;
767 		if ((dfi->kinds & deny_kinds) != 0)
768 			continue;
769 		if (namelen) {
770 			if (0 != memcmp(name, dfi->name, namelen))
771 				continue;
772 		}
773 		if (has_input) {
774 			bool hasit = false;
775 			for (int32 i = 0; i < dfi->in_format_count; i++)
776 				if (format_is_compatible(*has_input, dfi->in_formats[i])) {
777 					hasit = true;
778 					break;
779 				}
780 			if (!hasit)
781 				continue;
782 		}
783 		if (has_output) {
784 			bool hasit = false;
785 			for (int32 i = 0; i < dfi->out_format_count; i++)
786 				if (format_is_compatible(*has_output, dfi->out_formats[i])) {
787 					hasit = true;
788 					break;
789 				}
790 			if (!hasit)
791 				continue;
792 		}
793 
794 		out_info[*io_count] = dfi->node_info;
795 		*io_count += 1;
796 	}
797 
798 	return B_OK;
799 }
800 
801 status_t
802 NodeManager::GetDormantFlavorInfoFor(media_addon_id addon,
803 									 int32 flavor_id,
804 									 dormant_flavor_info *outFlavor)
805 {
806 	BAutolock lock(fLocker);
807 	dormant_addon_flavor_info *dafi;
808 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
809 		if (dafi->AddonID == addon && dafi->AddonFlavorID == flavor_id && dafi->InfoValid == true) {
810 			*outFlavor = dafi->Info;
811 			return B_OK;
812 		}
813 	}
814 	return B_ERROR;
815 }
816 
817 /**********************************************************************
818  * Default node management
819  **********************************************************************/
820 
821 status_t
822 NodeManager::SetDefaultNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input)
823 {
824 	BAutolock lock(fLocker);
825 	status_t err = B_BAD_VALUE;
826 	if (node)
827 		err = fDefaultManager->Set(node->node, NULL, 0, type);
828 	else if (input)
829 		err = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type);
830 	else if (info) {
831 		media_node_id node_id;
832 		int32 count = 1;
833 		if ((err=GetInstances(&node_id, &count, count, info->addon, info->flavor_id))!=B_OK)
834 			return err;
835 		err = fDefaultManager->Set(node_id, NULL, 0, type);
836 	}
837 	if(err==B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) {
838 		fDefaultManager->SaveState(this);
839 		Dump();
840 	}
841 	return err;
842 }
843 
844 status_t
845 NodeManager::GetDefaultNode(media_node_id *nodeid, char *input_name, int32 *input_id, node_type type)
846 {
847 	BAutolock lock(fLocker);
848 	return fDefaultManager->Get(nodeid, input_name, input_id, type);
849 }
850 
851 status_t
852 NodeManager::RescanDefaultNodes()
853 {
854 	BAutolock lock(fLocker);
855 	return fDefaultManager->Rescan();
856 }
857 
858 /**********************************************************************
859  * Cleanup of dead teams
860  **********************************************************************/
861 
862 void
863 NodeManager::CleanupTeam(team_id team)
864 {
865 	BAutolock lock(fLocker);
866 
867 	fDefaultManager->CleanupTeam(team);
868 
869 	PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team);
870 
871 	// XXX send notifications after removing nodes
872 
873 	// Cleanup node references
874 
875 	registered_node *rn;
876 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
877 		// if the gone team was the creator of some global dormant node instance, we now invalidate that
878 		// we may want to remove that global node, but I'm not sure
879 		if (rn->creator == team) {
880 			rn->creator = -1;
881 			// fall through
882 		}
883 		// if the team hosting this node is gone, remove node from database
884 		if (rn->team == team) {
885 			PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team %ld\n", rn->nodeid, team);
886 			fRegisteredNodeMap->RemoveCurrent();
887 			continue;
888 		}
889 		// check the list of teams that have references to this node, and remove the team
890 		team_id *pteam;
891 		int32 *prefcount;
892 		for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&prefcount); ) {
893 			rn->teamrefcount.GetCurrentKey(&pteam);
894 			if (*pteam == team) {
895 				PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node id %ld, team %ld\n", *prefcount, rn->nodeid, team);
896 				rn->teamrefcount.RemoveCurrent();
897 				break;
898 			}
899 		}
900 		// if the team refcount is now empty, also remove the node
901 		if (rn->teamrefcount.IsEmpty()) {
902 			PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that has no teams\n", rn->nodeid);
903 			fRegisteredNodeMap->RemoveCurrent();
904 		}
905 	}
906 
907 	// Cleanup addon references
908 	dormant_addon_flavor_info *dafi;
909 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
910 		bool b;
911 		int32 *count;
912 		b = dafi->TeamInstancesCount.Get(team, &count);
913 		if (b) {
914 			PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from addon %ld, flavor %ld\n", *count, dafi->AddonID, dafi->AddonFlavorID);
915 			dafi->GlobalInstancesCount -= *count;
916 			if (dafi->GlobalInstancesCount < 0)		// avoid underflow
917 				dafi->GlobalInstancesCount = 0;
918 			b = dafi->TeamInstancesCount.Remove(team);
919 			ASSERT(b);
920 		}
921 	}
922 }
923 
924 /**********************************************************************
925  * State saving/loading
926  **********************************************************************/
927 
928 status_t
929 NodeManager::LoadState()
930 {
931 	BAutolock lock(fLocker);
932 	return fDefaultManager->LoadState();
933 }
934 
935 status_t
936 NodeManager::SaveState()
937 {
938 	BAutolock lock(fLocker);
939 	return fDefaultManager->SaveState(this);
940 }
941 
942 /**********************************************************************
943  * Debugging
944  **********************************************************************/
945 
946 void
947 NodeManager::Dump()
948 {
949 	BAutolock lock(fLocker);
950 	printf("\n");
951 
952 	/* for each addon-id, the addon path map contians an entry_ref
953 	 */
954 	printf("NodeManager: addon path map follows:\n");
955 	entry_ref *ref;
956 	media_addon_id *id;
957 	for (fAddonPathMap->Rewind(); fAddonPathMap->GetNext(&ref); ) {
958 		fAddonPathMap->GetCurrentKey(&id);
959 		BPath path(ref);
960 		printf(" addon-id %ld, ref-name \"%s\", path \"%s\"\n", *id, ref->name, (path.InitCheck() == B_OK) ? path.Path() : "INVALID");
961 	}
962 	printf("NodeManager: list end\n");
963 	printf("\n");
964 
965 	/* for each node-id, the registered node map contians information about source of the node, users, etc.
966 	 */
967 	printf("NodeManager: registered nodes map follows:\n");
968 	registered_node *rn;
969 	for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) {
970 		printf("  node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n",
971 			rn->nodeid, rn->addon_id, rn->addon_flavor_id, rn->port, rn->creator, rn->team, rn->kinds, rn->name);
972 		printf("    teams (refcount): ");
973 		team_id *team;
974 		int32 *refcount;
975 		for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&refcount); ) {
976 			rn->teamrefcount.GetCurrentKey(&team);
977 			printf("%ld (%ld), ", *team, *refcount);
978 		}
979 		printf("\n");
980 		media_input *input;
981 		for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) {
982 			printf("    media_input: node-id %ld, node-port %ld, source-port %ld, source-id  %ld, dest-port %ld, dest-id %ld, name \"%s\"\n",
983 				input->node.node, input->node.port, input->source.port, input->source.id, input->destination.port, input->destination.id, input->name);
984 		}
985 		if (rn->inputlist.IsEmpty())
986 			printf("    media_input: none\n");
987 		media_output *output;
988 		for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) {
989 			printf("    media_output: node-id %ld, node-port %ld, source-port %ld, source-id  %ld, dest-port %ld, dest-id %ld, name \"%s\"\n",
990 				output->node.node, output->node.port, output->source.port, output->source.id, output->destination.port, output->destination.id, output->name);
991 		}
992 		if (rn->outputlist.IsEmpty())
993 			printf("    media_output: none\n");
994 	}
995 	printf("NodeManager: list end\n");
996 	printf("\n");
997 
998 	/*
999 	 */
1000 	printf("NodeManager: dormant flavor list follows:\n");
1001 	dormant_addon_flavor_info *dafi;
1002 	for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) {
1003 		printf("  AddonID %ld, AddonFlavorID %ld, MaxInstancesCount %ld, GlobalInstancesCount %ld, InfoValid %s\n",
1004 			dafi->AddonID, dafi->AddonFlavorID, dafi->MaxInstancesCount, dafi->GlobalInstancesCount, dafi->InfoValid ? "yes" : "no");
1005 		if (!dafi->InfoValid)
1006 			continue;
1007 		printf("    addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n",
1008 			dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name);
1009 		printf("    flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, possible_count %ld, in_format_count %ld, out_format_count %ld\n",
1010 			 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);
1011 		printf("    flavor-name \"%s\"\n", dafi->Info.name);
1012 		printf("    flavor-info \"%s\"\n", dafi->Info.info);
1013 	}
1014 	printf("NodeManager: list end\n");
1015 	fDefaultManager->Dump();
1016 }
1017 
1018 const char *
1019 get_node_type(node_type t)
1020 {
1021 	switch (t) {
1022 		#define CASE(c) case c: return #c;
1023 		CASE(VIDEO_INPUT)
1024 		CASE(AUDIO_INPUT)
1025 		CASE(VIDEO_OUTPUT)
1026 		CASE(AUDIO_MIXER)
1027 		CASE(AUDIO_OUTPUT)
1028 		CASE(AUDIO_OUTPUT_EX)
1029 		CASE(TIME_SOURCE)
1030 		CASE(SYSTEM_TIME_SOURCE)
1031 		default: return "unknown";
1032 	}
1033 };
1034 
1035