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