xref: /haiku/src/kits/media/MediaRoster.cpp (revision b84955d416f92e89c9d73999f545a2dec353d988)
1 /*
2  * Copyright 2015 Dario Casalinuovo
3  * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2008 Maurice Kalinowski, haiku@kaldience.com
5  *
6  * All rights reserved. Distributed under the terms of the MIT License.
7  */
8 
9 /*
10  * Copyright (c) 2002-2006 Marcus Overhagen <Marcus@Overhagen.de>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining
13  * a copy of this software and associated documentation files or portions
14  * thereof (the "Software"), to deal in the Software without restriction,
15  * including without limitation the rights to use, copy, modify, merge,
16  * publish, distribute, sublicense, and/or sell copies of the Software,
17  * and to permit persons to whom the Software is furnished to do so, subject
18  * to the following conditions:
19  *
20  *  * Redistributions of source code must retain the above copyright notice,
21  *    this list of conditions and the following disclaimer.
22  *
23  *  * Redistributions in binary form must reproduce the above copyright notice
24  *    in the  binary, as well as this list of conditions and the following
25  *    disclaimer in the documentation and/or other materials provided with
26  *    the distribution.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34  * THE SOFTWARE.
35  */
36 
37 
38 /* to comply with the license above, do not remove the following line */
39 char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002-2006 Marcus "
40 	"Overhagen <Marcus@Overhagen.de>";
41 
42 
43 #include <MediaRoster.h>
44 
45 #include <Application.h>
46 #include <Autolock.h>
47 #include <BufferConsumer.h>
48 #include <BufferProducer.h>
49 #include <Locker.h>
50 #include <Message.h>
51 #include <Messenger.h>
52 #include <MimeType.h>
53 #include <OS.h>
54 #include <ParameterWeb.h>
55 #include <Roster.h>
56 #include <StopWatch.h>
57 #include <String.h>
58 #include <TimeSource.h>
59 
60 #include <new>
61 
62 #include <AppMisc.h>
63 #include <DataExchange.h>
64 #include <MediaDebug.h>
65 #include <DormantNodeManager.h>
66 #include <MediaMisc.h>
67 #include <MediaRosterEx.h>
68 #include <Notifications.h>
69 #include <ServerInterface.h>
70 #include <SharedBufferList.h>
71 #include <TList.h>
72 
73 #include "TimeSourceObjectManager.h"
74 
75 
76 namespace BPrivate {
77 namespace media {
78 
79 
80 struct RosterNotification {
81 	BMessenger	messenger;
82 	int32		what;
83 };
84 
85 
86 struct SyncedMessage {
87 	BMessage* message;
88 };
89 
90 
91 struct LocalNode {
92 				LocalNode(BMediaNode* local_node)
93 					:
94 					node(local_node) {}
95 
96 				LocalNode()
97 					:
98 					node(NULL) {}
99 
100 	bool 		operator==(const LocalNode& a)
101 				{
102 					if (a.node == this->node)
103 						return true;
104 					return false;
105 				}
106 
107 	BMediaNode* node;
108 };
109 
110 
111 static bool sServerIsUp = false;
112 static List<RosterNotification> sNotificationList;
113 static BLocker sInitLocker("BMediaRoster::Roster locker");
114 static List<LocalNode> sRegisteredNodes;
115 
116 
117 class MediaRosterUndertaker {
118 public:
119 	~MediaRosterUndertaker()
120 	{
121 		BAutolock _(sInitLocker);
122 		if (BMediaRoster::CurrentRoster() != NULL) {
123 
124 			// Detect any forgotten node
125 			if (sRegisteredNodes.CountItems() > 0) {
126 				for (int32 i = 0; i < sRegisteredNodes.CountItems(); i++) {
127 					LocalNode* node = NULL;
128 					sRegisteredNodes.Get(i, &node);
129 					if (node != NULL) {
130 						ERROR("BMediaRoster: Node with ID %" B_PRId32
131 							" was not released correctly\n", node->node->ID());
132 					}
133 				}
134 			}
135 
136 			if (be_app != NULL)
137 				be_app->UnregisterLooper(BMediaRoster::CurrentRoster());
138 
139 			status_t err = B_ERROR;
140 			thread_id roster = BMediaRoster::CurrentRoster()->Thread();
141 
142 			BMediaRoster::CurrentRoster()->PostMessage(B_QUIT_REQUESTED);
143 
144 			wait_for_thread(roster, &err);
145 			if (err != B_OK)
146 				ERROR("BMediaRoster: wait_for_thread returned error");
147 		}
148 	}
149 };
150 
151 
152 static MediaRosterUndertaker sMediaRosterUndertaker;
153 
154 }	// namespace media
155 }	// namespace BPrivate
156 
157 using namespace BPrivate::media;
158 
159 
160 BMediaRosterEx::BMediaRosterEx(status_t* _error)
161 	:
162 	BMediaRoster(),
163 	fLaunchNotification(false),
164 	fAutoExit(false)
165 {
166 	gDormantNodeManager = new DormantNodeManager();
167 	gTimeSourceObjectManager = new TimeSourceObjectManager();
168 
169 	*_error = BuildConnections();
170 
171 	InitRosterDataExchange(BMessenger(this, this));
172 
173 	if (be_roster->StartWatching(BMessenger(this, this),
174 			B_REQUEST_LAUNCHED | B_REQUEST_QUIT) != B_OK) {
175 		*_error = B_ERROR;
176 	}
177 	sServerIsUp = BMediaRoster::IsRunning();
178 }
179 
180 
181 void
182 BMediaRosterEx::Quit()
183 {
184 	if (be_roster->StopWatching(BMessenger(this, this)) != B_OK)
185 			TRACE("Can't unregister roster notifications");
186 
187 	if (sNotificationList.CountItems() != 0)
188 		sNotificationList.MakeEmpty();
189 
190 	BMediaRoster::Quit();
191 }
192 
193 
194 status_t
195 BMediaRosterEx::BuildConnections()
196 {
197 	InitServerDataExchange();
198 	// register this application with the media server
199 	server_register_app_request request;
200 	server_register_app_reply reply;
201 	request.team = BPrivate::current_team();
202 	request.messenger = BMessenger(NULL, this);
203 	status_t status = QueryServer(SERVER_REGISTER_APP, &request,
204 		sizeof(request), &reply, sizeof(reply));
205 	if (status != B_OK)
206 		return B_MEDIA_SYSTEM_FAILURE;
207 
208 	return B_OK;
209 }
210 
211 
212 BMediaRosterEx::~BMediaRosterEx()
213 {
214 	CALLED();
215 
216 	delete gTimeSourceObjectManager;
217 	delete gDormantNodeManager;
218 
219 	// unregister this application with the media server
220 	server_unregister_app_request request;
221 	server_unregister_app_reply reply;
222 	request.team = BPrivate::current_team();
223 	QueryServer(SERVER_UNREGISTER_APP, &request, sizeof(request), &reply,
224 		sizeof(reply));
225 
226 	BPrivate::SharedBufferList::Invalidate();
227 }
228 
229 
230 void
231 BMediaRosterEx::RegisterLocalNode(BMediaNode* node)
232 {
233 	sRegisteredNodes.Insert(LocalNode(node));
234 }
235 
236 
237 void
238 BMediaRosterEx::UnregisterLocalNode(BMediaNode* node)
239 {
240 	int32 index = sRegisteredNodes.Find(LocalNode(node));
241 	if (index != -1)
242 		sRegisteredNodes.Remove(index);
243 }
244 
245 
246 void
247 BMediaRosterEx::EnableLaunchNotification(bool enable, bool autoExit)
248 {
249 	// NOTE: in theory, we should personalize it depending on each
250 	// request, but we are using it just in launch/shutdown_media_server,
251 	// so we are enough safe to don't care about that.
252 	fLaunchNotification = enable;
253 	fAutoExit = autoExit;
254 }
255 
256 
257 status_t
258 BMediaRosterEx::SaveNodeConfiguration(BMediaNode* node)
259 {
260 	int32 flavorID;
261 	BMediaAddOn* addon = node->AddOn(&flavorID);
262 	if (addon == NULL) {
263 		// NOTE: This node could have been created by an application,
264 		// it does not mean there is an error.
265 		// TODO: this check incorrectly triggers on BeOS R5 BT848 node
266 		TRACE("BMediaRosterEx::SaveNodeConfiguration node %" B_PRId32 " not "
267 			"instantiated from BMediaAddOn!\n", node->ID());
268 		return B_ERROR;
269 	}
270 
271 	media_addon_id addonID = addon->AddonID();
272 
273 	// TODO: fix this
274 	printf("### BMediaRosterEx::SaveNodeConfiguration should save addon-id "
275 		"%" B_PRId32 ", flavor-id %" B_PRId32 " config NOW!\n", addonID,
276 		flavorID);
277 	return B_OK;
278 }
279 
280 
281 status_t
282 BMediaRosterEx::LoadNodeConfiguration(media_addon_id addonID, int32 flavorID,
283 	BMessage *_msg)
284 {
285 	// TODO: fix this
286 	_msg->MakeEmpty(); // to be fully R5 compliant
287 	printf("### BMediaRosterEx::LoadNodeConfiguration should load addon-id "
288 		"%" B_PRId32 ", flavor-id %" B_PRId32 " config NOW!\n", addonID,
289 		flavorID);
290 	return B_OK;
291 }
292 
293 
294 status_t
295 BMediaRosterEx::IncrementAddonFlavorInstancesCount(media_addon_id addonID,
296 	int32 flavorID)
297 {
298 	server_change_flavor_instances_count_request request;
299 	server_change_flavor_instances_count_reply reply;
300 
301 	request.add_on_id = addonID;
302 	request.flavor_id = flavorID;
303 	request.delta = 1;
304 	request.team = BPrivate::current_team();
305 	return QueryServer(SERVER_CHANGE_FLAVOR_INSTANCES_COUNT, &request,
306 		sizeof(request), &reply, sizeof(reply));
307 }
308 
309 
310 status_t
311 BMediaRosterEx::DecrementAddonFlavorInstancesCount(media_addon_id addonID,
312 	int32 flavorID)
313 {
314 	server_change_flavor_instances_count_request request;
315 	server_change_flavor_instances_count_reply reply;
316 
317 	request.add_on_id = addonID;
318 	request.flavor_id = flavorID;
319 	request.delta = -1;
320 	request.team = BPrivate::current_team();
321 	return QueryServer(SERVER_CHANGE_FLAVOR_INSTANCES_COUNT, &request,
322 		sizeof(request), &reply, sizeof(reply));
323 }
324 
325 
326 status_t
327 BMediaRosterEx::ReleaseNodeAll(const media_node& node)
328 {
329 		CALLED();
330 	if (IS_INVALID_NODE(node))
331 		return B_MEDIA_BAD_NODE;
332 
333 	if (node.kind & NODE_KIND_NO_REFCOUNTING)
334 		return B_OK;
335 
336 	server_release_node_request request;
337 	server_release_node_reply reply;
338 	status_t rv;
339 
340 	request.node = node;
341 	request.team = BPrivate::current_team();
342 
343 	TRACE("BMediaRoster::ReleaseNodeAll, node %" B_PRId32 ", port %" B_PRId32
344 		", team %" B_PRId32 "\n",
345 		node.node, node.port, BPrivate::current_team());
346 
347 	rv = QueryServer(SERVER_RELEASE_NODE_ALL, &request, sizeof(request), &reply,
348 		sizeof(reply));
349 	if (rv != B_OK) {
350 		ERROR("BMediaRoster::ReleaseNodeAll failed to query media_server, "
351 			"retrying local, node %" B_PRId32 ", port %"
352 			B_PRId32 ", team %" B_PRId32 "!\n", node.node, node.port,
353 			BPrivate::current_team());
354 		node_final_release_command command;
355 		rv = SendToPort(node.port, NODE_FINAL_RELEASE, &command,
356 			sizeof(command));
357 		if (rv != B_OK) {
358 			ERROR("BMediaRoster::ReleaseNodeAll FAILED, node %" B_PRId32 ", port %"
359 				B_PRId32 ", team %" B_PRId32 "!\n", node.node, node.port,
360 				BPrivate::current_team());
361 		}
362 	}
363 	return rv;
364 }
365 
366 
367 status_t
368 BMediaRosterEx::SetNodeCreator(media_node_id node, team_id creator)
369 {
370 	server_set_node_creator_request request;
371 	server_set_node_creator_reply reply;
372 
373 	request.node = node;
374 	request.creator = creator;
375 	return QueryServer(SERVER_SET_NODE_CREATOR, &request, sizeof(request),
376 		&reply, sizeof(reply));
377 }
378 
379 
380 status_t
381 BMediaRosterEx::GetNode(node_type type, media_node* out_node,
382 	int32* out_input_id, BString* out_input_name)
383 {
384 	if (out_node == NULL)
385 		return B_BAD_VALUE;
386 
387 	server_get_node_request request;
388 	server_get_node_reply reply;
389 	status_t rv;
390 
391 	request.type = type;
392 	request.team = BPrivate::current_team();
393 	rv = QueryServer(SERVER_GET_NODE, &request, sizeof(request), &reply,
394 		sizeof(reply));
395 	if (rv != B_OK)
396 		return rv;
397 
398 	*out_node = reply.node;
399 	if (out_input_id)
400 		*out_input_id = reply.input_id;
401 	if (out_input_name)
402 		*out_input_name = reply.input_name;
403 	return rv;
404 }
405 
406 
407 status_t
408 BMediaRosterEx::SetNode(node_type type, const media_node* node,
409 	const dormant_node_info* info, const media_input* input)
410 {
411 	server_set_node_request request;
412 	server_set_node_reply reply;
413 
414 	request.type = type;
415 	request.use_node = node != NULL;
416 	if (node != NULL)
417 		request.node = *node;
418 	request.use_dni = info != NULL;
419 	if (info != NULL)
420 		request.dni = *info;
421 	request.use_input = input != NULL;
422 	if (input != NULL)
423 		request.input = *input;
424 
425 	return QueryServer(SERVER_SET_NODE, &request, sizeof(request), &reply,
426 		sizeof(reply));
427 }
428 
429 
430 status_t
431 BMediaRosterEx::GetAllOutputs(const media_node& node, List<media_output>* list)
432 {
433 	int32 cookie;
434 	status_t rv;
435 	status_t result;
436 
437 	PRINT(4, "BMediaRosterEx::GetAllOutputs() node %" B_PRId32 ", port %"
438 		B_PRId32 "\n", node.node, node.port);
439 
440 	if (!(node.kind & B_BUFFER_PRODUCER)) {
441 		ERROR("BMediaRosterEx::GetAllOutputs: node %" B_PRId32 " is not a "
442 			"B_BUFFER_PRODUCER\n", node.node);
443 		return B_MEDIA_BAD_NODE;
444 	}
445 
446 	result = B_OK;
447 	cookie = 0;
448 	list->MakeEmpty();
449 	for (;;) {
450 		producer_get_next_output_request request;
451 		producer_get_next_output_reply reply;
452 		request.cookie = cookie;
453 		rv = QueryPort(node.port, PRODUCER_GET_NEXT_OUTPUT, &request,
454 			sizeof(request), &reply, sizeof(reply));
455 		if (rv != B_OK)
456 			break;
457 		cookie = reply.cookie;
458 		if (!list->Insert(reply.output)) {
459 			ERROR("GetAllOutputs: list->Insert failed\n");
460 			result = B_ERROR;
461 		}
462 		#if DEBUG >= 3
463 			PRINT(3," next cookie %" B_PRId32 ", ", cookie);
464 			PRINT_OUTPUT("output ", reply.output);
465 		#endif
466 	}
467 
468 	producer_dispose_output_cookie_request request;
469 	producer_dispose_output_cookie_reply reply;
470 	QueryPort(node.port, PRODUCER_DISPOSE_OUTPUT_COOKIE, &request,
471 		sizeof(request), &reply, sizeof(reply));
472 
473 	return result;
474 }
475 
476 
477 status_t
478 BMediaRosterEx::GetAllOutputs(BBufferProducer* node, List<media_output>* list)
479 {
480 	int32 cookie;
481 	status_t result;
482 
483 	PRINT(4, "BMediaRosterEx::GetAllOutputs() (by pointer) node %" B_PRId32
484 		", port %" B_PRId32 "\n", node->ID(), node->ControlPort());
485 
486 	result = B_OK;
487 	cookie = 0;
488 	list->MakeEmpty();
489 	for (;;) {
490 		media_output output;
491 		if (B_OK != node->GetNextOutput(&cookie, &output))
492 			break;
493 		if (!list->Insert(output)) {
494 			ERROR("GetAllOutputs: list->Insert failed\n");
495 			result = B_ERROR;
496 		}
497 		#if DEBUG >= 3
498 			PRINT(3," next cookie %" B_PRId32 ", ", cookie);
499 			PRINT_OUTPUT("output ", output);
500 		#endif
501 	}
502 	node->DisposeOutputCookie(cookie);
503 	return result;
504 }
505 
506 
507 status_t
508 BMediaRosterEx::GetAllInputs(const media_node& node, List<media_input>* list)
509 {
510 	int32 cookie;
511 	status_t rv;
512 	status_t result;
513 
514 	PRINT(4, "BMediaRosterEx::GetAllInputs() node %" B_PRId32 ", port %"
515 		B_PRId32 "\n", node.node, node.port);
516 
517 	if (!(node.kind & B_BUFFER_CONSUMER)) {
518 		ERROR("BMediaRosterEx::GetAllInputs: node %" B_PRId32 " is not a "
519 			"B_BUFFER_CONSUMER\n", node.node);
520 		return B_MEDIA_BAD_NODE;
521 	}
522 
523 	result = B_OK;
524 	cookie = 0;
525 	list->MakeEmpty();
526 	for (;;) {
527 		consumer_get_next_input_request request;
528 		consumer_get_next_input_reply reply;
529 		request.cookie = cookie;
530 		rv = QueryPort(node.port, CONSUMER_GET_NEXT_INPUT, &request,
531 			sizeof(request), &reply, sizeof(reply));
532 		if (rv != B_OK)
533 			break;
534 		cookie = reply.cookie;
535 		if (!list->Insert(reply.input)) {
536 			ERROR("GetAllInputs: list->Insert failed\n");
537 			result = B_ERROR;
538 		}
539 		#if DEBUG >= 3
540 			PRINT(3," next cookie %" B_PRId32 ", ", cookie);
541 			PRINT_OUTPUT("input ", reply.input);
542 		#endif
543 	}
544 
545 	consumer_dispose_input_cookie_request request;
546 	consumer_dispose_input_cookie_reply reply;
547 	QueryPort(node.port, CONSUMER_DISPOSE_INPUT_COOKIE, &request,
548 		sizeof(request), &reply, sizeof(reply));
549 
550 	return result;
551 }
552 
553 
554 status_t
555 BMediaRosterEx::GetAllInputs(BBufferConsumer* node, List<media_input>* list)
556 {
557 	int32 cookie;
558 	status_t result;
559 
560 	PRINT(4, "BMediaRosterEx::GetAllInputs() (by pointer) node %" B_PRId32
561 		", port %" B_PRId32 "\n", node->ID(), node->ControlPort());
562 
563 	result = B_OK;
564 	cookie = 0;
565 	list->MakeEmpty();
566 	for (;;) {
567 		media_input input;
568 		if (B_OK != node->GetNextInput(&cookie, &input))
569 			break;
570 		if (!list->Insert(input)) {
571 			ERROR("GetAllInputs: list->Insert failed\n");
572 			result = B_ERROR;
573 		}
574 		#if DEBUG >= 3
575 			PRINT(3," next cookie %" B_PRId32 ", ", cookie);
576 			PRINT_INPUT("input ", input);
577 		#endif
578 	}
579 	node->DisposeInputCookie(cookie);
580 	return result;
581 }
582 
583 
584 status_t
585 BMediaRosterEx::PublishOutputs(const media_node& node, List<media_output>* list)
586 {
587 	server_publish_outputs_request request;
588 	server_publish_outputs_reply reply;
589 	media_output* output;
590 	media_output* outputs;
591 	int32 count;
592 	status_t rv;
593 
594 	count = list->CountItems();
595 	TRACE("PublishOutputs: publishing %" B_PRId32 "\n", count);
596 
597 	request.node = node;
598 	request.count = count;
599 	if (count > MAX_OUTPUTS) {
600 		void *start_addr;
601 		size_t size;
602 		size = ROUND_UP_TO_PAGE(count * sizeof(media_output));
603 		request.area = create_area("publish outputs", &start_addr,
604 			B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
605 		if (request.area < B_OK) {
606 			ERROR("PublishOutputs: failed to create area, %#" B_PRIx32 "\n",
607 				request.area);
608 			return (status_t)request.area;
609 		}
610 		outputs = static_cast<media_output *>(start_addr);
611 	} else {
612 		request.area = -1;
613 		outputs = request.outputs;
614 	}
615 	TRACE("PublishOutputs: area %" B_PRId32 "\n", request.area);
616 
617 	int i;
618 	for (i = 0, list->Rewind(); list->GetNext(&output); i++) {
619 		ASSERT(i < count);
620 		outputs[i] = *output;
621 	}
622 
623 	rv = QueryServer(SERVER_PUBLISH_OUTPUTS, &request, sizeof(request),
624 		&reply, sizeof(reply));
625 
626 	if (request.area != -1)
627 		delete_area(request.area);
628 
629 	return rv;
630 }
631 
632 
633 status_t
634 BMediaRosterEx::PublishInputs(const media_node& node, List<media_input>* list)
635 {
636 	server_publish_inputs_request request;
637 	server_publish_inputs_reply reply;
638 	media_input* input;
639 	media_input* inputs;
640 	int32 count;
641 	status_t rv;
642 
643 	count = list->CountItems();
644 	TRACE("PublishInputs: publishing %" B_PRId32 "\n", count);
645 
646 	request.node = node;
647 	request.count = count;
648 	if (count > MAX_INPUTS) {
649 		void* start_addr;
650 		size_t size;
651 		size = ROUND_UP_TO_PAGE(count * sizeof(media_input));
652 		request.area = create_area("publish inputs", &start_addr,
653 			B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
654 		if (request.area < B_OK) {
655 			ERROR("PublishInputs: failed to create area, %#" B_PRIx32 "\n",
656 				request.area);
657 			return (status_t)request.area;
658 		}
659 		inputs = static_cast<media_input *>(start_addr);
660 	} else {
661 		request.area = -1;
662 		inputs = request.inputs;
663 	}
664 	TRACE("PublishInputs: area %" B_PRId32 "\n", request.area);
665 
666 	int i;
667 	for (i = 0, list->Rewind(); list->GetNext(&input); i++) {
668 		ASSERT(i < count);
669 		inputs[i] = *input;
670 	}
671 
672 	rv = QueryServer(SERVER_PUBLISH_INPUTS, &request, sizeof(request),
673 		&reply, sizeof(reply));
674 
675 	if (request.area != -1)
676 		delete_area(request.area);
677 
678 	return rv;
679 }
680 
681 
682 BTimeSource*
683 BMediaRosterEx::MakeTimeSourceObject(media_node_id timeSourceID)
684 {
685 	media_node clone;
686 	status_t status = GetNodeFor(timeSourceID, &clone);
687 	if (status != B_OK) {
688 		ERROR("BMediaRosterEx::MakeTimeSourceObject: GetNodeFor failed: %s\n",
689 			strerror(status));
690 		return NULL;
691 	}
692 
693 	BTimeSource* source = gTimeSourceObjectManager->GetTimeSource(clone);
694 	if (source == NULL) {
695 		ERROR("BMediaRosterEx::MakeTimeSourceObject: GetTimeSource failed\n");
696 		return NULL;
697 	}
698 
699 	// TODO: release?
700 	ReleaseNode(clone);
701 
702 	return source;
703 }
704 
705 
706 //	#pragma mark - public BMediaRoster
707 
708 
709 status_t
710 BMediaRoster::GetVideoInput(media_node* _node)
711 {
712 	CALLED();
713 	return MediaRosterEx(this)->GetNode(VIDEO_INPUT, _node);
714 }
715 
716 
717 status_t
718 BMediaRoster::GetAudioInput(media_node* _node)
719 {
720 	CALLED();
721 	return MediaRosterEx(this)->GetNode(AUDIO_INPUT, _node);
722 }
723 
724 
725 status_t
726 BMediaRoster::GetVideoOutput(media_node* _node)
727 {
728 	CALLED();
729 	return MediaRosterEx(this)->GetNode(VIDEO_OUTPUT, _node);
730 }
731 
732 
733 status_t
734 BMediaRoster::GetAudioMixer(media_node* _node)
735 {
736 	CALLED();
737 	return MediaRosterEx(this)->GetNode(AUDIO_MIXER, _node);
738 }
739 
740 
741 status_t
742 BMediaRoster::GetAudioOutput(media_node* _node)
743 {
744 	CALLED();
745 	return MediaRosterEx(this)->GetNode(AUDIO_OUTPUT, _node);
746 }
747 
748 
749 status_t
750 BMediaRoster::GetAudioOutput(media_node* _node, int32* _inputID,
751 	BString* _inputName)
752 {
753 	CALLED();
754 	return MediaRosterEx(this)->GetNode(AUDIO_OUTPUT_EX, _node, _inputID,
755 		_inputName);
756 }
757 
758 
759 status_t
760 BMediaRoster::GetTimeSource(media_node* _node)
761 {
762 	CALLED();
763 	status_t rv;
764 
765 	// TODO: need to do this in a nicer way.
766 
767 	rv = MediaRosterEx(this)->GetNode(TIME_SOURCE, _node);
768 	if (rv != B_OK)
769 		return rv;
770 
771 	// We don't do reference counting for timesources, that's why we
772 	// release the node immediately.
773 	ReleaseNode(*_node);
774 
775 	// we need to remember to not use this node with server side reference counting
776 	_node->kind |= NODE_KIND_NO_REFCOUNTING;
777 	return B_OK;
778 }
779 
780 
781 status_t
782 BMediaRoster::SetVideoInput(const media_node& producer)
783 {
784 	CALLED();
785 	return MediaRosterEx(this)->SetNode(VIDEO_INPUT, &producer);
786 }
787 
788 
789 status_t
790 BMediaRoster::SetVideoInput(const dormant_node_info& producer)
791 {
792 	CALLED();
793 	return MediaRosterEx(this)->SetNode(VIDEO_INPUT, NULL, &producer);
794 }
795 
796 
797 status_t
798 BMediaRoster::SetAudioInput(const media_node& producer)
799 {
800 	CALLED();
801 	return MediaRosterEx(this)->SetNode(AUDIO_INPUT, &producer);
802 }
803 
804 
805 status_t
806 BMediaRoster::SetAudioInput(const dormant_node_info& producer)
807 {
808 	CALLED();
809 	return MediaRosterEx(this)->SetNode(AUDIO_INPUT, NULL, &producer);
810 }
811 
812 
813 status_t
814 BMediaRoster::SetVideoOutput(const media_node& consumer)
815 {
816 	CALLED();
817 	return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, &consumer);
818 }
819 
820 
821 status_t
822 BMediaRoster::SetVideoOutput(const dormant_node_info& consumer)
823 {
824 	CALLED();
825 	return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, NULL, &consumer);
826 }
827 
828 
829 status_t
830 BMediaRoster::SetAudioOutput(const media_node& consumer)
831 {
832 	CALLED();
833 	return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, &consumer);
834 }
835 
836 
837 status_t
838 BMediaRoster::SetAudioOutput(const media_input& input)
839 {
840 	CALLED();
841 	return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, NULL, &input);
842 }
843 
844 
845 status_t
846 BMediaRoster::SetAudioOutput(const dormant_node_info& consumer)
847 {
848 	CALLED();
849 	return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, &consumer);
850 }
851 
852 
853 status_t
854 BMediaRoster::GetNodeFor(media_node_id node, media_node* clone)
855 {
856 	CALLED();
857 	if (clone == NULL)
858 		return B_BAD_VALUE;
859 	if (IS_INVALID_NODEID(node))
860 		return B_MEDIA_BAD_NODE;
861 
862 	server_get_node_for_request request;
863 	server_get_node_for_reply reply;
864 	status_t rv;
865 
866 	request.node_id = node;
867 	request.team = BPrivate::current_team();
868 
869 	rv = QueryServer(SERVER_GET_NODE_FOR, &request, sizeof(request), &reply,
870 		sizeof(reply));
871 	if (rv != B_OK)
872 		return rv;
873 
874 	*clone = reply.clone;
875 	return B_OK;
876 }
877 
878 
879 status_t
880 BMediaRoster::GetSystemTimeSource(media_node* clone)
881 {
882 	CALLED();
883 	status_t rv;
884 
885 	// TODO: need to do this in a nicer way.
886 
887 	rv = MediaRosterEx(this)->GetNode(SYSTEM_TIME_SOURCE, clone);
888 	if (rv != B_OK)
889 		return rv;
890 
891 	// We don't do reference counting for timesources, that's why we
892 	// release the node immediately.
893 	ReleaseNode(*clone);
894 
895 	// we need to remember to not use this node with server side reference
896 	// counting
897 	clone->kind |= NODE_KIND_NO_REFCOUNTING;
898 
899 	return B_OK;
900 }
901 
902 
903 status_t
904 BMediaRoster::ReleaseNode(const media_node& node)
905 {
906 	CALLED();
907 	if (IS_INVALID_NODE(node))
908 		return B_MEDIA_BAD_NODE;
909 
910 	if (node.kind & NODE_KIND_NO_REFCOUNTING) {
911 		TRACE("BMediaRoster::ReleaseNode, trying to release reference "
912 			"counting disabled timesource, node %" B_PRId32 ", port %" B_PRId32
913 			", team %" B_PRId32 "\n", node.node, node.port,
914 			BPrivate::current_team());
915 		return B_OK;
916 	}
917 
918 	server_release_node_request request;
919 	server_release_node_reply reply;
920 	status_t rv;
921 
922 	request.node = node;
923 	request.team = BPrivate::current_team();
924 
925 	TRACE("BMediaRoster::ReleaseNode, node %" B_PRId32 ", port %" B_PRId32
926 		", team %" B_PRId32 "\n", node.node, node.port,
927 		BPrivate::current_team());
928 
929 	rv = QueryServer(SERVER_RELEASE_NODE, &request, sizeof(request), &reply,
930 		sizeof(reply));
931 	if (rv != B_OK) {
932 		ERROR("BMediaRoster::ReleaseNode FAILED, node %" B_PRId32 ", port %"
933 			B_PRId32 ", team %" B_PRId32 "!\n", node.node, node.port,
934 			BPrivate::current_team());
935 	}
936 	return rv;
937 }
938 
939 
940 BTimeSource*
941 BMediaRoster::MakeTimeSourceFor(const media_node& forNode)
942 {
943 	// MakeTimeSourceFor() returns a BTimeSource object
944 	// corresponding to the specified node's time source.
945 
946 	CALLED();
947 
948 	if (IS_SYSTEM_TIMESOURCE(forNode)) {
949 		// special handling for the system time source
950 		TRACE("BMediaRoster::MakeTimeSourceFor, asked for system time "
951 			"source\n");
952 		return MediaRosterEx(this)->MakeTimeSourceObject(
953 			NODE_SYSTEM_TIMESOURCE_ID);
954 	}
955 
956 	if (IS_INVALID_NODE(forNode)) {
957 		ERROR("BMediaRoster::MakeTimeSourceFor: for_node invalid, node %"
958 			B_PRId32 ", port %" B_PRId32 ", kinds 0x%" B_PRIx32 "\n",
959 			forNode.node, forNode.port, forNode.kind);
960 		return NULL;
961 	}
962 
963 	TRACE("BMediaRoster::MakeTimeSourceFor: node %" B_PRId32 " enter\n",
964 		forNode.node);
965 
966 	node_get_timesource_request request;
967 	node_get_timesource_reply reply;
968 	BTimeSource *source;
969 	status_t rv;
970 
971 	// ask the node to get it's current timesource id
972 	rv = QueryPort(forNode.port, NODE_GET_TIMESOURCE, &request,
973 		sizeof(request), &reply, sizeof(reply));
974 	if (rv != B_OK) {
975 		ERROR("BMediaRoster::MakeTimeSourceFor: request failed\n");
976 		return NULL;
977 	}
978 
979 	source = MediaRosterEx(this)->MakeTimeSourceObject(reply.timesource_id);
980 
981 	TRACE("BMediaRoster::MakeTimeSourceFor: node %" B_PRId32 " leave\n",
982 		forNode.node);
983 
984 	return source;
985 }
986 
987 
988 status_t
989 BMediaRoster::Connect(const media_source& from, const media_destination& to,
990 	media_format* _format, media_output* _output, media_input* _input)
991 {
992 	return BMediaRoster::Connect(from, to, _format, _output, _input, 0);
993 }
994 
995 
996 status_t
997 BMediaRoster::Connect(const media_source& from, const media_destination& to,
998 	media_format* io_format, media_output* out_output, media_input* out_input,
999 	uint32 in_flags, void* _reserved)
1000 {
1001 	CALLED();
1002 	if (io_format == NULL || out_output == NULL || out_input == NULL)
1003 		return B_BAD_VALUE;
1004 	if (IS_INVALID_SOURCE(from)) {
1005 		ERROR("BMediaRoster::Connect: media_source invalid\n");
1006 		return B_MEDIA_BAD_SOURCE;
1007 	}
1008 	if (IS_INVALID_DESTINATION(to)) {
1009 		ERROR("BMediaRoster::Connect: media_destination invalid\n");
1010 		return B_MEDIA_BAD_DESTINATION;
1011 	}
1012 
1013 	status_t rv;
1014 
1015 	// find the output and input nodes
1016 	// TODO: isn't there a easier way?
1017 	media_node sourcenode;
1018 	media_node destnode;
1019 	rv = GetNodeFor(NodeIDFor(from.port), &sourcenode);
1020 	if (rv != B_OK) {
1021 		ERROR("BMediaRoster::Connect: Can't find source node for port %"
1022 			B_PRId32 "\n", from.port);
1023 		return B_MEDIA_BAD_SOURCE;
1024 	}
1025 	ReleaseNode(sourcenode);
1026 	rv = GetNodeFor(NodeIDFor(to.port), &destnode);
1027 	if (rv != B_OK) {
1028 		ERROR("BMediaRoster::Connect: Can't find destination node for port "
1029 			"%" B_PRId32 "\n", to.port);
1030 		return B_MEDIA_BAD_DESTINATION;
1031 	}
1032 	ReleaseNode(destnode);
1033 
1034 	if (!(sourcenode.kind & B_BUFFER_PRODUCER)) {
1035 		ERROR("BMediaRoster::Connect: source node %" B_PRId32 " is not a "
1036 			"B_BUFFER_PRODUCER\n", sourcenode.node);
1037 		return B_MEDIA_BAD_SOURCE;
1038 	}
1039 	if (!(destnode.kind & B_BUFFER_CONSUMER)) {
1040 		ERROR("BMediaRoster::Connect: destination node %" B_PRId32 " is not a "
1041 			"B_BUFFER_CONSUMER\n", destnode.node);
1042 		return B_MEDIA_BAD_DESTINATION;
1043 	}
1044 
1045 	producer_format_proposal_request request1;
1046 	producer_format_proposal_reply reply1;
1047 
1048 	PRINT_FORMAT("BMediaRoster::Connect calling "
1049 		"BBufferProducer::FormatProposal with format  ", *io_format);
1050 
1051 	// BBufferProducer::FormatProposal
1052 	request1.output = from;
1053 	request1.format = *io_format;
1054 	rv = QueryPort(from.port, PRODUCER_FORMAT_PROPOSAL, &request1,
1055 		sizeof(request1), &reply1, sizeof(reply1));
1056 	if (rv != B_OK) {
1057 		ERROR("BMediaRoster::Connect: aborted after "
1058 			"BBufferProducer::FormatProposal, status = %#" B_PRIx32 "\n",rv);
1059 		return rv;
1060 	}
1061 	// reply1.format now contains the format proposed by the producer
1062 
1063 	consumer_accept_format_request request2;
1064 	consumer_accept_format_reply reply2;
1065 
1066 	PRINT_FORMAT("BMediaRoster::Connect calling "
1067 		"BBufferConsumer::AcceptFormat with format    ", reply1.format);
1068 
1069 	// BBufferConsumer::AcceptFormat
1070 	request2.dest = to;
1071 	request2.format = reply1.format;
1072 	rv = QueryPort(to.port, CONSUMER_ACCEPT_FORMAT, &request2,
1073 		sizeof(request2), &reply2, sizeof(reply2));
1074 	if (rv != B_OK) {
1075 		ERROR("BMediaRoster::Connect: aborted after "
1076 			"BBufferConsumer::AcceptFormat, status = %#" B_PRIx32 "\n",rv);
1077 		return rv;
1078 	}
1079 	// reply2.format now contains the format accepted by the consumer
1080 
1081 	// BBufferProducer::PrepareToConnect
1082 	producer_prepare_to_connect_request request3;
1083 	producer_prepare_to_connect_reply reply3;
1084 
1085 	PRINT_FORMAT("BMediaRoster::Connect calling "
1086 		"BBufferProducer::PrepareToConnect with format", reply2.format);
1087 
1088 	request3.source = from;
1089 	request3.destination = to;
1090 	request3.format = reply2.format;
1091 	strcpy(request3.name, "XXX some default name"); // TODO: fix this
1092 	rv = QueryPort(from.port, PRODUCER_PREPARE_TO_CONNECT, &request3,
1093 		sizeof(request3), &reply3, sizeof(reply3));
1094 	if (rv != B_OK) {
1095 		ERROR("BMediaRoster::Connect: aborted after "
1096 			"BBufferProducer::PrepareToConnect, status = %#" B_PRIx32 "\n", rv);
1097 		return rv;
1098 	}
1099 	// reply3.format is still our pretty media format
1100 	// reply3.out_source the real source to be used for the connection
1101 	// reply3.name the name BBufferConsumer::Connected will see in the
1102 	// outInput->name argument
1103 
1104 	// BBufferConsumer::Connected
1105 	consumer_connected_request request4;
1106 	consumer_connected_reply reply4;
1107 	status_t con_status;
1108 
1109 	PRINT_FORMAT("BMediaRoster::Connect calling BBufferConsumer::Connected() "
1110 		"with format       ", reply3.format);
1111 
1112 	request4.input.node = destnode;
1113 	request4.input.source = reply3.out_source;
1114 	request4.input.destination = to;
1115 	request4.input.format = reply3.format;
1116 	strcpy(request4.input.name, reply3.name);
1117 
1118 	con_status = QueryPort(to.port, CONSUMER_CONNECTED, &request4,
1119 		sizeof(request4), &reply4, sizeof(reply4));
1120 	if (con_status != B_OK) {
1121 		ERROR("BMediaRoster::Connect: aborting after "
1122 			"BBufferConsumer::Connected, status = %#" B_PRIx32 "\n",
1123 			con_status);
1124 		// we do NOT return here!
1125 	}
1126 	// con_status contains the status code to be supplied to
1127 	// BBufferProducer::Connect's status argument
1128 	// reply4.input contains the media_input that describes the connection
1129 	// from the consumer point of view
1130 
1131 	// BBufferProducer::Connect
1132 	producer_connect_request request5;
1133 	producer_connect_reply reply5;
1134 
1135 	PRINT_FORMAT("BMediaRoster::Connect calling BBufferProducer::Connect with "
1136 		"format         ", reply4.input.format);
1137 
1138 	request5.error = con_status;
1139 	request5.source = reply3.out_source;
1140 	request5.destination = reply4.input.destination;
1141 	request5.format = reply4.input.format;
1142 	strcpy(request5.name, reply4.input.name);
1143 	rv = QueryPort(reply4.input.source.port, PRODUCER_CONNECT, &request5,
1144 		sizeof(request5), &reply5, sizeof(reply5));
1145 	if (con_status != B_OK) {
1146 		ERROR("BMediaRoster::Connect: aborted\n");
1147 		return con_status;
1148 	}
1149 	if (rv != B_OK) {
1150 		ERROR("BMediaRoster::Connect: aborted after BBufferProducer::Connect()"
1151 			", status = %#" B_PRIx32 "\n", rv);
1152 		return rv;
1153 	}
1154 	// reply5.name contains the name assigned to the connection by the producer
1155 
1156 	// initilize connection info
1157 	*io_format = reply4.input.format;
1158 	*out_input = reply4.input;
1159 	out_output->node = sourcenode;
1160 	out_output->source = reply4.input.source;
1161 	out_output->destination = reply4.input.destination;
1162 	out_output->format = reply4.input.format;
1163 	strcpy(out_output->name, reply5.name);
1164 
1165 	// the connection is now made
1166 	PRINT_FORMAT("   format", *io_format);
1167 	PRINT_INPUT("   input", *out_input);
1168 	PRINT_OUTPUT("   output", *out_output);
1169 
1170 	// TODO: register connection with server
1171 	// TODO: we should just send a notification, instead of republishing all
1172 	// endpoints
1173 	List<media_output> outlist;
1174 	List<media_input> inlist;
1175 	if (MediaRosterEx(this)->GetAllOutputs(out_output->node , &outlist) == B_OK)
1176 		MediaRosterEx(this)->PublishOutputs(out_output->node , &outlist);
1177 	if (MediaRosterEx(this)->GetAllInputs(out_input->node , &inlist) == B_OK)
1178 		MediaRosterEx(this)->PublishInputs(out_input->node, &inlist);
1179 
1180 	// TODO: if (mute) BBufferProducer::EnableOutput(false)
1181 	if (in_flags & B_CONNECT_MUTED) {
1182 	}
1183 
1184 	// send a notification
1185 	BPrivate::media::notifications::ConnectionMade(*out_input, *out_output,
1186 		*io_format);
1187 
1188 	return B_OK;
1189 };
1190 
1191 
1192 status_t
1193 BMediaRoster::Disconnect(media_node_id source_nodeid,
1194 	const media_source& source, media_node_id destination_nodeid,
1195 	const media_destination& destination)
1196 {
1197 	CALLED();
1198 	if (IS_INVALID_NODEID(source_nodeid)) {
1199 		ERROR("BMediaRoster::Disconnect: source media_node_id invalid\n");
1200 		return B_MEDIA_BAD_SOURCE;
1201 	}
1202 	if (IS_INVALID_NODEID(destination_nodeid)) {
1203 		ERROR("BMediaRoster::Disconnect: destination media_node_id invalid\n");
1204 		return B_MEDIA_BAD_DESTINATION;
1205 	}
1206 	if (IS_INVALID_SOURCE(source)) {
1207 		ERROR("BMediaRoster::Disconnect: media_source invalid\n");
1208 		return B_MEDIA_BAD_SOURCE;
1209 	}
1210 	if (IS_INVALID_DESTINATION(destination)) {
1211 		ERROR("BMediaRoster::Disconnect: media_destination invalid\n");
1212 		return B_MEDIA_BAD_DESTINATION;
1213 	}
1214 
1215 	producer_disconnect_request request2;
1216 	producer_disconnect_reply reply2;
1217 	consumer_disconnected_request request1;
1218 	consumer_disconnected_reply reply1;
1219 	status_t rv1, rv2;
1220 
1221 	// TODO: we should ask the server if this connection really exists
1222 
1223 	request1.source = source;
1224 	request1.destination = destination;
1225 	request2.source = source;
1226 	request2.destination = destination;
1227 
1228 	rv1 = QueryPort(source.port, PRODUCER_DISCONNECT, &request1,
1229 		sizeof(request1), &reply1, sizeof(reply1));
1230 	rv2 = QueryPort(destination.port, CONSUMER_DISCONNECTED, &request2,
1231 		sizeof(request2), &reply2, sizeof(reply2));
1232 
1233 	// TODO: unregister connection with server
1234 	// TODO: we should just send a notification, instead of republishing all
1235 	// endpoints
1236 	List<media_output> outlist;
1237 	List<media_input> inlist;
1238 	media_node sourcenode;
1239 	media_node destnode;
1240 	if (GetNodeFor(source_nodeid, &sourcenode) == B_OK) {
1241 		if (!(sourcenode.kind & B_BUFFER_PRODUCER)) {
1242 			ERROR("BMediaRoster::Disconnect: source_nodeid %" B_PRId32
1243 				" is not a B_BUFFER_PRODUCER\n", source_nodeid);
1244 		}
1245 		if (MediaRosterEx(this)->GetAllOutputs(sourcenode , &outlist) == B_OK)
1246 			MediaRosterEx(this)->PublishOutputs(sourcenode , &outlist);
1247 		ReleaseNode(sourcenode);
1248 	} else {
1249 		ERROR("BMediaRoster::Disconnect: GetNodeFor source_nodeid %" B_PRId32
1250 			" failed\n", source_nodeid);
1251 	}
1252 	if (GetNodeFor(destination_nodeid, &destnode) == B_OK) {
1253 		if (!(destnode.kind & B_BUFFER_CONSUMER)) {
1254 			ERROR("BMediaRoster::Disconnect: destination_nodeid %" B_PRId32
1255 				" is not a B_BUFFER_CONSUMER\n", destination_nodeid);
1256 		}
1257 		if (MediaRosterEx(this)->GetAllInputs(destnode , &inlist) == B_OK)
1258 			MediaRosterEx(this)->PublishInputs(destnode, &inlist);
1259 		ReleaseNode(destnode);
1260 	} else {
1261 		ERROR("BMediaRoster::Disconnect: GetNodeFor destination_nodeid %"
1262 			B_PRId32 " failed\n", destination_nodeid);
1263 	}
1264 
1265 	// send a notification
1266 	BPrivate::media::notifications::ConnectionBroken(source, destination);
1267 
1268 	return rv1 != B_OK || rv2 != B_OK ? B_ERROR : B_OK;
1269 }
1270 
1271 
1272 status_t
1273 BMediaRoster::Disconnect(const media_output& output, const media_input& input)
1274 {
1275 	if (IS_INVALID_NODEID(output.node.node)) {
1276 		printf("BMediaRoster::Disconnect: output.node.node %" B_PRId32
1277 			" invalid\n", output.node.node);
1278 		return B_MEDIA_BAD_SOURCE;
1279 	}
1280 	if (IS_INVALID_NODEID(input.node.node)) {
1281 		printf("BMediaRoster::Disconnect: input.node.node %" B_PRId32
1282 			" invalid\n", input.node.node);
1283 		return B_MEDIA_BAD_DESTINATION;
1284 	}
1285 	if (!(output.node.kind & B_BUFFER_PRODUCER)) {
1286 		printf("BMediaRoster::Disconnect: output.node.kind 0x%" B_PRIx32
1287 			" is no B_BUFFER_PRODUCER\n", output.node.kind);
1288 		return B_MEDIA_BAD_SOURCE;
1289 	}
1290 	if (!(input.node.kind & B_BUFFER_CONSUMER)) {
1291 		printf("BMediaRoster::Disconnect: input.node.kind 0x%" B_PRIx32
1292 			" is no B_BUFFER_PRODUCER\n", input.node.kind);
1293 		return B_MEDIA_BAD_DESTINATION;
1294 	}
1295 	if (input.source.port != output.source.port) {
1296 		printf("BMediaRoster::Disconnect: input.source.port %" B_PRId32
1297 			" doesn't match output.source.port %" B_PRId32 "\n",
1298 			input.source.port, output.source.port);
1299 		return B_MEDIA_BAD_SOURCE;
1300 	}
1301 	if (input.source.id != output.source.id) {
1302 		printf("BMediaRoster::Disconnect: input.source.id %" B_PRId32
1303 			" doesn't match output.source.id %" B_PRId32 "\n", input.source.id,
1304 			output.source.id);
1305 		return B_MEDIA_BAD_SOURCE;
1306 	}
1307 	if (input.destination.port != output.destination.port) {
1308 		printf("BMediaRoster::Disconnect: input.destination.port %" B_PRId32
1309 			" doesn't match output.destination.port %" B_PRId32 "\n",
1310 			input.destination.port, output.destination.port);
1311 		return B_MEDIA_BAD_DESTINATION;
1312 	}
1313 	if (input.destination.id != output.destination.id) {
1314 		printf("BMediaRoster::Disconnect: input.destination.id %" B_PRId32
1315 			" doesn't match output.destination.id %" B_PRId32 "\n",
1316 			input.destination.id, output.destination.id);
1317 		return B_MEDIA_BAD_DESTINATION;
1318 	}
1319 
1320 	return Disconnect(output.node.node, output.source, input.node.node,
1321 		input.destination);
1322 }
1323 
1324 
1325 status_t
1326 BMediaRoster::StartNode(const media_node& node, bigtime_t atPerformanceTime)
1327 {
1328 	CALLED();
1329 	if (node.node <= 0)
1330 		return B_MEDIA_BAD_NODE;
1331 
1332 	TRACE("BMediaRoster::StartNode, node %" B_PRId32 ", at perf %" B_PRId64
1333 		"\n", node.node, atPerformanceTime);
1334 
1335 	node_start_command command;
1336 	command.performance_time = atPerformanceTime;
1337 
1338 	return SendToPort(node.port, NODE_START, &command, sizeof(command));
1339 }
1340 
1341 
1342 status_t
1343 BMediaRoster::StopNode(const media_node& node, bigtime_t atPerformanceTime,
1344 	bool immediate)
1345 {
1346 	CALLED();
1347 	if (IS_INVALID_NODE(node))
1348 		return B_MEDIA_BAD_NODE;
1349 
1350 	TRACE("BMediaRoster::StopNode, node %" B_PRId32 ", at perf %" B_PRId64
1351 		" %s\n", node.node, atPerformanceTime, immediate ? "NOW" : "");
1352 
1353 	node_stop_command command;
1354 	command.performance_time = atPerformanceTime;
1355 	command.immediate = immediate;
1356 
1357 	return SendToPort(node.port, NODE_STOP, &command, sizeof(command));
1358 }
1359 
1360 
1361 status_t
1362 BMediaRoster::SeekNode(const media_node& node, bigtime_t toMediaTime,
1363 	bigtime_t atPerformanceTime)
1364 {
1365 	CALLED();
1366 	if (IS_INVALID_NODE(node))
1367 		return B_MEDIA_BAD_NODE;
1368 
1369 	TRACE("BMediaRoster::SeekNode, node %" B_PRId32 ", at perf %" B_PRId64
1370 		", to perf %" B_PRId64 "\n", node.node, atPerformanceTime, toMediaTime);
1371 
1372 	node_seek_command command;
1373 	command.media_time = toMediaTime;
1374 	command.performance_time = atPerformanceTime;
1375 
1376 	return SendToPort(node.port, NODE_SEEK, &command, sizeof(command));
1377 }
1378 
1379 
1380 status_t
1381 BMediaRoster::StartTimeSource(const media_node& node, bigtime_t atRealTime)
1382 {
1383 	CALLED();
1384 	if (IS_SYSTEM_TIMESOURCE(node)) {
1385 		// TODO: debug this
1386 		//ERROR("BMediaRoster::StartTimeSource node %" B_PRId32 " is system timesource\n", node.node);
1387 		return B_OK;
1388 	}
1389 //	if (IS_SHADOW_TIMESOURCE(node)) {
1390 //		// TODO: debug this
1391 //		ERROR("BMediaRoster::StartTimeSource node %" B_PRId32 " is shadow timesource\n", node.node);
1392 //		return B_OK;
1393 //	}
1394 	if (IS_INVALID_NODE(node)) {
1395 		ERROR("BMediaRoster::StartTimeSource node %" B_PRId32 " invalid\n",
1396 			node.node);
1397 		return B_MEDIA_BAD_NODE;
1398 	}
1399 	if ((node.kind & B_TIME_SOURCE) == 0) {
1400 		ERROR("BMediaRoster::StartTimeSource node %" B_PRId32
1401 			" is no timesource\n", node.node);
1402 		return B_MEDIA_BAD_NODE;
1403 	}
1404 
1405 	TRACE("BMediaRoster::StartTimeSource, node %" B_PRId32 ", at real %"
1406 		B_PRId64 "\n", node.node, atRealTime);
1407 
1408 	BTimeSource::time_source_op_info msg;
1409 	msg.op = BTimeSource::B_TIMESOURCE_START;
1410 	msg.real_time = atRealTime;
1411 
1412 	return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg));
1413 }
1414 
1415 
1416 status_t
1417 BMediaRoster::StopTimeSource(const media_node& node, bigtime_t atRealTime,
1418 	bool immediate)
1419 {
1420 	CALLED();
1421 	if (IS_SYSTEM_TIMESOURCE(node)) {
1422 		// TODO: debug this
1423 		//ERROR("BMediaRoster::StopTimeSource node %ld is system timesource\n", node.node);
1424 		return B_OK;
1425 	}
1426 //	if (IS_SHADOW_TIMESOURCE(node)) {
1427 //		// TODO: debug this
1428 //		ERROR("BMediaRoster::StopTimeSource node %ld is shadow timesource\n", node.node);
1429 //		return B_OK;
1430 //	}
1431 	if (IS_INVALID_NODE(node)) {
1432 		ERROR("BMediaRoster::StopTimeSource node %" B_PRId32 " invalid\n",
1433 			node.node);
1434 		return B_MEDIA_BAD_NODE;
1435 	}
1436 	if ((node.kind & B_TIME_SOURCE) == 0) {
1437 		ERROR("BMediaRoster::StopTimeSource node %" B_PRId32 " is no "
1438 			"timesource\n", node.node);
1439 		return B_MEDIA_BAD_NODE;
1440 	}
1441 
1442 	TRACE("BMediaRoster::StopTimeSource, node %" B_PRId32 ", at real %" B_PRId64
1443 		" %s\n", node.node, atRealTime, immediate ? "NOW" : "");
1444 
1445 	BTimeSource::time_source_op_info msg;
1446 	msg.op = immediate ? BTimeSource::B_TIMESOURCE_STOP_IMMEDIATELY
1447 		: BTimeSource::B_TIMESOURCE_STOP;
1448 	msg.real_time = atRealTime;
1449 
1450 	return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg));
1451 }
1452 
1453 
1454 status_t
1455 BMediaRoster::SeekTimeSource(const media_node& node,
1456 	bigtime_t toPerformanceTime, bigtime_t atRealTime)
1457 {
1458 	CALLED();
1459 	if (IS_SYSTEM_TIMESOURCE(node)) {
1460 		// TODO: debug this
1461 		// ERROR("BMediaRoster::SeekTimeSource node %ld is system timesource\n", node.node);
1462 		// you can't seek the system time source, but
1463 		// returning B_ERROR would break StampTV
1464 		return B_OK;
1465 	}
1466 //	if (IS_SHADOW_TIMESOURCE(node)) {
1467 //		// TODO: debug this
1468 //		ERROR("BMediaRoster::SeekTimeSource node %ld is shadow timesource\n", node.node);
1469 //		return B_OK;
1470 //	}
1471 	if (IS_INVALID_NODE(node)) {
1472 		ERROR("BMediaRoster::SeekTimeSource node %" B_PRId32 " invalid\n",
1473 			node.node);
1474 		return B_MEDIA_BAD_NODE;
1475 	}
1476 	if ((node.kind & B_TIME_SOURCE) == 0) {
1477 		ERROR("BMediaRoster::SeekTimeSource node %" B_PRId32
1478 			" is no timesource\n", node.node);
1479 		return B_MEDIA_BAD_NODE;
1480 	}
1481 
1482 	TRACE("BMediaRoster::SeekTimeSource, node %" B_PRId32 ", at real %" B_PRId64
1483 		", to perf %" B_PRId64 "\n", node.node, atRealTime, toPerformanceTime);
1484 
1485 	BTimeSource::time_source_op_info msg;
1486 	msg.op = BTimeSource::B_TIMESOURCE_SEEK;
1487 	msg.real_time = atRealTime;
1488 	msg.performance_time = toPerformanceTime;
1489 
1490 	return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg));
1491 }
1492 
1493 
1494 status_t
1495 BMediaRoster::SyncToNode(const media_node& node, bigtime_t atTime,
1496 	bigtime_t timeout)
1497 {
1498 	TRACE("BMediaRoster::SyncToNode, node %" B_PRId32 ", at real %" B_PRId64
1499 		", at timeout %" B_PRId64 "\n", node.node, atTime, timeout);
1500 	if (IS_INVALID_NODE(node))
1501 		return B_MEDIA_BAD_NODE;
1502 
1503 	port_id waitPort = create_port(1, "SyncToNode wait port");
1504 	if (waitPort < B_OK)
1505 		return waitPort;
1506 
1507 	node_sync_to_request request;
1508 	node_sync_to_reply reply;
1509 	request.performance_time = atTime;
1510 	request.port = waitPort;
1511 
1512 	status_t status = QueryPort(node.port, NODE_SYNC_TO, &request,
1513 		sizeof(request), &reply, sizeof(reply));
1514 
1515 	if (status == B_OK) {
1516 		ssize_t readSize = read_port_etc(waitPort, NULL, &status,
1517 			sizeof(status), B_TIMEOUT, timeout);
1518 		if (readSize < 0)
1519 			status = readSize;
1520 	}
1521 	close_port(waitPort);
1522 	delete_port(waitPort);
1523 	return status;
1524 }
1525 
1526 
1527 status_t
1528 BMediaRoster::SetRunModeNode(const media_node& node, BMediaNode::run_mode mode)
1529 {
1530 	TRACE("BMediaRoster::SetRunModeNode, node %" B_PRId32 ", mode %d\n",
1531 		node.node, mode);
1532 	if (IS_INVALID_NODE(node))
1533 		return B_MEDIA_BAD_NODE;
1534 
1535 	node_set_run_mode_command msg;
1536 	msg.mode = mode;
1537 
1538 	return write_port(node.port, NODE_SET_RUN_MODE, &msg, sizeof(msg));
1539 }
1540 
1541 
1542 status_t
1543 BMediaRoster::PrerollNode(const media_node& node)
1544 {
1545 	CALLED();
1546 	if (IS_INVALID_NODE(node))
1547 		return B_MEDIA_BAD_NODE;
1548 
1549 	char dummy;
1550 	return write_port(node.port, NODE_PREROLL, &dummy, sizeof(dummy));
1551 }
1552 
1553 
1554 status_t
1555 BMediaRoster::RollNode(const media_node& node, bigtime_t startPerformance,
1556 	bigtime_t stopPerformance, bigtime_t atMediaTime)
1557 {
1558 	CALLED();
1559 	if (IS_INVALID_NODE(node))
1560 		return B_MEDIA_BAD_NODE;
1561 
1562 	TRACE("BMediaRoster::RollNode, node %" B_PRId32 ", at start perf %"
1563 		B_PRId64 ", at stop perf %" B_PRId64 ", at media time %"
1564 		B_PRId64 "\n", node.node, startPerformance,
1565 		stopPerformance, atMediaTime);
1566 
1567 	node_roll_command command;
1568 	command.start_performance_time = startPerformance;
1569 	command.stop_performance_time = stopPerformance;
1570 	command.seek_media_time = atMediaTime;
1571 
1572 	return write_port(node.port, NODE_ROLL, &command, sizeof(command));
1573 }
1574 
1575 
1576 status_t
1577 BMediaRoster::SetProducerRunModeDelay(const media_node& node,
1578 	bigtime_t delay, BMediaNode::run_mode mode)
1579 {
1580 	TRACE("BMediaRoster::SetProducerRunModeDelay, node %" B_PRId32 ", delay %"
1581 		B_PRId64 ", mode %d\n", node.node, delay, mode);
1582 	if (IS_INVALID_NODE(node))
1583 		return B_MEDIA_BAD_NODE;
1584 	if ((node.kind & B_BUFFER_PRODUCER) == 0)
1585 		return B_MEDIA_BAD_NODE;
1586 
1587 	producer_set_run_mode_delay_command command;
1588 	command.mode = mode;
1589 	command.delay = delay;
1590 
1591 	return SendToPort(node.port, PRODUCER_SET_RUN_MODE_DELAY, &command,
1592 		sizeof(command));
1593 }
1594 
1595 
1596 status_t
1597 BMediaRoster::SetProducerRate(const media_node& producer, int32 numer,
1598 	int32 denom)
1599 {
1600 	CALLED();
1601 	if (IS_INVALID_NODE(producer))
1602 		return B_MEDIA_BAD_NODE;
1603 	if ((producer.kind & B_BUFFER_PRODUCER) == 0)
1604 		return B_MEDIA_BAD_NODE;
1605 
1606 	producer_set_play_rate_request request;
1607 	request.numer = numer;
1608 	request.denom = denom;
1609 	status_t status = write_port(producer.node, PRODUCER_SET_PLAY_RATE,
1610 		&request, sizeof(request));
1611 	if (status != B_OK)
1612 		return status;
1613 
1614 	producer_set_play_rate_reply reply;
1615 	int32 code;
1616 	status = read_port(request.reply_port, &code, &reply, sizeof(reply));
1617 
1618 	return status < B_OK ? status : reply.result;
1619 }
1620 
1621 
1622 /*!	Nodes will have available inputs/outputs as long as they are capable
1623 	of accepting more connections. The node may create an additional
1624 	output or input as the currently available is taken into usage.
1625 */
1626 status_t
1627 BMediaRoster::GetLiveNodeInfo(const media_node& node,
1628 	live_node_info* out_live_info)
1629 {
1630 	CALLED();
1631 	if (out_live_info == NULL)
1632 		return B_BAD_VALUE;
1633 	if (IS_INVALID_NODE(node))
1634 		return B_MEDIA_BAD_NODE;
1635 
1636 	server_get_live_node_info_request request;
1637 	server_get_live_node_info_reply reply;
1638 	status_t rv;
1639 
1640 	request.node = node;
1641 
1642 	rv = QueryServer(SERVER_GET_LIVE_NODE_INFO, &request, sizeof(request),
1643 		&reply, sizeof(reply));
1644 	if (rv != B_OK)
1645 		return rv;
1646 
1647 	*out_live_info = reply.live_info;
1648 	return B_OK;
1649 }
1650 
1651 
1652 status_t
1653 BMediaRoster::GetLiveNodes(live_node_info* liveNodes, int32* _totalCount,
1654 	const media_format* hasInput, const media_format* hasOutput,
1655 	const char* name, uint64 nodeKinds)
1656 {
1657 	CALLED();
1658 	if (liveNodes == NULL || _totalCount == NULL || *_totalCount <= 0)
1659 		return B_BAD_VALUE;
1660 
1661 	// TODO: we also support the wildcard search as GetDormantNodes does.
1662 	// This needs to be documented
1663 
1664 	server_get_live_nodes_request request;
1665 	request.team = BPrivate::current_team();
1666 
1667 	request.max_count = *_totalCount;
1668 	request.has_input = hasInput != NULL;
1669 	if (hasInput != NULL) {
1670 		// TODO: we should not make a flat copy of media_format
1671 		request.input_format = *hasInput;
1672 	}
1673 	request.has_output = hasOutput != NULL;
1674 	if (hasOutput != NULL) {
1675 		// TODO: we should not make a flat copy of media_format
1676 		request.output_format = *hasOutput;
1677 	}
1678 	request.has_name = name != NULL;
1679 	if (name != NULL)
1680 		strlcpy(request.name, name, sizeof(request.name));
1681 	request.require_kinds = nodeKinds;
1682 
1683 	server_get_live_nodes_reply reply;
1684 	status_t status = QueryServer(SERVER_GET_LIVE_NODES, &request,
1685 		sizeof(request), &reply, sizeof(reply));
1686 	if (status != B_OK) {
1687 		ERROR("BMediaRoster::GetLiveNodes failed querying server: %s\n",
1688 			strerror(status));
1689 		*_totalCount = 0;
1690 		return status;
1691 	}
1692 
1693 	const live_node_info* info;
1694 	if (reply.area >= 0)
1695 		info = (live_node_info*)reply.address;
1696 	else
1697 		info = reply.live_info;
1698 
1699 	for (int32 i = 0; i < reply.count; i++)
1700 		liveNodes[i] = info[i];
1701 
1702 	if (reply.area >= 0)
1703 		delete_area(reply.area);
1704 
1705 	*_totalCount = reply.count;
1706 	return B_OK;
1707 }
1708 
1709 
1710 status_t
1711 BMediaRoster::GetFreeInputsFor(const media_node& node,
1712 	media_input * out_free_inputs, int32 buf_num_inputs,
1713 	int32 * out_total_count, media_type filter_type)
1714 {
1715 	CALLED();
1716 	if (IS_INVALID_NODE(node)) {
1717 		ERROR("BMediaRoster::GetFreeInputsFor: node %" B_PRId32 ", port %"
1718 			B_PRId32 " invalid\n", node.node, node.port);
1719 		return B_MEDIA_BAD_NODE;
1720 	}
1721 	if ((node.kind & B_BUFFER_CONSUMER) == 0) {
1722 		ERROR("BMediaRoster::GetFreeInputsFor: node %" B_PRId32 ", port %"
1723 			B_PRId32 " is not a consumer\n", node.node, node.port);
1724 		return B_MEDIA_BAD_NODE;
1725 	}
1726 	if (out_free_inputs == NULL || out_total_count == NULL)
1727 		return B_BAD_VALUE;
1728 
1729 	List<media_input> list;
1730 	media_input *input;
1731 	status_t rv;
1732 
1733 	*out_total_count = 0;
1734 
1735 	rv = MediaRosterEx(this)->GetAllInputs(node, &list);
1736 	if (B_OK != rv)
1737 		return rv;
1738 
1739 	PRINT(4, "BMediaRoster::GetFreeInputsFor node %" B_PRId32 ", max %" B_PRId32
1740 		", filter-type %" B_PRId32 "\n", node.node, buf_num_inputs,
1741 		filter_type);
1742 
1743 	int32 i;
1744 	for (i = 0, list.Rewind(); list.GetNext(&input);) {
1745 		if (filter_type != B_MEDIA_UNKNOWN_TYPE
1746 			&& filter_type != input->format.type) {
1747 			// media_type used, but doesn't match
1748 			continue;
1749 		}
1750 		if (input->source != media_source::null) {
1751 			// consumer source already connected
1752 			continue;
1753 		}
1754 
1755 		out_free_inputs[i] = *input;
1756 		*out_total_count += 1;
1757 		buf_num_inputs -= 1;
1758 		#if DEBUG >= 3
1759 			PRINT_OUTPUT("  input", out_free_inputs[i]);
1760 		#endif
1761 		if (buf_num_inputs == 0)
1762 			break;
1763 		i++;
1764 	}
1765 
1766 	MediaRosterEx(this)->PublishInputs(node, &list);
1767 	return B_OK;
1768 }
1769 
1770 
1771 status_t
1772 BMediaRoster::GetConnectedInputsFor(const media_node& node,
1773 	media_input* out_active_inputs, int32 buf_num_inputs,
1774 	int32* out_total_count)
1775 {
1776 	CALLED();
1777 	if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_CONSUMER) == 0)
1778 		return B_MEDIA_BAD_NODE;
1779 	if (out_active_inputs == NULL || out_total_count == NULL)
1780 		return B_BAD_VALUE;
1781 
1782 	List<media_input> list;
1783 	media_input *input;
1784 	status_t rv;
1785 
1786 	*out_total_count = 0;
1787 
1788 	rv = MediaRosterEx(this)->GetAllInputs(node, &list);
1789 	if (B_OK != rv)
1790 		return rv;
1791 
1792 	PRINT(4, "BMediaRoster::GetConnectedInputsFor node %" B_PRId32 ", max %"
1793 		B_PRId32 "\n", node.node, buf_num_inputs);
1794 
1795 	int32 i;
1796 	for (i = 0, list.Rewind(); list.GetNext(&input);) {
1797 		if (input->source == media_source::null)
1798 			continue; // consumer source not connected
1799 		out_active_inputs[i] = *input;
1800 		*out_total_count += 1;
1801 		buf_num_inputs -= 1;
1802 		#if DEBUG >= 3
1803 			PRINT_OUTPUT("  input ", out_active_inputs[i]);
1804 		#endif
1805 		if (buf_num_inputs == 0)
1806 			break;
1807 		i++;
1808 	}
1809 
1810 	MediaRosterEx(this)->PublishInputs(node, &list);
1811 	return B_OK;
1812 }
1813 
1814 
1815 status_t
1816 BMediaRoster::GetAllInputsFor(const media_node& node, media_input* out_inputs,
1817 	int32 buf_num_inputs, int32* out_total_count)
1818 {
1819 	CALLED();
1820 	if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_CONSUMER) == 0)
1821 		return B_MEDIA_BAD_NODE;
1822 	if (out_inputs == NULL || out_total_count == NULL)
1823 		return B_BAD_VALUE;
1824 
1825 	List<media_input> list;
1826 	media_input *input;
1827 	status_t rv;
1828 
1829 	*out_total_count = 0;
1830 
1831 	rv = MediaRosterEx(this)->GetAllInputs(node, &list);
1832 	if (B_OK != rv)
1833 		return rv;
1834 
1835 	PRINT(4, "BMediaRoster::GetAllInputsFor node %" B_PRId32 ", max %" B_PRId32
1836 		"\n", node.node, buf_num_inputs);
1837 
1838 	int32 i;
1839 	for (i = 0, list.Rewind(); list.GetNext(&input); i++) {
1840 		out_inputs[i] = *input;
1841 		*out_total_count += 1;
1842 		buf_num_inputs -= 1;
1843 		#if DEBUG >= 3
1844 			PRINT_OUTPUT("  input ", out_inputs[i]);
1845 		#endif
1846 		if (buf_num_inputs == 0)
1847 			break;
1848 	}
1849 
1850 	MediaRosterEx(this)->PublishInputs(node, &list);
1851 	return B_OK;
1852 }
1853 
1854 
1855 status_t
1856 BMediaRoster::GetFreeOutputsFor(const media_node& node,
1857 	media_output* out_free_outputs, int32 buf_num_outputs,
1858 	int32* out_total_count, media_type filter_type)
1859 {
1860 	CALLED();
1861 	if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0)
1862 		return B_MEDIA_BAD_NODE;
1863 	if (out_free_outputs == NULL || out_total_count == NULL)
1864 		return B_BAD_VALUE;
1865 
1866 	List<media_output> list;
1867 	media_output *output;
1868 	status_t rv;
1869 
1870 	*out_total_count = 0;
1871 
1872 	rv = MediaRosterEx(this)->GetAllOutputs(node, &list);
1873 	if (B_OK != rv)
1874 		return rv;
1875 
1876 	PRINT(4, "BMediaRoster::GetFreeOutputsFor node %" B_PRId32 ", max %"
1877 		B_PRId32 ", filter-type %" B_PRId32 "\n", node.node, buf_num_outputs,
1878 		filter_type);
1879 
1880 	int32 i;
1881 	for (i = 0, list.Rewind(); list.GetNext(&output);) {
1882 		if (filter_type != B_MEDIA_UNKNOWN_TYPE
1883 			&& filter_type != output->format.type) {
1884 			// media_type used, but doesn't match
1885 			continue;
1886 		}
1887 		if (output->destination != media_destination::null) {
1888 			// producer destination already connected
1889 			continue;
1890 		}
1891 
1892 		out_free_outputs[i] = *output;
1893 		*out_total_count += 1;
1894 		buf_num_outputs -= 1;
1895 		#if DEBUG >= 3
1896 			PRINT_OUTPUT("  output ", out_free_outputs[i]);
1897 		#endif
1898 		if (buf_num_outputs == 0)
1899 			break;
1900 		i++;
1901 	}
1902 
1903 	MediaRosterEx(this)->PublishOutputs(node, &list);
1904 	return B_OK;
1905 }
1906 
1907 
1908 status_t
1909 BMediaRoster::GetConnectedOutputsFor(const media_node& node,
1910 	media_output* out_active_outputs, int32 buf_num_outputs,
1911 	int32* out_total_count)
1912 {
1913 	CALLED();
1914 	if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0)
1915 		return B_MEDIA_BAD_NODE;
1916 	if (out_active_outputs == NULL || out_total_count == NULL)
1917 		return B_BAD_VALUE;
1918 
1919 	List<media_output> list;
1920 	media_output *output;
1921 	status_t rv;
1922 
1923 	*out_total_count = 0;
1924 
1925 	rv = MediaRosterEx(this)->GetAllOutputs(node, &list);
1926 	if (B_OK != rv)
1927 		return rv;
1928 
1929 	PRINT(4, "BMediaRoster::GetConnectedOutputsFor node %" B_PRId32 ", max %"
1930 		B_PRId32 "\n", node.node, buf_num_outputs);
1931 
1932 	int32 i;
1933 	for (i = 0, list.Rewind(); list.GetNext(&output);) {
1934 		if (output->destination == media_destination::null) {
1935 			// producer destination not connected
1936 			continue;
1937 		}
1938 		out_active_outputs[i] = *output;
1939 		*out_total_count += 1;
1940 		buf_num_outputs -= 1;
1941 		#if DEBUG >= 3
1942 			PRINT_OUTPUT("  output ", out_active_outputs[i]);
1943 		#endif
1944 		if (buf_num_outputs == 0)
1945 			break;
1946 		i++;
1947 	}
1948 
1949 	MediaRosterEx(this)->PublishOutputs(node, &list);
1950 	return B_OK;
1951 }
1952 
1953 
1954 status_t
1955 BMediaRoster::GetAllOutputsFor(const media_node& node,
1956 	media_output* out_outputs, int32 buf_num_outputs, int32* out_total_count)
1957 {
1958 	CALLED();
1959 	if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0)
1960 		return B_MEDIA_BAD_NODE;
1961 	if (out_outputs == NULL || out_total_count == NULL)
1962 		return B_BAD_VALUE;
1963 
1964 	List<media_output> list;
1965 	media_output *output;
1966 	status_t rv;
1967 
1968 	*out_total_count = 0;
1969 
1970 	rv = MediaRosterEx(this)->GetAllOutputs(node, &list);
1971 	if (B_OK != rv)
1972 		return rv;
1973 
1974 	PRINT(4, "BMediaRoster::GetAllOutputsFor node %" B_PRId32 ", max %" B_PRId32
1975 		"\n", node.node, buf_num_outputs);
1976 
1977 	int32 i;
1978 	for (i = 0, list.Rewind(); list.GetNext(&output); i++) {
1979 		out_outputs[i] = *output;
1980 		*out_total_count += 1;
1981 		buf_num_outputs -= 1;
1982 		#if DEBUG >= 3
1983 			PRINT_OUTPUT("  output ", out_outputs[i]);
1984 		#endif
1985 		if (buf_num_outputs == 0)
1986 			break;
1987 	}
1988 
1989 	MediaRosterEx(this)->PublishOutputs(node, &list);
1990 	return B_OK;
1991 }
1992 
1993 
1994 status_t
1995 BMediaRoster::StartWatching(const BMessenger& where)
1996 {
1997 	CALLED();
1998 	if (!where.IsValid()) {
1999 		ERROR("BMediaRoster::StartWatching: messenger invalid!\n");
2000 		return B_BAD_VALUE;
2001 	}
2002 	return BPrivate::media::notifications::Register(where, media_node::null,
2003 		B_MEDIA_WILDCARD);
2004 }
2005 
2006 
2007 status_t
2008 BMediaRoster::StartWatching(const BMessenger & where, int32 notificationType)
2009 {
2010 	CALLED();
2011 	if (!where.IsValid()) {
2012 		ERROR("BMediaRoster::StartWatching: messenger invalid!\n");
2013 		return B_BAD_VALUE;
2014 	}
2015 	if (!BPrivate::media::notifications::IsValidNotificationRequest(false,
2016 			notificationType)) {
2017 		ERROR("BMediaRoster::StartWatching: notificationType invalid!\n");
2018 		return B_BAD_VALUE;
2019 	}
2020 
2021 	// NOTE: we support only explicitly B_MEDIA_SERVER_STARTED/QUIT
2022 	// notifications. This should be cleared in documentation.
2023 
2024 	return BPrivate::media::notifications::Register(where, media_node::null,
2025 		notificationType);
2026 }
2027 
2028 
2029 status_t
2030 BMediaRoster::StartWatching(const BMessenger& where, const media_node& node,
2031 	int32 notificationType)
2032 {
2033 	CALLED();
2034 	if (!where.IsValid()) {
2035 		ERROR("BMediaRoster::StartWatching: messenger invalid!\n");
2036 		return B_BAD_VALUE;
2037 	}
2038 	if (IS_INVALID_NODE(node)) {
2039 		ERROR("BMediaRoster::StartWatching: node invalid!\n");
2040 		return B_MEDIA_BAD_NODE;
2041 	}
2042 	if (!BPrivate::media::notifications::IsValidNotificationRequest(true,
2043 			notificationType)) {
2044 		ERROR("BMediaRoster::StartWatching: notificationType invalid!\n");
2045 		return B_BAD_VALUE;
2046 	}
2047 	return BPrivate::media::notifications::Register(where, node,
2048 		notificationType);
2049 }
2050 
2051 
2052 status_t
2053 BMediaRoster::StopWatching(const BMessenger& where)
2054 {
2055 	CALLED();
2056 	// messenger may already be invalid, so we don't check this
2057 	return BPrivate::media::notifications::Unregister(where, media_node::null,
2058 		B_MEDIA_WILDCARD);
2059 }
2060 
2061 
2062 status_t
2063 BMediaRoster::StopWatching(const BMessenger& where, int32 notificationType)
2064 {
2065 	CALLED();
2066 	// messenger may already be invalid, so we don't check this
2067 	if (!BPrivate::media::notifications::IsValidNotificationRequest(false,
2068 			notificationType)) {
2069 		ERROR("BMediaRoster::StopWatching: notificationType invalid!\n");
2070 		return B_BAD_VALUE;
2071 	}
2072 	return BPrivate::media::notifications::Unregister(where, media_node::null,
2073 		notificationType);
2074 }
2075 
2076 
2077 status_t
2078 BMediaRoster::StopWatching(const BMessenger& where, const media_node& node,
2079 	int32 notificationType)
2080 {
2081 	CALLED();
2082 	// messenger may already be invalid, so we don't check this
2083 	if (IS_INVALID_NODE(node)) {
2084 		ERROR("BMediaRoster::StopWatching: node invalid!\n");
2085 		return B_MEDIA_BAD_NODE;
2086 	}
2087 	if (!BPrivate::media::notifications::IsValidNotificationRequest(true,
2088 			notificationType)) {
2089 		ERROR("BMediaRoster::StopWatching: notificationType invalid!\n");
2090 		return B_BAD_VALUE;
2091 	}
2092 	return BPrivate::media::notifications::Unregister(where, node,
2093 		notificationType);
2094 }
2095 
2096 
2097 status_t
2098 BMediaRoster::RegisterNode(BMediaNode* node)
2099 {
2100 	CALLED();
2101 	// addon-id = -1 (unused), addon-flavor-id = 0 (unused, too)
2102 	return MediaRosterEx(this)->RegisterNode(node, -1, 0);
2103 }
2104 
2105 
2106 status_t
2107 BMediaRosterEx::RegisterNode(BMediaNode* node, media_addon_id addOnID,
2108 	int32 flavorID)
2109 {
2110 	CALLED();
2111 	if (node == NULL)
2112 		return B_BAD_VALUE;
2113 
2114 	// some sanity check
2115 	// I'm not sure if the media kit warrants to call BMediaNode::AddOn() here.
2116 	// Perhaps we don't need it.
2117 	DEBUG_ONLY(
2118 		int32 testFlavorID;
2119 		BMediaAddOn* addon = node->AddOn(&testFlavorID);
2120 
2121 		ASSERT(addOnID == (addon != NULL ? addon->AddonID() : -1));
2122 //		ASSERT(flavorID == testFlavorID);
2123 	);
2124 
2125 	server_register_node_request request;
2126 	server_register_node_reply reply;
2127 
2128 	request.add_on_id = addOnID;
2129 	request.flavor_id = flavorID;
2130 	strcpy(request.name, node->Name());
2131 	request.kinds = node->Kinds();
2132 	request.port = node->ControlPort();
2133 	request.team = BPrivate::current_team();
2134 	request.timesource_id = node->fTimeSourceID;
2135 
2136 	TRACE("BMediaRoster::RegisterNode: sending SERVER_REGISTER_NODE: port "
2137 		"%" B_PRId32 ", kinds 0x%" B_PRIx64 ", team %" B_PRId32 ", name '%s'\n",
2138 		request.port, request.kinds, request.team, request.name);
2139 
2140 	status_t status = QueryServer(SERVER_REGISTER_NODE, &request,
2141 		sizeof(request), &reply, sizeof(reply));
2142 	if (status != B_OK) {
2143 		ERROR("BMediaRoster::RegisterNode: failed to register node %s: %s\n",
2144 			node->Name(), strerror(status));
2145 		return status;
2146 	}
2147 
2148 	TRACE("BMediaRoster::RegisterNode: QueryServer SERVER_REGISTER_NODE "
2149 		"finished\n");
2150 
2151 	// we are a friend class of BMediaNode and initialize this member variable
2152 	node->fNodeID = reply.node_id;
2153 	ASSERT(reply.node_id == node->Node().node);
2154 	ASSERT(reply.node_id == node->ID());
2155 
2156 	// if the BMediaNode also inherits from BTimeSource, we need to call
2157 	// BTimeSource::FinishCreate()
2158 	if ((node->Kinds() & B_TIME_SOURCE) != 0) {
2159 		if (BTimeSource* timeSource = dynamic_cast<BTimeSource*>(node))
2160 			timeSource->FinishCreate();
2161 	}
2162 
2163 	// call the callback
2164 	node->NodeRegistered();
2165 
2166 	TRACE("BMediaRoster::RegisterNode: NodeRegistered callback finished\n");
2167 
2168 	TRACE("BMediaRoster::RegisterNode: publishing inputs/outputs\n");
2169 
2170 	// register existing inputs and outputs with the
2171 	// media_server, this allows GetLiveNodes() to work
2172 	// with created, but unconnected nodes.
2173 	// The node control loop might not be running, or might deadlock
2174 	// if we send a message and wait for a reply here.
2175 	// We have a pointer to the node, and thus call the functions directly
2176 
2177 	if ((node->Kinds() & B_BUFFER_PRODUCER) != 0) {
2178 		if (BBufferProducer* producer = dynamic_cast<BBufferProducer*>(node)) {
2179 			List<media_output> list;
2180 			if (GetAllOutputs(producer, &list) == B_OK)
2181 				PublishOutputs(node->Node(), &list);
2182 		}
2183 	}
2184 	if ((node->Kinds() & B_BUFFER_CONSUMER) != 0) {
2185 		if (BBufferConsumer* consumer = dynamic_cast<BBufferConsumer*>(node)) {
2186 			List<media_input> list;
2187 			if (GetAllInputs(consumer, &list) == B_OK)
2188 				PublishInputs(node->Node(), &list);
2189 		}
2190 	}
2191 
2192 	TRACE("BMediaRoster::RegisterNode: sending NodesCreated\n");
2193 
2194 	BPrivate::media::notifications::NodesCreated(&reply.node_id, 1);
2195 
2196 	TRACE("BMediaRoster::RegisterNode: finished\n");
2197 
2198 /*
2199 	TRACE("BMediaRoster::RegisterNode: registered node name '%s', id %ld,
2200 		addon %ld, flavor %ld\n", node->Name(), node->ID(), addOnID, flavorID);
2201 	TRACE("BMediaRoster::RegisterNode: node this               %p\n", node);
2202 	TRACE("BMediaRoster::RegisterNode: node fConsumerThis      %p\n",
2203 		node->fConsumerThis);
2204 	TRACE("BMediaRoster::RegisterNode: node fProducerThis      %p\n",
2205 		node->fProducerThis);
2206 	TRACE("BMediaRoster::RegisterNode: node fFileInterfaceThis %p\n",
2207 		node->fFileInterfaceThis);
2208 	TRACE("BMediaRoster::RegisterNode: node fControllableThis  %p\n",
2209 		node->fControllableThis);
2210 	TRACE("BMediaRoster::RegisterNode: node fTimeSourceThis    %p\n",
2211 		node->fTimeSourceThis);
2212 */
2213 	return B_OK;
2214 }
2215 
2216 
2217 status_t
2218 BMediaRoster::UnregisterNode(BMediaNode* node)
2219 {
2220 	CALLED();
2221 	if (node == NULL)
2222 		return B_BAD_VALUE;
2223 
2224 	TRACE("BMediaRoster::UnregisterNode %"
2225 		B_PRId32 " (%p)\n", node->ID(), node);
2226 
2227 	if ((node->fKinds & NODE_KIND_NO_REFCOUNTING) !=0) {
2228 		TRACE("BMediaRoster::UnregisterNode, trying to unregister reference "
2229 			"counting disabled timesource, node %"
2230 			B_PRId32 " , port %" B_PRId32 " , team %" B_PRId32 "\n",
2231 			node->ID(), node->ControlPort(), BPrivate::current_team());
2232 		return B_OK;
2233 	}
2234 	if (node->ID() == NODE_UNREGISTERED_ID) {
2235 		PRINT(1, "Warning: BMediaRoster::UnregisterNode: node id %ld, name "
2236 			"'%s' already unregistered\n", node->ID(), node->Name());
2237 		return B_OK;
2238 	}
2239 	if (node->fRefCount != 0) {
2240 		PRINT(1, "Warning: BMediaRoster::UnregisterNode: node id %ld, name "
2241 			"'%s' has local reference count of %ld\n", node->ID(), node->Name(),
2242 			node->fRefCount);
2243 		// no return here, we continue and unregister!
2244 	}
2245 
2246 	// Calling BMediaAddOn::GetConfigurationFor(BMediaNode *node,
2247 	// BMessage *config) if this node was instanciated by an add-on needs to
2248 	// be done *somewhere*
2249 	// We can't do it here because it is already to late (destructor of the node
2250 	// might have been called).
2251 
2252 	server_unregister_node_request request;
2253 	request.node_id = node->ID();
2254 	request.team = BPrivate::current_team();
2255 
2256 	// send a notification
2257 	BPrivate::media::notifications::NodesDeleted(&request.node_id, 1);
2258 
2259 	server_unregister_node_reply reply;
2260 	reply.add_on_id = -1;
2261 	status_t status = QueryServer(SERVER_UNREGISTER_NODE, &request,
2262 		sizeof(request), &reply, sizeof(reply));
2263 	if (status != B_OK) {
2264 		ERROR("BMediaRoster::UnregisterNode: failed to unregister node id %"
2265 			B_PRId32 ", name '%s': %s\n", node->ID(), node->Name(),
2266 			strerror(status));
2267 		BMediaAddOn *addon = node->AddOn(&reply.flavor_id);
2268 		if (addon != NULL)
2269 			reply.add_on_id = addon->AddonID();
2270 	}
2271 
2272 	if (reply.add_on_id != -1) {
2273 		// TODO: this doesn't look right
2274 		// Small problem here, we can't use DormantNodeManager::PutAddOn(), as
2275 		// UnregisterNode() is called by a dormant node itself (by the
2276 		// destructor).
2277 		// The add-on that contains the node needs to remain in memory until the
2278 		// destructor execution is finished.
2279 		// DormantNodeManager::PutAddOnDelayed() will delay unloading.
2280 		gDormantNodeManager->PutAddOnDelayed(reply.add_on_id);
2281 
2282 		status = MediaRosterEx(this)->DecrementAddonFlavorInstancesCount(
2283 			reply.add_on_id, reply.flavor_id);
2284 		if (status != B_OK) {
2285 			ERROR("BMediaRoster::UnregisterNode: "
2286 				"DecrementAddonFlavorInstancesCount() failed\n");
2287 			// this is really a problem, but we can't fail now
2288 		}
2289 	}
2290 
2291 	// we are a friend class of BMediaNode and invalidate this member variable
2292 	node->fNodeID = NODE_UNREGISTERED_ID;
2293 
2294 	return status;
2295 }
2296 
2297 
2298 //!	Thread safe for multiple calls to Roster()
2299 /*static*/ BMediaRoster*
2300 BMediaRoster::Roster(status_t* out_error)
2301 {
2302 	BAutolock lock(sInitLocker);
2303 
2304 	if (be_app == NULL)
2305 		TRACE("Warning! You should have a valid BApplication.");
2306 
2307 	if (!lock.IsLocked())
2308 		return NULL;
2309 
2310 	if (out_error)
2311 		*out_error = B_OK;
2312 
2313 	if (sDefaultInstance == NULL) {
2314 		status_t err;
2315 		sDefaultInstance = new (std::nothrow) BMediaRosterEx(&err);
2316 		if (sDefaultInstance == NULL)
2317 			err = B_NO_MEMORY;
2318 		else if (err != B_OK) {
2319 			if (sDefaultInstance) {
2320 				sDefaultInstance->Lock();
2321 				sDefaultInstance->Quit();
2322 				sDefaultInstance = NULL;
2323 			}
2324 			if (out_error)
2325 				*out_error = err;
2326 		} else if (be_app != NULL) {
2327 			be_app->RegisterLooper(sDefaultInstance);
2328 		}
2329 	}
2330 
2331 	return sDefaultInstance;
2332 }
2333 
2334 
2335 /*static*/ BMediaRoster*
2336 BMediaRoster::CurrentRoster()
2337 {
2338 	return sDefaultInstance;
2339 }
2340 
2341 
2342 status_t
2343 BMediaRoster::SetTimeSourceFor(media_node_id node, media_node_id time_source)
2344 {
2345 	CALLED();
2346 	if (IS_INVALID_NODEID(node) || IS_INVALID_NODEID(time_source))
2347 		return B_BAD_VALUE;
2348 
2349 	media_node clone;
2350 	// We need to get a clone of the node to have a port id
2351 	status_t result = GetNodeFor(node, &clone);
2352 	if (result == B_OK) {
2353 		// We just send the request to set time_source-id as
2354 		// timesource to the node, the NODE_SET_TIMESOURCE handler
2355 		// code will do the real assignment.
2356 		result = B_OK;
2357 		node_set_timesource_command cmd;
2358 		cmd.timesource_id = time_source;
2359 		result = SendToPort(clone.port, NODE_SET_TIMESOURCE,
2360 			&cmd, sizeof(cmd));
2361 		if (result != B_OK) {
2362 			ERROR("BMediaRoster::SetTimeSourceFor"
2363 				"sending NODE_SET_TIMESOURCE failed, node id %"
2364 				B_PRId32 "\n", clone.node);
2365 		}
2366 		// We release the clone
2367 		result = ReleaseNode(clone);
2368 		if (result != B_OK) {
2369 			ERROR("BMediaRoster::SetTimeSourceFor, ReleaseNode failed,"
2370 				" node id %" B_PRId32 "\n", clone.node);
2371 		}
2372 	} else {
2373 		ERROR("BMediaRoster::SetTimeSourceFor GetCloneForID failed, "
2374 			"node id %" B_PRId32 "\n", node);
2375 	}
2376 
2377 	if (result == B_OK) {
2378 		// Notify the server
2379 		server_set_node_timesource_request request;
2380 		server_set_node_timesource_reply reply;
2381 
2382 		request.node_id = node;
2383 		request.timesource_id = time_source;
2384 
2385 		result = QueryServer(SERVER_SET_NODE_TIMESOURCE, &request,
2386 			sizeof(request), &reply, sizeof(reply));
2387 		if (result != B_OK) {
2388 			ERROR("BMediaRoster::SetTimeSourceFor, sending NODE_SET_TIMESOURCE "
2389 				"failed, node id %" B_PRId32 "\n", node);
2390 		} else {
2391 			TRACE("BMediaRoster::SetTimeSourceFor: node %" B_PRId32 " time source %"
2392 				B_PRId32 " OK\n", node, time_source);
2393 		}
2394 	}
2395 	return result;
2396 }
2397 
2398 
2399 status_t
2400 BMediaRoster::GetParameterWebFor(const media_node& node, BParameterWeb** _web)
2401 {
2402 	CALLED();
2403 	if (_web == NULL)
2404 		return B_BAD_VALUE;
2405 	if (IS_INVALID_NODE(node))
2406 		return B_MEDIA_BAD_NODE;
2407 	if ((node.kind & B_CONTROLLABLE) == 0)
2408 		return B_MEDIA_BAD_NODE;
2409 
2410 	controllable_get_parameter_web_request request;
2411 	controllable_get_parameter_web_reply reply;
2412 	int32 requestsize[] = {B_PAGE_SIZE, 4 * B_PAGE_SIZE, 16 * B_PAGE_SIZE,
2413 		64 * B_PAGE_SIZE, 128 * B_PAGE_SIZE, 256 * B_PAGE_SIZE, 0};
2414 	int32 size;
2415 
2416 	// TODO: it might be better to query the node for the (current) parameter
2417 	// size first
2418 	for (int i = 0; (size = requestsize[i]) != 0; i++) {
2419 		status_t rv;
2420 		area_id area;
2421 		void *data;
2422 		area = create_area("parameter web data", &data, B_ANY_ADDRESS, size,
2423 			B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
2424 		if (area < B_OK) {
2425 			ERROR("BMediaRoster::GetParameterWebFor couldn't create area of "
2426 				"size %" B_PRId32 "\n", size);
2427 			return B_ERROR;
2428 		}
2429 		request.max_size = size;
2430 		request.area = area;
2431 		rv = QueryPort(node.port, CONTROLLABLE_GET_PARAMETER_WEB, &request,
2432 			sizeof(request), &reply, sizeof(reply));
2433 		if (rv != B_OK) {
2434 			ERROR("BMediaRoster::GetParameterWebFor "
2435 				"CONTROLLABLE_GET_PARAMETER_WEB failed\n");
2436 			delete_area(area);
2437 			return B_ERROR;
2438 		}
2439 		if (reply.size == 0) {
2440 			// no parameter web available
2441 			// TODO: should we return an error?
2442 			ERROR("BMediaRoster::GetParameterWebFor node %" B_PRId32
2443 				" has no parameter web\n", node.node);
2444 			*_web = new (std::nothrow) BParameterWeb();
2445 			delete_area(area);
2446 			return *_web != NULL ? B_OK : B_NO_MEMORY;
2447 		}
2448 		if (reply.size > 0) {
2449 			// we got a flattened parameter web!
2450 			BParameterWeb* web = new (std::nothrow) BParameterWeb();
2451 			if (web == NULL)
2452 				rv = B_NO_MEMORY;
2453 			else {
2454 				rv = web->Unflatten(reply.code, data, reply.size);
2455 				if (rv != B_OK) {
2456 					ERROR("BMediaRoster::GetParameterWebFor Unflatten failed, "
2457 						"%s\n", strerror(rv));
2458 					delete web;
2459 				} else
2460 					*_web = web;
2461 			}
2462 
2463 			delete_area(area);
2464 			return rv;
2465 		}
2466 		delete_area(area);
2467 		ASSERT(reply.size == -1);
2468 		// parameter web data was too large
2469 		// loop and try a larger size
2470 	}
2471 	ERROR("BMediaRoster::GetParameterWebFor node %" B_PRId32 " has no "
2472 		"parameter web larger than %" B_PRId32 "\n", node.node, size);
2473 	return B_ERROR;
2474 }
2475 
2476 
2477 status_t
2478 BMediaRoster::StartControlPanel(const media_node& node, BMessenger* _messenger)
2479 {
2480 	CALLED();
2481 
2482 	controllable_start_control_panel_request request;
2483 	controllable_start_control_panel_reply reply;
2484 
2485 	request.node = node;
2486 
2487 	status_t rv;
2488 	rv = QueryPort(node.port, CONTROLLABLE_START_CONTROL_PANEL, &request,
2489 		sizeof(request), &reply, sizeof(reply));
2490 	if (rv != B_OK)
2491 		return rv;
2492 
2493 	if (reply.team != -1 && _messenger != NULL)
2494 		*_messenger = BMessenger(NULL, reply.team);
2495 
2496 	return B_OK;
2497 }
2498 
2499 
2500 status_t
2501 BMediaRoster::GetDormantNodes(dormant_node_info* _info, int32* _count,
2502 	const media_format* hasInput, const media_format* hasOutput,
2503 	const char* name, uint64 requireKinds, uint64 denyKinds)
2504 {
2505 	CALLED();
2506 	if (_info == NULL || _count == NULL || *_count <= 0)
2507 		return B_BAD_VALUE;
2508 
2509 	server_get_dormant_nodes_request request;
2510 	request.max_count = *_count;
2511 	request.has_input = hasInput != NULL;
2512 	if (hasInput != NULL) {
2513 		// TODO: we should not make a flat copy of media_format
2514 		request.input_format = *hasInput;
2515 	}
2516 	request.has_output = hasOutput != NULL;
2517 	if (hasOutput != NULL) {
2518 		// TODO: we should not make a flat copy of media_format
2519 		request.output_format = *hasOutput;
2520 	}
2521 
2522 	request.has_name = name != NULL;
2523 	if (name != NULL)
2524 		strlcpy(request.name, name, sizeof(request.name));
2525 
2526 	request.require_kinds = requireKinds;
2527 	request.deny_kinds = denyKinds;
2528 
2529 	server_get_dormant_nodes_reply reply;
2530 	status_t status = QueryServer(SERVER_GET_DORMANT_NODES, &request,
2531 		sizeof(request), &reply, sizeof(reply));
2532 	if (status != B_OK)
2533 		return status;
2534 
2535 	*_count = reply.count;
2536 
2537 	if (reply.count > 0) {
2538 		int32 code;
2539 		status = read_port(request.reply_port, &code, _info,
2540 			reply.count * sizeof(dormant_node_info));
2541 		if (status < B_OK)
2542 			reply.result = status;
2543 	}
2544 
2545 	return reply.result;
2546 }
2547 
2548 
2549 /*!	This function is used to do the real work of instantiating a dormant node.
2550 	It is either called by the media_addon_server to instantiate a global node,
2551 	or it gets called from BMediaRoster::InstantiateDormantNode() to create a
2552 	local one.
2553 
2554 	Checks concerning global/local are not done here.
2555 */
2556 status_t
2557 BMediaRosterEx::InstantiateDormantNode(media_addon_id addonID, int32 flavorID,
2558 	team_id creator, media_node *_node)
2559 {
2560 	// This function is always called from the correct context, if the node
2561 	// is supposed to be global, it is called from the media_addon_server.
2562 
2563 	// if B_FLAVOR_IS_GLOBAL, we need to use the BMediaAddOn object that
2564 	// resides in the media_addon_server
2565 
2566 	// RegisterNode() must be called for nodes instantiated from add-ons,
2567 	// since the media kit warrants that it's done automatically.
2568 
2569 	// addonID		Indicates the ID number of the media add-on in which the
2570 	//				node resides.
2571 	// flavorID		Indicates the internal ID number that the add-on uses to
2572 	//				identify the flavor, this is the number that was published
2573 	//				by BMediaAddOn::GetFlavorAt() in the
2574 	//				flavor_info::internal_id field.
2575 	// creator		The creator team is -1 if nodes are created locally. If
2576 	//				created globally, it will contain (while called in
2577 	//				media_addon_server context) the team-id of the team that
2578 	//				requested the instantiation.
2579 
2580 	TRACE("BMediaRosterEx::InstantiateDormantNode: addonID %" B_PRId32
2581 		", flavorID %" B_PRId32 "\n", addonID, flavorID);
2582 
2583 	// Get flavor_info from the server
2584 	dormant_flavor_info info;
2585 	status_t rv;
2586 	rv = GetDormantFlavorInfo(addonID, flavorID, &info);
2587 	if (rv != B_OK) {
2588 		ERROR("BMediaRosterEx::InstantiateDormantNode error: failed to get "
2589 			"dormant_flavor_info for addon-id %" B_PRId32 ", flavor-id %"
2590 			B_PRId32 "\n", addonID, flavorID);
2591 		return B_ERROR;
2592 	}
2593 
2594 	ASSERT(info.internal_id == flavorID);
2595 
2596 	// load the BMediaAddOn object
2597 	BMediaAddOn* addon = gDormantNodeManager->GetAddOn(addonID);
2598 	if (addon == NULL) {
2599 		ERROR("BMediaRosterEx::InstantiateDormantNode: GetAddon failed\n");
2600 		return B_ERROR;
2601 	}
2602 
2603 	// Now we need to try to increment the use count of this addon flavor
2604 	// in the server. This can fail if the total number instances of this
2605 	// flavor is limited.
2606 	rv = IncrementAddonFlavorInstancesCount(addonID, flavorID);
2607 	if (rv != B_OK) {
2608 		ERROR("BMediaRosterEx::InstantiateDormantNode error: can't create "
2609 			"more nodes for addon-id %" B_PRId32 ", flavor-id %" B_PRId32 "\n",
2610 			addonID, flavorID);
2611 		// Put the addon back into the pool
2612 		gDormantNodeManager->PutAddOn(addonID);
2613 		return B_ERROR;
2614 	}
2615 
2616 	BMessage config;
2617 	rv = LoadNodeConfiguration(addonID, flavorID, &config);
2618 	if (rv != B_OK) {
2619 		ERROR("BMediaRosterEx::InstantiateDormantNode: couldn't load "
2620 			"configuration for addon-id %" B_PRId32 ", flavor-id %" B_PRId32
2621 			"\n", addonID, flavorID);
2622 		// do not return, this is a minor problem, not a reason to fail
2623 	}
2624 
2625 	status_t status = B_OK;
2626 	BMediaNode* node = addon->InstantiateNodeFor(&info, &config, &status);
2627 	if (node == NULL) {
2628 		ERROR("BMediaRosterEx::InstantiateDormantNode: InstantiateNodeFor "
2629 			"failed\n");
2630 
2631 		// Put the addon back into the pool
2632 		gDormantNodeManager->PutAddOn(addonID);
2633 
2634 		// We must decrement the use count of this addon flavor in the
2635 		// server to compensate the increment done in the beginning.
2636 		rv = DecrementAddonFlavorInstancesCount(addonID, flavorID);
2637 		if (rv != B_OK) {
2638 			ERROR("BMediaRosterEx::InstantiateDormantNode: DecrementAddon"
2639 				"FlavorInstancesCount failed\n");
2640 		}
2641 		return status != B_OK ? status : B_ERROR;
2642 	}
2643 
2644 	rv = RegisterNode(node, addonID, flavorID);
2645 	if (rv != B_OK) {
2646 		ERROR("BMediaRosterEx::InstantiateDormantNode: RegisterNode failed\n");
2647 		delete node;
2648 		// Put the addon back into the pool
2649 		gDormantNodeManager->PutAddOn(addonID);
2650 		// We must decrement the use count of this addon flavor in the
2651 		// server to compensate the increment done in the beginning.
2652 		rv = DecrementAddonFlavorInstancesCount(addonID, flavorID);
2653 		if (rv != B_OK) {
2654 			ERROR("BMediaRosterEx::InstantiateDormantNode: DecrementAddon"
2655 				"FlavorInstancesCount failed\n");
2656 		}
2657 		return B_ERROR;
2658 	}
2659 
2660 	if (creator != -1) {
2661 		// send a message to the server to assign team "creator" as creator
2662 		// of node "node->ID()"
2663 		printf("!!! BMediaRosterEx::InstantiateDormantNode assigning team %"
2664 			B_PRId32 " as creator of node %" B_PRId32 "\n", creator,
2665 			node->ID());
2666 
2667 		rv = MediaRosterEx(this)->SetNodeCreator(node->ID(), creator);
2668 		if (rv != B_OK) {
2669 			ERROR("BMediaRosterEx::InstantiateDormantNode failed to assign "
2670 				"team %" B_PRId32 " as creator of node %" B_PRId32 "\n",
2671 				creator, node->ID());
2672 			// do not return, this is a minor problem, not a reason to fail
2673 		}
2674 	}
2675 
2676 	// RegisterNode() does remember the add-on id in the server
2677 	// and UnregisterNode() will call DormantNodeManager::PutAddon()
2678 	// when the node is unregistered.
2679 
2680 	*_node = node->Node();
2681 
2682 	TRACE("BMediaRosterEx::InstantiateDormantNode: addon-id %" B_PRId32
2683 		", flavor_id %" B_PRId32 " instanciated as node %" B_PRId32 ", port %"
2684 		B_PRId32 " in team %" B_PRId32 "\n", addonID, flavorID, _node->node,
2685 		_node->port, BPrivate::current_team());
2686 
2687 	return B_OK;
2688 }
2689 
2690 
2691 status_t
2692 BMediaRoster::InstantiateDormantNode(const dormant_node_info& info,
2693 	media_node* _node, uint32 flags)
2694 {
2695 	CALLED();
2696 	if (_node == NULL)
2697 		return B_BAD_VALUE;
2698 	if (info.addon <= B_OK) {
2699 		ERROR("BMediaRoster::InstantiateDormantNode error: addon-id %" B_PRId32
2700 			" invalid.\n", info.addon);
2701 		return B_BAD_VALUE;
2702 	}
2703 
2704 	printf("BMediaRoster::InstantiateDormantNode: addon-id %" B_PRId32
2705 		", flavor_id %" B_PRId32 ", flags 0x%" B_PRIx32 "\n", info.addon,
2706 		info.flavor_id, flags);
2707 
2708 	// Get flavor_info from the server
2709 	// TODO: this is a little overhead, as we get the full blown
2710 	// dormant_flavor_info,
2711 	// TODO: but only need the flags.
2712 	dormant_flavor_info flavorInfo;
2713 	status_t rv;
2714 	rv = MediaRosterEx(this)->GetDormantFlavorInfo(info.addon, info.flavor_id,
2715 		&flavorInfo);
2716 	if (rv != B_OK) {
2717 		ERROR("BMediaRoster::InstantiateDormantNode: failed to get "
2718 			"dormant_flavor_info for addon-id %" B_PRId32 ", flavor-id %"
2719 			B_PRId32 "\n", info.addon, info.flavor_id);
2720 		return B_NAME_NOT_FOUND;
2721 	}
2722 
2723 	ASSERT(flavorInfo.internal_id == info.flavor_id);
2724 
2725 #if DEBUG
2726 	printf("BMediaRoster::InstantiateDormantNode: name \"%s\", info \"%s\", "
2727 		"flavor_flags 0x%" B_PRIx32 ", internal_id %" B_PRId32
2728 		", possible_count %" B_PRId32 "\n", flavorInfo.name, flavorInfo.info,
2729 		flavorInfo.flavor_flags, flavorInfo.internal_id,
2730 		flavorInfo.possible_count);
2731 
2732 	if ((flags & B_FLAVOR_IS_LOCAL) != 0) {
2733 		printf("BMediaRoster::InstantiateDormantNode: caller requested "
2734 			"B_FLAVOR_IS_LOCAL\n");
2735 	}
2736 	if ((flags & B_FLAVOR_IS_GLOBAL) != 0) {
2737 		printf("BMediaRoster::InstantiateDormantNode: caller requested "
2738 			"B_FLAVOR_IS_GLOBAL\n");
2739 	}
2740 	if ((flavorInfo.flavor_flags & B_FLAVOR_IS_LOCAL) != 0) {
2741 		printf("BMediaRoster::InstantiateDormantNode: node requires "
2742 			"B_FLAVOR_IS_LOCAL\n");
2743 	}
2744 	if ((flavorInfo.flavor_flags & B_FLAVOR_IS_GLOBAL) != 0) {
2745 		printf("BMediaRoster::InstantiateDormantNode: node requires "
2746 			"B_FLAVOR_IS_GLOBAL\n");
2747 	}
2748 #endif
2749 
2750 	// Make sure that flags demanded by the dormant node and those requested
2751 	// by the caller are not incompatible.
2752 	if ((flavorInfo.flavor_flags & B_FLAVOR_IS_GLOBAL) != 0
2753 		&& (flags & B_FLAVOR_IS_LOCAL) != 0) {
2754 		ERROR("BMediaRoster::InstantiateDormantNode: requested "
2755 			"B_FLAVOR_IS_LOCAL, but dormant node has B_FLAVOR_IS_GLOBAL\n");
2756 		return B_NAME_NOT_FOUND;
2757 	}
2758 	if ((flavorInfo.flavor_flags & B_FLAVOR_IS_LOCAL) != 0
2759 		&& (flags & B_FLAVOR_IS_GLOBAL) != 0) {
2760 		ERROR("BMediaRoster::InstantiateDormantNode: requested "
2761 			"B_FLAVOR_IS_GLOBAL, but dormant node has B_FLAVOR_IS_LOCAL\n");
2762 		return B_NAME_NOT_FOUND;
2763 	}
2764 
2765 	// If either the node, or the caller requested to make the instance global
2766 	// we will do it by forwarding this request into the media_addon_server,
2767 	// which in turn will call BMediaRosterEx::InstantiateDormantNode to create
2768 	// the node there and make it globally available.
2769 	if ((flavorInfo.flavor_flags & B_FLAVOR_IS_GLOBAL) != 0
2770 		|| (flags & B_FLAVOR_IS_GLOBAL) != 0) {
2771 		TRACE("BMediaRoster::InstantiateDormantNode: creating global object "
2772 			"in media_addon_server\n");
2773 
2774 		add_on_server_instantiate_dormant_node_request request;
2775 		add_on_server_instantiate_dormant_node_reply reply;
2776 		request.add_on_id = info.addon;
2777 		request.flavor_id = info.flavor_id;
2778 		request.creator_team = BPrivate::current_team();
2779 			// creator team is allowed to also release global nodes
2780 		rv = QueryAddOnServer(ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE, &request,
2781 			sizeof(request), &reply, sizeof(reply));
2782 		if (rv == B_OK)
2783 			*_node = reply.node;
2784 	} else {
2785 		// creator team = -1, as this is a local node
2786 		rv = MediaRosterEx(this)->InstantiateDormantNode(info.addon,
2787 			info.flavor_id, -1, _node);
2788 	}
2789 	if (rv != B_OK) {
2790 		*_node = media_node::null;
2791 		return B_NAME_NOT_FOUND;
2792 	}
2793 	return B_OK;
2794 }
2795 
2796 
2797 status_t
2798 BMediaRoster::InstantiateDormantNode(const dormant_node_info& info,
2799 	media_node* _node)
2800 {
2801 	return InstantiateDormantNode(info, _node, 0);
2802 }
2803 
2804 
2805 status_t
2806 BMediaRoster::GetDormantNodeFor(const media_node& node,
2807 	dormant_node_info* _info)
2808 {
2809 	CALLED();
2810 	if (_info == NULL)
2811 		return B_BAD_VALUE;
2812 	if (IS_INVALID_NODE(node))
2813 		return B_MEDIA_BAD_NODE;
2814 
2815 	server_get_dormant_node_for_request request;
2816 	server_get_dormant_node_for_reply reply;
2817 	status_t rv;
2818 
2819 	request.node = node;
2820 
2821 	rv = QueryServer(SERVER_GET_DORMANT_NODE_FOR, &request, sizeof(request),
2822 		&reply, sizeof(reply));
2823 	if (rv != B_OK)
2824 		return rv;
2825 
2826 	*_info = reply.node_info;
2827 	return B_OK;
2828 }
2829 
2830 
2831 status_t
2832 BMediaRosterEx::GetDormantFlavorInfo(media_addon_id addonID, int32 flavorID,
2833 	dormant_flavor_info* _flavor)
2834 {
2835 	CALLED();
2836 	if (_flavor == NULL)
2837 		return B_BAD_VALUE;
2838 
2839 	// TODO: better use an area here as well!
2840 
2841 	server_get_dormant_flavor_info_reply* reply
2842 		= (server_get_dormant_flavor_info_reply*)malloc(16300);
2843 	if (reply == NULL)
2844 		return B_NO_MEMORY;
2845 
2846 	server_get_dormant_flavor_info_request request;
2847 	request.add_on_id = addonID;
2848 	request.flavor_id = flavorID;
2849 
2850 	status_t status = QueryServer(SERVER_GET_DORMANT_FLAVOR_INFO, &request,
2851 		sizeof(request), reply, 16300);
2852 	if (status != B_OK) {
2853 		free(reply);
2854 		return status;
2855 	}
2856 
2857 	if (reply->result == B_OK) {
2858 		status = _flavor->Unflatten(reply->type, &reply->flattened_data,
2859 			reply->flattened_size);
2860 	} else
2861 		status = reply->result;
2862 
2863 	free(reply);
2864 	return status;
2865 }
2866 
2867 
2868 status_t
2869 BMediaRoster::GetDormantFlavorInfoFor(const dormant_node_info& dormant,
2870 	dormant_flavor_info* _flavor)
2871 {
2872 	return MediaRosterEx(this)->GetDormantFlavorInfo(dormant.addon,
2873 		dormant.flavor_id, _flavor);
2874 }
2875 
2876 
2877 // Reports in outLatency the maximum latency found downstream from
2878 // the specified BBufferProducer, producer, given the current connections.
2879 status_t
2880 BMediaRoster::GetLatencyFor(const media_node& producer, bigtime_t* _latency)
2881 {
2882 	CALLED();
2883 	if (_latency == NULL)
2884 		return B_BAD_VALUE;
2885 	if (IS_INVALID_NODE(producer)
2886 		|| (producer.kind & B_BUFFER_PRODUCER) == 0)
2887 		return B_MEDIA_BAD_NODE;
2888 
2889 	producer_get_latency_request request;
2890 	producer_get_latency_reply reply;
2891 	status_t rv;
2892 
2893 	rv = QueryPort(producer.port, PRODUCER_GET_LATENCY, &request,
2894 		sizeof(request), &reply, sizeof(reply));
2895 	if (rv != B_OK)
2896 		return rv;
2897 
2898 	*_latency = reply.latency;
2899 
2900 //	printf("BMediaRoster::GetLatencyFor producer %ld has maximum latency %Ld\n", producer.node, *out_latency);
2901 	return B_OK;
2902 }
2903 
2904 
2905 status_t
2906 BMediaRoster::GetInitialLatencyFor(const media_node& producer,
2907 	bigtime_t* _latency, uint32* _flags)
2908 {
2909 	CALLED();
2910 	if (_latency == NULL)
2911 		return B_BAD_VALUE;
2912 	if (IS_INVALID_NODE(producer)
2913 		|| (producer.kind & B_BUFFER_PRODUCER) == 0)
2914 		return B_MEDIA_BAD_NODE;
2915 
2916 	producer_get_initial_latency_request request;
2917 	producer_get_initial_latency_reply reply;
2918 	status_t rv;
2919 
2920 	rv = QueryPort(producer.port, PRODUCER_GET_INITIAL_LATENCY, &request,
2921 		sizeof(request), &reply, sizeof(reply));
2922 	if (rv != B_OK)
2923 		return rv;
2924 
2925 	*_latency = reply.initial_latency;
2926 	if (_flags != NULL)
2927 		*_flags = reply.flags;
2928 
2929 	TRACE("BMediaRoster::GetInitialLatencyFor producer %" B_PRId32 " has "
2930 		"maximum initial latency %" B_PRId64 "\n", producer.node, *_latency);
2931 	return B_OK;
2932 }
2933 
2934 
2935 status_t
2936 BMediaRoster::GetStartLatencyFor(const media_node& timeSource,
2937 	bigtime_t* _latency)
2938 {
2939 	CALLED();
2940 	if (_latency == NULL)
2941 		return B_BAD_VALUE;
2942 	if (IS_INVALID_NODE(timeSource)
2943 		|| (timeSource.kind & B_TIME_SOURCE) == 0)
2944 		return B_MEDIA_BAD_NODE;
2945 
2946 	timesource_get_start_latency_request request;
2947 	timesource_get_start_latency_reply reply;
2948 	status_t rv;
2949 
2950 	rv = QueryPort(timeSource.port, TIMESOURCE_GET_START_LATENCY, &request,
2951 		sizeof(request), &reply, sizeof(reply));
2952 	if (rv != B_OK)
2953 		return rv;
2954 
2955 	*_latency = reply.start_latency;
2956 
2957 	TRACE("BMediaRoster::GetStartLatencyFor timesource %" B_PRId32 " has "
2958 		"maximum initial latency %" B_PRId64 "\n", timeSource.node, *_latency);
2959 	return B_OK;
2960 }
2961 
2962 
2963 status_t
2964 BMediaRoster::GetFileFormatsFor(const media_node& fileInterface,
2965 	media_file_format* _formats, int32* _numFormats)
2966 {
2967 	CALLED();
2968 
2969 	if (IS_INVALID_NODE(fileInterface)
2970 		|| (fileInterface.kind & B_FILE_INTERFACE) == 0)
2971 		return B_MEDIA_BAD_NODE;
2972 
2973 	if (_numFormats == NULL || *_numFormats < 1)
2974 		return B_BAD_VALUE;
2975 
2976 	fileinterface_get_formats_request request;
2977 	fileinterface_get_formats_reply reply;
2978 
2979 	media_file_format* formats;
2980 	size_t needSize = sizeof(media_file_format) * *_numFormats;
2981 	size_t size = (needSize + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1);
2982 
2983 	area_id area = create_area("formats area", (void**)&formats,
2984 		B_ANY_ADDRESS, size, B_NO_LOCK,
2985 		B_READ_AREA | B_WRITE_AREA);
2986 
2987 	if (area < 0)
2988 		return B_NO_MEMORY;
2989 
2990 	request.num_formats = *_numFormats;
2991 	request.data_area = area;
2992 
2993 	status_t status = QueryPort(fileInterface.port,
2994 		FILEINTERFACE_GET_FORMATS, &request,
2995 		sizeof(request), &reply, sizeof(reply));
2996 
2997 	if (status == B_OK) {
2998 		memcpy(_formats, formats, sizeof(media_file_format)*reply.filled_slots);
2999 		*_numFormats = reply.filled_slots;
3000 	}
3001 	delete_area(area);
3002 	return status;
3003 }
3004 
3005 
3006 status_t
3007 BMediaRoster::SetRefFor(const media_node& file_interface, const entry_ref& file,
3008 	bool createAndTruncate, bigtime_t* _length)
3009 {
3010 	CALLED();
3011 
3012 	if (IS_INVALID_NODE(file_interface)
3013 		|| (file_interface.kind & B_FILE_INTERFACE) == 0)
3014 		return B_MEDIA_BAD_NODE;
3015 
3016 	fileinterface_set_ref_request request;
3017 	fileinterface_set_ref_reply reply;
3018 	status_t rv;
3019 
3020 	request.device = file.device;
3021 	request.directory = file.directory;
3022 	strcpy(request.name, file.name);
3023 	request.create = createAndTruncate;
3024 	if (_length != NULL)
3025 		request.duration = *_length;
3026 
3027 	rv = QueryPort(file_interface.port, FILEINTERFACE_SET_REF, &request,
3028 		sizeof(request), &reply, sizeof(reply));
3029 	if (rv != B_OK)
3030 		return rv;
3031 
3032 	if (!createAndTruncate && _length)
3033 		*_length = reply.duration;
3034 
3035 	return B_OK;
3036 }
3037 
3038 
3039 status_t
3040 BMediaRoster::GetRefFor(const media_node& node, entry_ref* _file,
3041 	BMimeType* mimeType)
3042 {
3043 	CALLED();
3044 
3045 	if (IS_INVALID_NODE(node)
3046 		|| (node.kind & B_FILE_INTERFACE) == 0)
3047 		return B_MEDIA_BAD_NODE;
3048 
3049 	if (!_file)
3050 		return B_BAD_VALUE;
3051 
3052 	fileinterface_get_ref_request request;
3053 	fileinterface_get_ref_reply reply;
3054 	status_t rv;
3055 
3056 	rv = QueryPort(node.port, FILEINTERFACE_GET_REF, &request, sizeof(request),
3057 		&reply, sizeof(reply));
3058 	if (rv != B_OK)
3059 		return rv;
3060 
3061 	*_file = entry_ref(reply.device, reply.directory, reply.name);
3062 
3063 	if (mimeType)
3064 		mimeType->SetTo(reply.mimetype);
3065 
3066 	return B_OK;
3067 }
3068 
3069 
3070 status_t
3071 BMediaRoster::SniffRefFor(const media_node& file_interface,
3072 	const entry_ref& file, BMimeType* mimeType, float* _capability)
3073 {
3074 	CALLED();
3075 
3076 	if (IS_INVALID_NODE(file_interface)
3077 		|| (file_interface.kind & B_FILE_INTERFACE) == 0)
3078 		return B_MEDIA_BAD_NODE;
3079 
3080 	if (mimeType == NULL || _capability == NULL)
3081 		return B_BAD_VALUE;
3082 
3083 	fileinterface_sniff_ref_request request;
3084 	fileinterface_sniff_ref_reply reply;
3085 	status_t rv;
3086 
3087 	request.device = file.device;
3088 	request.directory = file.directory;
3089 	strcpy(request.name, file.name);
3090 
3091 	rv = QueryPort(file_interface.port, FILEINTERFACE_SNIFF_REF, &request,
3092 		sizeof(request), &reply, sizeof(reply));
3093 	if (rv != B_OK)
3094 		return rv;
3095 
3096 	mimeType->SetTo(reply.mimetype);
3097 	*_capability = reply.capability;
3098 
3099 	return B_OK;
3100 }
3101 
3102 
3103 /*!	This is the generic "here's a file, now can someone please play it"
3104 	interface.
3105 */
3106 status_t
3107 BMediaRoster::SniffRef(const entry_ref& file, uint64 requireNodeKinds,
3108 	dormant_node_info* _node, BMimeType* mimeType)
3109 {
3110 	CALLED();
3111 
3112 	TRACE("BMediaRoster::SniffRef looking for a node to handle %s: 0x%" B_PRIx64
3113 		"\n", file.name, requireNodeKinds);
3114 
3115 	if (_node == NULL)
3116 		return B_BAD_VALUE;
3117 
3118 	BMimeType aMimeType;
3119 
3120 	dormant_node_info nodes[30];
3121 	int32 count = 30;
3122 	int32 highestCapability = -1;
3123 	float capability;
3124 
3125 	media_node node;
3126 
3127 	// Get all dormant nodes using GetDormantNodes
3128 	if (GetDormantNodes(nodes, &count, NULL, NULL, NULL, requireNodeKinds | B_FILE_INTERFACE, 0) == B_OK) {
3129 		// Call SniffRefFor on each node that matches requireNodeKinds
3130 		for (int32 i=0;i<count;i++) {
3131 			if (InstantiateDormantNode(nodes[i], &node) == B_OK) {
3132 
3133 				if (SniffRefFor(node, file, &aMimeType, &capability) == B_OK) {
3134 					// find the first node that has 100% capability
3135 					TRACE("%s has a %f%% chance of playing file\n",nodes[i].name, capability * 100.0);
3136 					if (capability == 1.0) {
3137 						highestCapability = i;
3138 						break;
3139 					}
3140 				}
3141 				ReleaseNode(node);
3142 			}
3143 		}
3144 
3145 		if (highestCapability != -1) {
3146 			*_node = nodes[highestCapability];
3147 
3148 			TRACE("BMediaRoster::SniffRef: found a node %s addon-id %" B_PRId32
3149 				", flavor_id %" B_PRId32 "\n",
3150 			nodes[highestCapability].name, nodes[highestCapability].addon,
3151 				nodes[highestCapability].flavor_id);
3152 
3153 			if (mimeType != NULL) {
3154 				//*mimeType = aMimeType; -- need a copy constructor
3155 			}
3156 
3157 			return B_OK;
3158 		}
3159 
3160 	}
3161 
3162 	return B_ERROR;
3163 }
3164 
3165 
3166 status_t
3167 BMediaRoster::GetDormantNodeForType(const BMimeType& type,
3168 	uint64 requireNodeKinds, dormant_node_info* _node)
3169 {
3170 	UNIMPLEMENTED();
3171 	return B_ERROR;
3172 }
3173 
3174 
3175 status_t
3176 BMediaRoster::GetReadFileFormatsFor(const dormant_node_info& node,
3177 	media_file_format* _readFormats, int32 readCount, int32* _readCount)
3178 {
3179 	UNIMPLEMENTED();
3180 	return B_ERROR;
3181 }
3182 
3183 
3184 status_t
3185 BMediaRoster::GetWriteFileFormatsFor(const dormant_node_info& node,
3186 	media_file_format* _write_formats, int32 writeCount, int32* _writeCount)
3187 {
3188 	UNIMPLEMENTED();
3189 	return B_ERROR;
3190 }
3191 
3192 
3193 status_t
3194 BMediaRoster::GetFormatFor(const media_output& output, media_format* _format,
3195 	uint32 flags)
3196 {
3197 	CALLED();
3198 	if (_format == NULL)
3199 		return B_BAD_VALUE;
3200 	if ((output.node.kind & B_BUFFER_PRODUCER) == 0)
3201 		return B_MEDIA_BAD_NODE;
3202 	if (IS_INVALID_SOURCE(output.source))
3203 		return B_MEDIA_BAD_SOURCE;
3204 
3205 	producer_format_suggestion_requested_request request;
3206 	producer_format_suggestion_requested_reply reply;
3207 	status_t rv;
3208 
3209 	request.type = B_MEDIA_UNKNOWN_TYPE;
3210 	request.quality = 0; // TODO: what should this be?
3211 
3212 	rv = QueryPort(output.source.port, PRODUCER_FORMAT_SUGGESTION_REQUESTED,
3213 		&request, sizeof(request), &reply, sizeof(reply));
3214 	if (rv != B_OK)
3215 		return rv;
3216 
3217 	*_format = reply.format;
3218 	return B_OK;
3219 }
3220 
3221 
3222 status_t
3223 BMediaRoster::GetFormatFor(const media_input& input, media_format* _format,
3224 	uint32 flags)
3225 {
3226 	CALLED();
3227 	if (_format == NULL)
3228 		return B_BAD_VALUE;
3229 	if ((input.node.kind & B_BUFFER_CONSUMER) == 0)
3230 		return B_MEDIA_BAD_NODE;
3231 	if (IS_INVALID_DESTINATION(input.destination))
3232 		return B_MEDIA_BAD_DESTINATION;
3233 
3234 	consumer_accept_format_request request;
3235 	consumer_accept_format_reply reply;
3236 	status_t rv;
3237 
3238 	request.dest = input.destination;
3239 	request.format.Clear(); // wildcard
3240 
3241 	rv = QueryPort(input.destination.port, CONSUMER_ACCEPT_FORMAT, &request,
3242 		sizeof(request), &reply, sizeof(reply));
3243 	if (rv != B_OK)
3244 		return rv;
3245 
3246 	*_format = reply.format;
3247 	return B_OK;
3248 }
3249 
3250 
3251 status_t
3252 BMediaRoster::GetFormatFor(const media_node& node, media_format* _format,
3253 	float quality)
3254 {
3255 	UNIMPLEMENTED();
3256 	if (_format == NULL)
3257 		return B_BAD_VALUE;
3258 	if (IS_INVALID_NODE(node))
3259 		return B_MEDIA_BAD_NODE;
3260 	if ((node.kind & (B_BUFFER_CONSUMER | B_BUFFER_PRODUCER)) == 0)
3261 		return B_MEDIA_BAD_NODE;
3262 
3263 	return B_ERROR;
3264 }
3265 
3266 
3267 ssize_t
3268 BMediaRoster::GetNodeAttributesFor(const media_node& node,
3269 	media_node_attribute* _array, size_t maxCount)
3270 {
3271 	CALLED();
3272 
3273 	if (IS_INVALID_NODE(node))
3274 		return B_MEDIA_BAD_NODE;
3275 
3276 	node_get_attributes_for_request request;
3277 	node_get_attributes_for_reply reply;
3278 	status_t status;
3279 
3280 	media_node_attribute* addr = NULL;
3281 	size_t totalSize = maxCount*sizeof(media_node_attribute);
3282 	size_t size = (totalSize + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1);
3283 
3284 	area_id dataArea = create_area("attributes area", (void**)&addr,
3285 		B_ANY_ADDRESS, size, B_NO_LOCK,
3286 		B_READ_AREA | B_WRITE_AREA);
3287 	// No need to memset the padding
3288 	memset(addr, 0, totalSize);
3289 
3290 	if (dataArea < 0)
3291 		return B_NO_MEMORY;
3292 
3293 	request.count = maxCount;
3294 	request.area = dataArea;
3295 
3296 	status = QueryPort(node.port, NODE_GET_ATTRIBUTES_FOR, &request,
3297 		sizeof(request), &reply, sizeof(reply));
3298 	if (status != B_OK)
3299 		return status;
3300 
3301 	memcpy(_array, addr, reply.filled_count
3302 		* sizeof(media_node_attribute));
3303 
3304 	delete_area(dataArea);
3305 	return reply.filled_count;
3306 }
3307 
3308 
3309 media_node_id
3310 BMediaRoster::NodeIDFor(port_id port)
3311 {
3312 	CALLED();
3313 
3314 	server_node_id_for_request request;
3315 	server_node_id_for_reply reply;
3316 	status_t rv;
3317 
3318 	request.port = port;
3319 
3320 	rv = QueryServer(SERVER_NODE_ID_FOR, &request, sizeof(request), &reply,
3321 		sizeof(reply));
3322 	if (rv != B_OK) {
3323 		ERROR("BMediaRoster::NodeIDFor: failed (error %#" B_PRIx32 ")\n", rv);
3324 		return -1;
3325 	}
3326 
3327 	return reply.node_id;
3328 }
3329 
3330 
3331 status_t
3332 BMediaRoster::GetInstancesFor(media_addon_id addon, int32 flavor,
3333 	media_node_id* _id, int32* _count)
3334 {
3335 	CALLED();
3336 	if (_id == NULL)
3337 		return B_BAD_VALUE;
3338 	if (_count && *_count <= 0)
3339 		return B_BAD_VALUE;
3340 
3341 	server_get_instances_for_request request;
3342 	server_get_instances_for_reply reply;
3343 	status_t rv;
3344 
3345 	request.max_count = (_count ? *_count : 1);
3346 	request.add_on_id = addon;
3347 	request.flavor_id = flavor;
3348 
3349 	rv = QueryServer(SERVER_GET_INSTANCES_FOR, &request, sizeof(request),
3350 		&reply, sizeof(reply));
3351 	if (rv != B_OK) {
3352 		ERROR("BMediaRoster::GetLiveNodes failed\n");
3353 		return rv;
3354 	}
3355 
3356 	if (_count)
3357 		*_count = reply.count;
3358 	if (reply.count > 0)
3359 		memcpy(_id, reply.node_id, sizeof(media_node_id) * reply.count);
3360 
3361 	return B_OK;
3362 }
3363 
3364 
3365 bool
3366 BMediaRoster::IsRunning()
3367 {
3368 	return be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)
3369 		&& be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE);
3370 }
3371 
3372 
3373 ssize_t
3374 BMediaRoster::AudioBufferSizeFor(int32 channelCount, uint32 sampleFormat,
3375 	float frameRate, bus_type busKind)
3376 {
3377 	bigtime_t bufferDuration;
3378 	ssize_t bufferSize;
3379 
3380 	if (busKind == B_ISA_BUS || busKind == B_PCMCIA_BUS)
3381 		bufferDuration = 25000;
3382 	else
3383 		bufferDuration = 10000;
3384 
3385 	bufferSize = (sampleFormat & 0xf) * channelCount
3386 		* (ssize_t)((frameRate * bufferDuration) / 1000000.0);
3387 
3388 	printf("Suggested buffer duration %" B_PRId64 ", size %" B_PRIdSSIZE "\n",
3389 		bufferDuration, bufferSize);
3390 
3391 	return bufferSize;
3392 }
3393 
3394 
3395 /*!	Use MediaFlags to inquire about specific features of the Media Kit.
3396 	Returns < 0 for "not present", positive size for output data size.
3397 	0 means that the capability is present, but no data about it.
3398 */
3399 /*static*/ ssize_t
3400 BMediaRoster::MediaFlags(media_flags cap, void* buffer, size_t maxSize)
3401 {
3402 	UNIMPLEMENTED();
3403 	return 0;
3404 }
3405 
3406 
3407 //	#pragma mark - BLooper overrides
3408 
3409 
3410 void
3411 BMediaRoster::MessageReceived(BMessage* message)
3412 {
3413 	switch (message->what) {
3414 		case MEDIA_ROSTER_REQUEST_NOTIFICATIONS:
3415 		{
3416 			RosterNotification notification;
3417 			if (message->FindInt32(NOTIFICATION_PARAM_WHAT, &notification.what)
3418 					!= B_OK) {
3419 				TRACE("BMediaRoster MEDIA_ROSTER_REQUEST_NOTIFICATIONS can't"
3420 					"find what parameter");
3421 				return;
3422 			}
3423 			if (message->FindMessenger(NOTIFICATION_PARAM_MESSENGER,
3424 					&notification.messenger) != B_OK) {
3425 				TRACE("BMediaRoster MEDIA_ROSTER_REQUEST_NOTIFICATIONS can't"
3426 					"find messenger");
3427 				return;
3428 			}
3429 			sNotificationList.Insert(notification);
3430 			return;
3431 		}
3432 
3433 		case MEDIA_ROSTER_CANCEL_NOTIFICATIONS:
3434 		{
3435 			RosterNotification notification;
3436 			if (message->FindInt32(NOTIFICATION_PARAM_WHAT, &notification.what)
3437 					!= B_OK) {
3438 				TRACE("BMediaRoster MEDIA_ROSTER_CANCEL_NOTIFICATIONS can't"
3439 					"find what parameter");
3440 				return;
3441 			}
3442 			if (message->FindMessenger(NOTIFICATION_PARAM_MESSENGER,
3443 					&notification.messenger) != B_OK) {
3444 				TRACE("BMediaRoster MEDIA_ROSTER_CANCEL_NOTIFICATIONS can't"
3445 					"find messenger");
3446 				return;
3447 			}
3448 			for (int32 i = 0; i < sNotificationList.CountItems(); i++) {
3449 				RosterNotification* current;
3450 				if (sNotificationList.Get(i, &current) != true)
3451 					return;
3452 				if (current->what == notification.what
3453 						&& current->messenger == notification.messenger) {
3454 					sNotificationList.Remove(i);
3455 					return;
3456 				}
3457 			}
3458 			return;
3459 		}
3460 
3461 		case B_SOME_APP_LAUNCHED:
3462 		{
3463 			BString mimeSig;
3464 			if (message->FindString("be:signature", &mimeSig) != B_OK)
3465 				return;
3466 			if (mimeSig != B_MEDIA_ADDON_SERVER_SIGNATURE
3467 					&& mimeSig != B_MEDIA_SERVER_SIGNATURE)
3468 				return;
3469 
3470 			TRACE("BMediaRoster::MessageReceived media services are going up.");
3471 
3472 			if (BMediaRoster::IsRunning()) {
3473 				// Wait for media services to wake up and restore our friendship
3474 				if (MediaRosterEx(this)->BuildConnections() != B_OK) {
3475 					TRACE("BMediaRoster::MessageReceived can't reconnect"
3476 						"to media_server.");
3477 				}
3478 			}
3479 			return;
3480 		}
3481 
3482 		case B_SOME_APP_QUIT:
3483 		{
3484 			BString mimeSig;
3485 			if (message->FindString("be:signature", &mimeSig) != B_OK)
3486 				return;
3487 			if (mimeSig != B_MEDIA_ADDON_SERVER_SIGNATURE
3488 					&& mimeSig != B_MEDIA_SERVER_SIGNATURE)
3489 				return;
3490 
3491 			TRACE("BMediaRoster::MessageReceived media services are down.");
3492 
3493 			// Send the notification to our subscribers
3494 			if (!BMediaRoster::IsRunning() && sServerIsUp == true) {
3495 				sServerIsUp = false;
3496 				for (int32 i = 0; i < sNotificationList.CountItems(); i++) {
3497 					RosterNotification* current;
3498 					if (sNotificationList.Get(i, &current) != true)
3499 						return;
3500 					if (current->what == B_MEDIA_SERVER_QUIT) {
3501 						if (current->messenger.SendMessage(
3502 								B_MEDIA_SERVER_QUIT) != B_OK) {
3503 							if(!current->messenger.IsValid())
3504 								sNotificationList.Remove(i);
3505 						}
3506 					}
3507 				}
3508 			}
3509 			return;
3510 		}
3511 
3512 		case MEDIA_SERVER_ALIVE:
3513 		{
3514 			if (!BMediaRoster::IsRunning())
3515 				return;
3516 
3517 			sServerIsUp = true;
3518 
3519 			TRACE("BMediaRoster::MessageReceived media services are"
3520 				" finally up.");
3521 
3522 			if (MediaRosterEx(this)->fLaunchNotification) {
3523 				progress_startup(100, NULL, NULL);
3524 				if (MediaRosterEx(this)->fAutoExit)
3525 					MediaRosterEx(this)->fLaunchNotification = false;
3526 			}
3527 
3528 			// Send the notification to our subscribers
3529 			for (int32 i = 0; i < sNotificationList.CountItems(); i++) {
3530 				RosterNotification* current;
3531 				if (sNotificationList.Get(i, &current) != true)
3532 					return;
3533 				if (current->what == B_MEDIA_SERVER_STARTED) {
3534 					if (current->messenger.SendMessage(
3535 							B_MEDIA_SERVER_STARTED) != B_OK) {
3536 						if(!current->messenger.IsValid())
3537 							sNotificationList.Remove(i);
3538 					}
3539 				}
3540 			}
3541 			return;
3542 		}
3543 
3544 		case NODE_FINAL_RELEASE:
3545 		{
3546 			// This function is called by a BMediaNode to delete
3547 			// itself, as this needs to be done from another thread
3548 			// context, it is done here.
3549 
3550 			BMediaNode* node = NULL;
3551 			status_t err = message->FindPointer("node",
3552 				reinterpret_cast<void **>(&node));
3553 			if (err == B_OK && node != NULL)
3554 				node->Release();
3555 			else {
3556 				TRACE("BMediaRoster::MessageReceived: CRITICAL! received"
3557 					"a release request but the node can't be found.");
3558 			}
3559 			return;
3560 		}
3561 
3562 		default:
3563 			BLooper::MessageReceived(message);
3564 			break;
3565 	}
3566 }
3567 
3568 
3569 bool
3570 BMediaRoster::QuitRequested()
3571 {
3572 	CALLED();
3573 	return true;
3574 }
3575 
3576 
3577 BHandler*
3578 BMediaRoster::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
3579 	int32 form, const char* property)
3580 {
3581 	return BLooper::ResolveSpecifier(msg, index, specifier, form, property);
3582 }
3583 
3584 
3585 status_t
3586 BMediaRoster::GetSupportedSuites(BMessage* data)
3587 {
3588 	return BLooper::GetSupportedSuites(data);
3589 }
3590 
3591 
3592 BMediaRoster::~BMediaRoster()
3593 {
3594 	CALLED();
3595 
3596 	// Unset the global instance pointer, the destructor is also called
3597 	// if a client app calls Lock(); and Quit(); directly.
3598 	sDefaultInstance = NULL;
3599 }
3600 
3601 //	#pragma mark - private BMediaRoster
3602 
3603 // FBC reserved virtuals
3604 status_t BMediaRoster::_Reserved_MediaRoster_0(void*) { return B_ERROR; }
3605 status_t BMediaRoster::_Reserved_MediaRoster_1(void*) { return B_ERROR; }
3606 status_t BMediaRoster::_Reserved_MediaRoster_2(void*) { return B_ERROR; }
3607 status_t BMediaRoster::_Reserved_MediaRoster_3(void*) { return B_ERROR; }
3608 status_t BMediaRoster::_Reserved_MediaRoster_4(void*) { return B_ERROR; }
3609 status_t BMediaRoster::_Reserved_MediaRoster_5(void*) { return B_ERROR; }
3610 status_t BMediaRoster::_Reserved_MediaRoster_6(void*) { return B_ERROR; }
3611 status_t BMediaRoster::_Reserved_MediaRoster_7(void*) { return B_ERROR; }
3612 
3613 
3614 BMediaRoster::BMediaRoster()
3615 	:
3616 	BLooper("_BMediaRoster_", B_URGENT_DISPLAY_PRIORITY,
3617 		B_LOOPER_PORT_DEFAULT_CAPACITY)
3618 {
3619 	CALLED();
3620 
3621 	// start the looper
3622 	Run();
3623 }
3624 
3625 // #pragma mark - static variables
3626 
3627 BMediaRoster* BMediaRoster::sDefaultInstance = NULL;
3628