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