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