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