xref: /haiku/src/apps/mediaplayer/media_node_framework/NodeManager.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
3  * Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
4  * All Rights Reserved. Distributed under the terms of the MIT license.
5  */
6 
7 
8 //! This class controls our media nodes and general playback
9 
10 
11 #include "NodeManager.h"
12 
13 #include <stdio.h>
14 #include <string.h>
15 
16 #include <MediaRoster.h>
17 #include <scheduler.h>
18 #include <TimeSource.h>
19 
20 #include "AudioProducer.h"
21 #include "AudioSupplier.h"
22 #include "VideoConsumer.h"
23 #include "VideoProducer.h"
24 #include "VideoSupplier.h"
25 
26 
27 // debugging
28 //#define TRACE_NODE_MANAGER
29 #ifdef TRACE_NODE_MANAGER
30 #	define TRACE(x...)	printf(x)
31 #	define ERROR(x...)	fprintf(stderr, x)
32 #else
33 #	define TRACE(x...)
34 #	define ERROR(x...)	fprintf(stderr, x)
35 #endif
36 
37 #define print_error(str, status) printf(str ", error: %s\n", strerror(status))
38 
39 
40 NodeManager::Connection::Connection()
41 	:
42 	connected(false)
43 {
44 }
45 
46 
47 // #pragma mark -
48 
49 
50 NodeManager::NodeManager()
51 	:
52 	PlaybackManager(),
53 	fMediaRoster(NULL),
54 	fAudioProducer(NULL),
55 	fVideoConsumer(NULL),
56 	fVideoProducer(NULL),
57 	fTimeSource(media_node::null),
58 	fAudioConnection(),
59 	fVideoConnection(),
60 	fPerformanceTimeBase(0),
61 	fStatus(B_NO_INIT),
62 	fVideoTarget(NULL),
63 	fAudioSupplier(NULL),
64 	fVideoSupplier(NULL),
65 	fVideoBounds(0, 0, -1, -1),
66 	fPeakListener(NULL)
67 {
68 }
69 
70 
71 NodeManager::~NodeManager()
72 {
73 	_StopNodes();
74 	_TearDownNodes();
75 }
76 
77 
78 status_t
79 NodeManager::Init(BRect videoBounds, float videoFrameRate,
80 	color_space preferredVideoFormat, float audioFrameRate,
81 	uint32 audioChannels, int32 loopingMode, bool loopingEnabled,
82 	float speed, uint32 enabledNodes, bool useOverlays)
83 {
84 	// init base class
85 	PlaybackManager::Init(videoFrameRate, true, loopingMode, loopingEnabled,
86 		speed);
87 
88 	// get some objects from a derived class
89 	if (fVideoTarget == NULL)
90 		fVideoTarget = CreateVideoTarget();
91 
92 	if (fVideoSupplier == NULL)
93 		fVideoSupplier = CreateVideoSupplier();
94 
95 	if (fAudioSupplier == NULL)
96 		fAudioSupplier = CreateAudioSupplier();
97 
98 	return FormatChanged(videoBounds, videoFrameRate, preferredVideoFormat,
99 		audioFrameRate, audioChannels, enabledNodes, useOverlays, true);
100 }
101 
102 
103 status_t
104 NodeManager::InitCheck()
105 {
106 	return fStatus;
107 }
108 
109 
110 void
111 NodeManager::SetPlayMode(int32 mode, bool continuePlaying)
112 {
113 	if (fVideoConsumer != NULL && fMediaRoster != NULL) {
114 		BMediaNode::run_mode runMode = mode > 0 ?
115 			BMediaNode::B_DROP_DATA : BMediaNode::B_OFFLINE;
116 		status_t ret = fMediaRoster->SetRunModeNode(fVideoConnection.consumer,
117 			runMode);
118 		if (ret != B_OK) {
119 			printf("NodeManager::SetPlayMode(%" B_PRId32 "), setting run mode "
120 				"failed: %s\n", mode, strerror(ret));
121 		}
122 	}
123 
124 	PlaybackManager::SetPlayMode(mode, continuePlaying);
125 }
126 
127 
128 status_t
129 NodeManager::CleanupNodes()
130 {
131 	_StopNodes();
132 	return _TearDownNodes(false);
133 }
134 
135 
136 status_t
137 NodeManager::FormatChanged(BRect videoBounds, float videoFrameRate,
138 	color_space preferredVideoFormat, float audioFrameRate,
139 	uint32 audioChannels, uint32 enabledNodes, bool useOverlays, bool force)
140 {
141 	TRACE("NodeManager::FormatChanged()\n");
142 
143 	if (!force && videoBounds == VideoBounds()
144 		&& videoFrameRate == FramesPerSecond()) {
145 		TRACE("   -> reusing existing nodes\n");
146 		// TODO: if enabledNodes would indicate that audio or video
147 		// is no longer needed, or, worse yet, suddenly needed when
148 		// it wasn't before, then we should not return here!
149 		PlaybackManager::Init(videoFrameRate, false, LoopMode(),
150 			IsLoopingEnabled(), Speed(), MODE_PLAYING_PAUSED_FORWARD,
151 			CurrentFrame());
152 		return B_OK;
153 	}
154 
155 	_StopNodes();
156 	_TearDownNodes();
157 
158 	PlaybackManager::Init(videoFrameRate, true, LoopMode(), IsLoopingEnabled(),
159 		Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
160 
161 	SetVideoBounds(videoBounds);
162 
163 	status_t ret = _SetUpNodes(preferredVideoFormat, enabledNodes,
164 		useOverlays, audioFrameRate, audioChannels);
165 	if (ret == B_OK)
166 		_StartNodes();
167 	else
168 		fprintf(stderr, "unable to setup nodes: %s\n", strerror(ret));
169 
170 	return ret;
171 }
172 
173 
174 bigtime_t
175 NodeManager::RealTimeForTime(bigtime_t time) const
176 {
177 	bigtime_t result = 0;
178 	if (fVideoProducer) {
179 		result = fVideoProducer->TimeSource()->RealTimeFor(
180 			fPerformanceTimeBase + time, 0);
181 	} else if (fAudioProducer) {
182 		result = fAudioProducer->TimeSource()->RealTimeFor(
183 			fPerformanceTimeBase + time, 0);
184 	}
185 //printf("NodeManager::RealTimeForTime(%lld) -> %lld\n", time, result);
186 	return result;
187 }
188 
189 
190 bigtime_t
191 NodeManager::TimeForRealTime(bigtime_t time) const
192 {
193 	bigtime_t result = 0;
194 	if (fVideoProducer) {
195 		result = fVideoProducer->TimeSource()->PerformanceTimeFor(time)
196 			- fPerformanceTimeBase;
197 	} else if (fAudioProducer) {
198 		result = fAudioProducer->TimeSource()->PerformanceTimeFor(time)
199 			- fPerformanceTimeBase;
200 	}
201 	return result;
202 }
203 
204 
205 void
206 NodeManager::SetCurrentAudioTime(bigtime_t time)
207 {
208 //printf("NodeManager::SetCurrentAudioTime(%lld)\n", time);
209 	PlaybackManager::SetCurrentAudioTime(time);
210 	if (!fVideoProducer) {
211 		// running without video, update video time as well
212 		PlaybackManager::SetCurrentVideoTime(time);
213 	}
214 }
215 
216 
217 void
218 NodeManager::SetVideoBounds(BRect bounds)
219 {
220 	if (bounds != fVideoBounds) {
221 		fVideoBounds = bounds;
222 		NotifyVideoBoundsChanged(fVideoBounds);
223 	}
224 }
225 
226 
227 BRect
228 NodeManager::VideoBounds() const
229 {
230 	return fVideoBounds;
231 }
232 
233 
234 void
235 NodeManager::SetVideoTarget(VideoTarget* videoTarget)
236 {
237 	if (videoTarget != fVideoTarget) {
238 		fVideoTarget = videoTarget;
239 		if (fVideoConsumer)
240 			fVideoConsumer->SetTarget(fVideoTarget);
241 	}
242 }
243 
244 
245 VideoTarget*
246 NodeManager::GetVideoTarget() const
247 {
248 	return fVideoTarget;
249 }
250 
251 
252 void
253 NodeManager::SetVolume(float percent)
254 {
255 	// TODO: would be nice to set the volume on the system mixer input of
256 	// our audio node...
257 }
258 
259 
260 void
261 NodeManager::SetPeakListener(BHandler* handler)
262 {
263 	fPeakListener = handler;
264 	if (fAudioProducer)
265 		fAudioProducer->SetPeakListener(fPeakListener);
266 }
267 
268 
269 // #pragma mark -
270 
271 
272 status_t
273 NodeManager::_SetUpNodes(color_space preferredVideoFormat, uint32 enabledNodes,
274 	bool useOverlays, float audioFrameRate, uint32 audioChannels)
275 {
276 	TRACE("NodeManager::_SetUpNodes()\n");
277 
278 	// find the media roster
279 	fStatus = B_OK;
280 	fMediaRoster = BMediaRoster::Roster(&fStatus);
281 	if (fStatus != B_OK) {
282 		print_error("Can't find the media roster", fStatus);
283 		fMediaRoster = NULL;
284 		return fStatus;
285 	}
286 
287 	// find the time source
288 	fStatus = fMediaRoster->GetTimeSource(&fTimeSource);
289 	if (fStatus != B_OK) {
290 		print_error("Can't get a time source", fStatus);
291 		return fStatus;
292 	}
293 
294 	// setup the video nodes
295 	if (enabledNodes != AUDIO_ONLY) {
296 		fStatus = _SetUpVideoNodes(preferredVideoFormat, useOverlays);
297 		if (fStatus != B_OK) {
298 			print_error("Error setting up video nodes", fStatus);
299 			return fStatus;
300 		}
301 	} else
302 		printf("running without video node\n");
303 
304 	// setup the audio nodes
305 	if (enabledNodes != VIDEO_ONLY) {
306 		fStatus = _SetUpAudioNodes(audioFrameRate, audioChannels);
307 		if (fStatus != B_OK) {
308 			print_error("Error setting up audio nodes", fStatus);
309 			return fStatus;
310 		}
311 		fNoAudio = false;
312 	} else {
313 		fNoAudio = true;
314 		printf("running without audio node\n");
315 	}
316 
317 	return fStatus;
318 }
319 
320 
321 status_t
322 NodeManager::_SetUpVideoNodes(color_space preferredVideoFormat,
323 	bool useOverlays)
324 {
325 	// create the video producer node
326 	fVideoProducer = new VideoProducer(NULL, "MediaPlayer video out", 0,
327 		this, fVideoSupplier);
328 
329 	// register the producer node
330 	fStatus = fMediaRoster->RegisterNode(fVideoProducer);
331 	if (fStatus != B_OK) {
332 		print_error("Can't register the video producer", fStatus);
333 		return fStatus;
334 	}
335 
336 	// make sure the Media Roster knows that we're using the node
337 //	fMediaRoster->GetNodeFor(fVideoProducer->Node().node,
338 //		&fVideoConnection.producer);
339 	fVideoConnection.producer = fVideoProducer->Node();
340 
341 	// create the video consumer node
342 	fVideoConsumer = new VideoConsumer("MediaPlayer video in", NULL, 0, this,
343 		fVideoTarget);
344 
345 	// register the consumer node
346 	fStatus = fMediaRoster->RegisterNode(fVideoConsumer);
347 	if (fStatus != B_OK) {
348 		print_error("Can't register the video consumer", fStatus);
349 		return fStatus;
350 	}
351 
352 	// make sure the Media Roster knows that we're using the node
353 //	fMediaRoster->GetNodeFor(fVideoConsumer->Node().node,
354 //		&fVideoConnection.consumer);
355 	fVideoConnection.consumer = fVideoConsumer->Node();
356 
357 	// find free producer output
358 	media_input videoInput;
359 	media_output videoOutput;
360 	int32 count = 1;
361 	fStatus = fMediaRoster->GetFreeOutputsFor(fVideoConnection.producer,
362 		&videoOutput, 1, &count, B_MEDIA_RAW_VIDEO);
363 	if (fStatus != B_OK || count < 1) {
364 		fStatus = B_RESOURCE_UNAVAILABLE;
365 		print_error("Can't find an available video stream", fStatus);
366 		return fStatus;
367 	}
368 
369 	// find free consumer input
370 	count = 1;
371 	fStatus = fMediaRoster->GetFreeInputsFor(fVideoConnection.consumer,
372 		&videoInput, 1, &count, B_MEDIA_RAW_VIDEO);
373 	if (fStatus != B_OK || count < 1) {
374 		fStatus = B_RESOURCE_UNAVAILABLE;
375 		print_error("Can't find an available connection to the video window",
376 			fStatus);
377 		return fStatus;
378 	}
379 
380 	// connect the nodes
381 	media_format format;
382 	format.type = B_MEDIA_RAW_VIDEO;
383 	media_raw_video_format videoFormat = {
384 		FramesPerSecond(), 1, 0,
385 		(uint32)fVideoBounds.IntegerWidth(),
386 		B_VIDEO_TOP_LEFT_RIGHT, 1, 1,
387 		{
388 			preferredVideoFormat,
389 			(uint32)(fVideoBounds.IntegerWidth() + 1),
390 			(uint32)(fVideoBounds.IntegerHeight() + 1),
391 			0, 0, 0
392 		}
393 	};
394 	format.u.raw_video = videoFormat;
395 
396 	// connect video producer to consumer (hopefully using overlays)
397 	fVideoConsumer->SetTryOverlay(useOverlays);
398 	fStatus = fMediaRoster->Connect(videoOutput.source, videoInput.destination,
399 		&format, &videoOutput, &videoInput);
400 
401 	if (fStatus != B_OK) {
402 		print_error("Can't connect the video source to the video window... "
403 			"trying without overlays", fStatus);
404 
405 		uint32 flags = 0;
406 		bool supported = bitmaps_support_space(
407 			format.u.raw_video.display.format, &flags);
408 		if (!supported || (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) {
409 			// cannot create bitmaps with such a color space
410 			// or BViews don't support drawing it, fallback to B_RGB32
411 			format.u.raw_video.display.format = B_RGB32;
412 			printf("NodeManager::_SetupVideoNodes() - falling back to "
413 				"B_RGB32\n");
414 		}
415 
416 		fVideoConsumer->SetTryOverlay(false);
417 		// connect video producer to consumer (not using overlays and using
418 		// a colorspace that BViews support drawing)
419 		fStatus = fMediaRoster->Connect(videoOutput.source,
420 			videoInput.destination, &format, &videoOutput, &videoInput);
421 	}
422 	// bail if second attempt failed too
423 	if (fStatus != B_OK) {
424 		print_error("Can't connect the video source to the video window",
425 			fStatus);
426 		return fStatus;
427 	}
428 
429 	// the inputs and outputs might have been reassigned during the
430 	// nodes' negotiation of the Connect().  That's why we wait until
431 	// after Connect() finishes to save their contents.
432 	fVideoConnection.format = format;
433 	fVideoConnection.source = videoOutput.source;
434 	fVideoConnection.destination = videoInput.destination;
435 	fVideoConnection.connected = true;
436 
437 	// set time sources
438 	fStatus = fMediaRoster->SetTimeSourceFor(fVideoConnection.producer.node,
439 		fTimeSource.node);
440 	if (fStatus != B_OK) {
441 		print_error("Can't set the timesource for the video source", fStatus);
442 		return fStatus;
443 	}
444 
445 	fStatus = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
446 		fTimeSource.node);
447 	if (fStatus != B_OK) {
448 		print_error("Can't set the timesource for the video window", fStatus);
449 		return fStatus;
450 	}
451 
452 	return fStatus;
453 }
454 
455 
456 status_t
457 NodeManager::_SetUpAudioNodes(float audioFrameRate, uint32 audioChannels)
458 {
459 	fAudioProducer = new AudioProducer("MediaPlayer audio out", fAudioSupplier);
460 	fAudioProducer->SetPeakListener(fPeakListener);
461 	fStatus = fMediaRoster->RegisterNode(fAudioProducer);
462 	if (fStatus != B_OK) {
463 		print_error("unable to register audio producer node!\n", fStatus);
464 		return fStatus;
465 	}
466 	// make sure the Media Roster knows that we're using the node
467 //	fMediaRoster->GetNodeFor(fAudioProducer->Node().node,
468 //							 &fAudioConnection.producer);
469 	fAudioConnection.producer = fAudioProducer->Node();
470 
471 	// connect to the mixer
472 	fStatus = fMediaRoster->GetAudioMixer(&fAudioConnection.consumer);
473 	if (fStatus != B_OK) {
474 		print_error("unable to get the system mixer", fStatus);
475 		return fStatus;
476 	}
477 
478 	fMediaRoster->SetTimeSourceFor(fAudioConnection.producer.node,
479 		fTimeSource.node);
480 
481 	// got the nodes; now we find the endpoints of the connection
482 	media_input mixerInput;
483 	media_output soundOutput;
484 	int32 count = 1;
485 	fStatus = fMediaRoster->GetFreeOutputsFor(fAudioConnection.producer,
486 		&soundOutput, 1, &count);
487 	if (fStatus != B_OK) {
488 		print_error("unable to get a free output from the producer node",
489 			fStatus);
490 		return fStatus;
491 	}
492 	count = 1;
493 	fStatus = fMediaRoster->GetFreeInputsFor(fAudioConnection.consumer,
494 		&mixerInput, 1, &count);
495 	if (fStatus != B_OK) {
496 		print_error("unable to get a free input to the mixer", fStatus);
497 		return fStatus;
498 	}
499 
500 	// got the endpoints; now we connect it!
501 	media_format audioFormat;
502 	audioFormat.type = B_MEDIA_RAW_AUDIO;
503 	audioFormat.u.raw_audio = media_raw_audio_format::wildcard;
504 	audioFormat.u.raw_audio.frame_rate = audioFrameRate;
505 	audioFormat.u.raw_audio.channel_count = audioChannels;
506 	fStatus = fMediaRoster->Connect(soundOutput.source, mixerInput.destination,
507 		&audioFormat, &soundOutput, &mixerInput);
508 	if (fStatus != B_OK) {
509 		print_error("unable to connect audio nodes", fStatus);
510 		return fStatus;
511 	}
512 
513 	// the inputs and outputs might have been reassigned during the
514 	// nodes' negotiation of the Connect().  That's why we wait until
515 	// after Connect() finishes to save their contents.
516 	fAudioConnection.format = audioFormat;
517 	fAudioConnection.source = soundOutput.source;
518 	fAudioConnection.destination = mixerInput.destination;
519 	fAudioConnection.connected = true;
520 
521 	// Set an appropriate run mode for the producer
522 	fMediaRoster->SetRunModeNode(fAudioConnection.producer,
523 		BMediaNode::B_INCREASE_LATENCY);
524 
525 	return fStatus;
526 }
527 
528 
529 status_t
530 NodeManager::_TearDownNodes(bool disconnect)
531 {
532 	TRACE("NodeManager::_TearDownNodes()\n");
533 	status_t err = B_OK;
534 	fMediaRoster = BMediaRoster::Roster(&err);
535 	if (err != B_OK) {
536 		fprintf(stderr, "NodeManager::_TearDownNodes() - error getting media "
537 			"roster: %s\n", strerror(err));
538 		fMediaRoster = NULL;
539 	}
540 
541 	if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
542 		// disconnect
543 		if (fMediaRoster) {
544 		TRACE("  disconnecting video...\n");
545 			err = fMediaRoster->Disconnect(fVideoConnection.producer.node,
546 				fVideoConnection.source, fVideoConnection.consumer.node,
547 				fVideoConnection.destination);
548 			if (err < B_OK)
549 				print_error("unable to disconnect video nodes", err);
550 		} else {
551 			fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
552 				"disconnect video nodes, no media server!\n");
553 		}
554 		fVideoConnection.connected = false;
555 	}
556 	if (fVideoProducer) {
557 		TRACE("  releasing video producer...\n");
558 		fVideoProducer->Release();
559 		fVideoProducer = NULL;
560 	}
561 	if (fVideoConsumer) {
562 		TRACE("  releasing video consumer...\n");
563 		fVideoConsumer->Release();
564 		fVideoConsumer = NULL;
565 	}
566 	if (fAudioProducer) {
567 		disconnect = fAudioConnection.connected;
568 		// Ordinarily we'd stop *all* of the nodes in the chain at this point.
569 		// However, one of the nodes is the System Mixer, and stopping the
570 		// Mixer is a  Bad Idea (tm). So, we just disconnect from it, and
571 		// release our references to the nodes that we're using.  We *are*
572 		// supposed to do that even for global nodes like the Mixer.
573 		if (fMediaRoster != NULL && disconnect) {
574 			TRACE("  disconnecting audio...\n");
575 			err = fMediaRoster->Disconnect(fAudioConnection.producer.node,
576 				fAudioConnection.source, fAudioConnection.consumer.node,
577 				fAudioConnection.destination);
578 			if (err < B_OK) {
579 				print_error("unable to disconnect audio nodes", err);
580 				disconnect = false;
581 			}
582 		} else {
583 			fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
584 				"disconnect audio nodes, no media server!\n");
585 		}
586 
587 		TRACE("  releasing audio producer...\n");
588 		fAudioProducer->Release();
589 		fAudioProducer = NULL;
590 		fAudioConnection.connected = false;
591 
592 		if (fMediaRoster != NULL && disconnect) {
593 			TRACE("  releasing audio consumer...\n");
594 			fMediaRoster->ReleaseNode(fAudioConnection.consumer);
595 		} else {
596 			fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
597 				"audio consumer (system mixer)!\n");
598 		}
599 	}
600 
601 	TRACE("NodeManager::_TearDownNodes() done\n");
602 	return err;
603 }
604 
605 
606 status_t
607 NodeManager::_StartNodes()
608 {
609 	status_t status = B_NO_INIT;
610 	if (!fMediaRoster)
611 		return status;
612 
613 	bigtime_t latency = 0;
614 	bigtime_t initLatency = 0;
615 	if (fVideoProducer && fVideoConsumer) {
616 		// figure out what recording delay to use
617 		status = fMediaRoster->GetLatencyFor(fVideoConnection.producer,
618 			&latency);
619 		if (status < B_OK) {
620 			print_error("error getting latency for video producer",
621 				status);
622 		} else
623 			TRACE("video latency: %lld\n", latency);
624 		status = fMediaRoster->SetProducerRunModeDelay(
625 			fVideoConnection.producer, latency);
626 		if (status < B_OK) {
627 			print_error("error settings run mode delay for video producer",
628 				status);
629 		}
630 
631 		// start the nodes
632 		status = fMediaRoster->GetInitialLatencyFor(
633 			fVideoConnection.producer, &initLatency);
634 		if (status < B_OK) {
635 			print_error("error getting initial latency for video producer",
636 				status);
637 		}
638 	}
639 	initLatency += estimate_max_scheduling_latency();
640 
641 	if (fAudioProducer) {
642 		// TODO: was this supposed to be added to initLatency?!?
643 		bigtime_t audioLatency = 0;
644 		status = fMediaRoster->GetLatencyFor(fAudioConnection.producer,
645 			&audioLatency);
646 		TRACE("audio latency: %lld\n", audioLatency);
647 	}
648 
649 	BTimeSource* timeSource;
650 	if (fVideoProducer) {
651 		timeSource = fMediaRoster->MakeTimeSourceFor(
652 			fVideoConnection.producer);
653 	} else {
654 		timeSource = fMediaRoster->MakeTimeSourceFor(
655 			fAudioConnection.producer);
656 	}
657 	bool running = timeSource->IsRunning();
658 
659 	// workaround for people without sound cards
660 	// because the system time source won't be running
661 	bigtime_t real = BTimeSource::RealTime();
662 	if (!running) {
663 		status = fMediaRoster->StartTimeSource(fTimeSource, real);
664 		if (status != B_OK) {
665 			timeSource->Release();
666 			print_error("cannot start time source!", status);
667 			return status;
668 		}
669 		status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real);
670 		if (status != B_OK) {
671 			timeSource->Release();
672 			print_error("cannot seek time source!", status);
673 			return status;
674 		}
675 	}
676 
677 	bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
678 		+ initLatency);
679 
680 	timeSource->Release();
681 
682 	// start the nodes
683 	if (fVideoProducer && fVideoConsumer) {
684 		status = fMediaRoster->StartNode(fVideoConnection.consumer, perf);
685 		if (status != B_OK) {
686 			print_error("Can't start the video consumer", status);
687 			return status;
688 		}
689 		status = fMediaRoster->StartNode(fVideoConnection.producer, perf);
690 		if (status != B_OK) {
691 			print_error("Can't start the video producer", status);
692 			return status;
693 		}
694 	}
695 
696 	if (fAudioProducer) {
697 		status = fMediaRoster->StartNode(fAudioConnection.producer, perf);
698 		if (status != B_OK) {
699 			print_error("Can't start the audio producer", status);
700 			return status;
701 		}
702 	}
703 
704 	fPerformanceTimeBase = perf;
705 
706 	return status;
707 }
708 
709 
710 void
711 NodeManager::_StopNodes()
712 {
713 	TRACE("NodeManager::_StopNodes()\n");
714 	fMediaRoster = BMediaRoster::Roster();
715 	if (fMediaRoster != NULL) {
716 		// begin mucking with the media roster
717 		if (fVideoProducer != NULL) {
718 			TRACE("  stopping video producer...\n");
719 			fMediaRoster->StopNode(fVideoConnection.producer, 0, true);
720 		}
721 		if (fAudioProducer != NULL) {
722 			TRACE("  stopping audio producer...\n");
723 			fMediaRoster->StopNode(fAudioConnection.producer, 0, true);
724 				// synchronous stop
725 		}
726 		if (fVideoConsumer != NULL) {
727 			TRACE("  stopping video consumer...\n");
728 			fMediaRoster->StopNode(fVideoConnection.consumer, 0, true);
729 		}
730 		TRACE("  all nodes stopped\n");
731 	}
732 	TRACE("NodeManager::_StopNodes() done\n");
733 }
734