1 // NodeRef.cpp 2 3 #include "NodeRef.h" 4 5 #include "Connection.h" 6 #include "NodeGroup.h" 7 //#include "NodeGroup_transport_thread.h" 8 #include "NodeManager.h" 9 #include "NodeSyncThread.h" 10 11 #include "AddOnHost.h" 12 13 #include <Entry.h> 14 #include <MediaRoster.h> 15 #include <TimeSource.h> 16 17 #include <algorithm> 18 #include <functional> 19 #include <iterator> 20 #include <stdexcept> 21 22 #include "functional_tools.h" 23 #include "node_manager_impl.h" 24 #include "SoundUtils.h" 25 26 // -------------------------------------------------------- // 27 28 using namespace std; 29 30 __USE_CORTEX_NAMESPACE 31 32 #define D_METHOD(x) //PRINT (x) 33 #define D_MESSAGE(x) //PRINT (x) 34 #define D_ROSTER(x) //PRINT (x) 35 #define D_LOCK(x) //PRINT (x) 36 37 // -------------------------------------------------------- // 38 // addon_hint 39 // 40 // Contains information that can be used to reconstruct this 41 // node later on. 42 // -------------------------------------------------------- // 43 44 // [e.moon 29sep99] added 'file' 45 46 struct NodeRef::addon_hint { 47 48 addon_hint( 49 const dormant_node_info* _dormantInfo, 50 const entry_ref* _file=0) : 51 dormantInfo(*_dormantInfo), 52 file(_file ? new entry_ref(*_file) : 0) {} 53 54 ~addon_hint() { 55 if(file) delete file; 56 } 57 58 void setFile( 59 const entry_ref* _file) { 60 ASSERT(_file); 61 if(file) delete file; 62 file = new entry_ref(*_file); 63 } 64 65 dormant_node_info dormantInfo; 66 entry_ref* file; 67 }; 68 69 // -------------------------------------------------------- // 70 // *** ctor/dtor 71 // -------------------------------------------------------- // 72 73 // free the node (this call will result in the eventual 74 // deletion of the object.) 75 76 status_t NodeRef::release() { 77 78 // release the node, if possible: 79 status_t err = releaseNode(); 80 81 // hand off to parent release() implementation 82 status_t parentErr = _inherited::release(); 83 return (parentErr < B_OK) ? parentErr : err; 84 } 85 86 NodeRef::~NodeRef() { 87 D_METHOD(("~NodeRef[%s]\n", name())); 88 Autolock _l(m_manager); 89 90 // remove from NodeManager 91 m_manager->_removeRef(id()); 92 93 // [14oct99 e.moon] kill position-report thread if necessary 94 if(m_positionThread) 95 _stopPositionThread(); 96 97 if(m_addonHint) { 98 delete m_addonHint; 99 m_addonHint = 0; 100 } 101 102 // close Media Roster connection [e.moon 11oct99] 103 BMediaRoster* r = BMediaRoster::Roster(); 104 if(m_watching) { 105 r->StopWatching( 106 BMessenger(this), 107 node(), 108 B_MEDIA_WILDCARD); 109 } 110 } 111 112 // -------------------------------------------------------- // 113 // *** const accessors 114 // -------------------------------------------------------- // 115 116 // [e.moon 13oct99] moved to header 117 118 //inline const media_node& NodeRef::node() const { return m_info.node; } 119 //inline uint32 NodeRef::kind() const { return m_info.node.kind; } 120 //inline const live_node_info& NodeRef::nodeInfo() const { return m_info; } 121 //inline media_node_id NodeRef::id() const { return m_info.node.node; } 122 //inline const char* NodeRef::name() const { return m_info.name; } 123 124 // -------------------------------------------------------- // 125 // *** member access 126 // -------------------------------------------------------- // 127 128 // turn cycle mode (looping) on or off 129 130 void NodeRef::setCycling( 131 bool cycle) { 132 Autolock _l(this); 133 134 D_METHOD(( 135 "NodeRef::setCycling(%s)\n", 136 cycle ? "true" : "false")); 137 138 if(cycle == m_cycle) 139 return; 140 141 m_cycle = cycle; 142 143 if(m_group) { 144 m_group->_refCycleChanged(this); 145 146 // +++++ if running, get started... 147 } 148 149 // notify 150 BMessage m(M_CYCLING_CHANGED); 151 m.AddBool("cycling", cycle); 152 notify(&m); 153 } 154 155 bool NodeRef::isCycling() const { 156 return m_cycle; 157 } 158 159 160 // is the node running? 161 162 bool NodeRef::isRunning() const { 163 return m_running; 164 } 165 166 // was the node created via NodeManager::instantiate()? 167 bool NodeRef::isInternal() const { 168 return m_implFlags & _INTERNAL; 169 } 170 171 172 // fetch the group; may return 0 if the node has no connections 173 NodeGroup* NodeRef::group() const { 174 Autolock _l(this); 175 return m_group; 176 } 177 178 // flag access 179 uint32 NodeRef::flags() const { 180 Autolock _l(this); 181 return m_flags; 182 } 183 184 void NodeRef::setFlags( 185 uint32 flags) { 186 Autolock _l(this); 187 m_flags = flags; 188 } 189 190 //// has this reference been released? 191 //bool NodeRef::isReleased() const { 192 // // no lock necessary for bool access -- right? +++++ 193 // return m_released; 194 //} 195 // 196 197 // [e.moon 29sep99] 198 // access addon-hint info 199 // - returns B_BAD_VALUE if not an add-on node created by this NodeManager 200 201 status_t NodeRef::getDormantNodeInfo( 202 dormant_node_info* outInfo) { 203 204 if(!m_addonHint) 205 return B_BAD_VALUE; 206 207 *outInfo = m_addonHint->dormantInfo; 208 return B_OK; 209 } 210 211 // [e.moon 29sep99] 212 // access file being played 213 // - returns B_BAD_VALUE if not an add-on node created by this NodeManager, 214 // or if the node has no associated file 215 216 status_t NodeRef::getFile( 217 entry_ref* outFile) { 218 219 Autolock _l(this); 220 221 if(!m_addonHint || !m_addonHint->file) 222 return B_BAD_VALUE; 223 224 *outFile = *(m_addonHint->file); 225 return B_OK; 226 } 227 228 // [e.moon 8dec99] 229 // set file to play 230 231 status_t NodeRef::setFile( 232 const entry_ref& file, 233 bigtime_t* outDuration) { 234 235 Autolock _l(this); 236 237 bigtime_t dur; 238 status_t err = m_manager->roster->SetRefFor( 239 node(), 240 file, 241 false, 242 &dur); 243 if(err == B_OK) { 244 if(outDuration) 245 *outDuration = dur; 246 if(m_addonHint) 247 m_addonHint->setFile(&file); 248 } 249 250 return err; 251 } 252 253 // [e.moon 23oct99] 254 // returns true if the media_node has been released (call releaseNode() to 255 // make this happen.) 256 257 bool NodeRef::isNodeReleased() const { 258 return m_nodeReleased; 259 } 260 261 // -------------------------------------------------------- // 262 // *** run-mode operations 263 // -------------------------------------------------------- // 264 265 void NodeRef::setRunMode( 266 uint32 runMode, 267 bigtime_t delay) { 268 Autolock _l(this); 269 270 ASSERT(runMode <= BMediaNode::B_RECORDING); 271 m_runMode = runMode; 272 if(runMode == BMediaNode::B_RECORDING) 273 m_recordingDelay = delay; 274 275 // send notification to all observers 276 BMessage m(M_RUN_MODE_CHANGED); 277 m.AddInt32("nodeID", id()); 278 m.AddInt32("runMode", runMode); 279 280 if(runMode == BMediaNode::B_RECORDING && m_recordingDelay != 0) 281 m.AddInt64("delay", m_recordingDelay); 282 283 notify(&m); 284 285 // update real run mode 286 if(m_group) 287 _setRunMode(m_group->runMode(), m_recordingDelay); 288 } 289 290 uint32 NodeRef::runMode() const { 291 Autolock _l(this); 292 return m_runMode; 293 } 294 295 bigtime_t NodeRef::recordingDelay() const { 296 Autolock _l(this); 297 return m_recordingDelay; 298 } 299 300 // calculates the minimum amount of delay needed for 301 // B_RECORDING mode 302 // +++++ 15sep99: returns biggest_output_buffer_duration * 2 303 // +++++ 28sep99: adds downstream latency 304 305 bigtime_t NodeRef::calculateRecordingModeDelay() { 306 PRINT(( 307 "NodeRef::calculateRecordingModeDelay()\n")); 308 status_t err; 309 310 bigtime_t maxBufferDur = 0LL; 311 312 vector<Connection> outputConnections; 313 err = getOutputConnections(outputConnections); 314 for( 315 vector<Connection>::iterator it = outputConnections.begin(); 316 it != outputConnections.end(); ++it) { 317 318 bigtime_t bufferDur = buffer_duration((*it).format().u.raw_audio); 319 if(bufferDur > maxBufferDur) 320 maxBufferDur = bufferDur; 321 } 322 323 bigtime_t latency = 0LL; 324 err = m_manager->roster->GetLatencyFor( 325 node(), &latency); 326 327 PRINT(( 328 " %Ld\n", latency)); 329 330 return latency; // +++++ stab in the dark 28sep99 331 // return maxBufferDur + latency; 332 } 333 334 // -------------------------------------------------------- // 335 // *** connection access 336 // -------------------------------------------------------- // 337 338 // connection access: vector versions 339 340 status_t NodeRef::getInputConnections( 341 vector<Connection>& ioConnections, 342 media_type filterType) const { 343 Autolock _l(this); 344 345 NodeManager::con_map::iterator it, itEnd; 346 it = m_manager->m_conDestinationMap.lower_bound(m_info.node.node); 347 itEnd = m_manager->m_conDestinationMap.upper_bound(m_info.node.node); 348 349 for(; it != itEnd; ++it) { 350 if(filterType == B_MEDIA_UNKNOWN_TYPE || 351 (*it).second->format().type == filterType) { 352 353 ioConnections.push_back(*((*it).second)); 354 } 355 } 356 357 return B_OK; 358 } 359 360 361 status_t NodeRef::getOutputConnections( 362 vector<Connection>& ioConnections, 363 media_type filterType) const { 364 Autolock _l(this); 365 366 NodeManager::con_map::iterator it, itEnd; 367 it = m_manager->m_conSourceMap.lower_bound(m_info.node.node); 368 itEnd = m_manager->m_conSourceMap.upper_bound(m_info.node.node); 369 370 for(; it != itEnd; ++it) { 371 if(filterType == B_MEDIA_UNKNOWN_TYPE || 372 (*it).second->format().type == filterType) { 373 374 ioConnections.push_back(*((*it).second)); 375 } 376 } 377 378 return B_OK; 379 } 380 381 // connection access: flat array versions 382 383 status_t NodeRef::getInputConnections( 384 Connection* outConnections, 385 int32 maxConnections, 386 int32* outNumConnections, 387 media_type filterType) const { 388 Autolock _l(this); 389 390 NodeManager::con_map::iterator it, itEnd; 391 it = m_manager->m_conDestinationMap.lower_bound(m_info.node.node); 392 itEnd = m_manager->m_conDestinationMap.upper_bound(m_info.node.node); 393 394 int32 count = 0; 395 396 for(; it != itEnd && count < maxConnections; ++it) { 397 if(filterType == B_MEDIA_UNKNOWN_TYPE || 398 (*it).second->format().type == filterType) { 399 400 outConnections[count++] = *((*it).second); 401 } 402 } 403 404 *outNumConnections = count; 405 406 return B_OK; 407 } 408 409 status_t NodeRef::getOutputConnections( 410 Connection* outConnections, 411 int32 maxConnections, 412 int32* outNumConnections, 413 media_type filterType) const { 414 Autolock _l(this); 415 416 NodeManager::con_map::iterator it, itEnd; 417 it = m_manager->m_conSourceMap.lower_bound(m_info.node.node); 418 itEnd = m_manager->m_conSourceMap.upper_bound(m_info.node.node); 419 420 int32 count = 0; 421 422 for(; it != itEnd && count < maxConnections; ++it) { 423 if(filterType == B_MEDIA_UNKNOWN_TYPE || 424 (*it).second->format().type == filterType) { 425 426 outConnections[count++] = *((*it).second); 427 } 428 } 429 430 *outNumConnections = count; 431 432 return B_OK; 433 } 434 435 // -------------------------------------------------------- // 436 // *** position reporting/listening 437 // -------------------------------------------------------- // 438 439 bool NodeRef::positionReportsEnabled() const { 440 return m_positionReportsEnabled; 441 } 442 443 // start thread if necessary 444 void NodeRef::enablePositionReports() { 445 Autolock _l(this); 446 447 if(m_flags & NO_POSITION_REPORTING) 448 return; 449 450 if(m_positionReportsEnabled) 451 return; 452 453 m_positionReportsEnabled = true; 454 if(m_running) { 455 // start the thread 456 _startPositionThread(); 457 } 458 } 459 460 // stop thread if necessary 461 void NodeRef::disablePositionReports() { 462 Autolock _l(this); 463 464 if(!m_positionReportsEnabled) 465 return; 466 467 m_positionReportsEnabled = false; 468 if(m_running) { 469 // shut down the thread 470 _stopPositionThread(); 471 } 472 } 473 474 // Fetch the approximate current position: 475 // Returns the last reported position, and the 476 // performance time at which that position was reached. If the 477 // transport has never been started, the start position and 478 // a performance time of 0 will be returned. If position updating 479 // isn't currently enabled, returns B_NOT_ALLOWED. 480 481 status_t NodeRef::getLastPosition( 482 bigtime_t* outPosition, 483 bigtime_t* outPerfTime) const { 484 485 Autolock _l(this); 486 487 if(!m_positionReportsEnabled) 488 return B_NOT_ALLOWED; 489 490 *outPosition = m_lastPosition; 491 *outPerfTime = m_tpLastPositionUpdate; 492 return B_OK; 493 } 494 495 // Subscribe to regular position reports: 496 // Position reporting isn't rolled into the regular IObservable 497 // interface because a large number of messages are generated 498 // (the frequency can be changed; see below.) 499 500 status_t NodeRef::addPositionObserver( 501 BHandler* handler) { 502 ASSERT(handler); 503 504 // try to create messenger 505 status_t error; 506 BMessenger m(handler, NULL, &error); 507 if(error < B_OK) { 508 PRINT(( 509 "* NodeRef::addPositionListener(): BMessenger() failed:\n" 510 " %s\n" 511 " handler %p\n", 512 strerror(error), handler)); 513 return error; 514 } 515 516 // add to the invoker 517 Autolock _l(this); 518 m_positionInvoker.AddTarget(handler); 519 520 // enable position updates: 521 if(!m_positionReportsEnabled) 522 enablePositionReports(); 523 524 return B_OK; 525 } 526 527 status_t NodeRef::removePositionObserver( 528 BHandler* handler) { 529 ASSERT(handler); 530 531 Autolock _l(this); 532 533 // look for listener 534 int32 index = m_positionInvoker.IndexOfTarget(handler); 535 if(index == -1) 536 return B_ERROR; 537 538 // remove it 539 m_positionInvoker.RemoveTarget(index); 540 541 // last observer removed? kill thread. [e.moon 12oct99] 542 if(m_positionReportsEnabled && !m_positionInvoker.CountTargets()) 543 disablePositionReports(); 544 545 return B_OK; 546 } 547 548 // Set how often position updates will be sent: 549 // Realistically, period should be > 10000 or so. 550 551 status_t NodeRef::setPositionUpdatePeriod( 552 bigtime_t period) { 553 554 Autolock _l(this); 555 if(period < 1000LL) 556 return B_BAD_VALUE; 557 m_positionUpdatePeriod = period; 558 return B_OK; 559 } 560 561 bigtime_t NodeRef::positionUpdatePeriod() const{ 562 Autolock _l(this); 563 return m_positionUpdatePeriod; 564 } 565 566 // -------------------------------------------------------- // 567 // *** BMediaRoster wrappers & convenience methods 568 // -------------------------------------------------------- // 569 570 // release the media node 571 // (if allowed, will trigger the release/deletion of this object) 572 status_t NodeRef::releaseNode() { 573 574 D_METHOD(( 575 "NodeRef[%s]::releaseNode()\n", name())); 576 status_t err; 577 578 Autolock _l(m_manager); 579 580 if(isReleased() || m_nodeReleased) 581 return B_NOT_ALLOWED; 582 583 if(m_group) 584 m_group->removeNode(this); 585 586 // kill off sync thread 587 if(m_positionThread) { 588 delete m_positionThread; 589 m_positionThread = 0; 590 } 591 592 if(m_implFlags & _INTERNAL) { 593 594 // tear down all connections if the node was created by 595 // NodeManager 596 vector<Connection> c_set; 597 getInputConnections(c_set); 598 getOutputConnections(c_set); 599 600 // [e.moon 13oct99] making PPC compiler happy 601 // for_each( 602 // c_set.begin(), 603 // c_set.end(), 604 // bound_method( 605 // *m_manager, 606 // &NodeManager::disconnect 607 // ) 608 // ); 609 610 for(vector<Connection>::iterator it = c_set.begin(); 611 it != c_set.end(); ++it) { 612 err = m_manager->disconnect(*it); 613 if(err < B_OK) { 614 PRINT(( 615 "! NodeRef('%s')::releaseNode():\n" 616 " NodeManager::disconnect('%s'->'%s') failed:\n" 617 " %s\n", 618 name(), 619 (*it).outputName(), (*it).inputName(), 620 strerror(err))); 621 } 622 } 623 624 // +++++ ensure that the connections were really broken? 625 } 626 627 err = B_OK; 628 if(!(m_implFlags & _NO_RELEASE)) { 629 630 // PRINT(( 631 // "### releasing node %ld\n", 632 // id())); 633 // 634 // free the node 635 D_ROSTER(("# roster->ReleaseNode(%ld)\n", m_info.node.node)); 636 err = BMediaRoster::Roster()->ReleaseNode( 637 m_info.node); 638 639 if(err < B_OK) { 640 PRINT(( 641 "!!! ReleaseNode(%ld) failed:\n" 642 " %s\n", 643 m_info.node.node, 644 strerror(err))); 645 } 646 647 if( 648 (m_implFlags & _INTERNAL) && 649 m_manager->m_useAddOnHost) { 650 651 // ask add-on host to release the node 652 err = AddOnHost::ReleaseInternalNode(m_info); 653 if(err < B_OK) { 654 PRINT(( 655 "!!! AddOnHost::ReleaseInternalNode(%ld) failed:\n" 656 " %s\n", 657 m_info.node.node, 658 strerror(err))); 659 } 660 } 661 } 662 else { 663 // PRINT(("- not releasing node\n")); 664 } 665 666 m_nodeReleased = true; 667 return err; 668 } 669 670 671 // calculate total (internal + downstream) latency for this node 672 673 status_t NodeRef::totalLatency( 674 bigtime_t* outLatency) const { 675 676 return BMediaRoster::Roster()->GetLatencyFor( 677 m_info.node, 678 outLatency); 679 } 680 681 // retrieve input/output matching the given destination/source. 682 // returns B_MEDIA_BAD_[SOURCE | DESTINATION] if the destination 683 // or source don't correspond to this node. 684 685 class match_input_destination { public: 686 const media_destination& dest; 687 match_input_destination(const media_destination& _dest) : dest(_dest) {} 688 bool operator()(const media_input& input) const { 689 return input.destination == dest; 690 } 691 }; 692 693 class match_output_source { public: 694 const media_source& source; 695 match_output_source(const media_source& _source) : source(_source) {} 696 bool operator()(const media_output& output) const { 697 return output.source == source; 698 } 699 }; 700 701 status_t NodeRef::findInput( 702 const media_destination& forDestination, 703 media_input* outInput) const { 704 705 status_t err; 706 707 vector<media_input> inputs; 708 vector<media_input>::const_iterator it; 709 inputs.reserve(32); 710 711 // check free inputs 712 err = getFreeInputs(inputs); 713 if(err < B_OK) 714 return err; 715 716 it = find_if( 717 inputs.begin(), inputs.end(), 718 match_input_destination(forDestination)); 719 720 if(it != inputs.end()) { 721 *outInput = *it; 722 return B_OK; 723 } 724 725 // check connected inputs 726 inputs.clear(); 727 err = getConnectedInputs(inputs); 728 if(err < B_OK) 729 return err; 730 731 it = find_if( 732 inputs.begin(), inputs.end(), 733 match_input_destination(forDestination)); 734 735 if(it != inputs.end()) { 736 *outInput = *it; 737 return B_OK; 738 } 739 return B_MEDIA_BAD_DESTINATION; 740 } 741 742 status_t NodeRef::findOutput( 743 const media_source& forSource, 744 media_output* outOutput) const { 745 746 status_t err; 747 748 vector<media_output> outputs; 749 vector<media_output>::const_iterator it; 750 outputs.reserve(32); 751 752 // check free outputs 753 err = getFreeOutputs(outputs); 754 if(err < B_OK) 755 return err; 756 757 it = find_if( 758 outputs.begin(), outputs.end(), 759 match_output_source(forSource)); 760 761 if(it != outputs.end()) { 762 *outOutput = *it; 763 return B_OK; 764 } 765 766 // check connected outputs 767 outputs.clear(); 768 err = getConnectedOutputs(outputs); 769 if(err < B_OK) 770 return err; 771 772 it = find_if( 773 outputs.begin(), outputs.end(), 774 match_output_source(forSource)); 775 776 if(it != outputs.end()) { 777 *outOutput = *it; 778 return B_OK; 779 } 780 781 return B_MEDIA_BAD_SOURCE; 782 } 783 784 785 // endpoint matching (given name and/or format as 'hints') 786 787 template <class T> 788 class match_endpoint_name_format : public unary_function<T, bool> { 789 public: 790 const char* name; 791 const media_format* format; 792 793 match_endpoint_name_format(const char* _name, const media_format* _format) : 794 name(_name), format(_format) {} 795 bool operator()(const T& endpoint) const { 796 // test name, if given 797 if(name && strcmp(endpoint.name, name) != 0) 798 return false; 799 // test format, if given 800 media_format* f1 = const_cast<media_format*>(format); 801 media_format* f2 = const_cast<media_format*>(&endpoint.format); 802 if(format && !f1->Matches(f2)) 803 return false; 804 return true; 805 } 806 }; 807 808 template <class T> 809 class match_endpoint_name_type : public unary_function<T, bool> { 810 public: 811 const char* name; 812 media_type type; 813 814 match_endpoint_name_type(const char* _name, media_type _type) : 815 name(_name), type(_type) {} 816 bool operator()(const T& endpoint) const { 817 // test name, if given 818 if(name && strcmp(endpoint.name, name) != 0) 819 return false; 820 // test type, if given 821 if(type != B_MEDIA_UNKNOWN_TYPE && 822 type != endpoint.format.type) 823 return false; 824 825 return true; 826 } 827 }; 828 829 template <class T> 830 class match_endpoint_type : public unary_function<T, bool> { 831 public: 832 media_type type; 833 834 match_endpoint_type(media_type _type) : 835 type(_type) {} 836 bool operator()(const T& endpoint) const { 837 // test type, if given 838 if(type != B_MEDIA_UNKNOWN_TYPE && 839 type != endpoint.format.type) 840 return false; 841 842 return true; 843 } 844 }; 845 846 status_t NodeRef::findFreeInput( 847 media_input* outInput, 848 const media_format* format /*=0*/, 849 const char* name /*=0*/) const { 850 851 status_t err; 852 853 vector<media_input> inputs; 854 vector<media_input>::const_iterator it; 855 inputs.reserve(32); 856 857 err = getFreeInputs(inputs); 858 if(err < B_OK) 859 return err; 860 861 it = find_if( 862 inputs.begin(), 863 inputs.end(), 864 match_endpoint_name_format<media_input>(name, format)); 865 866 if(it != inputs.end()) { 867 *outInput = *it; 868 return B_OK; 869 } 870 return B_ERROR; 871 } 872 873 status_t NodeRef::findFreeInput( 874 media_input* outInput, 875 media_type type /*=B_MEDIA_UNKNOWN_TYPE*/, 876 const char* name /*=0*/) const { 877 878 status_t err; 879 880 vector<media_input> inputs; 881 vector<media_input>::const_iterator it; 882 inputs.reserve(32); 883 884 err = getFreeInputs(inputs); 885 if(err < B_OK) 886 return err; 887 888 it = find_if( 889 inputs.begin(), 890 inputs.end(), 891 match_endpoint_name_type<media_input>(name, type)); 892 if(it != inputs.end()) { 893 *outInput = *it; 894 return B_OK; 895 } 896 return B_ERROR; 897 } 898 899 status_t NodeRef::findFreeOutput( 900 media_output* outOutput, 901 const media_format* format /*=0*/, 902 const char* name /*=0*/) const { 903 904 status_t err; 905 906 vector<media_output> outputs; 907 vector<media_output>::const_iterator it; 908 outputs.reserve(32); 909 910 err = getFreeOutputs(outputs); 911 if(err < B_OK) 912 return err; 913 914 it = find_if( 915 outputs.begin(), 916 outputs.end(), 917 match_endpoint_name_format<media_output>(name, format)); 918 if(it != outputs.end()) { 919 *outOutput = *it; 920 return B_OK; 921 } 922 return B_ERROR; 923 } 924 925 status_t NodeRef::findFreeOutput( 926 media_output* outOutput, 927 media_type type /*=B_MEDIA_UNKNOWN_TYPE*/, 928 const char* name /*=0*/) const { 929 930 status_t err; 931 932 vector<media_output> outputs; 933 vector<media_output>::const_iterator it; 934 outputs.reserve(32); 935 936 err = getFreeOutputs(outputs); 937 if(err < B_OK) 938 return err; 939 940 it = find_if( 941 outputs.begin(), 942 outputs.end(), 943 match_endpoint_name_type<media_output>(name, type)); 944 if(it != outputs.end()) { 945 *outOutput = *it; 946 return B_OK; 947 } 948 return B_ERROR; 949 } 950 951 952 // node endpoint access: vector versions (wrappers for BMediaRoster 953 // calls.) 954 955 status_t NodeRef::getFreeInputs( 956 vector<media_input>& ioInputs, 957 media_type filterType) const { 958 959 BMediaRoster* r = BMediaRoster::Roster(); 960 status_t err; 961 962 int32 count; 963 int32 bufferInc = 16; 964 int32 inputBufferSize = 16; 965 media_input* inputBuffer = new media_input[inputBufferSize]; 966 967 while(true) { 968 err = r->GetFreeInputsFor( 969 m_info.node, inputBuffer, inputBufferSize, &count, filterType); 970 if(err < B_OK) 971 return err; 972 973 if(count == inputBufferSize) { 974 // buffer too small; increase & try again 975 inputBufferSize += bufferInc; 976 delete [] inputBuffer; 977 inputBuffer = new media_input[inputBufferSize]; 978 continue; 979 } 980 981 if(count) 982 // copy found inputs into vector 983 copy(inputBuffer, inputBuffer + count, 984 back_inserter(ioInputs)); 985 986 break; 987 } 988 989 // fix missing node info 990 _fixInputs(ioInputs); 991 992 delete [] inputBuffer; 993 return B_OK; 994 } 995 996 // +++++ broken? 997 status_t NodeRef::getConnectedInputs( 998 vector<media_input>& ioInputs, 999 media_type filterType) const { 1000 1001 BMediaRoster* r = BMediaRoster::Roster(); 1002 status_t err; 1003 1004 int32 count; 1005 int32 bufferInc = 16; 1006 int32 inputBufferSize = 16; 1007 media_input* inputBuffer = new media_input[inputBufferSize]; 1008 1009 while(true) { 1010 err = r->GetConnectedInputsFor( 1011 m_info.node, inputBuffer, inputBufferSize, &count); 1012 if(err < B_OK) 1013 return err; 1014 1015 if(count == inputBufferSize) { 1016 // buffer too small; increase & try again 1017 inputBufferSize += bufferInc; 1018 delete [] inputBuffer; 1019 inputBuffer = new media_input[inputBufferSize]; 1020 continue; 1021 } 1022 1023 if(count) 1024 // copy found inputs matching the given type into vector 1025 remove_copy_if(inputBuffer, inputBuffer + count, 1026 back_inserter(ioInputs), 1027 not1(match_endpoint_type<media_input>(filterType))); 1028 1029 break; 1030 } 1031 1032 // fix missing node info 1033 _fixInputs(ioInputs); 1034 1035 delete [] inputBuffer; 1036 return B_OK; 1037 } 1038 1039 status_t NodeRef::getFreeOutputs( 1040 vector<media_output>& ioOutputs, 1041 media_type filterType) const { 1042 1043 BMediaRoster* r = BMediaRoster::Roster(); 1044 status_t err; 1045 1046 int32 count; 1047 int32 bufferInc = 16; 1048 int32 outputBufferSize = 16; 1049 media_output* outputBuffer = new media_output[outputBufferSize]; 1050 1051 while(true) { 1052 err = r->GetFreeOutputsFor( 1053 m_info.node, outputBuffer, outputBufferSize, &count, filterType); 1054 if(err < B_OK) 1055 return err; 1056 1057 if(count == outputBufferSize) { 1058 // buffer too small; increase & try again 1059 outputBufferSize += bufferInc; 1060 delete [] outputBuffer; 1061 outputBuffer = new media_output[outputBufferSize]; 1062 continue; 1063 } 1064 1065 if(count) 1066 // copy found outputs into vector 1067 copy(outputBuffer, outputBuffer + count, 1068 back_inserter(ioOutputs)); 1069 1070 break; 1071 } 1072 1073 // fix missing node info 1074 _fixOutputs(ioOutputs); 1075 1076 delete [] outputBuffer; 1077 return B_OK; 1078 } 1079 1080 status_t NodeRef::getConnectedOutputs( 1081 vector<media_output>& ioOutputs, 1082 media_type filterType) const { 1083 1084 BMediaRoster* r = BMediaRoster::Roster(); 1085 status_t err; 1086 1087 int32 count; 1088 int32 bufferInc = 16; 1089 int32 outputBufferSize = 16; 1090 media_output* outputBuffer = new media_output[outputBufferSize]; 1091 1092 while(true) { 1093 err = r->GetConnectedOutputsFor( 1094 m_info.node, outputBuffer, outputBufferSize, &count); 1095 if(err < B_OK) 1096 return err; 1097 1098 if(count == outputBufferSize) { 1099 // buffer too small; increase & try again 1100 outputBufferSize += bufferInc; 1101 delete [] outputBuffer; 1102 outputBuffer = new media_output[outputBufferSize]; 1103 continue; 1104 } 1105 1106 if(count) 1107 // copy found outputs matching the given type into vector 1108 remove_copy_if(outputBuffer, outputBuffer + count, 1109 back_inserter(ioOutputs), 1110 not1(match_endpoint_type<media_output>(filterType))); 1111 1112 break; 1113 } 1114 1115 // fix missing node info 1116 _fixOutputs(ioOutputs); 1117 1118 delete [] outputBuffer; 1119 return B_OK; 1120 } 1121 1122 1123 // node endpoint access: array versions (wrappers for BMediaRoster 1124 // calls.) 1125 1126 status_t NodeRef::getFreeInputs( 1127 media_input* outInputs, 1128 int32 maxInputs, 1129 int32* outNumInputs, 1130 media_type filterType) const { 1131 1132 status_t err = BMediaRoster::Roster()->GetFreeInputsFor( 1133 m_info.node, outInputs, maxInputs, outNumInputs, filterType); 1134 1135 if(err < B_OK) 1136 return err; 1137 1138 // fix missing node info 1139 _fixInputs(outInputs, *outNumInputs); 1140 return err; 1141 } 1142 1143 1144 status_t NodeRef::getConnectedInputs( 1145 media_input* outInputs, 1146 int32 maxInputs, 1147 int32* outNumInputs) const { 1148 1149 status_t err = BMediaRoster::Roster()->GetConnectedInputsFor( 1150 m_info.node, outInputs, maxInputs, outNumInputs); 1151 1152 if(err < B_OK) 1153 return err; 1154 1155 // fix missing node info 1156 _fixInputs(outInputs, *outNumInputs); 1157 return err; 1158 } 1159 1160 status_t NodeRef::getFreeOutputs( 1161 media_output* outOutputs, 1162 int32 maxOutputs, 1163 int32* outNumOutputs, 1164 media_type filterType) const { 1165 1166 status_t err = BMediaRoster::Roster()->GetFreeOutputsFor( 1167 m_info.node, outOutputs, maxOutputs, outNumOutputs, filterType); 1168 1169 if(err < B_OK) 1170 return err; 1171 1172 // fix missing node info 1173 _fixOutputs(outOutputs, *outNumOutputs); 1174 return err; 1175 } 1176 1177 status_t NodeRef::getConnectedOutputs( 1178 media_output* outOutputs, 1179 int32 maxOutputs, 1180 int32* outNumOutputs) const { 1181 1182 status_t err = BMediaRoster::Roster()->GetConnectedOutputsFor( 1183 m_info.node, outOutputs, maxOutputs, outNumOutputs); 1184 1185 if(err < B_OK) 1186 return err; 1187 1188 // fix missing node info 1189 _fixOutputs(outOutputs, *outNumOutputs); 1190 return err; 1191 } 1192 1193 1194 // -------------------------------------------------------- // 1195 // *** IPersistent 1196 // -------------------------------------------------------- // 1197 1198 // ! 1199 #if CORTEX_XML 1200 // ! 1201 1202 // +++++ 1203 1204 // ! 1205 #endif /*CORTEX_XML*/ 1206 // ! 1207 1208 // -------------------------------------------------------- // 1209 // *** BHandler: 1210 // -------------------------------------------------------- // 1211 1212 void NodeRef::MessageReceived( 1213 BMessage* message) { 1214 1215 D_MESSAGE(( 1216 "NodeRef['%s']::MessageReceived(): %c%c%c%c\n", 1217 name(), 1218 message->what >> 24, 1219 (message->what >> 16) & 0xff, 1220 (message->what >> 8) & 0xff, 1221 (message->what) & 0xff)); 1222 status_t err; 1223 1224 switch(message->what) { 1225 case M_SET_RUN_MODE: 1226 { 1227 // set run mode & delay (if given) 1228 int32 runMode; 1229 bigtime_t delay = 0LL; 1230 err = message->FindInt32("runMode", &runMode); 1231 if(err < B_OK) { 1232 PRINT(( 1233 "! NodeRef::MessageReceived(M_SET_RUN_MODE): no value found.\n")); 1234 break; 1235 } 1236 if(runMode == BMediaNode::B_RECORDING) 1237 message->FindInt64("delay", &delay); // optional 1238 1239 setRunMode(runMode, delay); 1240 } 1241 break; 1242 1243 1244 case M_PREROLL: 1245 // +++++ 1246 break; 1247 1248 case M_SET_CYCLING: 1249 { 1250 bool cycling; 1251 err = message->FindBool("cycling", &cycling); 1252 if(err < B_OK) { 1253 int32 val; 1254 err = message->FindInt32("be:value", &val); 1255 if(err < B_OK) { 1256 PRINT(( 1257 "! NodeRef::MessageReceived(M_SET_CYCLING): no value found.\n")); 1258 break; 1259 } 1260 cycling = val; 1261 } 1262 1263 setCycling(cycling); 1264 } 1265 break; 1266 1267 case B_MEDIA_NODE_STOPPED: 1268 // PRINT(("### B_MEDIA_NODE_STOPPED\n")); 1269 // 1270 // if still marked running, let the group know [e.moon 11oct99] 1271 if(m_running) { 1272 m_running = false; 1273 m_stopQueued = false; 1274 1275 if(m_group) { 1276 Autolock _l(m_group); 1277 m_group->_refStopped(this); 1278 } 1279 } 1280 1281 break; 1282 1283 case NodeSyncThread::M_SYNC_COMPLETE: { 1284 // [e.moon 14oct99] position-report messages are now sent 1285 // by the NodeSyncThread. 1286 1287 Autolock _l(this); 1288 1289 // unpack message 1290 bigtime_t when, position; 1291 err = message->FindInt64("perfTime", &when); 1292 ASSERT(err == B_OK); 1293 err = message->FindInt64("position", &position); 1294 ASSERT(err == B_OK); 1295 1296 _handlePositionUpdate(when, position); 1297 break; 1298 } 1299 1300 default: 1301 _inherited::MessageReceived(message); 1302 } 1303 } 1304 1305 // -------------------------------------------------------- // 1306 // *** IObservable: [20aug99] 1307 // -------------------------------------------------------- // 1308 1309 void NodeRef::observerAdded( 1310 const BMessenger& observer) { 1311 1312 BMessage m(M_OBSERVER_ADDED); 1313 m.AddInt32("nodeID", id()); 1314 m.AddMessenger("target", BMessenger(this)); 1315 observer.SendMessage(&m); 1316 } 1317 1318 void NodeRef::observerRemoved( 1319 const BMessenger& observer) { 1320 1321 BMessage m(M_OBSERVER_REMOVED); 1322 m.AddInt32("nodeID", id()); 1323 m.AddMessenger("target", BMessenger(this)); 1324 observer.SendMessage(&m); 1325 } 1326 1327 void NodeRef::notifyRelease() { 1328 1329 BMessage m(M_RELEASED); 1330 m.AddInt32("nodeID", id()); 1331 m.AddMessenger("target", BMessenger(this)); 1332 notify(&m); 1333 } 1334 1335 void NodeRef::releaseComplete() { 1336 // +++++ 1337 } 1338 1339 // -------------------------------------------------------- // 1340 // *** ILockable: pass lock requests to parent group, 1341 // or manager if no group found 1342 // -------------------------------------------------------- // 1343 1344 // this is hideous. [24aug99] 1345 // it must die soon. 1346 1347 // The two-stage lock appears safe UNLESS it's multiply acquired 1348 // (and the NodeManager is locked somewhere in between.) Then 1349 // it's deadlocks all around... 1350 1351 // safe two-stage lock (only WRITE locking is supported) 1352 // Notes: 1353 // a) a NodeRef either belongs to a group (m_group != 0) or 1354 // is free. If the ref is free, the NodeManager is the 1355 // target for locking; otherwise the group is the 'lockee'. 1356 // b) operations which affect a NodeRef's group affiliation 1357 // (ie. adding or removing a node to/from a group) must 1358 // lock first the NodeManager, then the NodeGroup. The 1359 // locks should be released in the opposite order. 1360 1361 bool NodeRef::lock( 1362 lock_t type, 1363 bigtime_t timeout) { 1364 1365 D_LOCK(("*** NodeRef::lock(): %ld\n", find_thread(0))); 1366 1367 ASSERT(type == WRITE); 1368 ASSERT(m_manager); 1369 1370 // lock manager 1371 if(!m_manager->lock(type, timeout)) 1372 return false; 1373 1374 // transfer lock to group, if any 1375 NodeGroup* group = m_group; 1376 if(!group) 1377 return true; 1378 1379 bool ret = m_group->lock(type, timeout); 1380 1381 m_manager->unlock(); 1382 1383 D_LOCK(("*** NodeRef::lock() ACQUIRED: %ld\n", find_thread(0))); 1384 1385 return ret; 1386 } 1387 1388 bool NodeRef::unlock( 1389 lock_t type) { 1390 1391 D_LOCK(("*** NodeRef::unlock(): %ld\n", find_thread(0))); 1392 1393 ASSERT(type == WRITE); 1394 ASSERT(m_manager); 1395 1396 NodeGroup* group = m_group; 1397 if(group) { 1398 bool ret = m_group->unlock(type); 1399 D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0))); 1400 return ret; 1401 } 1402 1403 bool ret = m_manager->unlock(type); 1404 1405 D_LOCK(("*** NodeRef::unlock() RELEASED: %ld\n", find_thread(0))); 1406 return ret; 1407 } 1408 1409 bool NodeRef::isLocked( 1410 lock_t type) const { 1411 1412 ASSERT(type == WRITE); 1413 ASSERT(m_manager); 1414 1415 NodeGroup* group = m_group; 1416 if(group) 1417 return m_group->isLocked(type); 1418 1419 return m_manager->isLocked(type); 1420 } 1421 1422 // -------------------------------------------------------- // 1423 // *** ctor 1424 // -------------------------------------------------------- // 1425 1426 NodeRef::NodeRef( 1427 const media_node& node, 1428 NodeManager* manager, 1429 uint32 userFlags, 1430 uint32 implFlags) : 1431 1432 m_manager(manager), 1433 m_group(0), 1434 m_flags(userFlags), 1435 m_implFlags(implFlags), 1436 m_runMode(0), 1437 m_recordingDelay(0LL), 1438 m_watching(false), 1439 m_addonHint(0), 1440 m_positionReportsEnabled(false), 1441 m_positionReportsStarted(false), 1442 m_positionUpdatePeriod(s_defaultPositionUpdatePeriod), 1443 m_tpLastPositionUpdate(0LL), 1444 m_lastPosition(0LL), 1445 m_positionThread(0), 1446 m_running(false), 1447 m_nodeReleased(false), 1448 m_cycle(false), 1449 m_prerolled(false), 1450 // m_cycleSyncThread(0), 1451 m_stopQueued(false), 1452 m_latency(0LL) { 1453 1454 ASSERT(manager); 1455 1456 if(!m_manager->Lock()) { 1457 ASSERT(!"m_manager->Lock() failed"); 1458 } 1459 m_manager->AddHandler(this); 1460 m_manager->Unlock(); 1461 1462 // fetch node details 1463 BMediaRoster* r = BMediaRoster::Roster(); 1464 status_t err = r->GetLiveNodeInfo( 1465 node, 1466 &m_info); 1467 1468 if(err < B_OK) { 1469 PRINT(( 1470 "!!! NodeRef(): BMediaRoster::GetLiveNodeInfo(%ld) failed:\n" 1471 " %s\n", 1472 node.node, 1473 strerror(err))); 1474 // at least store node info 1475 m_info.node = node; 1476 } 1477 1478 // name self after node 1479 SetName(m_info.name); 1480 1481 // init Media Roster connection [e.moon 11oct99] 1482 if(!(m_flags & NO_ROSTER_WATCH)) { 1483 r->StartWatching( 1484 BMessenger(this), 1485 m_info.node, 1486 B_MEDIA_NODE_STOPPED); 1487 m_watching = true; 1488 } 1489 } 1490 // -------------------------------------------------------- // 1491 // *** endpoint-fixing operations (no lock required) 1492 // -------------------------------------------------------- // 1493 1494 template <class T> 1495 class fixEndpointFn : public unary_function<T&, void> { 1496 const media_node& node; 1497 public: 1498 fixEndpointFn(const media_node& _n) : node(_n) {} 1499 void operator()(T& endpoint) { 1500 // PRINT(( 1501 // "fixEndpointFn(): endpoint '%s', node ID %ld\n", 1502 // endpoint.name, endpoint.node.node)); 1503 if(endpoint.node != node) { 1504 PRINT(( 1505 " fixing '%s'\n", endpoint.name)); 1506 endpoint.node = node; 1507 } 1508 } 1509 }; 1510 1511 // 'fix' (fill in node if needed) sets of inputs/outputs 1512 void NodeRef::_fixInputs( 1513 media_input* inputs, 1514 int32 count) const { 1515 1516 D_METHOD(( 1517 "NodeRef[%s]::fixInputs()\n", m_info.name)); 1518 1519 for_each( 1520 inputs, 1521 inputs+count, 1522 fixEndpointFn<media_input>(node())); 1523 } 1524 1525 void NodeRef::_fixInputs( 1526 vector<media_input>& inputs) const { 1527 1528 D_METHOD(( 1529 "NodeRef[%s]::fixInputs()\n", m_info.name)); 1530 1531 for_each( 1532 inputs.begin(), 1533 inputs.end(), 1534 fixEndpointFn<media_input>(node())); 1535 } 1536 1537 void NodeRef::_fixOutputs( 1538 media_output* outputs, 1539 int32 count) const { 1540 1541 D_METHOD(( 1542 "NodeRef[%s]::fixOutputs()\n", m_info.name)); 1543 1544 for_each( 1545 outputs, 1546 outputs+count, 1547 fixEndpointFn<media_output>(node())); 1548 } 1549 1550 void NodeRef::_fixOutputs( 1551 vector<media_output>& outputs) const { 1552 1553 D_METHOD(( 1554 "NodeRef[%s]::fixOutputs()\n", m_info.name)); 1555 1556 for_each( 1557 outputs.begin(), 1558 outputs.end(), 1559 fixEndpointFn<media_output>(node())); 1560 } 1561 1562 // -------------------------------------------------------- // 1563 // *** internal/NodeManager operations (LOCK REQUIRED) 1564 // -------------------------------------------------------- // 1565 1566 // call after instantiation to register the dormant_node_info 1567 // used to select this add-on node 1568 1569 void NodeRef::_setAddonHint( 1570 const dormant_node_info* info, 1571 const entry_ref* file) { 1572 1573 assert_locked(this); 1574 1575 if(m_addonHint) 1576 delete m_addonHint; 1577 1578 m_addonHint = new addon_hint(info, file); 1579 } 1580 1581 // call to set a new group; if 0, the node must have no 1582 // connections 1583 void NodeRef::_setGroup( 1584 NodeGroup* group) { 1585 assert_locked(this); 1586 1587 m_group = group; 1588 1589 if(!LockLooper()) { 1590 ASSERT(!"LockLooper() failed."); 1591 } 1592 BMessage m(M_GROUP_CHANGED); 1593 m.AddInt32("nodeID", (int32)m_info.node.node); 1594 m.AddInt32("groupID", m_group ? (int32)m_group->id() : 0); 1595 notify(&m); 1596 UnlockLooper(); 1597 } 1598 1599 // *** NodeGroup API *** 1600 // 9aug99: moved from NodeGroup 1601 1602 // initialize the given node's transport-state members 1603 // (this may be called from the transport thread or from 1604 // an API-implementation method.) 1605 1606 status_t NodeRef::_initTransportState() { 1607 assert_locked(this); 1608 1609 D_METHOD(( 1610 "NodeRef('%s')::_initTransportState()\n", 1611 name())); 1612 1613 // init transport state for this node 1614 m_prerolled = false; 1615 m_tpStart = 0LL; 1616 m_tpLastSeek = 0LL; 1617 m_lastSeekPos = 0LL; 1618 1619 // +++++ init position reporting stuff here? 1620 1621 return B_OK; 1622 } 1623 1624 status_t NodeRef::_setTimeSource( 1625 media_node_id timeSourceID) { 1626 assert_locked(this); 1627 1628 D_METHOD(( 1629 "NodeRef('%s')::_setTimeSource(%ld)\n", 1630 name(), timeSourceID)); 1631 status_t err; 1632 1633 // set time source 1634 ASSERT(timeSourceID != media_node::null.node); 1635 D_ROSTER(("# roster->SetTimeSourceFor()\n")); 1636 err = m_manager->roster->SetTimeSourceFor( 1637 id(), timeSourceID); 1638 1639 if(err < B_OK) { 1640 PRINT(( 1641 "* NodeRef('%s')::_setTimeSource(%ld):\n" 1642 " SetTimeSourceFor() failed: %s\n", 1643 name(), timeSourceID, strerror(err))); 1644 } 1645 1646 return err; 1647 } 1648 1649 status_t NodeRef::_setRunMode( 1650 const uint32 runMode, 1651 bigtime_t delay) { 1652 assert_locked(this); 1653 1654 D_METHOD(( 1655 "NodeRef('%s')::_setRunMode(%ld : %Ld)\n", 1656 name(), runMode, delay)); 1657 status_t err; 1658 1659 1660 BMediaNode::run_mode m = 1661 // if group is in offline mode, so are all its nodes 1662 (runMode == BMediaNode::B_OFFLINE) ? 1663 (BMediaNode::run_mode)runMode : 1664 // if non-0, the node's setting is used 1665 (m_runMode > 0) ? 1666 (BMediaNode::run_mode)m_runMode : 1667 (BMediaNode::run_mode)runMode; 1668 ASSERT(m > 0); 1669 1670 // +++++ additional producer run-mode delay support here? 1671 1672 if( 1673 kind() & B_BUFFER_PRODUCER && 1674 runMode == BMediaNode::B_RECORDING) { 1675 1676 D_ROSTER(("# roster->SetProducerRunModeDelay()\n")); 1677 err = m_manager->roster->SetProducerRunModeDelay( 1678 node(), delay, m); 1679 if(err < B_OK) { 1680 PRINT(( 1681 "NodeRef('%s')::_setRunMode(): SetProducerRunModeDelay(%Ld) failed: %s\n", 1682 name(), delay, strerror(err))); 1683 } 1684 } else { 1685 1686 D_ROSTER(("# roster->SetRunModeNode()\n")); 1687 err = m_manager->roster->SetRunModeNode( 1688 node(), m); 1689 if(err < B_OK) { 1690 PRINT(( 1691 "NodeRef('%s')::_setRunMode(): SetRunModeNode(%ld) failed: %s\n", 1692 name(), m, strerror(err))); 1693 } 1694 } 1695 1696 return err; 1697 } 1698 1699 status_t NodeRef::_setRunModeAuto( 1700 const uint32 runMode) { 1701 1702 if( 1703 kind() && B_BUFFER_PRODUCER && 1704 runMode == BMediaNode::B_RECORDING) { 1705 1706 return _setRunMode( 1707 runMode, 1708 calculateRecordingModeDelay()); 1709 1710 } else 1711 return _setRunMode(runMode); 1712 } 1713 1714 // seek and preroll the given node. 1715 // *** this method should not be called from the transport thread 1716 // (since preroll operations can block for a relatively long time.) 1717 // 1718 // returns B_NOT_ALLOWED if the node is running, or if its NO_PREROLL 1719 // flag is set; otherwise, returns B_OK on success or a Media Roster 1720 // error. 1721 1722 status_t NodeRef::_preroll( 1723 bigtime_t position) { 1724 assert_locked(this); 1725 1726 D_METHOD(( 1727 "NodeRef('%s')::_preroll(%Ld)\n", 1728 name(), position)); 1729 status_t err; 1730 1731 // make sure the node can be and wants to be prerolled 1732 if(m_running || 1733 m_flags & NO_PREROLL) 1734 return B_NOT_ALLOWED; 1735 1736 if(!(m_flags & NO_SEEK)) { 1737 // seek the node first 1738 err = BMediaRoster::Roster()->SeekNode( 1739 node(), 1740 position, 1741 0LL); 1742 1743 if(err < B_OK) { 1744 PRINT(( 1745 "*** NodeRef('%s')::_preroll(%Ld): BMediaRoster::SeekNode():\n" 1746 " %s\n", 1747 name(), position, strerror(err))); 1748 return err; 1749 } 1750 } 1751 1752 // preroll the node (*** this blocks until the node's Preroll() 1753 // implementation returns ***) 1754 1755 err = BMediaRoster::Roster()->PrerollNode( 1756 node()); 1757 1758 if(err < B_OK) { 1759 PRINT(( 1760 "*** NodeRef('%s')::_preroll(%Ld): BMediaRoster::PrerollNode():\n" 1761 " %s\n", 1762 name(), position, strerror(err))); 1763 return err; 1764 } 1765 1766 m_prerolled = true; 1767 m_tpLastSeek = 0LL; 1768 m_lastSeekPos = position; 1769 1770 return B_OK; 1771 } 1772 1773 // seek the given node if possible 1774 // (this may be called from the transport thread or from 1775 // an API-implementation method.) 1776 1777 status_t NodeRef::_seek( 1778 bigtime_t position, 1779 bigtime_t when) { 1780 assert_locked(this); 1781 1782 D_METHOD(( 1783 "NodeRef('%s')::_seek(to %Ld, at %Ld)\n", 1784 name(), position, when)); 1785 1786 if(m_flags & NO_SEEK) 1787 // the node should not be seek'd 1788 return B_OK; 1789 1790 if(m_prerolled && m_lastSeekPos == position) 1791 // the node has already been advanced to the proper position 1792 return B_OK; 1793 1794 // do it 1795 status_t err = BMediaRoster::Roster()->SeekNode( 1796 node(), position, when); 1797 1798 if(err < B_OK) { 1799 PRINT(( 1800 "*** NodeRef('%s')::_seek(to %Ld, at %Ld): BMediaRoster::SeekNode():\n" 1801 " %s\n", 1802 name(), position, when, strerror(err))); 1803 return err; 1804 } 1805 1806 // update node state 1807 m_tpLastSeek = when; 1808 m_lastSeekPos = position; 1809 1810 // node can't be considered prerolled after a seek 1811 m_prerolled = false; 1812 1813 return B_OK; 1814 } 1815 1816 // seek the given (stopped) node 1817 // (this may be called from the transport thread or from 1818 // an API-implementation method.) 1819 1820 status_t NodeRef::_seekStopped( 1821 bigtime_t position) { 1822 assert_locked(this); 1823 1824 D_METHOD(( 1825 "NodeRef('%s')::_seekStopped(to %Ld)\n", 1826 name(), position)); 1827 1828 if(m_running) 1829 return B_NOT_ALLOWED; 1830 1831 return _seek(position, 0LL); 1832 } 1833 1834 1835 // start the given node, if possible & necessary, at 1836 // the given time 1837 // (this may be called from the transport thread or from 1838 // an API-implementation method.) 1839 1840 status_t NodeRef::_start( 1841 bigtime_t when) { 1842 assert_locked(this); 1843 1844 D_METHOD(( 1845 "NodeRef('%s')::_start(at %Ld)\n", 1846 name(), when)); 1847 1848 if(isRunning()) { 1849 D_METHOD(( 1850 " * node already running; aborting\n")); 1851 return B_OK; // +++++ is this technically an error? 1852 } 1853 1854 // +++++ is this proper? 1855 ASSERT(m_group); 1856 ASSERT( 1857 m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING || 1858 m_group->m_transportState == NodeGroup::TRANSPORT_STARTING); 1859 1860 if(m_flags & NO_START_STOP) { 1861 D_METHOD(( 1862 " * NO_START_STOP; aborting\n")); 1863 return B_OK; 1864 } 1865 1866 D_ROSTER(("# roster->StartNode(%ld)\n", id())); 1867 status_t err = BMediaRoster::Roster()->StartNode( 1868 node(), when); 1869 1870 if(err < B_OK) { 1871 PRINT(( 1872 " * StartNode(%ld) failed: '%s'\n", 1873 id(), strerror(err))); 1874 return err; 1875 } 1876 1877 // update state 1878 m_running = true; 1879 m_tpStart = when; 1880 1881 // fetch new node latency 1882 _updateLatency(); 1883 1884 // start position tracking thread if needed 1885 m_positionReportsStarted = false; 1886 if(m_positionReportsEnabled) 1887 _startPositionThread(); 1888 1889 return B_OK; 1890 } 1891 1892 // stop the given node (which may or may not still be 1893 // a member of this group.) 1894 // (this may be called from the transport thread or from 1895 // an API-implementation method.) 1896 1897 status_t NodeRef::_stop() { 1898 assert_locked(this); 1899 1900 D_METHOD(( 1901 "NodeRef('%s')::_stop()\n", 1902 name())); 1903 1904 if(!isRunning()) 1905 return B_OK; // +++++ error? 1906 1907 if(m_flags & NO_START_STOP || m_flags & NO_STOP) 1908 return B_OK; 1909 1910 D_ROSTER(("# roster->StopNode(%ld)\n", id())); 1911 status_t err = BMediaRoster::Roster()->StopNode( 1912 node(), 0, true); 1913 1914 if(err < B_OK) 1915 return err; 1916 1917 // 9aug99: refuse further position notification 1918 _stopPositionThread(); 1919 1920 // clear node's state 1921 m_running = false; 1922 m_stopQueued = false; // asked for immediate stop [e.moon 11oct99] 1923 return _initTransportState(); 1924 } 1925 1926 // roll the given node, if possible 1927 // (this may be called from the transport thread or from 1928 // an API-implementation method.) 1929 status_t NodeRef::_roll( 1930 bigtime_t start, 1931 bigtime_t stop, 1932 bigtime_t position) { 1933 assert_locked(this); 1934 1935 D_METHOD(( 1936 "NodeRef('%s')::_roll(%Ld to %Ld, from %Ld)\n", 1937 name(), start, stop, position)); 1938 status_t err; 1939 1940 // roll only if the node can be started & stopped, 1941 // AND if this NodeRef is watching the Media Roster. 1942 if( 1943 m_flags & NO_START_STOP || 1944 m_flags & NO_STOP || 1945 m_flags & NO_ROSTER_WATCH) 1946 return B_NOT_ALLOWED; 1947 1948 if(isRunning()) 1949 return B_NOT_ALLOWED; 1950 1951 ASSERT(m_group); 1952 ASSERT( 1953 m_group->m_transportState == NodeGroup::TRANSPORT_RUNNING || 1954 m_group->m_transportState == NodeGroup::TRANSPORT_STARTING); 1955 1956 D_ROSTER(("# roster->RollNode(%ld)\n", id())); 1957 if(m_flags & NO_SEEK) 1958 err = BMediaRoster::Roster()->RollNode( 1959 node(), start, stop); 1960 else 1961 err = BMediaRoster::Roster()->RollNode( 1962 node(), start, stop, position); 1963 1964 if(err < B_OK) { 1965 PRINT(( 1966 "NodeRef('%s')::_roll(%Ld to %Ld, from %Ld)\n" 1967 "!!! BMediaRoster::RollNode(%ld) failed: '%s'\n", 1968 name(), start, stop, position, id(), strerror(err))); 1969 return err; 1970 } 1971 1972 // update state 1973 m_running = true; 1974 m_stopQueued = true; // remember that node will stop on its own [e.moon 11oct99] 1975 m_tpStart = start; 1976 1977 // fetch new node latency 1978 _updateLatency(); 1979 1980 // start position tracking thread if needed 1981 m_positionReportsStarted = false; 1982 if(m_positionReportsEnabled) 1983 _startPositionThread(); 1984 1985 return B_OK; 1986 } 1987 1988 // [28sep99 e.moon] 1989 // refresh the node's current latency; if I reference 1990 // a B_RECORDING node, update its 'producer delay'. 1991 1992 status_t NodeRef::_updateLatency() { 1993 assert_locked(this); 1994 1995 // [11nov99 e.moon] don't bother if it's not a producer: 1996 if(!(kind() & B_BUFFER_PRODUCER)) { 1997 m_latency = 0LL; 1998 return B_OK; 1999 } 2000 2001 bigtime_t latency; 2002 status_t err = BMediaRoster::Roster()->GetLatencyFor( 2003 node(), 2004 &latency); 2005 if(err < B_OK) { 2006 PRINT(( 2007 "* NodeRef('%s')::_updateLatency(): GetLatencyFor() failed:\n" 2008 " %s\n", 2009 name(), strerror(err))); 2010 2011 return err; 2012 } 2013 2014 // success 2015 m_latency = latency; 2016 2017 // update run-mode & delay if necessary 2018 if( 2019 m_runMode == BMediaNode::B_RECORDING || 2020 (m_runMode == 0 && m_group && m_group->runMode() == BMediaNode::B_RECORDING)) 2021 _setRunModeAuto(BMediaNode::B_RECORDING); 2022 2023 return B_OK; 2024 } 2025 2026 // Figure the earliest time at which the given node can be started. 2027 // Also calculates the position at which it should start from to 2028 // play in sync with other nodes in the group, if the transport is 2029 // running; if stopped, *outPosition will be set to the current 2030 // start position. 2031 // Pass the estimated amount of time needed to prepare the 2032 // node for playback (ie. preroll & a little fudge factor) in 2033 // startDelay. 2034 // 2035 // (this may be called from the transport thread or from 2036 // an API-implementation method.) 2037 2038 status_t NodeRef::_calcStartTime( 2039 bigtime_t startDelay, 2040 bigtime_t* outTime, 2041 bigtime_t* outPosition) { 2042 assert_locked(this); 2043 2044 // +++++ 2045 2046 return B_ERROR; 2047 } 2048 2049 2050 // -------------------------------------------------------- // 2051 // *** Position and cycle thread management *** (LOCK REQUIRED) 2052 // -------------------------------------------------------- // 2053 2054 status_t NodeRef::_startPositionThread() { 2055 assert_locked(this); 2056 ASSERT(m_group); 2057 status_t err; 2058 2059 if(!m_positionReportsEnabled) 2060 return B_NOT_ALLOWED; 2061 2062 if(m_positionThread) 2063 _stopPositionThread(); 2064 2065 m_positionThread = new NodeSyncThread( 2066 m_info.node, 2067 new BMessenger(this)); 2068 2069 // send an initial position report if necessary 2070 if(!m_positionReportsStarted) { 2071 m_positionReportsStarted = true; 2072 2073 err = _handlePositionUpdate( 2074 m_tpStart, 2075 m_lastSeekPos); 2076 2077 if(err < B_OK) { 2078 PRINT(( 2079 "* NodeRef::_startPositionThread(): _handlePositionUpdate() failed:\n" 2080 " %s\\n", 2081 strerror(err))); 2082 return err; 2083 } 2084 } 2085 else { 2086 2087 // figure when the last jump in position occurred 2088 bigtime_t tpFrom = (m_tpLastSeek > m_tpStart) ? m_tpLastSeek : m_tpStart; 2089 2090 // figure the corresponding position 2091 bigtime_t lastPosition = m_lastSeekPos; 2092 2093 // figure the next time for a position report 2094 BTimeSource* ts = m_group->m_timeSourceObj; 2095 2096 bigtime_t tpTarget = ts->Now() + m_positionUpdatePeriod; 2097 bigtime_t targetPosition = lastPosition + (tpTarget-tpFrom); 2098 2099 err = _schedulePositionUpdate( 2100 tpTarget, 2101 targetPosition); 2102 2103 if(err < B_OK) { 2104 PRINT(( 2105 "* NodeRef::_createPositionThread(): _schedulePositionUpdate() failed:\n" 2106 " %s\\n", 2107 strerror(err))); 2108 return err; 2109 } 2110 } 2111 2112 return B_OK; 2113 } 2114 2115 status_t NodeRef::_handlePositionUpdate( 2116 bigtime_t perfTime, 2117 bigtime_t position) { 2118 assert_locked(this); 2119 status_t err; 2120 2121 if(!m_running) { 2122 PRINT(( 2123 "* NodeRef::_handlePositionUpdate(): not running.\n")); 2124 return B_NOT_ALLOWED; 2125 } 2126 2127 if(!m_positionReportsEnabled) { 2128 PRINT(( 2129 "* NodeRef::_handlePositionUpdate(): position reports disabled.\n")); 2130 return B_NOT_ALLOWED; 2131 } 2132 2133 // store info 2134 m_tpLastPositionUpdate = perfTime; 2135 m_lastPosition = position; 2136 2137 // relay notification to all 'position listeners' 2138 _notifyPosition(perfTime, position); 2139 2140 // schedule next update 2141 err = _schedulePositionUpdate( 2142 perfTime + m_positionUpdatePeriod, 2143 position + m_positionUpdatePeriod); 2144 2145 if(err < B_OK) { 2146 PRINT(( 2147 "* NodeRef::_handlePositionUpdate(): _schedulePositionUpdate() failed:\n" 2148 " %s\n", 2149 strerror(err))); 2150 } 2151 return err; 2152 } 2153 2154 status_t NodeRef::_schedulePositionUpdate( 2155 bigtime_t when, 2156 bigtime_t position) { 2157 assert_locked(this); 2158 status_t err; 2159 2160 if(!m_positionReportsEnabled) 2161 return B_NOT_ALLOWED; 2162 ASSERT(m_positionThread); 2163 2164 if(m_cycle && m_group->_cycleValid()) { 2165 if(position >= m_group->endPosition()) { 2166 // snap to start of next cycle 2167 when = m_group->_cycleBoundary(); 2168 position = m_group->startPosition(); 2169 } 2170 } 2171 2172 //// position_sync_msg m = { 2173 //// id(), 2174 //// m_group->id(), 2175 //// when, 2176 //// position 2177 //// }; 2178 // 2179 //// PRINT(( 2180 //// "NodeRef::_schedulePositionUpdate():\n" 2181 //// " when = %Ld\n" 2182 //// " position = %Ld\n", 2183 //// when, position)); 2184 // 2185 // m_positionSyncThread->setPosition(position); 2186 // 2187 // if(first) 2188 // err = m_positionSyncThread->go(when); 2189 // else 2190 // err = m_positionSyncThread->reschedule(when); 2191 2192 err = m_positionThread->sync(when, position, B_INFINITE_TIMEOUT); 2193 2194 if(err < B_OK) { 2195 PRINT(( 2196 "! NodeRef::_schedulePositionUpdate(): m_positionThread->sync() failed:\n" 2197 " %s\n", strerror(err))); 2198 } 2199 return err; 2200 } 2201 2202 status_t NodeRef::_stopPositionThread() { 2203 assert_locked(this); 2204 2205 if(!m_positionThread) 2206 return B_NOT_ALLOWED; 2207 2208 delete m_positionThread; 2209 m_positionThread = 0; 2210 2211 return B_OK; 2212 } 2213 2214 2215 // Send a message to all position listeners 2216 status_t NodeRef::_notifyPosition( 2217 bigtime_t when, 2218 bigtime_t position) { 2219 assert_locked(this); 2220 status_t err = B_OK; 2221 2222 if(!m_positionReportsEnabled) 2223 return B_NOT_ALLOWED; 2224 2225 BMessage message(M_POSITION); 2226 message.AddInt32("nodeID", id()); 2227 message.AddInt64("when", when); 2228 message.AddInt64("position", position); 2229 2230 m_positionInvoker.Invoke(&message); 2231 2232 return err; 2233 } 2234 2235 // END -- NodeRef.cpp -- 2236