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