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
50const char*
51get_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
74NodeManager::NodeManager()
75	:
76	BLocker("node manager"),
77	fNextAddOnID(1),
78	fNextNodeID(1),
79	fDefaultManager(new DefaultManager)
80{
81}
82
83
84NodeManager::~NodeManager()
85{
86	delete fDefaultManager;
87}
88
89
90// #pragma mark - Default node management
91
92
93status_t
94NodeManager::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
123status_t
124NodeManager::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
132status_t
133NodeManager::RescanDefaultNodes()
134{
135	BAutolock _(this);
136	return fDefaultManager->Rescan();
137}
138
139
140// #pragma mark - Live node management
141
142
143status_t
144NodeManager::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
178status_t
179NodeManager::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
218status_t
219NodeManager::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
284status_t
285NodeManager::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
315status_t
316NodeManager::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
341status_t
342NodeManager::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*/
379status_t
380NodeManager::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
412status_t
413NodeManager::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
427status_t
428NodeManager::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
454status_t
455NodeManager::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
481status_t
482NodeManager::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
523status_t
524NodeManager::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
564status_t
565NodeManager::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
592status_t
593NodeManager::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
614status_t
615NodeManager::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*/
705status_t
706NodeManager::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
725void
726NodeManager::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
744void
745NodeManager::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
756status_t
757NodeManager::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.
774status_t
775NodeManager::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
835void
836NodeManager::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
858void
859NodeManager::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
878status_t
879NodeManager::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
923status_t
924NodeManager::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
958void
959NodeManager::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
971status_t
972NodeManager::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
1042status_t
1043NodeManager::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
1066void
1067NodeManager::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
1139status_t
1140NodeManager::LoadState()
1141{
1142	BAutolock _(this);
1143	return fDefaultManager->LoadState();
1144}
1145
1146status_t
1147NodeManager::SaveState()
1148{
1149	BAutolock _(this);
1150	return fDefaultManager->SaveState(this);
1151}
1152
1153
1154void
1155NodeManager::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
1266status_t
1267NodeManager::_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