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