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