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