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