1 /* 2 * Copyright (c) 1999-2000, Eric Moon. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions, and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 // RouteAppNodeManager.cpp 33 34 #include "RouteAppNodeManager.h" 35 36 #include "MediaIcon.h" 37 #include "NodeGroup.h" 38 #include "NodeRef.h" 39 #include "Connection.h" 40 41 #include "route_app_io.h" 42 #include "ConnectionIO.h" 43 #include "DormantNodeIO.h" 44 #include "LiveNodeIO.h" 45 #include "MediaFormatIO.h" 46 #include "MessageIO.h" 47 #include "NodeSetIOContext.h" 48 #include "StringContent.h" 49 #include "MediaString.h" 50 51 #include <Autolock.h> 52 #include <Debug.h> 53 #include <Entry.h> 54 #include <Path.h> 55 56 #include <TimeSource.h> 57 58 #include <cstring> 59 #include <cstdlib> 60 #include <cstdio> 61 #include <typeinfo> 62 63 #include "set_tools.h" 64 65 using namespace std; 66 67 __USE_CORTEX_NAMESPACE 68 69 #define D_METHOD(x) //PRINT (x) 70 #define D_HOOK(x) //PRINT (x) 71 #define D_SETTINGS(x) //PRINT (x) 72 73 // -------------------------------------------------------- // 74 // *** ctor/dtor 75 // -------------------------------------------------------- // 76 77 RouteAppNodeManager::~RouteAppNodeManager() { 78 79 _freeIcons(); 80 } 81 82 RouteAppNodeManager::RouteAppNodeManager( 83 bool useAddOnHost) : 84 NodeManager(useAddOnHost), 85 m_nextGroupNumber(1) { 86 87 // pre-cache icons? +++++ 88 89 } 90 91 // -------------------------------------------------------- // 92 // *** group management 93 // -------------------------------------------------------- // 94 95 // -------------------------------------------------------- // 96 // *** icon management 97 // -------------------------------------------------------- // 98 99 // fetch cached icon for the given live node; the MediaIcon 100 // instance is guaranteed to last as long as this object. 101 // Returns 0 if the node doesn't exist. 102 103 const MediaIcon* RouteAppNodeManager::mediaIconFor( 104 media_node_id nodeID, 105 icon_size iconSize) { 106 107 BAutolock _l(this); 108 109 uint64 key = _makeIconKey(nodeID, iconSize); 110 111 icon_map::const_iterator it = m_iconMap.find(key); 112 if(it != m_iconMap.end()) { 113 // already cached 114 return (*it).second; 115 } 116 117 // look up live_node_info 118 NodeRef* ref; 119 status_t err = getNodeRef(nodeID, &ref); 120 if(err < B_OK) 121 return 0; 122 123 return mediaIconFor(ref->nodeInfo(), iconSize); 124 } 125 126 const MediaIcon* RouteAppNodeManager::mediaIconFor( 127 live_node_info nodeInfo, 128 icon_size iconSize) { 129 130 uint64 key = _makeIconKey(nodeInfo.node.node, iconSize); 131 132 icon_map::const_iterator it = m_iconMap.find(key); 133 if(it != m_iconMap.end()) { 134 // already cached 135 return (*it).second; 136 } 137 138 // create & cache icon 139 MediaIcon* icon = new MediaIcon( 140 nodeInfo, iconSize); 141 142 m_iconMap.insert( 143 icon_map::value_type(key, icon)); 144 145 return icon; 146 } 147 148 // -------------------------------------------------------- // 149 // *** error handling 150 // -------------------------------------------------------- // 151 152 status_t RouteAppNodeManager::setLogTarget( 153 const BMessenger& target) { 154 155 BAutolock _l(this); 156 157 if(!target.IsValid()) 158 return B_BAD_VALUE; 159 160 m_logTarget = target; 161 return B_OK; 162 } 163 164 // -------------------------------------------------------- // 165 // NodeManager hook implementations 166 // -------------------------------------------------------- // 167 168 void RouteAppNodeManager::nodeCreated( 169 NodeRef* ref) { 170 171 // prepare the log message 172 BMessage logMsg(M_LOG); 173 BString title = "Node '"; 174 title << ref->name() << "' created"; 175 logMsg.AddString("title", title); 176 177 // create a default group for the node 178 // [em 8feb00] 179 NodeGroup* g = createGroup(ref->name()); 180 181 if(ref->kind() & B_TIME_SOURCE) { 182 // notify observers 183 BMessage m(M_TIME_SOURCE_CREATED); 184 m.AddInt32("nodeID", ref->id()); 185 notify(&m); 186 } 187 188 // adopt node's time source if it's not the system clock (the default) 189 // [em 20mar00] 190 media_node systemClock; 191 status_t err = roster->GetSystemTimeSource(&systemClock); 192 if(err == B_OK) 193 { 194 BTimeSource* ts = roster->MakeTimeSourceFor(ref->node()); 195 if (ts == NULL) 196 return; 197 if(ts->Node() != systemClock) 198 { 199 g->setTimeSource(ts->Node()); 200 logMsg.AddString("line", "Synced to system clock"); 201 } 202 ts->Release(); 203 } 204 205 g->addNode(ref); 206 207 m_logTarget.SendMessage(&logMsg); 208 } 209 210 void RouteAppNodeManager::nodeDeleted( 211 const NodeRef* ref) { 212 213 // prepare the log message 214 BMessage logMsg(M_LOG); 215 BString title = "Node '"; 216 title << ref->name() << "' released"; 217 logMsg.AddString("title", title); 218 219 if(ref->kind() & B_TIME_SOURCE) { 220 // notify observers 221 BMessage m(M_TIME_SOURCE_DELETED); 222 m.AddInt32("nodeID", ref->id()); 223 notify(&m); 224 } 225 226 m_logTarget.SendMessage(&logMsg); 227 } 228 229 void RouteAppNodeManager::connectionMade( 230 Connection* connection) { 231 232 D_HOOK(( 233 "@ RouteAppNodeManager::connectionMade()\n")); 234 235 status_t err; 236 237 // prepare the log message 238 BMessage logMsg(M_LOG); 239 BString title = "Connection "; 240 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 241 title << "'" << connection->outputName() << "' "; 242 } 243 title << "made"; 244 logMsg.AddString("title", title); 245 246 if(!(connection->flags() & Connection::INTERNAL)) 247 // don't react to connection Cortex didn't make 248 return; 249 250 // create or merge groups 251 NodeRef *producer, *consumer; 252 err = getNodeRef(connection->sourceNode(), &producer); 253 if(err < B_OK) { 254 D_HOOK(( 255 "!!! RouteAppNodeManager::connectionMade():\n" 256 " sourceNode (%ld) not found\n", 257 connection->sourceNode())); 258 return; 259 } 260 err = getNodeRef(connection->destinationNode(), &consumer); 261 if(err < B_OK) { 262 D_HOOK(( 263 "!!! RouteAppNodeManager::connectionMade():\n" 264 " destinationNode (%ld) not found\n", 265 connection->destinationNode())); 266 return; 267 } 268 269 // add node names to log messages 270 BString line = "Between:"; 271 logMsg.AddString("line", line); 272 line = " "; 273 line << producer->name() << " and "; 274 line << consumer->name(); 275 logMsg.AddString("line", line); 276 277 // add format to log message 278 line = "Negotiated format:"; 279 logMsg.AddString("line", line); 280 line = " "; 281 line << MediaString::getStringFor(connection->format(), false); 282 logMsg.AddString("line", line); 283 284 NodeGroup *group = 0; 285 BString groupName = "Untitled group "; 286 if(_canGroup(producer) && _canGroup(consumer)) 287 { 288 if (producer->group() && consumer->group() && 289 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) && 290 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 291 { 292 // merge into consumers group 293 group = consumer->group(); 294 mergeGroups(producer->group(), group); 295 } 296 else if (producer->group() && 297 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 298 { // add consumer to producers group 299 group = producer->group(); 300 group->addNode(consumer); 301 } 302 else if (consumer->group() && 303 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 304 { // add producer to consumers group 305 group = consumer->group(); 306 group->addNode(producer); 307 } 308 else 309 { // make new group for both 310 groupName << m_nextGroupNumber++; 311 group = createGroup(groupName.String()); 312 group->addNode(producer); 313 group->addNode(consumer); 314 } 315 } 316 else if(_canGroup(producer) && !producer->group()) 317 { // make new group for producer 318 groupName << m_nextGroupNumber++; 319 group = createGroup(groupName.String()); 320 group->addNode(producer); 321 } 322 else if(_canGroup(consumer) && !consumer->group()) 323 { // make new group for consumer 324 groupName << m_nextGroupNumber++; 325 group = createGroup(groupName.String()); 326 group->addNode(consumer); 327 } 328 329 m_logTarget.SendMessage(&logMsg); 330 } 331 332 void RouteAppNodeManager::connectionBroken( 333 const Connection* connection) { 334 335 D_HOOK(( 336 "@ RouteAppNodeManager::connectionBroken()\n")); 337 338 // prepare the log message 339 BMessage logMsg(M_LOG); 340 BString title = "Connection "; 341 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 342 title << "'" << connection->outputName() << "' "; 343 } 344 title << "broken"; 345 logMsg.AddString("title", title); 346 347 if(!(connection->flags() & Connection::INTERNAL)) 348 // don't react to connection Cortex didn't make 349 return; 350 351 status_t err; 352 353 // if the source and destination nodes belong to the same group, 354 // and if no direct or indirect connection remains between the 355 // source and destination nodes, split groups 356 357 NodeRef *producer, *consumer; 358 err = getNodeRef(connection->sourceNode(), &producer); 359 if(err < B_OK) { 360 D_HOOK(( 361 "!!! RouteAppNodeManager::connectionMade():\n" 362 " sourceNode (%ld) not found\n", 363 connection->sourceNode())); 364 return; 365 } 366 err = getNodeRef(connection->destinationNode(), &consumer); 367 if(err < B_OK) { 368 D_HOOK(( 369 "!!! RouteAppNodeManager::connectionMade():\n" 370 " destinationNode (%ld) not found\n", 371 connection->destinationNode())); 372 return; 373 } 374 375 // add node names to log messages 376 BString line = "Between:"; 377 logMsg.AddString("line", line); 378 line = " "; 379 line << producer->name() << " and "; 380 line << consumer->name(); 381 logMsg.AddString("line", line); 382 383 if( 384 producer->group() && 385 producer->group() == consumer->group() && 386 !findRoute(producer->id(), consumer->id())) { 387 388 NodeGroup *newGroup; 389 splitGroup(producer, consumer, &newGroup); 390 } 391 392 m_logTarget.SendMessage(&logMsg); 393 } 394 395 void RouteAppNodeManager::connectionFailed( 396 const media_output & output, 397 const media_input & input, 398 const media_format & format, 399 status_t error) { 400 D_HOOK(( 401 "@ RouteAppNodeManager::connectionFailed()\n")); 402 403 status_t err; 404 405 // prepare the log message 406 BMessage logMsg(M_LOG); 407 BString title = "Connection failed"; 408 logMsg.AddString("title", title); 409 logMsg.AddInt32("error", error); 410 411 NodeRef *producer, *consumer; 412 err = getNodeRef(output.node.node, &producer); 413 if(err < B_OK) { 414 return; 415 } 416 err = getNodeRef(input.node.node, &consumer); 417 if(err < B_OK) { 418 return; 419 } 420 421 // add node names to log messages 422 BString line = "Between:"; 423 logMsg.AddString("line", line); 424 line = " "; 425 line << producer->name() << " and " << consumer->name(); 426 logMsg.AddString("line", line); 427 428 // add format to log message 429 line = "Tried format:"; 430 logMsg.AddString("line", line); 431 line = " "; 432 line << MediaString::getStringFor(format, true); 433 logMsg.AddString("line", line); 434 435 // and send it 436 m_logTarget.SendMessage(&logMsg); 437 } 438 439 // -------------------------------------------------------- // 440 // *** IPersistent 441 // -------------------------------------------------------- // 442 443 void RouteAppNodeManager::xmlExportBegin( 444 ExportContext& context) const { 445 446 status_t err; 447 448 try { 449 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 450 context.beginElement(_NODE_SET_ELEMENT); 451 452 // validate the node set 453 for(int n = set.countNodes()-1; n >= 0; --n) { 454 media_node_id id = set.nodeAt(n); 455 ASSERT(id != media_node::null.node); 456 457 // fetch node 458 NodeRef* ref; 459 err = getNodeRef(id, &ref); 460 if(err < B_OK) { 461 D_SETTINGS(( 462 "! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id)); 463 464 set.removeNodeAt(n); 465 continue; 466 } 467 // skip unless internal 468 if(!ref->isInternal()) { 469 D_SETTINGS(( 470 "! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id)); 471 472 set.removeNodeAt(n); 473 continue; 474 } 475 } 476 } 477 catch(bad_cast& e) { 478 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 479 } 480 } 481 482 void RouteAppNodeManager::xmlExportAttributes( 483 ExportContext& context) const {} 484 485 void RouteAppNodeManager::xmlExportContent( 486 ExportContext& context) const { 487 488 status_t err; 489 490 try { 491 NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context); 492 context.beginContent(); 493 494 // write nodes; enumerate connections 495 typedef map<uint32,Connection> connection_map; 496 connection_map connections; 497 int count = nodeSet.countNodes(); 498 for(int n = 0; n < count; ++n) { 499 media_node_id id = nodeSet.nodeAt(n); 500 ASSERT(id != media_node::null.node); 501 502 // fetch node 503 NodeRef* ref; 504 err = getNodeRef(id, &ref); 505 if(err < B_OK) { 506 D_SETTINGS(( 507 "! RouteAppNodeManager::xmlExportContent():\n" 508 " getNodeRef(%ld) failed: '%s'\n", 509 id, strerror(err))); 510 continue; 511 } 512 513 // fetch connections 514 vector<Connection> conSet; 515 ref->getInputConnections(conSet); 516 ref->getOutputConnections(conSet); 517 for(uint32 c = 0; c < conSet.size(); ++c) 518 // non-unique connections will be rejected: 519 connections.insert( 520 connection_map::value_type(conSet[c].id(), conSet[c])); 521 522 // create an IO object for the node & write it 523 DormantNodeIO io(ref, nodeSet.keyAt(n)); 524 if(context.writeObject(&io) < B_OK) 525 // abort 526 return; 527 } 528 529 // write connections 530 for(connection_map::const_iterator it = connections.begin(); 531 it != connections.end(); ++it) { 532 533 ConnectionIO io( 534 &(*it).second, 535 this, 536 &nodeSet); 537 if(context.writeObject(&io) < B_OK) 538 // abort 539 return; 540 541 } 542 543 // +++++ write groups 544 545 // write UI state 546 { 547 BMessage m; 548 nodeSet.exportUIState(&m); 549 context.beginElement(_UI_STATE_ELEMENT); 550 context.beginContent(); 551 MessageIO io(&m); 552 context.writeObject(&io); 553 context.endElement(); 554 } 555 } 556 catch(bad_cast& e) { 557 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 558 } 559 } 560 561 void RouteAppNodeManager::xmlExportEnd( 562 ExportContext& context) const { 563 564 context.endElement(); 565 } 566 567 // -------------------------------------------------------- // 568 // IMPORT 569 // -------------------------------------------------------- // 570 571 void RouteAppNodeManager::xmlImportBegin( 572 ImportContext& context) { 573 574 } 575 576 void RouteAppNodeManager::xmlImportAttribute( 577 const char* key, 578 const char* value, 579 ImportContext& context) {} 580 581 void RouteAppNodeManager::xmlImportContent( 582 const char* data, 583 uint32 length, 584 ImportContext& context) {} 585 586 void RouteAppNodeManager::xmlImportChild( 587 IPersistent* child, 588 ImportContext& context) { 589 590 status_t err; 591 592 if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) { 593 DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child); 594 ASSERT(io); 595 596 NodeRef* newRef; 597 err = io->instantiate(this, &newRef); 598 if(err == B_OK) { 599 // created node; add an entry to the set stored in the 600 // ImportContext for later reference 601 try { 602 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 603 set.addNode(newRef->id(), io->nodeKey()); 604 } 605 catch(bad_cast& e) { 606 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 607 } 608 } 609 else { 610 D_SETTINGS(( 611 "!!! RouteAppNodeManager::xmlImportChild():\n" 612 " DormantNodeIO::instantiate() failed:\n" 613 " '%s'\n", 614 strerror(err))); 615 } 616 } 617 else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) { 618 ConnectionIO* io = dynamic_cast<ConnectionIO*>(child); 619 ASSERT(io); 620 621 // instantiate the connection 622 Connection con; 623 err = io->instantiate( 624 this, 625 dynamic_cast<NodeSetIOContext*>(&context), 626 &con); 627 if(err < B_OK) { 628 D_SETTINGS(( 629 "!!! ConnectionIO::instantiate() failed:\n" 630 " '%s'\n", strerror(err))); 631 } 632 633 // +++++ group magic? 634 635 } 636 else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) { 637 // +++++ 638 } 639 else if( 640 context.parentElement() && 641 !strcmp(context.parentElement(), _UI_STATE_ELEMENT)) { 642 643 // expect a nested message 644 MessageIO* io = dynamic_cast<MessageIO*>(child); 645 if(!io) { 646 BString err; 647 err << 648 "RouteAppNodeManager: unexpected child '" << 649 context.element() << "'\n"; 650 context.reportError(err.String()); 651 } 652 653 // hand it off via the extended context object 654 try { 655 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 656 set.importUIState(io->message()); 657 } 658 catch(bad_cast& e) { 659 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 660 } 661 } 662 663 } 664 665 void RouteAppNodeManager::xmlImportComplete( 666 ImportContext& context) { 667 668 } 669 670 // -------------------------------------------------------- // 671 672 /*static*/ 673 void RouteAppNodeManager::AddTo( 674 XML::DocumentType* docType) { 675 676 // set up the document type 677 678 MessageIO::AddTo(docType); 679 MediaFormatIO::AddTo(docType); 680 ConnectionIO::AddTo(docType); 681 DormantNodeIO::AddTo(docType); 682 LiveNodeIO::AddTo(docType); 683 _add_string_elements(docType); 684 } 685 686 // -------------------------------------------------------- // 687 // implementation 688 // -------------------------------------------------------- // 689 690 uint64 RouteAppNodeManager::_makeIconKey( 691 media_node_id nodeID, icon_size iconSize) { 692 693 return ((uint64)nodeID) << 32 | iconSize; 694 } 695 696 void RouteAppNodeManager::_readIconKey( 697 uint64 key, media_node_id& nodeID, icon_size& iconSize) { 698 699 nodeID = key >> 32; 700 iconSize = icon_size(key & 0xffffffff); 701 } 702 703 void RouteAppNodeManager::_freeIcons() { 704 705 ptr_map_delete( 706 m_iconMap.begin(), 707 m_iconMap.end()); 708 } 709 710 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const { 711 712 // sanity check & easy cases 713 ASSERT(ref); 714 if(ref->isInternal()) 715 return true; 716 717 // bar 'touchy' system nodes 718 if(ref == audioMixerNode() || ref == audioOutputNode()) 719 return false; 720 721 return true; 722 } 723 724 // END -- RouteAppNodeManager.cpp -- 725