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