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