2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
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.
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.
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.
32 // RouteAppNodeManager.cpp
34 #include "RouteAppNodeManager.h"
36 #include "MediaIcon.h"
37 #include "NodeGroup.h"
39 #include "Connection.h"
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"
56 #include <TimeSource.h>
63 #include "set_tools.h"
67 __USE_CORTEX_NAMESPACE
69 #define D_METHOD(x) //PRINT (x)
70 #define D_HOOK(x) //PRINT (x)
71 #define D_SETTINGS(x) //PRINT (x)
73 // -------------------------------------------------------- //
75 // -------------------------------------------------------- //
77 RouteAppNodeManager::~RouteAppNodeManager() {
82 RouteAppNodeManager::RouteAppNodeManager(
84 NodeManager(useAddOnHost
),
85 m_nextGroupNumber(1) {
87 // pre-cache icons? +++++
91 // -------------------------------------------------------- //
92 // *** group management
93 // -------------------------------------------------------- //
95 // -------------------------------------------------------- //
96 // *** icon management
97 // -------------------------------------------------------- //
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.
103 const MediaIcon
* RouteAppNodeManager::mediaIconFor(
104 media_node_id nodeID
,
105 icon_size iconSize
) {
109 uint64 key
= _makeIconKey(nodeID
, iconSize
);
111 icon_map::const_iterator it
= m_iconMap
.find(key
);
112 if(it
!= m_iconMap
.end()) {
117 // look up live_node_info
119 status_t err
= getNodeRef(nodeID
, &ref
);
123 return mediaIconFor(ref
->nodeInfo(), iconSize
);
126 const MediaIcon
* RouteAppNodeManager::mediaIconFor(
127 live_node_info nodeInfo
,
128 icon_size iconSize
) {
130 uint64 key
= _makeIconKey(nodeInfo
.node
.node
, iconSize
);
132 icon_map::const_iterator it
= m_iconMap
.find(key
);
133 if(it
!= m_iconMap
.end()) {
138 // create & cache icon
139 MediaIcon
* icon
= new MediaIcon(
143 icon_map::value_type(key
, icon
));
148 // -------------------------------------------------------- //
149 // *** error handling
150 // -------------------------------------------------------- //
152 status_t
RouteAppNodeManager::setLogTarget(
153 const BMessenger
& target
) {
157 if(!target
.IsValid())
160 m_logTarget
= target
;
164 // -------------------------------------------------------- //
165 // NodeManager hook implementations
166 // -------------------------------------------------------- //
168 void RouteAppNodeManager::nodeCreated(
171 // prepare the log message
172 BMessage
logMsg(M_LOG
);
173 BString title
= "Node '";
174 title
<< ref
->name() << "' created";
175 logMsg
.AddString("title", title
);
177 // create a default group for the node
179 NodeGroup
* g
= createGroup(ref
->name());
181 if(ref
->kind() & B_TIME_SOURCE
) {
183 BMessage
m(M_TIME_SOURCE_CREATED
);
184 m
.AddInt32("nodeID", ref
->id());
188 // adopt node's time source if it's not the system clock (the default)
190 media_node systemClock
;
191 status_t err
= roster
->GetSystemTimeSource(&systemClock
);
194 BTimeSource
* ts
= roster
->MakeTimeSourceFor(ref
->node());
197 if(ts
->Node() != systemClock
)
199 g
->setTimeSource(ts
->Node());
200 logMsg
.AddString("line", "Synced to system clock");
207 m_logTarget
.SendMessage(&logMsg
);
210 void RouteAppNodeManager::nodeDeleted(
211 const NodeRef
* ref
) {
213 // prepare the log message
214 BMessage
logMsg(M_LOG
);
215 BString title
= "Node '";
216 title
<< ref
->name() << "' released";
217 logMsg
.AddString("title", title
);
219 if(ref
->kind() & B_TIME_SOURCE
) {
221 BMessage
m(M_TIME_SOURCE_DELETED
);
222 m
.AddInt32("nodeID", ref
->id());
226 m_logTarget
.SendMessage(&logMsg
);
229 void RouteAppNodeManager::connectionMade(
230 Connection
* connection
) {
233 "@ RouteAppNodeManager::connectionMade()\n"));
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() << "' ";
244 logMsg
.AddString("title", title
);
246 if(!(connection
->flags() & Connection::INTERNAL
))
247 // don't react to connection Cortex didn't make
250 // create or merge groups
251 NodeRef
*producer
, *consumer
;
252 err
= getNodeRef(connection
->sourceNode(), &producer
);
255 "!!! RouteAppNodeManager::connectionMade():\n"
256 " sourceNode (%ld) not found\n",
257 connection
->sourceNode()));
260 err
= getNodeRef(connection
->destinationNode(), &consumer
);
263 "!!! RouteAppNodeManager::connectionMade():\n"
264 " destinationNode (%ld) not found\n",
265 connection
->destinationNode()));
269 // add node names to log messages
270 BString line
= "Between:";
271 logMsg
.AddString("line", line
);
273 line
<< producer
->name() << " and ";
274 line
<< consumer
->name();
275 logMsg
.AddString("line", line
);
277 // add format to log message
278 line
= "Negotiated format:";
279 logMsg
.AddString("line", line
);
281 line
<< MediaString::getStringFor(connection
->format(), false);
282 logMsg
.AddString("line", line
);
284 NodeGroup
*group
= 0;
285 BString groupName
= "Untitled group ";
286 if(_canGroup(producer
) && _canGroup(consumer
))
288 if (producer
->group() && consumer
->group() &&
289 !(producer
->group()->groupFlags() & NodeGroup::GROUP_LOCKED
) &&
290 !(consumer
->group()->groupFlags() & NodeGroup::GROUP_LOCKED
))
292 // merge into consumers group
293 group
= consumer
->group();
294 mergeGroups(producer
->group(), group
);
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
);
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
);
309 { // make new group for both
310 groupName
<< m_nextGroupNumber
++;
311 group
= createGroup(groupName
.String());
312 group
->addNode(producer
);
313 group
->addNode(consumer
);
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
);
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
);
329 m_logTarget
.SendMessage(&logMsg
);
332 void RouteAppNodeManager::connectionBroken(
333 const Connection
* connection
) {
336 "@ RouteAppNodeManager::connectionBroken()\n"));
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() << "' ";
345 logMsg
.AddString("title", title
);
347 if(!(connection
->flags() & Connection::INTERNAL
))
348 // don't react to connection Cortex didn't make
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
357 NodeRef
*producer
, *consumer
;
358 err
= getNodeRef(connection
->sourceNode(), &producer
);
361 "!!! RouteAppNodeManager::connectionMade():\n"
362 " sourceNode (%ld) not found\n",
363 connection
->sourceNode()));
366 err
= getNodeRef(connection
->destinationNode(), &consumer
);
369 "!!! RouteAppNodeManager::connectionMade():\n"
370 " destinationNode (%ld) not found\n",
371 connection
->destinationNode()));
375 // add node names to log messages
376 BString line
= "Between:";
377 logMsg
.AddString("line", line
);
379 line
<< producer
->name() << " and ";
380 line
<< consumer
->name();
381 logMsg
.AddString("line", line
);
385 producer
->group() == consumer
->group() &&
386 !findRoute(producer
->id(), consumer
->id())) {
389 splitGroup(producer
, consumer
, &newGroup
);
392 m_logTarget
.SendMessage(&logMsg
);
395 void RouteAppNodeManager::connectionFailed(
396 const media_output
& output
,
397 const media_input
& input
,
398 const media_format
& format
,
401 "@ RouteAppNodeManager::connectionFailed()\n"));
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
);
411 NodeRef
*producer
, *consumer
;
412 err
= getNodeRef(output
.node
.node
, &producer
);
416 err
= getNodeRef(input
.node
.node
, &consumer
);
421 // add node names to log messages
422 BString line
= "Between:";
423 logMsg
.AddString("line", line
);
425 line
<< producer
->name() << " and " << consumer
->name();
426 logMsg
.AddString("line", line
);
428 // add format to log message
429 line
= "Tried format:";
430 logMsg
.AddString("line", line
);
432 line
<< MediaString::getStringFor(format
, true);
433 logMsg
.AddString("line", line
);
436 m_logTarget
.SendMessage(&logMsg
);
439 // -------------------------------------------------------- //
441 // -------------------------------------------------------- //
443 void RouteAppNodeManager::xmlExportBegin(
444 ExportContext
& context
) const {
449 NodeSetIOContext
& set
= dynamic_cast<NodeSetIOContext
&>(context
);
450 context
.beginElement(_NODE_SET_ELEMENT
);
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
);
459 err
= getNodeRef(id
, &ref
);
462 "! RVNM::xmlExportBegin(): node %ld doesn't exist\n", id
));
467 // skip unless internal
468 if(!ref
->isInternal()) {
470 "! RVNM::xmlExportBegin(): node %ld not internal; skipping.\n", id
));
478 context
.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
482 void RouteAppNodeManager::xmlExportAttributes(
483 ExportContext
& context
) const {}
485 void RouteAppNodeManager::xmlExportContent(
486 ExportContext
& context
) const {
491 NodeSetIOContext
& nodeSet
= dynamic_cast<NodeSetIOContext
&>(context
);
492 context
.beginContent();
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
);
504 err
= getNodeRef(id
, &ref
);
507 "! RouteAppNodeManager::xmlExportContent():\n"
508 " getNodeRef(%ld) failed: '%s'\n",
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:
520 connection_map::value_type(conSet
[c
].id(), conSet
[c
]));
522 // create an IO object for the node & write it
523 DormantNodeIO
io(ref
, nodeSet
.keyAt(n
));
524 if(context
.writeObject(&io
) < B_OK
)
530 for(connection_map::const_iterator it
= connections
.begin();
531 it
!= connections
.end(); ++it
) {
537 if(context
.writeObject(&io
) < B_OK
)
543 // +++++ write groups
548 nodeSet
.exportUIState(&m
);
549 context
.beginElement(_UI_STATE_ELEMENT
);
550 context
.beginContent();
552 context
.writeObject(&io
);
553 context
.endElement();
557 context
.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
561 void RouteAppNodeManager::xmlExportEnd(
562 ExportContext
& context
) const {
564 context
.endElement();
567 // -------------------------------------------------------- //
569 // -------------------------------------------------------- //
571 void RouteAppNodeManager::xmlImportBegin(
572 ImportContext
& context
) {
576 void RouteAppNodeManager::xmlImportAttribute(
579 ImportContext
& context
) {}
581 void RouteAppNodeManager::xmlImportContent(
584 ImportContext
& context
) {}
586 void RouteAppNodeManager::xmlImportChild(
588 ImportContext
& context
) {
592 if(!strcmp(context
.element(), _DORMANT_NODE_ELEMENT
)) {
593 DormantNodeIO
* io
= dynamic_cast<DormantNodeIO
*>(child
);
597 err
= io
->instantiate(this, &newRef
);
599 // created node; add an entry to the set stored in the
600 // ImportContext for later reference
602 NodeSetIOContext
& set
= dynamic_cast<NodeSetIOContext
&>(context
);
603 set
.addNode(newRef
->id(), io
->nodeKey());
606 context
.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
611 "!!! RouteAppNodeManager::xmlImportChild():\n"
612 " DormantNodeIO::instantiate() failed:\n"
617 else if(!strcmp(context
.element(), _CONNECTION_ELEMENT
)) {
618 ConnectionIO
* io
= dynamic_cast<ConnectionIO
*>(child
);
621 // instantiate the connection
623 err
= io
->instantiate(
625 dynamic_cast<NodeSetIOContext
*>(&context
),
629 "!!! ConnectionIO::instantiate() failed:\n"
630 " '%s'\n", strerror(err
)));
633 // +++++ group magic?
636 else if(!strcmp(context
.element(), _NODE_GROUP_ELEMENT
)) {
640 context
.parentElement() &&
641 !strcmp(context
.parentElement(), _UI_STATE_ELEMENT
)) {
643 // expect a nested message
644 MessageIO
* io
= dynamic_cast<MessageIO
*>(child
);
648 "RouteAppNodeManager: unexpected child '" <<
649 context
.element() << "'\n";
650 context
.reportError(err
.String());
653 // hand it off via the extended context object
655 NodeSetIOContext
& set
= dynamic_cast<NodeSetIOContext
&>(context
);
656 set
.importUIState(io
->message());
659 context
.reportError("RouteAppNodeManager: expected a NodeSetIOContext\n");
665 void RouteAppNodeManager::xmlImportComplete(
666 ImportContext
& context
) {
670 // -------------------------------------------------------- //
673 void RouteAppNodeManager::AddTo(
674 XML::DocumentType
* docType
) {
676 // set up the document type
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
);
686 // -------------------------------------------------------- //
688 // -------------------------------------------------------- //
690 uint64
RouteAppNodeManager::_makeIconKey(
691 media_node_id nodeID
, icon_size iconSize
) {
693 return ((uint64
)nodeID
) << 32 | iconSize
;
696 void RouteAppNodeManager::_readIconKey(
697 uint64 key
, media_node_id
& nodeID
, icon_size
& iconSize
) {
700 iconSize
= icon_size(key
& 0xffffffff);
703 void RouteAppNodeManager::_freeIcons() {
710 bool RouteAppNodeManager::_canGroup(NodeRef
* ref
) const {
712 // sanity check & easy cases
714 if(ref
->isInternal())
717 // bar 'touchy' system nodes
718 if(ref
== audioMixerNode() || ref
== audioOutputNode())
724 // END -- RouteAppNodeManager.cpp --