xref: /haiku/src/servers/media/NodeManager.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
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 
31 #include "NodeManager.h"
32 
33 #include <Autolock.h>
34 #include <Entry.h>
35 #include <MediaAddOn.h>
36 #include <MediaDefs.h>
37 #include <Message.h>
38 #include <Messenger.h>
39 #include <OS.h>
40 #include <Path.h>
41 
42 #include <debug.h>
43 #include <MediaMisc.h>
44 
45 #include "AppManager.h"
46 #include "DefaultManager.h"
47 #include "media_server.h"
48 
49 
50 const char*
51 get_node_type(node_type type)
52 {
53 #define CASE(c) case c: return #c;
54 	switch (type) {
55 		CASE(VIDEO_INPUT)
56 		CASE(AUDIO_INPUT)
57 		CASE(VIDEO_OUTPUT)
58 		CASE(AUDIO_MIXER)
59 		CASE(AUDIO_OUTPUT)
60 		CASE(AUDIO_OUTPUT_EX)
61 		CASE(TIME_SOURCE)
62 		CASE(SYSTEM_TIME_SOURCE)
63 
64 		default:
65 			return "unknown";
66 	}
67 #undef CASE
68 }
69 
70 
71 // #pragma mark -
72 
73 
74 NodeManager::NodeManager()
75 	:
76 	BLocker("node manager"),
77 	fNextAddOnID(1),
78 	fNextNodeID(1),
79 	fDefaultManager(new DefaultManager)
80 {
81 }
82 
83 
84 NodeManager::~NodeManager()
85 {
86 	delete fDefaultManager;
87 }
88 
89 
90 // #pragma mark - Default node management
91 
92 
93 status_t
94 NodeManager::SetDefaultNode(node_type type, const media_node* node,
95 	const dormant_node_info* info, const media_input* input)
96 {
97 	BAutolock _(this);
98 
99 	status_t status = B_BAD_VALUE;
100 	if (node != NULL)
101 		status = fDefaultManager->Set(node->node, NULL, 0, type);
102 	else if (input != NULL) {
103 		status = fDefaultManager->Set(input->node.node, input->name,
104 			input->destination.id, type);
105 	} else if (info != NULL) {
106 		media_node_id nodeID;
107 		int32 count = 1;
108 		status = GetInstances(info->addon, info->flavor_id, &nodeID, &count,
109 			count);
110 		if (status == B_OK)
111 			status = fDefaultManager->Set(nodeID, NULL, 0, type);
112 	}
113 
114 	if (status == B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT
115 			|| type == AUDIO_OUTPUT || type == AUDIO_INPUT)) {
116 		fDefaultManager->SaveState(this);
117 		Dump();
118 	}
119 	return status;
120 }
121 
122 
123 status_t
124 NodeManager::GetDefaultNode(node_type type, media_node_id* _nodeID,
125 	char* inputName, int32* _inputID)
126 {
127 	BAutolock _(this);
128 	return fDefaultManager->Get(_nodeID, inputName, _inputID, type);
129 }
130 
131 
132 status_t
133 NodeManager::RescanDefaultNodes()
134 {
135 	BAutolock _(this);
136 	return fDefaultManager->Rescan();
137 }
138 
139 
140 // #pragma mark - Live node management
141 
142 
143 status_t
144 NodeManager::RegisterNode(media_addon_id addOnID, int32 flavorID,
145 	const char* name, uint64 kinds, port_id port, team_id team,
146 	media_node_id* _nodeID)
147 {
148 	BAutolock _(this);
149 
150 	registered_node node;
151 	node.node_id = fNextNodeID;
152 	node.add_on_id = addOnID;
153 	node.flavor_id = flavorID;
154 	strlcpy(node.name, name, sizeof(node.name));
155 	node.kinds = kinds;
156 	node.port = port;
157 	node.containing_team = team;
158 	node.creator = -1; // will be set later
159 	node.ref_count = 1;
160 
161 	try {
162 		node.team_ref_count.insert(std::make_pair(team, 1));
163 
164 		fNodeMap.insert(std::make_pair(fNextNodeID, node));
165 	} catch (std::bad_alloc& exception) {
166 		return B_NO_MEMORY;
167 	}
168 
169 	*_nodeID = fNextNodeID++;
170 
171 	TRACE("NodeManager::RegisterNode: node %ld, addon_id %ld, flavor_id %ld, "
172 		"name \"%s\", kinds %#Lx, port %ld, team %ld\n", *_nodeID, addOnID,
173 		flavorID, name, kinds, port, team);
174 	return B_OK;
175 }
176 
177 
178 status_t
179 NodeManager::UnregisterNode(media_node_id id, team_id team,
180 	media_addon_id* _addOnID, int32* _flavorID)
181 {
182 	TRACE("NodeManager::UnregisterNode enter: node %ld, team %ld\n", id, team);
183 
184 	BAutolock _(this);
185 
186 	NodeMap::iterator found = fNodeMap.find(id);
187 	if (found == fNodeMap.end()) {
188 		ERROR("NodeManager::UnregisterNode: couldn't find node %ld (team "
189 			"%ld)\n", id, team);
190 		return B_ERROR;
191 	}
192 
193 	registered_node& node = found->second;
194 
195 	if (node.containing_team != team) {
196 		ERROR("NodeManager::UnregisterNode: team %ld tried to unregister "
197 			"node %ld, but it was instantiated by team %ld\n", team, id,
198 			node.containing_team);
199 		return B_ERROR;
200 	}
201 	if (node.ref_count != 1) {
202 		ERROR("NodeManager::UnregisterNode: node %ld, team %ld has ref count "
203 			"%ld (should be 1)\n", id, team, node.ref_count);
204 		//return B_ERROR;
205 	}
206 
207 	*_addOnID = node.add_on_id;
208 	*_flavorID = node.flavor_id;
209 
210 	fNodeMap.erase(found);
211 
212 	TRACE("NodeManager::UnregisterNode leave: node %ld, addon_id %ld, "
213 		"flavor_id %ld team %ld\n", id, *_addOnID, *_flavorID, team);
214 	return B_OK;
215 }
216 
217 
218 status_t
219 NodeManager::ReleaseNodeReference(media_node_id id, team_id team)
220 {
221 	TRACE("NodeManager::ReleaseNodeReference enter: node %ld, team %ld\n", id,
222 		team);
223 
224 	BAutolock _(this);
225 
226 	NodeMap::iterator found = fNodeMap.find(id);
227 	if (found == fNodeMap.end()) {
228 		ERROR("NodeManager::ReleaseNodeReference: node %ld not found\n", id);
229 		return B_ERROR;
230 	}
231 
232 	registered_node& node = found->second;
233 
234 	TeamCountMap::iterator teamRef = node.team_ref_count.find(team);
235 	if (teamRef == node.team_ref_count.end()) {
236 		// Normally it is an error to release a node in another team. But we
237 		// make one exception: if the node is global, and the creator team
238 		// tries to release it, we will release it in the the
239 		// media_addon_server.
240 		team_id addOnServer = gAppManager->AddOnServerTeam();
241 		teamRef = node.team_ref_count.find(addOnServer);
242 
243 		if (node.creator == team && teamRef != node.team_ref_count.end()) {
244 			PRINT(1, "!!! NodeManager::ReleaseNodeReference doing global "
245 				"release!\n");
246 			node.creator = -1; // invalidate!
247 			team = addOnServer;
248 		} else {
249 			ERROR("NodeManager::ReleaseNodeReference: node %ld has no team "
250 				"%ld references\n", id, team);
251 			return B_ERROR;
252 		}
253 	}
254 
255 #if DEBUG
256 	int32 teamCount = teamRef->second - 1;
257 #endif
258 
259 	if (--teamRef->second == 0)
260 		node.team_ref_count.erase(teamRef);
261 
262 	if (--node.ref_count == 0) {
263 		PRINT(1, "NodeManager::ReleaseNodeReference: detected released node is "
264 			"now unused, node %ld\n", id);
265 
266 		// TODO: remove!
267 		node_final_release_command command;
268 		status_t status = SendToPort(node.port, NODE_FINAL_RELEASE, &command,
269 			sizeof(command));
270 		if (status != B_OK) {
271 			ERROR("NodeManager::ReleaseNodeReference: can't send command to "
272 				"node %ld\n", id);
273 			// ignore error
274 		}
275 	}
276 
277 	TRACE("NodeManager::ReleaseNodeReference leave: node %ld, team %ld, "
278 		"ref %ld, team ref %ld\n", id, team, node.ref_count, teamCount);
279 	return B_OK;
280 }
281 
282 
283 status_t
284 NodeManager::SetNodeCreator(media_node_id id, team_id creator)
285 {
286 	TRACE("NodeManager::SetNodeCreator node %ld, creator %ld\n", id, creator);
287 
288 	BAutolock _(this);
289 
290 	NodeMap::iterator found = fNodeMap.find(id);
291 	if (found == fNodeMap.end()) {
292 		ERROR("NodeManager::SetNodeCreator: node %ld not found\n", id);
293 		return B_ERROR;
294 	}
295 
296 	registered_node& node = found->second;
297 
298 	if (node.creator != -1) {
299 		ERROR("NodeManager::SetNodeCreator: node %ld is already assigned "
300 			"creator %ld\n", id, node.creator);
301 		return B_ERROR;
302 	}
303 
304 	node.creator = creator;
305 	return B_OK;
306 }
307 
308 
309 status_t
310 NodeManager::GetCloneForID(media_node_id id, team_id team, media_node* node)
311 {
312 	TRACE("NodeManager::GetCloneForID enter: node %ld team %ld\n", id, team);
313 
314 	BAutolock _(this);
315 
316 	status_t status = _AcquireNodeReference(id, team);
317 	if (status != B_OK) {
318 		ERROR("NodeManager::GetCloneForID: couldn't increment ref count, "
319 			"node %ld team %ld\n", id, team);
320 		return status;
321 	}
322 
323 	NodeMap::iterator found = fNodeMap.find(id);
324 	if (found == fNodeMap.end()) {
325 		ERROR("NodeManager::GetCloneForID: node %ld not found\n", id);
326 		return B_ERROR;
327 	}
328 
329 	registered_node& registeredNode = found->second;
330 
331 	node->node = registeredNode.node_id;
332 	node->port = registeredNode.port;
333 	node->kind = registeredNode.kinds;
334 
335 	TRACE("NodeManager::GetCloneForID leave: node %ld team %ld\n", id, team);
336 	return B_OK;
337 }
338 
339 
340 /*!	This function locates the default "node" for the requested "type" and
341 	returns a clone.
342 	If the requested type is AUDIO_OUTPUT_EX, also "input_name" and "input_id"
343 	need to be set and returned, as this is required by
344 	BMediaRoster::GetAudioOutput(media_node *out_node, int32 *out_input_id,
345 		BString *out_input_name).
346 */
347 status_t
348 NodeManager::GetClone(node_type type, team_id team, media_node* node,
349 	char* inputName, int32* _inputID)
350 {
351 	BAutolock _(this);
352 
353 	TRACE("NodeManager::GetClone enter: team %ld, type %d (%s)\n", team, type, get_node_type(type));
354 
355 	media_node_id id;
356 	status_t status = GetDefaultNode(type, &id, inputName, _inputID);
357 	if (status != B_OK) {
358 		ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %ld, "
359 			"type %d (%s)\n", team, type, get_node_type(type));
360 		*node = media_node::null;
361 		return status;
362 	}
363 	ASSERT(id > 0);
364 
365 	status = GetCloneForID(id, team, node);
366 	if (status != B_OK) {
367 		ERROR("NodeManager::GetClone: couldn't GetCloneForID, id %ld, team "
368 			"%ld, type %d (%s)\n", id, team, type, get_node_type(type));
369 		*node = media_node::null;
370 		return status;
371 	}
372 	ASSERT(id == node->node);
373 
374 	TRACE("NodeManager::GetClone leave: node id %ld, node port %ld, node "
375 		"kind %#lx\n", node->node, node->port, node->kind);
376 	return B_OK;
377 }
378 
379 
380 status_t
381 NodeManager::ReleaseNode(const media_node& node, team_id team)
382 {
383 	TRACE("NodeManager::ReleaseNode enter: node %ld team %ld\n", node.node,
384 		team);
385 
386 	if (ReleaseNodeReference(node.node, team) != B_OK) {
387 		ERROR("NodeManager::ReleaseNode: couldn't decrement node %ld team %ld "
388 			"ref count\n", node.node, team);
389 	}
390 
391 	return B_OK;
392 }
393 
394 
395 status_t
396 NodeManager::PublishInputs(const media_node& node, const media_input* inputs,
397 	int32 count)
398 {
399 	BAutolock _(this);
400 
401 	NodeMap::iterator found = fNodeMap.find(node.node);
402 	if (found == fNodeMap.end()) {
403 		ERROR("NodeManager::PublishInputs: node %ld not found\n", node.node);
404 		return B_ERROR;
405 	}
406 
407 	registered_node& registeredNode = found->second;
408 
409 	registeredNode.input_list.clear();
410 
411 	try {
412 		for (int32 i = 0; i < count; i++)
413 			registeredNode.input_list.push_back(inputs[i]);
414 	} catch (std::bad_alloc& exception) {
415 		return B_NO_MEMORY;
416 	}
417 
418 	return B_OK;
419 }
420 
421 
422 status_t
423 NodeManager::PublishOutputs(const media_node &node, const media_output* outputs,
424 	int32 count)
425 {
426 	BAutolock _(this);
427 
428 	NodeMap::iterator found = fNodeMap.find(node.node);
429 	if (found == fNodeMap.end()) {
430 		ERROR("NodeManager::PublishOutputs: node %ld not found\n", node.node);
431 		return B_ERROR;
432 	}
433 
434 	registered_node& registeredNode = found->second;
435 
436 	registeredNode.output_list.clear();
437 
438 	try {
439 		for (int32 i = 0; i < count; i++)
440 			registeredNode.output_list.push_back(outputs[i]);
441 	} catch (std::bad_alloc& exception) {
442 		return B_NO_MEMORY;
443 	}
444 
445 	return B_OK;
446 }
447 
448 
449 status_t
450 NodeManager::FindNodeID(port_id port, media_node_id* _id)
451 {
452 	BAutolock _(this);
453 
454 	NodeMap::iterator iterator = fNodeMap.begin();
455 	for (; iterator != fNodeMap.end(); iterator++) {
456 		registered_node& node = iterator->second;
457 
458 		if (node.port == port) {
459 			*_id = node.node_id;
460 			TRACE("NodeManager::FindNodeID found port %ld, node %ld\n", port,
461 				node.node_id);
462 			return B_OK;
463 		}
464 
465 		OutputList::iterator outIterator = node.output_list.begin();
466 		for (; outIterator != node.output_list.end(); outIterator++) {
467 			if (outIterator->source.port == port) {
468 				*_id = node.node_id;
469 				TRACE("NodeManager::FindNodeID found output port %ld, node "
470 					"%ld\n", port, node.node_id);
471 				return B_OK;
472 			}
473 		}
474 
475 		InputList::iterator inIterator = node.input_list.begin();
476 		for (; inIterator != node.input_list.end(); inIterator++) {
477 			if (inIterator->destination.port == port) {
478 				*_id = node.node_id;
479 				TRACE("NodeManager::FindNodeID found input port %ld, node "
480 					"%ld\n", port, node.node_id);
481 				return B_OK;
482 			}
483 		}
484 	}
485 
486 	ERROR("NodeManager::FindNodeID failed, port %ld\n", port);
487 	return B_ERROR;
488 }
489 
490 
491 status_t
492 NodeManager::GetDormantNodeInfo(const media_node& node,
493 	dormant_node_info* nodeInfo)
494 {
495 	// TODO: not sure if this is correct
496 	BAutolock _(this);
497 
498 	NodeMap::iterator found = fNodeMap.find(node.node);
499 	if (found == fNodeMap.end()) {
500 		ERROR("NodeManager::GetDormantNodeInfo: node %ld not found\n",
501 			node.node);
502 		return B_ERROR;
503 	}
504 
505 	registered_node& registeredNode = found->second;
506 
507 	if (registeredNode.add_on_id == -1
508 		&& node.node != NODE_SYSTEM_TIMESOURCE_ID) {
509 		// This function must return an error if the node is application owned
510 		TRACE("NodeManager::GetDormantNodeInfo NODE IS APPLICATION OWNED! "
511 			"node %ld, add_on_id %ld, flavor_id %ld, name \"%s\"\n", node.node,
512 			registeredNode.add_on_id, registeredNode.flavor_id,
513 			registeredNode.name);
514 		return B_ERROR;
515 	}
516 
517 	ASSERT(node.port == registeredNode.port);
518 	ASSERT((node.kind & NODE_KIND_COMPARE_MASK)
519 		== (registeredNode.kinds & NODE_KIND_COMPARE_MASK));
520 
521 	nodeInfo->addon = registeredNode.add_on_id;
522 	nodeInfo->flavor_id = registeredNode.flavor_id;
523 	strlcpy(nodeInfo->name, registeredNode.name, sizeof(nodeInfo->name));
524 
525 	TRACE("NodeManager::GetDormantNodeInfo node %ld, add_on_id %ld, "
526 		"flavor_id %ld, name \"%s\"\n", node.node, registeredNode.add_on_id,
527 		registeredNode.flavor_id, registeredNode.name);
528 	return B_OK;
529 }
530 
531 
532 status_t
533 NodeManager::GetLiveNodeInfo(const media_node& node, live_node_info* liveInfo)
534 {
535 	BAutolock _(this);
536 
537 	NodeMap::iterator found = fNodeMap.find(node.node);
538 	if (found == fNodeMap.end()) {
539 		ERROR("NodeManager::GetLiveNodeInfo: node %ld not found\n",
540 			node.node);
541 		return B_ERROR;
542 	}
543 
544 	registered_node& registeredNode = found->second;
545 
546 	ASSERT(node.port == registeredNode.port);
547 	ASSERT((node.kind & NODE_KIND_COMPARE_MASK)
548 		== (registeredNode.kinds & NODE_KIND_COMPARE_MASK));
549 
550 	liveInfo->node = node;
551 	liveInfo->hint_point = BPoint(0, 0);
552 	strlcpy(liveInfo->name, registeredNode.name, sizeof(liveInfo->name));
553 
554 	TRACE("NodeManager::GetLiveNodeInfo node %ld, name = \"%s\"\n", node.node,
555 		registeredNode.name);
556 	return B_OK;
557 }
558 
559 
560 status_t
561 NodeManager::GetInstances(media_addon_id addOnID, int32 flavorID,
562 	media_node_id* ids, int32* _count, int32 maxCount)
563 {
564 	BAutolock _(this);
565 
566 	NodeMap::iterator iterator = fNodeMap.begin();
567 	int32 count = 0;
568 	for (; iterator != fNodeMap.end() && count < maxCount; iterator++) {
569 		registered_node& node = iterator->second;
570 
571 		if (node.add_on_id == addOnID && node.flavor_id == flavorID)
572 			ids[count++] = node.node_id;
573 	}
574 
575 	TRACE("NodeManager::GetInstances found %ld instances for addon_id %ld, "
576 		"flavor_id %ld\n", count, addOnID, flavorID);
577 	*_count = count;
578 	return B_OK;
579 }
580 
581 
582 status_t
583 NodeManager::GetLiveNodes(LiveNodeList& liveNodes, int32 maxCount,
584 	const media_format* inputFormat, const media_format* outputFormat,
585 	const char* name, uint64 requireKinds)
586 {
587 	TRACE("NodeManager::GetLiveNodes: maxCount %ld, in-format %p, out-format "
588 		"%p, name %s, require kinds 0x%Lx\n", maxCount, inputFormat,
589 		outputFormat, name != NULL ? name : "NULL", requireKinds);
590 
591 	BAutolock _(this);
592 
593 	// Determine the count of byte to compare when checking for a name with
594 	// or without wildcard
595 	size_t nameLength = 0;
596 	if (name != NULL) {
597 		nameLength = strlen(name);
598 		if (nameLength > 0 && name[nameLength - 1] == '*')
599 			nameLength--;
600 	}
601 
602 	NodeMap::iterator iterator = fNodeMap.begin();
603 	int32 count = 0;
604 	for (; iterator != fNodeMap.end() && count < maxCount; iterator++) {
605 		registered_node& node = iterator->second;
606 
607 		if ((node.kinds & requireKinds) != requireKinds)
608 			continue;
609 
610 		if (nameLength != 0) {
611 			if (strncmp(name, node.name, nameLength) != 0)
612 				continue;
613 		}
614 
615 		if (inputFormat != NULL) {
616 			bool found = false;
617 
618 			for (InputList::iterator inIterator = node.input_list.begin();
619 					inIterator != node.input_list.end(); inIterator++) {
620 				media_input& input = *inIterator;
621 
622 				if (format_is_compatible(*inputFormat, input.format)) {
623 					found = true;
624 					break;
625 				}
626 			}
627 
628 			if (!found)
629 				continue;
630 		}
631 
632 		if (outputFormat != NULL) {
633 			bool found = false;
634 
635 			for (OutputList::iterator outIterator = node.output_list.begin();
636 					outIterator != node.output_list.end(); outIterator++) {
637 				media_output& output = *outIterator;
638 
639 				if (format_is_compatible(*outputFormat, output.format)) {
640 					found = true;
641 					break;
642 				}
643 			}
644 
645 			if (!found)
646 				continue;
647 		}
648 
649 		live_node_info info;
650 		info.node.node = node.node_id;
651 		info.node.port = node.port;
652 		info.node.kind = node.kinds;
653 		info.hint_point = BPoint(0, 0);
654 		strlcpy(info.name, node.name, sizeof(info.name));
655 
656 		try {
657 			liveNodes.push_back(info);
658 		} catch (std::bad_alloc& exception) {
659 			return B_NO_MEMORY;
660 		}
661 
662 		count++;
663 	}
664 
665 	TRACE("NodeManager::GetLiveNodes found %ld\n", count);
666 	return B_OK;
667 }
668 
669 
670 /*!	Add media_node_id of all live nodes to the message
671 	int32 "media_node_id" (multiple items)
672 */
673 status_t
674 NodeManager::GetLiveNodes(BMessage* message)
675 {
676 	BAutolock _(this);
677 
678 	NodeMap::iterator iterator = fNodeMap.begin();
679 	for (; iterator != fNodeMap.end(); iterator++) {
680 		registered_node& node = iterator->second;
681 
682 		if (message->AddInt32("media_node_id", node.node_id) != B_OK)
683 			return B_NO_MEMORY;
684 	}
685 
686 	return B_OK;
687 }
688 
689 
690 // #pragma mark - Registration of BMediaAddOns
691 
692 
693 void
694 NodeManager::RegisterAddOn(const entry_ref& ref, media_addon_id* _newID)
695 {
696 	BAutolock _(this);
697 
698 	media_addon_id id = fNextAddOnID++;
699 
700 	printf("NodeManager::RegisterAddOn: ref-name \"%s\", assigning id %ld\n",
701 		ref.name, id);
702 
703 	try {
704 		fPathMap.insert(std::make_pair(id, ref));
705 		*_newID = id;
706 	} catch (std::bad_alloc& exception) {
707 		*_newID = -1;
708 	}
709 }
710 
711 
712 void
713 NodeManager::UnregisterAddOn(media_addon_id addOnID)
714 {
715 	PRINT(1, "NodeManager::UnregisterAddOn: id %ld\n", addOnID);
716 
717 	BAutolock _(this);
718 
719 	RemoveDormantFlavorInfo(addOnID);
720 	fPathMap.erase(addOnID);
721 }
722 
723 
724 status_t
725 NodeManager::GetAddOnRef(media_addon_id addOnID, entry_ref* ref)
726 {
727 	BAutolock _(this);
728 
729 	PathMap::iterator found = fPathMap.find(addOnID);
730 	if (found == fPathMap.end())
731 		return B_ERROR;
732 
733 	*ref = found->second;
734 	return B_OK;
735 }
736 
737 
738 // #pragma mark - Registration of node flavors, published by BMediaAddOns
739 
740 
741 //!	This function is only used (indirectly) by the media_addon_server.
742 status_t
743 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info& flavorInfo)
744 {
745 	PRINT(1, "NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld, "
746 		"name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n",
747 		flavorInfo.node_info.addon, flavorInfo.node_info.flavor_id,
748 		flavorInfo.node_info.name, flavorInfo.name, flavorInfo.info);
749 
750 	BAutolock _(this);
751 
752 	// Try to find the addon-id/flavor-id in the list.
753 	// If it already exists, update the info, but don't change its instance
754 	// count.
755 
756 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
757 			iterator != fDormantFlavors.end(); iterator++) {
758 		dormant_add_on_flavor_info& info = *iterator;
759 
760 		if (info.add_on_id != flavorInfo.node_info.addon
761 			|| info.flavor_id != flavorInfo.node_info.flavor_id)
762 			continue;
763 
764 		if (info.info_valid) {
765 			ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, "
766 				"flavor-id %ld does already exist\n", info.info.node_info.addon,
767 				info.info.node_info.flavor_id);
768 		}
769 
770 		TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, "
771 			"flavor-id %ld\n", info.info.node_info.addon,
772 			info.info.node_info.flavor_id);
773 
774 		info.max_instances_count = flavorInfo.possible_count > 0
775 			? flavorInfo.possible_count : INT32_MAX;
776 		info.info_valid = true;
777 		info.info = flavorInfo;
778 		return B_OK;
779 	}
780 
781 	// Insert information into the list
782 
783 	dormant_add_on_flavor_info info;
784 	info.add_on_id = flavorInfo.node_info.addon;
785 	info.flavor_id = flavorInfo.node_info.flavor_id;
786 	info.max_instances_count = flavorInfo.possible_count > 0
787 		? flavorInfo.possible_count : INT32_MAX;
788 	info.instances_count = 0;
789 	info.info_valid = true;
790 	info.info = flavorInfo;
791 
792 	try {
793 		fDormantFlavors.push_back(info);
794 	} catch (std::bad_alloc& exception) {
795 		return B_NO_MEMORY;
796 	}
797 
798 	return B_OK;
799 }
800 
801 
802 //!	This function is only used (indirectly) by the media_addon_server
803 void
804 NodeManager::InvalidateDormantFlavorInfo(media_addon_id addOnID)
805 {
806 	BAutolock _(this);
807 
808 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
809 			iterator != fDormantFlavors.end(); iterator++) {
810 		dormant_add_on_flavor_info& info = *iterator;
811 
812 		if (info.add_on_id == addOnID && info.info_valid) {
813 			PRINT(1, "NodeManager::InvalidateDormantFlavorInfo, addon-id %ld, "
814 				"flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info "
815 				"\"%s\"\n", info.info.node_info.addon,
816 				info.info.node_info.flavor_id, info.info.node_info.name,
817 				info.info.name, info.info.info);
818 
819 			info.info_valid = false;
820 		}
821 	}
822 }
823 
824 
825 //!	This function is only used (indirectly) by the media_addon_server
826 void
827 NodeManager::RemoveDormantFlavorInfo(media_addon_id addOnID)
828 {
829 	BAutolock _(this);
830 
831 	for (size_t index = 0; index < fDormantFlavors.size(); index++) {
832 		dormant_add_on_flavor_info& info = fDormantFlavors[index];
833 
834 		if (info.add_on_id == addOnID) {
835 			PRINT(1, "NodeManager::RemoveDormantFlavorInfo, addon-id %ld, "
836 				"flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info "
837 				"\"%s\"\n", info.info.node_info.addon,
838 				info.info.node_info.flavor_id, info.info.node_info.name,
839 				info.info.name, info.info.info);
840 			fDormantFlavors.erase(fDormantFlavors.begin() + index--);
841 		}
842 	}
843 }
844 
845 
846 status_t
847 NodeManager::IncrementFlavorInstancesCount(media_addon_id addOnID,
848 	int32 flavorID, team_id team)
849 {
850 	BAutolock _(this);
851 
852 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
853 			iterator != fDormantFlavors.end(); iterator++) {
854 		dormant_add_on_flavor_info& info = *iterator;
855 
856 		if (info.add_on_id != addOnID || info.flavor_id != flavorID)
857 			continue;
858 
859 		if (info.instances_count >= info.max_instances_count) {
860 			// maximum (or more) instances already exist
861 			ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %ld, "
862 				"flavor-id %ld maximum (or more) instances already exist\n",
863 				addOnID, flavorID);
864 			return B_ERROR;
865 		}
866 
867 		TeamCountMap::iterator teamInstance
868 			= info.team_instances_count.find(team);
869 		if (teamInstance == info.team_instances_count.end()) {
870 			// This is the team's first instance
871 			try {
872 				info.team_instances_count.insert(std::make_pair(team, 1));
873 			} catch (std::bad_alloc& exception) {
874 				return B_NO_MEMORY;
875 			}
876 		} else {
877 			// Just increase its ref count
878 			teamInstance->second++;
879 		}
880 
881 		info.instances_count++;
882 		return B_OK;
883 	}
884 
885 	ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %ld, "
886 		"flavor-id %ld not found\n", addOnID, flavorID);
887 	return B_ERROR;
888 }
889 
890 
891 status_t
892 NodeManager::DecrementFlavorInstancesCount(media_addon_id addOnID,
893 	int32 flavorID, team_id team)
894 {
895 	BAutolock _(this);
896 
897 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
898 			iterator != fDormantFlavors.end(); iterator++) {
899 		dormant_add_on_flavor_info& info = *iterator;
900 
901 		if (info.add_on_id != addOnID || info.flavor_id != flavorID)
902 			continue;
903 
904 		TeamCountMap::iterator teamInstance
905 			= info.team_instances_count.find(team);
906 		if (teamInstance == info.team_instances_count.end()) {
907 			ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %ld, "
908 				"flavor-id %ld team %ld has no references\n", addOnID, flavorID,
909 				team);
910 			return B_ERROR;
911 		}
912 		if (--teamInstance->second == 0)
913 			info.team_instances_count.erase(teamInstance);
914 
915 		info.instances_count--;
916 		return B_OK;
917 	}
918 
919 	ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %ld, "
920 		"flavor-id %ld not found\n", addOnID, flavorID);
921 	return B_ERROR;
922 }
923 
924 
925 //!	This function is called when the media_addon_server has crashed
926 void
927 NodeManager::CleanupDormantFlavorInfos()
928 {
929 	PRINT(1, "NodeManager::CleanupDormantFlavorInfos\n");
930 
931 	BAutolock _(this);
932 	fDormantFlavors.clear();
933 	// TODO: FlavorsChanged() notification
934 
935 	PRINT(1, "NodeManager::CleanupDormantFlavorInfos done\n");
936 }
937 
938 
939 status_t
940 NodeManager::GetDormantNodes(dormant_node_info* infos, int32* _count,
941 	const media_format* input, const media_format* output, const char* name,
942 	uint64 requireKinds, uint64 denyKinds)
943 {
944 	BAutolock _(this);
945 
946 	// Determine the count of byte to compare when checking for a name with
947 	// or without wildcard
948 	size_t nameLength = 0;
949 	if (name != NULL) {
950 		nameLength = strlen(name);
951 		if (nameLength > 0 && name[nameLength - 1] == '*')
952 			nameLength--;
953 	}
954 
955 	int32 maxCount = *_count;
956 	int32 count = 0;
957 
958 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
959 			iterator != fDormantFlavors.end() && count < maxCount; iterator++) {
960 		dormant_add_on_flavor_info& info = *iterator;
961 
962 		if (!info.info_valid)
963 			continue;
964 
965 		if ((info.info.kinds & requireKinds) != requireKinds
966 			|| (info.info.kinds & denyKinds) != 0)
967 			continue;
968 
969 		if (nameLength != 0) {
970 			if (strncmp(name, info.info.name, nameLength) != 0)
971 				continue;
972 		}
973 
974 		if (input != NULL) {
975 			bool found = false;
976 
977 			for (int32 i = 0; i < info.info.in_format_count; i++) {
978 				if (format_is_compatible(*input, info.info.in_formats[i])) {
979 					found = true;
980 					break;
981 				}
982 			}
983 
984 			if (!found)
985 				continue;
986 		}
987 
988 		if (output != NULL) {
989 			bool found = false;
990 
991 			for (int32 i = 0; i < info.info.out_format_count; i++) {
992 				if (format_is_compatible(*output, info.info.out_formats[i])) {
993 					found = true;
994 					break;
995 				}
996 			}
997 
998 			if (!found)
999 				continue;
1000 		}
1001 
1002 		infos[count++] = info.info.node_info;
1003 	}
1004 
1005 	*_count = count;
1006 	return B_OK;
1007 }
1008 
1009 
1010 status_t
1011 NodeManager::GetDormantFlavorInfoFor(media_addon_id addOnID, int32 flavorID,
1012 	dormant_flavor_info* flavorInfo)
1013 {
1014 	BAutolock _(this);
1015 
1016 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
1017 			iterator != fDormantFlavors.end(); iterator++) {
1018 		dormant_add_on_flavor_info& info = *iterator;
1019 
1020 		if (info.add_on_id == addOnID && info.flavor_id == flavorID
1021 			&& info.info_valid) {
1022 			*flavorInfo = info.info;
1023 			return B_OK;
1024 		}
1025 	}
1026 
1027 	return B_ERROR;
1028 }
1029 
1030 
1031 // #pragma mark - Misc.
1032 
1033 
1034 void
1035 NodeManager::CleanupTeam(team_id team)
1036 {
1037 	BAutolock _(this);
1038 
1039 	fDefaultManager->CleanupTeam(team);
1040 
1041 	PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team);
1042 
1043 	// TODO: send notifications after removing nodes
1044 
1045 	// Cleanup node references
1046 
1047 	for (NodeMap::iterator iterator = fNodeMap.begin();
1048 			iterator != fNodeMap.end();) {
1049 		registered_node& node = iterator->second;
1050 		NodeMap::iterator remove = iterator++;
1051 
1052 		// If the gone team was the creator of some global dormant node
1053 		// instance, we now invalidate that we may want to remove that
1054 		// global node, but I'm not sure
1055 		if (node.creator == team) {
1056 			node.creator = -1;
1057 			// fall through
1058 		}
1059 
1060 		// If the team hosting this node is gone, remove node from database
1061 		if (node.containing_team == team) {
1062 			PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team "
1063 				"%ld\n", node.node_id, team);
1064 			fNodeMap.erase(remove);
1065 			continue;
1066 		}
1067 
1068 		// Check the list of teams that have references to this node, and
1069 		// remove the team
1070 		TeamCountMap::iterator teamRef = node.team_ref_count.find(team);
1071 		if (teamRef != node.team_ref_count.end()) {
1072 			PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node "
1073 				"id %ld, team %ld\n", teamRef->second, node.node_id, team);
1074 			node.ref_count -= teamRef->second;
1075 			if (node.ref_count == 0) {
1076 				PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that "
1077 					"has no teams\n", node.node_id);
1078 
1079 				fNodeMap.erase(remove);
1080 			} else
1081 				node.team_ref_count.erase(teamRef);
1082 		}
1083 	}
1084 
1085 	// Cleanup add-on references
1086 
1087 	for (size_t index = 0; index < fDormantFlavors.size(); index++) {
1088 		dormant_add_on_flavor_info& flavorInfo = fDormantFlavors[index];
1089 
1090 		TeamCountMap::iterator instanceCount
1091 			= flavorInfo.team_instances_count.find(team);
1092 		if (instanceCount != flavorInfo.team_instances_count.end()) {
1093 			PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from "
1094 				"addon %ld, flavor %ld\n", instanceCount->second,
1095 				flavorInfo.add_on_id, flavorInfo.flavor_id);
1096 
1097 			flavorInfo.instances_count -= instanceCount->second;
1098 			if (flavorInfo.instances_count <= 0)
1099 				fDormantFlavors.erase(fDormantFlavors.begin() + index--);
1100 			else
1101 				flavorInfo.team_instances_count.erase(team);
1102 		}
1103 	}
1104 }
1105 
1106 
1107 status_t
1108 NodeManager::LoadState()
1109 {
1110 	BAutolock _(this);
1111 	return fDefaultManager->LoadState();
1112 }
1113 
1114 status_t
1115 NodeManager::SaveState()
1116 {
1117 	BAutolock _(this);
1118 	return fDefaultManager->SaveState(this);
1119 }
1120 
1121 
1122 void
1123 NodeManager::Dump()
1124 {
1125 	BAutolock _(this);
1126 
1127 	// for each addon-id, the add-on path map contains an entry_ref
1128 
1129 	printf("\nNodeManager: addon path map follows:\n");
1130 
1131 	for (PathMap::iterator iterator = fPathMap.begin();
1132 			iterator != fPathMap.end(); iterator++) {
1133 		BPath path(&iterator->second);
1134 		printf(" addon-id %ld, path \"%s\"\n", iterator->first,
1135 			path.InitCheck() == B_OK ? path.Path() : "INVALID");
1136 	}
1137 
1138 	printf("NodeManager: list end\n\n");
1139 
1140 	// for each node-id, the registered node map contians information about
1141 	// source of the node, users, etc.
1142 
1143 	printf("NodeManager: registered nodes map follows:\n");
1144 	for (NodeMap::iterator iterator = fNodeMap.begin();
1145 			iterator != fNodeMap.end(); iterator++) {
1146 		registered_node& node = iterator->second;
1147 
1148 		printf("  node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, "
1149 			"creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n", node.node_id,
1150 			node.add_on_id, node.flavor_id, node.port, node.creator,
1151 			node.containing_team, node.kinds, node.name);
1152 
1153 		printf("    teams (refcount): ");
1154 		for (TeamCountMap::iterator refsIterator = node.team_ref_count.begin();
1155 				refsIterator != node.team_ref_count.end(); refsIterator++) {
1156 			printf("%ld (%ld), ", refsIterator->first, refsIterator->second);
1157 		}
1158 		printf("\n");
1159 
1160 		for (InputList::iterator inIterator = node.input_list.begin();
1161 				inIterator != node.input_list.end(); inIterator++) {
1162 			media_input& input = *inIterator;
1163 			printf("    media_input: node-id %ld, node-port %ld, source-port "
1164 				"%ld, source-id  %ld, dest-port %ld, dest-id %ld, name "
1165 				"\"%s\"\n", input.node.node, input.node.port, input.source.port,
1166 				input.source.id, input.destination.port, input.destination.id,
1167 				input.name);
1168 		}
1169 		if (node.input_list.empty())
1170 			printf("    media_input: none\n");
1171 
1172 		for (OutputList::iterator outIterator = node.output_list.begin();
1173 				outIterator != node.output_list.end(); outIterator++) {
1174 			media_output& output = *outIterator;
1175 			printf("    media_output: node-id %ld, node-port %ld, source-port "
1176 				"%ld, source-id  %ld, dest-port %ld, dest-id %ld, name "
1177 				"\"%s\"\n", output.node.node, output.node.port,
1178 				output.source.port, output.source.id, output.destination.port,
1179 				output.destination.id, output.name);
1180 		}
1181 		if (node.output_list.empty())
1182 			printf("    media_output: none\n");
1183 	}
1184 
1185 	printf("NodeManager: list end\n");
1186 	printf("\n");
1187 
1188 	// Dormant add-on flavors
1189 
1190 	printf("NodeManager: dormant flavor list follows:\n");
1191 
1192 	for (DormantFlavorList::iterator iterator = fDormantFlavors.begin();
1193 			iterator != fDormantFlavors.end(); iterator++) {
1194 		dormant_add_on_flavor_info& flavorInfo = *iterator;
1195 
1196 		printf("  addon-id %ld, flavor-id %ld, max instances count %ld, "
1197 			"instances count %ld, info valid %s\n",
1198 			flavorInfo.add_on_id, flavorInfo.flavor_id,
1199 			flavorInfo.max_instances_count, flavorInfo.instances_count,
1200 			flavorInfo.info_valid ? "yes" : "no");
1201 		printf("    teams (instances): ");
1202 		for (TeamCountMap::iterator countIterator
1203 					= flavorInfo.team_instances_count.begin();
1204 				countIterator != flavorInfo.team_instances_count.end();
1205 				countIterator++) {
1206 			printf("%ld (%ld), ", countIterator->first, countIterator->second);
1207 		}
1208 		printf("\n");
1209 		if (!flavorInfo.info_valid)
1210 			continue;
1211 
1212 		printf("    addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n",
1213 			flavorInfo.info.node_info.addon,
1214 			flavorInfo.info.node_info.flavor_id,
1215 			flavorInfo.info.node_info.name);
1216 		printf("    flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, "
1217 			"possible_count %ld, in_format_count %ld, out_format_count %ld\n",
1218 			flavorInfo.info.kinds, flavorInfo.info.flavor_flags,
1219 			flavorInfo.info.internal_id, flavorInfo.info.possible_count,
1220 			flavorInfo.info.in_format_count, flavorInfo.info.out_format_count);
1221 		printf("    flavor-name \"%s\"\n", flavorInfo.info.name);
1222 		printf("    flavor-info \"%s\"\n", flavorInfo.info.info);
1223 	}
1224 	printf("NodeManager: list end\n");
1225 
1226 	fDefaultManager->Dump();
1227 }
1228 
1229 
1230 // #pragma mark - private methods
1231 
1232 
1233 status_t
1234 NodeManager::_AcquireNodeReference(media_node_id id, team_id team)
1235 {
1236 	TRACE("NodeManager::_AcquireNodeReference enter: node %ld, team %ld\n", id,
1237 		team);
1238 
1239 	BAutolock _(this);
1240 
1241 	NodeMap::iterator found = fNodeMap.find(id);
1242 	if (found == fNodeMap.end()) {
1243 		ERROR("NodeManager::_AcquireNodeReference: node %ld not found\n", id);
1244 		return B_ERROR;
1245 	}
1246 
1247 	registered_node& node = found->second;
1248 
1249 	TeamCountMap::iterator teamRef = node.team_ref_count.find(team);
1250 	if (teamRef == node.team_ref_count.end()) {
1251 		// This is the team's first reference
1252 		try {
1253 			node.team_ref_count.insert(std::make_pair(team, 1));
1254 		} catch (std::bad_alloc& exception) {
1255 			return B_NO_MEMORY;
1256 		}
1257 	} else {
1258 		// Just increase its ref count
1259 		teamRef->second++;
1260 	}
1261 
1262 	node.ref_count++;
1263 
1264 	TRACE("NodeManager::_AcquireNodeReference leave: node %ld, team %ld, "
1265 		"ref %ld, team ref %ld\n", id, team, node.ref_count,
1266 		node.team_ref_count.find(team)->second);
1267 	return B_OK;
1268 }
1269