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
40NodeManager::Connection::Connection()
41	:
42	connected(false)
43{
44	memset(&format, 0, sizeof(media_format));
45}
46
47
48// #pragma mark -
49
50
51NodeManager::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
72NodeManager::~NodeManager()
73{
74	_StopNodes();
75	_TearDownNodes();
76}
77
78
79status_t
80NodeManager::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
104status_t
105NodeManager::InitCheck()
106{
107	return fStatus;
108}
109
110
111void
112NodeManager::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
131status_t
132NodeManager::CleanupNodes()
133{
134	_StopNodes();
135	return _TearDownNodes(false);
136}
137
138
139status_t
140NodeManager::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
177bigtime_t
178NodeManager::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
193bigtime_t
194NodeManager::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
208void
209NodeManager::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
220void
221NodeManager::SetVideoBounds(BRect bounds)
222{
223	if (bounds != fVideoBounds) {
224		fVideoBounds = bounds;
225		NotifyVideoBoundsChanged(fVideoBounds);
226	}
227}
228
229
230BRect
231NodeManager::VideoBounds() const
232{
233	return fVideoBounds;
234}
235
236
237void
238NodeManager::SetVideoTarget(VideoTarget* videoTarget)
239{
240	if (videoTarget != fVideoTarget) {
241		fVideoTarget = videoTarget;
242		if (fVideoConsumer)
243			fVideoConsumer->SetTarget(fVideoTarget);
244	}
245}
246
247
248VideoTarget*
249NodeManager::GetVideoTarget() const
250{
251	return fVideoTarget;
252}
253
254
255void
256NodeManager::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
263void
264NodeManager::SetPeakListener(BHandler* handler)
265{
266	fPeakListener = handler;
267	if (fAudioProducer)
268		fAudioProducer->SetPeakListener(fPeakListener);
269}
270
271
272// #pragma mark -
273
274
275status_t
276NodeManager::_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		}
319fNoAudio = false;
320	} else {
321fNoAudio = 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
332status_t
333NodeManager::_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
467status_t
468NodeManager::_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
540status_t
541NodeManager::_TearDownNodes(bool disconnect)
542{
543TRACE("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) {
559TRACE("  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) {
572TRACE("  releasing video producer...\n");
573		fVideoProducer->Release();
574		fVideoProducer = NULL;
575	}
576	if (fVideoConsumer) {
577TRACE("  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) {
589TRACE("  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
602TRACE("  releasing audio producer...\n");
603		fAudioProducer->Release();
604		fAudioProducer = NULL;
605		fAudioConnection.connected = false;
606
607		if (fMediaRoster && disconnect) {
608TRACE("  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();
618TRACE("NodeManager::_TearDownNodes() done\n");
619	return err;
620}
621
622
623status_t
624NodeManager::_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
733void
734NodeManager::_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