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