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