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->Node() != systemClock) 196 { 197 g->setTimeSource(ts->Node()); 198 logMsg.AddString("line", "Synced to system clock"); 199 } 200 ts->Release(); 201 } 202 203 g->addNode(ref); 204 205 m_logTarget.SendMessage(&logMsg); 206 } 207 208 void RouteAppNodeManager::nodeDeleted( 209 const NodeRef* ref) { 210 211 // prepare the log message 212 BMessage logMsg(M_LOG); 213 BString title = "Node '"; 214 title << ref->name() << "' released"; 215 logMsg.AddString("title", title); 216 217 if(ref->kind() & B_TIME_SOURCE) { 218 // notify observers 219 BMessage m(M_TIME_SOURCE_DELETED); 220 m.AddInt32("nodeID", ref->id()); 221 notify(&m); 222 } 223 224 m_logTarget.SendMessage(&logMsg); 225 } 226 227 void RouteAppNodeManager::connectionMade( 228 Connection* connection) { 229 230 D_HOOK(( 231 "@ RouteAppNodeManager::connectionMade()\n")); 232 233 status_t err; 234 235 // prepare the log message 236 BMessage logMsg(M_LOG); 237 BString title = "Connection "; 238 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 239 title << "'" << connection->outputName() << "' "; 240 } 241 title << "made"; 242 logMsg.AddString("title", title); 243 244 if(!(connection->flags() & Connection::INTERNAL)) 245 // don't react to connection Cortex didn't make 246 return; 247 248 // create or merge groups 249 NodeRef *producer, *consumer; 250 err = getNodeRef(connection->sourceNode(), &producer); 251 if(err < B_OK) { 252 D_HOOK(( 253 "!!! RouteAppNodeManager::connectionMade():\n" 254 " sourceNode (%ld) not found\n", 255 connection->sourceNode())); 256 return; 257 } 258 err = getNodeRef(connection->destinationNode(), &consumer); 259 if(err < B_OK) { 260 D_HOOK(( 261 "!!! RouteAppNodeManager::connectionMade():\n" 262 " destinationNode (%ld) not found\n", 263 connection->destinationNode())); 264 return; 265 } 266 267 // add node names to log messages 268 BString line = "Between:"; 269 logMsg.AddString("line", line); 270 line = " "; 271 line << producer->name() << " and "; 272 line << consumer->name(); 273 logMsg.AddString("line", line); 274 275 // add format to log message 276 line = "Negotiated format:"; 277 logMsg.AddString("line", line); 278 line = " "; 279 line << MediaString::getStringFor(connection->format(), false); 280 logMsg.AddString("line", line); 281 282 NodeGroup *group = 0; 283 BString groupName = "Untitled group "; 284 if(_canGroup(producer) && _canGroup(consumer)) 285 { 286 if (producer->group() && consumer->group() && 287 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED) && 288 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 289 { 290 // merge into consumers group 291 group = consumer->group(); 292 mergeGroups(producer->group(), group); 293 } 294 else if (producer->group() && 295 !(producer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 296 { // add consumer to producers group 297 group = producer->group(); 298 group->addNode(consumer); 299 } 300 else if (consumer->group() && 301 !(consumer->group()->groupFlags() & NodeGroup::GROUP_LOCKED)) 302 { // add producer to consumers group 303 group = consumer->group(); 304 group->addNode(producer); 305 } 306 else 307 { // make new group for both 308 groupName << m_nextGroupNumber++; 309 group = createGroup(groupName.String()); 310 group->addNode(producer); 311 group->addNode(consumer); 312 } 313 } 314 else if(_canGroup(producer) && !producer->group()) 315 { // make new group for producer 316 groupName << m_nextGroupNumber++; 317 group = createGroup(groupName.String()); 318 group->addNode(producer); 319 } 320 else if(_canGroup(consumer) && !consumer->group()) 321 { // make new group for consumer 322 groupName << m_nextGroupNumber++; 323 group = createGroup(groupName.String()); 324 group->addNode(consumer); 325 } 326 327 m_logTarget.SendMessage(&logMsg); 328 } 329 330 void RouteAppNodeManager::connectionBroken( 331 const Connection* connection) { 332 333 D_HOOK(( 334 "@ RouteAppNodeManager::connectionBroken()\n")); 335 336 // prepare the log message 337 BMessage logMsg(M_LOG); 338 BString title = "Connection "; 339 if (strcmp(connection->outputName(), connection->inputName()) == 0) { 340 title << "'" << connection->outputName() << "' "; 341 } 342 title << "broken"; 343 logMsg.AddString("title", title); 344 345 if(!(connection->flags() & Connection::INTERNAL)) 346 // don't react to connection Cortex didn't make 347 return; 348 349 status_t err; 350 351 // if the source and destination nodes belong to the same group, 352 // and if no direct or indirect connection remains between the 353 // source and destination nodes, split groups 354 355 NodeRef *producer, *consumer; 356 err = getNodeRef(connection->sourceNode(), &producer); 357 if(err < B_OK) { 358 D_HOOK(( 359 "!!! RouteAppNodeManager::connectionMade():\n" 360 " sourceNode (%ld) not found\n", 361 connection->sourceNode())); 362 return; 363 } 364 err = getNodeRef(connection->destinationNode(), &consumer); 365 if(err < B_OK) { 366 D_HOOK(( 367 "!!! RouteAppNodeManager::connectionMade():\n" 368 " destinationNode (%ld) not found\n", 369 connection->destinationNode())); 370 return; 371 } 372 373 // add node names to log messages 374 BString line = "Between:"; 375 logMsg.AddString("line", line); 376 line = " "; 377 line << producer->name() << " and "; 378 line << consumer->name(); 379 logMsg.AddString("line", line); 380 381 if( 382 producer->group() && 383 producer->group() == consumer->group() && 384 !findRoute(producer->id(), consumer->id())) { 385 386 NodeGroup *newGroup; 387 splitGroup(producer, consumer, &newGroup); 388 } 389 390 m_logTarget.SendMessage(&logMsg); 391 } 392 393 void RouteAppNodeManager::connectionFailed( 394 const media_output & output, 395 const media_input & input, 396 const media_format & format, 397 status_t error) { 398 D_HOOK(( 399 "@ RouteAppNodeManager::connectionFailed()\n")); 400 401 status_t err; 402 403 // prepare the log message 404 BMessage logMsg(M_LOG); 405 BString title = "Connection failed"; 406 logMsg.AddString("title", title); 407 logMsg.AddInt32("error", error); 408 409 NodeRef *producer, *consumer; 410 err = getNodeRef(output.node.node, &producer); 411 if(err < B_OK) { 412 return; 413 } 414 err = getNodeRef(input.node.node, &consumer); 415 if(err < B_OK) { 416 return; 417 } 418 419 // add node names to log messages 420 BString line = "Between:"; 421 logMsg.AddString("line", line); 422 line = " "; 423 line << producer->name() << " and " << consumer->name(); 424 logMsg.AddString("line", line); 425 426 // add format to log message 427 line = "Tried format:"; 428 logMsg.AddString("line", line); 429 line = " "; 430 line << MediaString::getStringFor(format, true); 431 logMsg.AddString("line", line); 432 433 // and send it 434 m_logTarget.SendMessage(&logMsg); 435 } 436 437 // -------------------------------------------------------- // 438 // *** IPersistent 439 // -------------------------------------------------------- // 440 441 void RouteAppNodeManager::xmlExportBegin( 442 ExportContext& context) const { 443 444 status_t err; 445 446 try { 447 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 448 context.beginElement(_NODE_SET_ELEMENT); 449 450 // validate the node set 451 for(int n = set.countNodes()-1; n >= 0; --n) { 452 media_node_id id = set.nodeAt(n); 453 ASSERT(id != media_node::null.node); 454 455 // fetch node 456 NodeRef* ref; 457 err = getNodeRef(id, &ref); 458 if(err < B_OK) { 459 D_SETTINGS(( 460 "! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id)); 461 462 set.removeNodeAt(n); 463 continue; 464 } 465 // skip unless internal 466 if(!ref->isInternal()) { 467 D_SETTINGS(( 468 "! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id)); 469 470 set.removeNodeAt(n); 471 continue; 472 } 473 } 474 } 475 catch(bad_cast& e) { 476 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 477 } 478 } 479 480 void RouteAppNodeManager::xmlExportAttributes( 481 ExportContext& context) const {} 482 483 void RouteAppNodeManager::xmlExportContent( 484 ExportContext& context) const { 485 486 status_t err; 487 488 try { 489 NodeSetIOContext& nodeSet = dynamic_cast<NodeSetIOContext&>(context); 490 context.beginContent(); 491 492 // write nodes; enumerate connections 493 typedef map<uint32,Connection> connection_map; 494 connection_map connections; 495 int count = nodeSet.countNodes(); 496 for(int n = 0; n < count; ++n) { 497 media_node_id id = nodeSet.nodeAt(n); 498 ASSERT(id != media_node::null.node); 499 500 // fetch node 501 NodeRef* ref; 502 err = getNodeRef(id, &ref); 503 if(err < B_OK) { 504 D_SETTINGS(( 505 "! RouteAppNodeManager::xmlExportContent():\n" 506 " getNodeRef(%ld) failed: '%s'\n", 507 id, strerror(err))); 508 continue; 509 } 510 511 // fetch connections 512 vector<Connection> conSet; 513 ref->getInputConnections(conSet); 514 ref->getOutputConnections(conSet); 515 for(uint32 c = 0; c < conSet.size(); ++c) 516 // non-unique connections will be rejected: 517 connections.insert( 518 connection_map::value_type(conSet[c].id(), conSet[c])); 519 520 // create an IO object for the node & write it 521 DormantNodeIO io(ref, nodeSet.keyAt(n)); 522 if(context.writeObject(&io) < B_OK) 523 // abort 524 return; 525 } 526 527 // write connections 528 for(connection_map::const_iterator it = connections.begin(); 529 it != connections.end(); ++it) { 530 531 ConnectionIO io( 532 &(*it).second, 533 this, 534 &nodeSet); 535 if(context.writeObject(&io) < B_OK) 536 // abort 537 return; 538 539 } 540 541 // +++++ write groups 542 543 // write UI state 544 { 545 BMessage m; 546 nodeSet.exportUIState(&m); 547 context.beginElement(_UI_STATE_ELEMENT); 548 context.beginContent(); 549 MessageIO io(&m); 550 context.writeObject(&io); 551 context.endElement(); 552 } 553 } 554 catch(bad_cast& e) { 555 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 556 } 557 } 558 559 void RouteAppNodeManager::xmlExportEnd( 560 ExportContext& context) const { 561 562 context.endElement(); 563 } 564 565 // -------------------------------------------------------- // 566 // IMPORT 567 // -------------------------------------------------------- // 568 569 void RouteAppNodeManager::xmlImportBegin( 570 ImportContext& context) { 571 572 } 573 574 void RouteAppNodeManager::xmlImportAttribute( 575 const char* key, 576 const char* value, 577 ImportContext& context) {} 578 579 void RouteAppNodeManager::xmlImportContent( 580 const char* data, 581 uint32 length, 582 ImportContext& context) {} 583 584 void RouteAppNodeManager::xmlImportChild( 585 IPersistent* child, 586 ImportContext& context) { 587 588 status_t err; 589 590 if(!strcmp(context.element(), _DORMANT_NODE_ELEMENT)) { 591 DormantNodeIO* io = dynamic_cast<DormantNodeIO*>(child); 592 ASSERT(io); 593 594 NodeRef* newRef; 595 err = io->instantiate(this, &newRef); 596 if(err == B_OK) { 597 // created node; add an entry to the set stored in the 598 // ImportContext for later reference 599 try { 600 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 601 set.addNode(newRef->id(), io->nodeKey()); 602 } 603 catch(bad_cast& e) { 604 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 605 } 606 } 607 else { 608 D_SETTINGS(( 609 "!!! RouteAppNodeManager::xmlImportChild():\n" 610 " DormantNodeIO::instantiate() failed:\n" 611 " '%s'\n", 612 strerror(err))); 613 } 614 } 615 else if(!strcmp(context.element(), _CONNECTION_ELEMENT)) { 616 ConnectionIO* io = dynamic_cast<ConnectionIO*>(child); 617 ASSERT(io); 618 619 // instantiate the connection 620 Connection con; 621 err = io->instantiate( 622 this, 623 dynamic_cast<NodeSetIOContext*>(&context), 624 &con); 625 if(err < B_OK) { 626 D_SETTINGS(( 627 "!!! ConnectionIO::instantiate() failed:\n" 628 " '%s'\n", strerror(err))); 629 } 630 631 // +++++ group magic? 632 633 } 634 else if(!strcmp(context.element(), _NODE_GROUP_ELEMENT)) { 635 // +++++ 636 } 637 else if( 638 context.parentElement() && 639 !strcmp(context.parentElement(), _UI_STATE_ELEMENT)) { 640 641 // expect a nested message 642 MessageIO* io = dynamic_cast<MessageIO*>(child); 643 if(!io) { 644 BString err; 645 err << 646 "RouteAppNodeManager: unexpected child '" << 647 context.element() << "'\n"; 648 context.reportError(err.String()); 649 } 650 651 // hand it off via the extended context object 652 try { 653 NodeSetIOContext& set = dynamic_cast<NodeSetIOContext&>(context); 654 set.importUIState(io->message()); 655 } 656 catch(bad_cast& e) { 657 context.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n"); 658 } 659 } 660 661 } 662 663 void RouteAppNodeManager::xmlImportComplete( 664 ImportContext& context) { 665 666 } 667 668 // -------------------------------------------------------- // 669 670 /*static*/ 671 void RouteAppNodeManager::AddTo( 672 XML::DocumentType* docType) { 673 674 // set up the document type 675 676 MessageIO::AddTo(docType); 677 MediaFormatIO::AddTo(docType); 678 ConnectionIO::AddTo(docType); 679 DormantNodeIO::AddTo(docType); 680 LiveNodeIO::AddTo(docType); 681 _add_string_elements(docType); 682 } 683 684 // -------------------------------------------------------- // 685 // implementation 686 // -------------------------------------------------------- // 687 688 uint64 RouteAppNodeManager::_makeIconKey( 689 media_node_id nodeID, icon_size iconSize) { 690 691 return ((uint64)nodeID) << 32 | iconSize; 692 } 693 694 void RouteAppNodeManager::_readIconKey( 695 uint64 key, media_node_id& nodeID, icon_size& iconSize) { 696 697 nodeID = key >> 32; 698 iconSize = icon_size(key & 0xffffffff); 699 } 700 701 void RouteAppNodeManager::_freeIcons() { 702 703 ptr_map_delete( 704 m_iconMap.begin(), 705 m_iconMap.end()); 706 } 707 708 bool RouteAppNodeManager::_canGroup(NodeRef* ref) const { 709 710 // sanity check & easy cases 711 ASSERT(ref); 712 if(ref->isInternal()) 713 return true; 714 715 // bar 'touchy' system nodes 716 if(ref == audioMixerNode() || ref == audioOutputNode()) 717 return false; 718 719 return true; 720 } 721 722 // END -- RouteAppNodeManager.cpp -- 723