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