1 /*
2 * Copyright 2015 Dario Casalinuovo
3 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
4 * Copyright 2008 Maurice Kalinowski, haiku@kaldience.com
5 *
6 * All rights reserved. Distributed under the terms of the MIT License.
7 */
8
9 /*
10 * Copyright (c) 2002-2006 Marcus Overhagen <Marcus@Overhagen.de>
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files or portions
14 * thereof (the "Software"), to deal in the Software without restriction,
15 * including without limitation the rights to use, copy, modify, merge,
16 * publish, distribute, sublicense, and/or sell copies of the Software,
17 * and to permit persons to whom the Software is furnished to do so, subject
18 * to the following conditions:
19 *
20 * * Redistributions of source code must retain the above copyright notice,
21 * this list of conditions and the following disclaimer.
22 *
23 * * Redistributions in binary form must reproduce the above copyright notice
24 * in the binary, as well as this list of conditions and the following
25 * disclaimer in the documentation and/or other materials provided with
26 * the distribution.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 * THE SOFTWARE.
35 */
36
37
38 /* to comply with the license above, do not remove the following line */
39 char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002-2006 Marcus "
40 "Overhagen <Marcus@Overhagen.de>";
41
42
43 #include <MediaRoster.h>
44
45 #include <Application.h>
46 #include <Autolock.h>
47 #include <BufferConsumer.h>
48 #include <BufferProducer.h>
49 #include <Locker.h>
50 #include <Message.h>
51 #include <Messenger.h>
52 #include <MimeType.h>
53 #include <OS.h>
54 #include <ParameterWeb.h>
55 #include <Roster.h>
56 #include <StopWatch.h>
57 #include <String.h>
58 #include <TimeSource.h>
59
60 #include <new>
61
62 #include <AppMisc.h>
63 #include <DataExchange.h>
64 #include <MediaDebug.h>
65 #include <DormantNodeManager.h>
66 #include <MediaMisc.h>
67 #include <MediaRosterEx.h>
68 #include <Notifications.h>
69 #include <ServerInterface.h>
70 #include <SharedBufferList.h>
71 #include <TList.h>
72
73 #include "PortPool.h"
74 #include "TimeSourceObjectManager.h"
75
76
77 namespace BPrivate {
78 namespace media {
79
80
81 struct RosterNotification {
82 BMessenger messenger;
83 int32 what;
84 };
85
86
87 struct SyncedMessage {
88 BMessage* message;
89 };
90
91
92 struct LocalNode {
LocalNodeBPrivate::media::LocalNode93 LocalNode(BMediaNode* local_node)
94 :
95 node(local_node) {}
96
LocalNodeBPrivate::media::LocalNode97 LocalNode()
98 :
99 node(NULL) {}
100
operator ==BPrivate::media::LocalNode101 bool operator==(const LocalNode& a)
102 {
103 if (a.node == this->node)
104 return true;
105 return false;
106 }
107
108 BMediaNode* node;
109 };
110
111
112 static bool sServerIsUp = false;
113 static List<RosterNotification> sNotificationList;
114 static BLocker sInitLocker("BMediaRoster::Roster locker");
115 static List<LocalNode> sRegisteredNodes;
116
117
118 // 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.
122 class MediaRosterUndertaker {
123 public:
MediaRosterUndertaker()124 MediaRosterUndertaker()
125 {
126 gPortPool = new PortPool();
127 }
128
~MediaRosterUndertaker()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
165 static MediaRosterUndertaker sMediaRosterUndertaker;
166
167 } // namespace media
168 } // namespace BPrivate
169
170 using namespace BPrivate::media;
171
172
BMediaRosterEx(status_t * _error)173 BMediaRosterEx::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
194 void
Quit()195 BMediaRosterEx::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
207 status_t
BuildConnections()208 BMediaRosterEx::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
~BMediaRosterEx()225 BMediaRosterEx::~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
243 void
RegisterLocalNode(BMediaNode * node)244 BMediaRosterEx::RegisterLocalNode(BMediaNode* node)
245 {
246 sRegisteredNodes.Insert(LocalNode(node));
247 }
248
249
250 void
UnregisterLocalNode(BMediaNode * node)251 BMediaRosterEx::UnregisterLocalNode(BMediaNode* node)
252 {
253 int32 index = sRegisteredNodes.Find(LocalNode(node));
254 if (index != -1)
255 sRegisteredNodes.Remove(index);
256 }
257
258
259 void
EnableLaunchNotification(bool enable,bool autoExit)260 BMediaRosterEx::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
270 status_t
SaveNodeConfiguration(BMediaNode * node)271 BMediaRosterEx::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
294 status_t
LoadNodeConfiguration(media_addon_id addonID,int32 flavorID,BMessage * _msg)295 BMediaRosterEx::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
307 status_t
IncrementAddonFlavorInstancesCount(media_addon_id addonID,int32 flavorID)308 BMediaRosterEx::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
323 status_t
DecrementAddonFlavorInstancesCount(media_addon_id addonID,int32 flavorID)324 BMediaRosterEx::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
339 status_t
ReleaseNodeAll(const media_node & node)340 BMediaRosterEx::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
380 status_t
SetNodeCreator(media_node_id node,team_id creator)381 BMediaRosterEx::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
393 status_t
GetNode(node_type type,media_node * out_node,int32 * out_input_id,BString * out_input_name)394 BMediaRosterEx::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
420 status_t
SetNode(node_type type,const media_node * node,const dormant_node_info * info,const media_input * input)421 BMediaRosterEx::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
443 status_t
GetAllOutputs(const media_node & node,List<media_output> * list)444 BMediaRosterEx::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
490 status_t
GetAllOutputs(BBufferProducer * node,List<media_output> * list)491 BMediaRosterEx::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
520 status_t
GetAllInputs(const media_node & node,List<media_input> * list)521 BMediaRosterEx::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
567 status_t
GetAllInputs(BBufferConsumer * node,List<media_input> * list)568 BMediaRosterEx::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
597 status_t
PublishOutputs(const media_node & node,List<media_output> * list)598 BMediaRosterEx::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
647 status_t
PublishInputs(const media_node & node,List<media_input> * list)648 BMediaRosterEx::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
697 BTimeSource*
MakeTimeSourceObject(media_node_id timeSourceID)698 BMediaRosterEx::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
724 status_t
GetVideoInput(media_node * _node)725 BMediaRoster::GetVideoInput(media_node* _node)
726 {
727 CALLED();
728 return MediaRosterEx(this)->GetNode(VIDEO_INPUT, _node);
729 }
730
731
732 status_t
GetAudioInput(media_node * _node)733 BMediaRoster::GetAudioInput(media_node* _node)
734 {
735 CALLED();
736 return MediaRosterEx(this)->GetNode(AUDIO_INPUT, _node);
737 }
738
739
740 status_t
GetVideoOutput(media_node * _node)741 BMediaRoster::GetVideoOutput(media_node* _node)
742 {
743 CALLED();
744 return MediaRosterEx(this)->GetNode(VIDEO_OUTPUT, _node);
745 }
746
747
748 status_t
GetAudioMixer(media_node * _node)749 BMediaRoster::GetAudioMixer(media_node* _node)
750 {
751 CALLED();
752 return MediaRosterEx(this)->GetNode(AUDIO_MIXER, _node);
753 }
754
755
756 status_t
GetAudioOutput(media_node * _node)757 BMediaRoster::GetAudioOutput(media_node* _node)
758 {
759 CALLED();
760 return MediaRosterEx(this)->GetNode(AUDIO_OUTPUT, _node);
761 }
762
763
764 status_t
GetAudioOutput(media_node * _node,int32 * _inputID,BString * _inputName)765 BMediaRoster::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
774 status_t
GetTimeSource(media_node * _node)775 BMediaRoster::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
796 status_t
SetVideoInput(const media_node & producer)797 BMediaRoster::SetVideoInput(const media_node& producer)
798 {
799 CALLED();
800 return MediaRosterEx(this)->SetNode(VIDEO_INPUT, &producer);
801 }
802
803
804 status_t
SetVideoInput(const dormant_node_info & producer)805 BMediaRoster::SetVideoInput(const dormant_node_info& producer)
806 {
807 CALLED();
808 return MediaRosterEx(this)->SetNode(VIDEO_INPUT, NULL, &producer);
809 }
810
811
812 status_t
SetAudioInput(const media_node & producer)813 BMediaRoster::SetAudioInput(const media_node& producer)
814 {
815 CALLED();
816 return MediaRosterEx(this)->SetNode(AUDIO_INPUT, &producer);
817 }
818
819
820 status_t
SetAudioInput(const dormant_node_info & producer)821 BMediaRoster::SetAudioInput(const dormant_node_info& producer)
822 {
823 CALLED();
824 return MediaRosterEx(this)->SetNode(AUDIO_INPUT, NULL, &producer);
825 }
826
827
828 status_t
SetVideoOutput(const media_node & consumer)829 BMediaRoster::SetVideoOutput(const media_node& consumer)
830 {
831 CALLED();
832 return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, &consumer);
833 }
834
835
836 status_t
SetVideoOutput(const dormant_node_info & consumer)837 BMediaRoster::SetVideoOutput(const dormant_node_info& consumer)
838 {
839 CALLED();
840 return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, NULL, &consumer);
841 }
842
843
844 status_t
SetAudioOutput(const media_node & consumer)845 BMediaRoster::SetAudioOutput(const media_node& consumer)
846 {
847 CALLED();
848 return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, &consumer);
849 }
850
851
852 status_t
SetAudioOutput(const media_input & input)853 BMediaRoster::SetAudioOutput(const media_input& input)
854 {
855 CALLED();
856 return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, NULL, &input);
857 }
858
859
860 status_t
SetAudioOutput(const dormant_node_info & consumer)861 BMediaRoster::SetAudioOutput(const dormant_node_info& consumer)
862 {
863 CALLED();
864 return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, &consumer);
865 }
866
867
868 status_t
GetNodeFor(media_node_id node,media_node * clone)869 BMediaRoster::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
894 status_t
GetSystemTimeSource(media_node * clone)895 BMediaRoster::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
918 status_t
ReleaseNode(const media_node & node)919 BMediaRoster::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
955 BTimeSource*
MakeTimeSourceFor(const media_node & forNode)956 BMediaRoster::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
1003 status_t
Connect(const media_source & from,const media_destination & to,media_format * _format,media_output * _output,media_input * _input)1004 BMediaRoster::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
1011 status_t
Connect(const media_source & from,const media_destination & to,media_format * io_format,media_output * out_output,media_input * out_input,uint32 in_flags,void * _reserved)1012 BMediaRoster::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
1207 status_t
Disconnect(media_node_id source_nodeid,const media_source & source,media_node_id destination_nodeid,const media_destination & destination)1208 BMediaRoster::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
1287 status_t
Disconnect(const media_output & output,const media_input & input)1288 BMediaRoster::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
1340 status_t
StartNode(const media_node & node,bigtime_t atPerformanceTime)1341 BMediaRoster::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
1357 status_t
StopNode(const media_node & node,bigtime_t atPerformanceTime,bool immediate)1358 BMediaRoster::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
1376 status_t
SeekNode(const media_node & node,bigtime_t toMediaTime,bigtime_t atPerformanceTime)1377 BMediaRoster::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
1395 status_t
StartTimeSource(const media_node & node,bigtime_t atRealTime)1396 BMediaRoster::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
1431 status_t
StopTimeSource(const media_node & node,bigtime_t atRealTime,bool immediate)1432 BMediaRoster::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
1469 status_t
SeekTimeSource(const media_node & node,bigtime_t toPerformanceTime,bigtime_t atRealTime)1470 BMediaRoster::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
1509 status_t
SyncToNode(const media_node & node,bigtime_t atTime,bigtime_t timeout)1510 BMediaRoster::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
1542 status_t
SetRunModeNode(const media_node & node,BMediaNode::run_mode mode)1543 BMediaRoster::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
1557 status_t
PrerollNode(const media_node & node)1558 BMediaRoster::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
1569 status_t
RollNode(const media_node & node,bigtime_t startPerformance,bigtime_t stopPerformance,bigtime_t atMediaTime)1570 BMediaRoster::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
1591 status_t
SetProducerRunModeDelay(const media_node & node,bigtime_t delay,BMediaNode::run_mode mode)1592 BMediaRoster::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
1611 status_t
SetProducerRate(const media_node & producer,int32 numer,int32 denom)1612 BMediaRoster::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 */
1641 status_t
GetLiveNodeInfo(const media_node & node,live_node_info * out_live_info)1642 BMediaRoster::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
1667 status_t
GetLiveNodes(live_node_info * liveNodes,int32 * _totalCount,const media_format * hasInput,const media_format * hasOutput,const char * name,uint64 nodeKinds)1668 BMediaRoster::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
1725 status_t
GetFreeInputsFor(const media_node & node,media_input * out_free_inputs,int32 buf_num_inputs,int32 * out_total_count,media_type filter_type)1726 BMediaRoster::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
1786 status_t
GetConnectedInputsFor(const media_node & node,media_input * out_active_inputs,int32 buf_num_inputs,int32 * out_total_count)1787 BMediaRoster::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
1830 status_t
GetAllInputsFor(const media_node & node,media_input * out_inputs,int32 buf_num_inputs,int32 * out_total_count)1831 BMediaRoster::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
1870 status_t
GetFreeOutputsFor(const media_node & node,media_output * out_free_outputs,int32 buf_num_outputs,int32 * out_total_count,media_type filter_type)1871 BMediaRoster::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
1923 status_t
GetConnectedOutputsFor(const media_node & node,media_output * out_active_outputs,int32 buf_num_outputs,int32 * out_total_count)1924 BMediaRoster::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
1969 status_t
GetAllOutputsFor(const media_node & node,media_output * out_outputs,int32 buf_num_outputs,int32 * out_total_count)1970 BMediaRoster::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
2009 status_t
StartWatching(const BMessenger & where)2010 BMediaRoster::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
2022 status_t
StartWatching(const BMessenger & where,int32 notificationType)2023 BMediaRoster::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
2044 status_t
StartWatching(const BMessenger & where,const media_node & node,int32 notificationType)2045 BMediaRoster::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
2067 status_t
StopWatching(const BMessenger & where)2068 BMediaRoster::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
2077 status_t
StopWatching(const BMessenger & where,int32 notificationType)2078 BMediaRoster::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
2092 status_t
StopWatching(const BMessenger & where,const media_node & node,int32 notificationType)2093 BMediaRoster::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
2112 status_t
RegisterNode(BMediaNode * node)2113 BMediaRoster::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
2121 status_t
RegisterNode(BMediaNode * node,media_addon_id addOnID,int32 flavorID)2122 BMediaRosterEx::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
2232 status_t
UnregisterNode(BMediaNode * node)2233 BMediaRoster::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*
Roster(status_t * out_error)2315 BMediaRoster::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*
CurrentRoster()2351 BMediaRoster::CurrentRoster()
2352 {
2353 return sDefaultInstance;
2354 }
2355
2356
2357 status_t
SetTimeSourceFor(media_node_id node,media_node_id time_source)2358 BMediaRoster::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
2414 status_t
GetParameterWebFor(const media_node & node,BParameterWeb ** _web)2415 BMediaRoster::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
2492 status_t
StartControlPanel(const media_node & node,BMessenger * _messenger)2493 BMediaRoster::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
2515 status_t
GetDormantNodes(dormant_node_info * _info,int32 * _count,const media_format * hasInput,const media_format * hasOutput,const char * name,uint64 requireKinds,uint64 denyKinds)2516 BMediaRoster::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 */
2571 status_t
InstantiateDormantNode(media_addon_id addonID,int32 flavorID,team_id creator,media_node * _node)2572 BMediaRosterEx::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
2706 status_t
InstantiateDormantNode(const dormant_node_info & info,media_node * _node,uint32 flags)2707 BMediaRoster::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
2812 status_t
InstantiateDormantNode(const dormant_node_info & info,media_node * _node)2813 BMediaRoster::InstantiateDormantNode(const dormant_node_info& info,
2814 media_node* _node)
2815 {
2816 return InstantiateDormantNode(info, _node, 0);
2817 }
2818
2819
2820 status_t
GetDormantNodeFor(const media_node & node,dormant_node_info * _info)2821 BMediaRoster::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
2846 status_t
GetDormantFlavorInfo(media_addon_id addonID,int32 flavorID,dormant_flavor_info * _flavor)2847 BMediaRosterEx::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
2883 status_t
GetDormantFlavorInfoFor(const dormant_node_info & dormant,dormant_flavor_info * _flavor)2884 BMediaRoster::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.
2894 status_t
GetLatencyFor(const media_node & producer,bigtime_t * _latency)2895 BMediaRoster::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
2920 status_t
GetInitialLatencyFor(const media_node & producer,bigtime_t * _latency,uint32 * _flags)2921 BMediaRoster::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
2950 status_t
GetStartLatencyFor(const media_node & timeSource,bigtime_t * _latency)2951 BMediaRoster::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
2978 status_t
GetFileFormatsFor(const media_node & fileInterface,media_file_format * _formats,int32 * _numFormats)2979 BMediaRoster::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
3021 status_t
SetRefFor(const media_node & file_interface,const entry_ref & file,bool createAndTruncate,bigtime_t * _length)3022 BMediaRoster::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
3054 status_t
GetRefFor(const media_node & node,entry_ref * _file,BMimeType * mimeType)3055 BMediaRoster::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
3085 status_t
SniffRefFor(const media_node & file_interface,const entry_ref & file,BMimeType * mimeType,float * _capability)3086 BMediaRoster::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 */
3121 status_t
SniffRef(const entry_ref & file,uint64 requireNodeKinds,dormant_node_info * _node,BMimeType * mimeType)3122 BMediaRoster::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
3181 status_t
GetDormantNodeForType(const BMimeType & type,uint64 requireNodeKinds,dormant_node_info * _node)3182 BMediaRoster::GetDormantNodeForType(const BMimeType& type,
3183 uint64 requireNodeKinds, dormant_node_info* _node)
3184 {
3185 UNIMPLEMENTED();
3186 return B_ERROR;
3187 }
3188
3189
3190 status_t
GetReadFileFormatsFor(const dormant_node_info & node,media_file_format * _readFormats,int32 readCount,int32 * _readCount)3191 BMediaRoster::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
3199 status_t
GetWriteFileFormatsFor(const dormant_node_info & node,media_file_format * _write_formats,int32 writeCount,int32 * _writeCount)3200 BMediaRoster::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
3208 status_t
GetFormatFor(const media_output & output,media_format * _format,uint32 flags)3209 BMediaRoster::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
3237 status_t
GetFormatFor(const media_input & input,media_format * _format,uint32 flags)3238 BMediaRoster::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
3266 status_t
GetFormatFor(const media_node & node,media_format * _format,float quality)3267 BMediaRoster::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
3282 ssize_t
GetNodeAttributesFor(const media_node & node,media_node_attribute * _array,size_t maxCount)3283 BMediaRoster::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
3324 media_node_id
NodeIDFor(port_id port)3325 BMediaRoster::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
3346 status_t
GetInstancesFor(media_addon_id addon,int32 flavor,media_node_id * _id,int32 * _count)3347 BMediaRoster::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
3380 bool
IsRunning()3381 BMediaRoster::IsRunning()
3382 {
3383 return be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)
3384 && be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE);
3385 }
3386
3387
3388 ssize_t
AudioBufferSizeFor(int32 channelCount,uint32 sampleFormat,float frameRate,bus_type busKind)3389 BMediaRoster::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
MediaFlags(media_flags cap,void * buffer,size_t maxSize)3415 BMediaRoster::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
3425 void
MessageReceived(BMessage * message)3426 BMediaRoster::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, ¬ification.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 ¬ification.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, ¬ification.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 ¬ification.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, ¤t) != 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, ¤t) != 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, ¤t) != 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
3584 bool
QuitRequested()3585 BMediaRoster::QuitRequested()
3586 {
3587 CALLED();
3588 return true;
3589 }
3590
3591
3592 BHandler*
ResolveSpecifier(BMessage * msg,int32 index,BMessage * specifier,int32 form,const char * property)3593 BMediaRoster::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
3600 status_t
GetSupportedSuites(BMessage * data)3601 BMediaRoster::GetSupportedSuites(BMessage* data)
3602 {
3603 return BLooper::GetSupportedSuites(data);
3604 }
3605
3606
~BMediaRoster()3607 BMediaRoster::~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
_Reserved_MediaRoster_0(void *)3619 status_t BMediaRoster::_Reserved_MediaRoster_0(void*) { return B_ERROR; }
_Reserved_MediaRoster_1(void *)3620 status_t BMediaRoster::_Reserved_MediaRoster_1(void*) { return B_ERROR; }
_Reserved_MediaRoster_2(void *)3621 status_t BMediaRoster::_Reserved_MediaRoster_2(void*) { return B_ERROR; }
_Reserved_MediaRoster_3(void *)3622 status_t BMediaRoster::_Reserved_MediaRoster_3(void*) { return B_ERROR; }
_Reserved_MediaRoster_4(void *)3623 status_t BMediaRoster::_Reserved_MediaRoster_4(void*) { return B_ERROR; }
_Reserved_MediaRoster_5(void *)3624 status_t BMediaRoster::_Reserved_MediaRoster_5(void*) { return B_ERROR; }
_Reserved_MediaRoster_6(void *)3625 status_t BMediaRoster::_Reserved_MediaRoster_6(void*) { return B_ERROR; }
_Reserved_MediaRoster_7(void *)3626 status_t BMediaRoster::_Reserved_MediaRoster_7(void*) { return B_ERROR; }
3627
3628
BMediaRoster()3629 BMediaRoster::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
3642 BMediaRoster* BMediaRoster::sDefaultInstance = NULL;
3643