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