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