xref: /haiku/src/servers/media/DefaultManager.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 /*
2  * Copyright 2002, 2003 Marcus Overhagen, Jérôme Duval. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 #include <Application.h>
6 #include <OS.h>
7 #include <MediaNode.h>
8 #include <MediaRoster.h>
9 #include <TimeSource.h>
10 #include <string.h>
11 #include <storage/File.h>
12 #include <storage/FindDirectory.h>
13 #include <storage/Path.h>
14 #include "DefaultManager.h"
15 #include "DormantNodeManager.h"
16 #include "NodeManager.h"
17 #include "debug.h"
18 
19 /* no locking used in this file, we assume that the caller (NodeManager) does it.
20  */
21 
22 
23 #define MAX_NODE_INFOS 10
24 #define MAX_INPUT_INFOS 10
25 
26 const uint32 kMsgHeader = 'sepx';
27 const uint32 kMsgTypeVideoIn = 0xffffffef;
28 const uint32 kMsgTypeVideoOut = 0xffffffee;
29 const uint32 kMsgTypeAudioIn = 0xfffffffe;
30 const uint32 kMsgTypeAudioOut = 0xffffffff;
31 
32 const char *kDefaultManagerType 			= "be:_default";
33 const char *kDefaultManagerAddon 			= "be:_addon_id";
34 const char *kDefaultManagerFlavorId 		= "be:_internal_id";
35 const char *kDefaultManagerFlavorName 		= "be:_flavor_name";
36 const char *kDefaultManagerPath 			= "be:_path";
37 const char *kDefaultManagerInput 			= "be:_input_id";
38 
39 const char *kDefaultManagerSettings			= "Media/MDefaultManager";
40 
41 
42 DefaultManager::DefaultManager()
43  :	fMixerConnected(false),
44  	fPhysicalVideoOut(-1),
45 	fPhysicalVideoIn(-1),
46 	fPhysicalAudioOut(-1),
47 	fPhysicalAudioIn(-1),
48 	fSystemTimeSource(-1),
49 	fTimeSource(-1),
50 	fAudioMixer(-1),
51 	fPhysicalAudioOutInputID(0)
52 {
53 	strcpy(fPhysicalAudioOutInputName, "default");
54 	fBeginHeader[0] = 0xab00150b;
55 	fBeginHeader[1] = 0x18723462;
56 	fBeginHeader[2] = 0x00000002;
57 	fEndHeader[0] = 0x7465726d;
58 	fEndHeader[1] = 0x6d666c67;
59 	fEndHeader[2] = 0x00000002;
60 }
61 
62 DefaultManager::~DefaultManager()
63 {
64 }
65 
66 // this is called by the media_server *before* any add-ons have been loaded
67 status_t
68 DefaultManager::LoadState()
69 {
70 	CALLED();
71 	status_t err = B_OK;
72 	BPath path;
73 	if((err = find_directory(B_USER_SETTINGS_DIRECTORY, &path))!=B_OK)
74 		return err;
75 
76 	path.Append(kDefaultManagerSettings);
77 
78 	BFile file(path.Path(), B_READ_ONLY);
79 
80 	uint32 category_count;
81     if (file.Read(fBeginHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3)
82 		return B_ERROR;
83 	TRACE("0x%08lx %ld\n", fBeginHeader[0], fBeginHeader[0]);
84 	TRACE("0x%08lx %ld\n", fBeginHeader[1], fBeginHeader[1]);
85 	TRACE("0x%08lx %ld\n", fBeginHeader[2], fBeginHeader[2]);
86 	if (file.Read(&category_count, sizeof(uint32)) < (int32)sizeof(uint32))
87 		return B_ERROR;
88 	while (category_count--) {
89 		BMessage settings;
90 		uint32 msg_header;
91 		uint32 default_type;
92 		if (file.Read(&msg_header, sizeof(uint32)) < (int32)sizeof(uint32))
93 			return B_ERROR;
94 		if (file.Read(&default_type, sizeof(uint32)) < (int32)sizeof(uint32))
95 			return B_ERROR;
96 		if(settings.Unflatten(&file)==B_OK) {
97 			settings.PrintToStream();
98 			fMsgList.AddItem(new BMessage(settings));
99 		}
100 	}
101 	if (file.Read(fEndHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3)
102 		return B_ERROR;
103 	return B_OK;
104 }
105 
106 status_t
107 DefaultManager::SaveState(NodeManager *node_manager)
108 {
109 	CALLED();
110 	status_t err = B_OK;
111 	BPath path;
112 	BList list;
113 	if((err = find_directory(B_USER_SETTINGS_DIRECTORY, &path))!=B_OK)
114 		return err;
115 
116 	path.Append(kDefaultManagerSettings);
117 
118 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
119 
120 	uint32 default_types[] = {kMsgTypeVideoIn, kMsgTypeVideoOut, kMsgTypeAudioIn, kMsgTypeAudioOut};
121 	uint32 media_node_ids[] = {fPhysicalVideoIn, fPhysicalVideoOut, fPhysicalAudioIn, fPhysicalAudioOut};
122 	for(uint32 i=0; i<sizeof(default_types)/sizeof(default_types[0]); i++) {
123 		BMessage *settings = new BMessage();
124 		settings->AddInt32(kDefaultManagerType, default_types[i]);
125 
126 		// we call the node manager to have more infos about nodes
127 		dormant_node_info info;
128 		media_node node;
129 		entry_ref ref;
130 		if (node_manager->GetCloneForId(&node, media_node_ids[i], be_app->Team()) != B_OK)
131 			continue;
132 		if (node_manager->GetDormantNodeInfo(&info, node) != B_OK)
133 			continue;
134 		if (node_manager->DecrementGlobalRefCount(media_node_ids[i], be_app->Team()) != B_OK)
135 			continue;
136 		if (node_manager->GetAddonRef(&ref, info.addon)!=B_OK)
137 			continue;
138 
139 		BPath path(&ref);
140 		settings->AddInt32(kDefaultManagerAddon, info.addon);
141 		settings->AddInt32(kDefaultManagerFlavorId, info.flavor_id);
142 		settings->AddInt32(kDefaultManagerInput, default_types[i] == kMsgTypeAudioOut ? fPhysicalAudioOutInputID : 0);
143 		settings->AddString(kDefaultManagerFlavorName, info.name);
144 		settings->AddString(kDefaultManagerPath, path.Path());
145 
146 		list.AddItem(settings);
147 		TRACE("message %s added\n", info.name);
148 	}
149 
150 	if (file.Write(fBeginHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3)
151 		return B_ERROR;
152 	int32 category_count = list.CountItems();
153     if (file.Write(&category_count, sizeof(uint32)) < (int32)sizeof(uint32))
154 		return B_ERROR;
155 
156 	for (int32 i = 0; i < category_count; i++) {
157 		BMessage *settings = (BMessage *)list.ItemAt(i);
158 		uint32 default_type;
159 		if (settings->FindInt32(kDefaultManagerType, (int32*)&default_type) < B_OK)
160 			return B_ERROR;
161 		if (file.Write(&kMsgHeader, sizeof(uint32)) < (int32)sizeof(uint32))
162 			return B_ERROR;
163 		if (file.Write(&default_type, sizeof(uint32)) < (int32)sizeof(uint32))
164 			return B_ERROR;
165 		if(settings->Flatten(&file) < B_OK)
166 			return B_ERROR;
167 		delete settings;
168 	}
169 	if (file.Write(fEndHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3)
170 		return B_ERROR;
171 
172 	return B_OK;
173 }
174 
175 status_t
176 DefaultManager::Set(media_node_id node_id, const char *input_name, int32 input_id, node_type type)
177 {
178 	CALLED();
179 	TRACE("DefaultManager::Set type : %i, node : %li, input : %li\n", type, node_id, input_id);
180 	switch (type) {
181 		case VIDEO_INPUT:
182 			fPhysicalVideoIn = node_id;
183 			return B_OK;
184 		case AUDIO_INPUT:
185 			fPhysicalAudioIn = node_id;
186 			return B_OK;
187 		case VIDEO_OUTPUT:
188 			fPhysicalVideoOut = node_id;
189 			return B_OK;
190 		case AUDIO_MIXER:
191 			return B_ERROR;
192 		case AUDIO_OUTPUT:
193 			fPhysicalAudioOut = node_id;
194 			fPhysicalAudioOutInputID = input_id;
195 			strcpy(fPhysicalAudioOutInputName, input_name ? input_name : "<null>");
196 			return B_OK;
197 		case TIME_SOURCE:
198 			return B_ERROR;
199 
200 		case SYSTEM_TIME_SOURCE: //called by the media_server's ServerApp::StartSystemTimeSource()
201 		{
202 			ASSERT(fSystemTimeSource == -1);
203 			fSystemTimeSource = node_id;
204 			return B_OK;
205 		}
206 
207 		default:
208 		{
209 			ERROR("DefaultManager::Set Error: called with unknown type %d\n", type);
210 			return B_ERROR;
211 		}
212 	}
213 }
214 
215 status_t
216 DefaultManager::Get(media_node_id *nodeid, char *input_name, int32 *inputid, node_type type)
217 {
218 	CALLED();
219 	switch (type) {
220 		case VIDEO_INPUT: 		// output: nodeid
221 			if (fPhysicalVideoIn == -1)
222 				return B_ERROR;
223 			*nodeid = fPhysicalVideoIn;
224 			return B_OK;
225 
226 		case AUDIO_INPUT: 		// output: nodeid
227 			if (fPhysicalAudioIn == -1)
228 				return B_ERROR;
229 			*nodeid = fPhysicalAudioIn;
230 			return B_OK;
231 
232 		case VIDEO_OUTPUT: 		// output: nodeid
233 			if (fPhysicalVideoOut == -1)
234 				return B_ERROR;
235 			*nodeid = fPhysicalVideoOut;
236 			return B_OK;
237 
238 		case AUDIO_OUTPUT:		// output: nodeid
239 			if (fPhysicalAudioOut == -1)
240 				return B_ERROR;
241 			*nodeid = fPhysicalAudioOut;
242 			return B_OK;
243 
244 		case AUDIO_OUTPUT_EX:	// output: nodeid, input_name, input_id
245 			if (fPhysicalAudioOut == -1)
246 				return B_ERROR;
247 			*nodeid = fPhysicalAudioOut;
248 			*inputid = fPhysicalAudioOutInputID;
249 			strcpy(input_name, fPhysicalAudioOutInputName);
250 			return B_OK;
251 
252 		case AUDIO_MIXER:		// output: nodeid
253 			if (fAudioMixer == -1)
254 				return B_ERROR;
255 			*nodeid = fAudioMixer;
256 			return B_OK;
257 
258 		case TIME_SOURCE:
259 			if (fTimeSource != -1)
260 				*nodeid = fTimeSource;
261 			else
262 				*nodeid = fSystemTimeSource;
263 			return B_OK;
264 
265 		case SYSTEM_TIME_SOURCE:
266 			*nodeid = fSystemTimeSource;
267 			return B_OK;
268 
269 		default:
270 		{
271 			ERROR("DefaultManager::Get Error: called with unknown type %d\n", type);
272 			return B_ERROR;
273 		}
274 	}
275 }
276 
277 // this is called by the media_server *after* the initial add-on loading has been done
278 status_t
279 DefaultManager::Rescan()
280 {
281 	thread_id	fThreadId = spawn_thread(rescan_thread, "rescan defaults", 8, this);
282 	resume_thread(fThreadId);
283 	return B_OK;
284 }
285 
286 int32
287 DefaultManager::rescan_thread(void *arg)
288 {
289 	reinterpret_cast<DefaultManager *>(arg)->RescanThread();
290 	return 0;
291 }
292 
293 void
294 DefaultManager::RescanThread()
295 {
296 	printf("DefaultManager::RescanThread() enter\n");
297 
298 	// We do not search for the system time source,
299 	// it should already exist
300 	ASSERT(fSystemTimeSource != -1);
301 
302 	if (fPhysicalVideoOut == -1) {
303 		FindPhysical(&fPhysicalVideoOut, kMsgTypeVideoOut, false, B_MEDIA_RAW_VIDEO);
304 		FindPhysical(&fPhysicalVideoOut, kMsgTypeVideoOut, false, B_MEDIA_ENCODED_VIDEO);
305 	}
306 	if (fPhysicalVideoIn == -1) {
307 		FindPhysical(&fPhysicalVideoIn, kMsgTypeVideoIn, true, B_MEDIA_RAW_VIDEO);
308 		FindPhysical(&fPhysicalVideoIn, kMsgTypeVideoIn, true, B_MEDIA_ENCODED_VIDEO);
309 	}
310 	if (fPhysicalAudioOut == -1)
311 		FindPhysical(&fPhysicalAudioOut, kMsgTypeAudioOut, false, B_MEDIA_RAW_AUDIO);
312 	if (fPhysicalAudioIn == -1)
313 		FindPhysical(&fPhysicalAudioIn, kMsgTypeAudioIn, true, B_MEDIA_RAW_AUDIO);
314 	if (fAudioMixer == -1)
315 		FindAudioMixer();
316 
317 	// The normal time source is searched for after the
318 	// Physical Audio Out has been created.
319 	if (fTimeSource == -1)
320 		FindTimeSource();
321 
322 	// Connect the mixer and physical audio out (soundcard)
323 	if (!fMixerConnected && fAudioMixer != -1 && fPhysicalAudioOut != -1) {
324 		fMixerConnected = B_OK == ConnectMixerToOutput();
325 		if (!fMixerConnected)
326 			ERROR("DefaultManager: failed to connect mixer and soundcard\n");
327 	} else {
328 		ERROR("DefaultManager: Did not try to connect mixer and soundcard\n");
329 	}
330 
331 	printf("DefaultManager::RescanThread() leave\n");
332 }
333 
334 
335 void
336 DefaultManager::FindPhysical(volatile media_node_id *id, uint32 default_type, bool isInput, media_type type)
337 {
338 	live_node_info info[MAX_NODE_INFOS];
339 	media_format format;
340 	int32 count;
341 	status_t rv;
342 	BMessage *msg = NULL;
343 	BPath msgPath;
344 	dormant_node_info msgDninfo;
345 	int32 input_id;
346 	bool isAudio = type & B_MEDIA_RAW_AUDIO;
347 
348 	for(int32 i=0; i<fMsgList.CountItems(); i++) {
349 		msg = (BMessage *)fMsgList.ItemAt(i);
350 		int32 msgType;
351 		if(msg->FindInt32(kDefaultManagerType, &msgType)==B_OK && ((uint32)msgType == default_type)) {
352 			const char *name = NULL;
353 			const char *path = NULL;
354 			msg->FindInt32(kDefaultManagerAddon, &msgDninfo.addon);
355 			msg->FindInt32(kDefaultManagerFlavorId, &msgDninfo.flavor_id);
356 			msg->FindInt32(kDefaultManagerInput, &input_id);
357 			msg->FindString(kDefaultManagerFlavorName, &name);
358 			msg->FindString(kDefaultManagerPath, &path);
359 			if(name)
360 				strcpy(msgDninfo.name, name);
361 			if(path)
362 				msgPath = BPath(path);
363 			break;
364 		}
365 	}
366 
367 	memset(&format, 0, sizeof(format));
368 	format.type = type;
369 	count = MAX_NODE_INFOS;
370 	rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count,
371 		isInput ? NULL : &format, isInput ? &format : NULL, NULL,
372 		isInput ? B_BUFFER_PRODUCER | B_PHYSICAL_INPUT : B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT);
373 	if (rv != B_OK || count < 1) {
374 		ERROR("Couldn't find physical %s %s node\n", isAudio ? "audio" : "video", isInput ? "input" : "output");
375 		return;
376 	}
377 	for (int i = 0; i < count; i++)
378 		TRACE("info[%d].name %s\n", i, info[i].name);
379 
380 	for (int i = 0; i < count; i++) {
381 		if (isAudio) {
382 			if (isInput) {
383 				if (0 == strcmp(info[i].name, "None In")) {
384 					// we keep the Null audio driver if none else matchs
385 					*id = info[i].node.node;
386 					continue;
387 				}
388 				if (0 == strcmp(info[i].name, "DV Input")) // skip the Firewire audio driver
389 					continue;
390 			} else {
391 				if (0 == strcmp(info[i].name, "None Out")) {
392 					// we keep the Null audio driver if none else matchs
393 					*id = info[i].node.node;
394 					if(msg)
395 						fPhysicalAudioOutInputID = input_id;
396 					continue;
397 				}
398 				if (0 == strcmp(info[i].name, "DV Output")) // skip the Firewire audio driver
399 					continue;
400 			}
401 		}
402 		if(msg) {	// we have a default info msg
403 			dormant_node_info dninfo;
404 			if(BMediaRoster::Roster()->GetDormantNodeFor(info[i].node, &dninfo) != B_OK) {
405 				ERROR("Couldn't GetDormantNodeFor\n");
406 				continue;
407 			}
408 			if(dninfo.flavor_id!=msgDninfo.flavor_id
409 				|| strcmp(dninfo.name, msgDninfo.name)!=0) {
410 				ERROR("Doesn't match flavor or name\n");
411 				continue;
412 			}
413 			BPath path;
414 			if((_DormantNodeManager->FindAddonPath(&path, dninfo.addon)!=B_OK)
415 				|| (path != msgPath)) {
416 				ERROR("Doesn't match : path\n");
417 				continue;
418 			}
419 		}
420 		TRACE("Default physical %s %s \"%s\" created!\n",
421 			isAudio ? "audio" : "video", isInput ? "input" : "output", info[i].name);
422 		*id = info[i].node.node;
423 		if(msg && isAudio && !isInput)
424 			fPhysicalAudioOutInputID = input_id;
425 		return;
426 	}
427 }
428 
429 
430 void
431 DefaultManager::FindTimeSource()
432 {
433 	live_node_info info[MAX_NODE_INFOS];
434 	media_format input; /* a physical audio output has a logical data input (DAC)*/
435 	int32 count;
436 	status_t rv;
437 
438 	/* First try to use the current default physical audio out
439 	 */
440 	if (fPhysicalAudioOut != -1) {
441 		media_node clone;
442 		if (B_OK == BMediaRoster::Roster()->GetNodeFor(fPhysicalAudioOut, &clone)) {
443 			if (clone.kind & B_TIME_SOURCE) {
444 				fTimeSource = clone.node;
445 				BMediaRoster::Roster()->StartTimeSource(clone, system_time() + 1000);
446 				BMediaRoster::Roster()->ReleaseNode(clone);
447 				printf("Default DAC timesource created!\n");
448 				return;
449 			}
450 			BMediaRoster::Roster()->ReleaseNode(clone);
451 		} else {
452 			printf("Default DAC is not a timesource!\n");
453 		}
454 	} else {
455 		printf("Default DAC node does not exist!\n");
456 	}
457 
458 	/* Now try to find another physical audio out node
459 	 */
460 	memset(&input, 0, sizeof(input));
461 	input.type = B_MEDIA_RAW_AUDIO;
462 	count = MAX_NODE_INFOS;
463 	rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, &input, NULL, NULL, B_TIME_SOURCE | B_PHYSICAL_OUTPUT);
464 	if (rv == B_OK && count >= 1) {
465 		for (int i = 0; i < count; i++)
466 			printf("info[%d].name %s\n", i, info[i].name);
467 
468 		for (int i = 0; i < count; i++) {
469 			// The BeOS R5 None Out node pretend to be a physical time source, that is pretty dumb
470 			if (0 == strcmp(info[i].name, "None Out")) // skip the Null audio driver
471 				continue;
472 			if (0 != strstr(info[i].name, "DV Output")) // skip the Firewire audio driver
473 				continue;
474 			printf("Default DAC timesource \"%s\" created!\n", info[i].name);
475 			fTimeSource = info[i].node.node;
476 			BMediaRoster::Roster()->StartTimeSource(info[i].node, system_time() + 1000);
477 			return;
478 		}
479 	} else {
480 		printf("Couldn't find DAC timesource node\n");
481 	}
482 
483 	/* XXX we might use other audio or video clock timesources
484 	 */
485 }
486 
487 void
488 DefaultManager::FindAudioMixer()
489 {
490 	live_node_info info;
491 	int32 count;
492 	status_t rv;
493 
494 	count = 1;
495 	rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, NULL, NULL, NULL, B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER);
496 	if (rv != B_OK || count != 1) {
497 		printf("Couldn't find audio mixer node\n");
498 		return;
499 	}
500 	fAudioMixer = info.node.node;
501 	printf("Default audio mixer node created\n");
502 }
503 
504 status_t
505 DefaultManager::ConnectMixerToOutput()
506 {
507 	BMediaRoster 		*roster;
508 	media_node 			timesource;
509 	media_node 			mixer;
510 	media_node 			soundcard;
511 	media_input			inputs[MAX_INPUT_INFOS];
512 	media_input 		input;
513 	media_output 		output;
514 	media_input 		newinput;
515 	media_output 		newoutput;
516 	media_format 		format;
517 	BTimeSource * 		ts;
518 	bigtime_t 			start_at;
519 	int32 				count;
520 	status_t 			rv;
521 
522 	roster = BMediaRoster::Roster();
523 
524 	rv = roster->GetNodeFor(fPhysicalAudioOut, &soundcard);
525 	if (rv != B_OK) {
526 		printf("DefaultManager: failed to find soundcard (physical audio output)\n");
527 		return B_ERROR;
528 	}
529 
530 	rv = roster->GetNodeFor(fAudioMixer, &mixer);
531 	if (rv != B_OK) {
532 		roster->ReleaseNode(soundcard);
533 		printf("DefaultManager: failed to find mixer\n");
534 		return B_ERROR;
535 	}
536 
537 	// we now have the mixer and soundcard nodes,
538 	// find a free input/output and connect them
539 
540 	rv = roster->GetFreeOutputsFor(mixer, &output, 1, &count, B_MEDIA_RAW_AUDIO);
541 	if (rv != B_OK || count != 1) {
542 		printf("DefaultManager: can't find free mixer output\n");
543 		rv = B_ERROR;
544 		goto finish;
545 	}
546 
547 	rv = roster->GetFreeInputsFor(soundcard, inputs, MAX_INPUT_INFOS, &count, B_MEDIA_RAW_AUDIO);
548 	if (rv != B_OK || count < 1) {
549 		printf("DefaultManager: can't find free soundcard inputs\n");
550 		rv = B_ERROR;
551 		goto finish;
552 	}
553 
554 	for (int32 i = 0; i < count; i++) {
555 		input = inputs[i];
556 		if(input.destination.id == fPhysicalAudioOutInputID)
557 			break;
558 	}
559 
560 	for (int i = 0; i < 6; i++) {
561 		switch (i) {
562 			case 0:
563 				printf("DefaultManager: Trying connect in native format (1)\n");
564 				if (B_OK != roster->GetFormatFor(input, &format)) {
565 					ERROR("DefaultManager: GetFormatFor failed\n");
566 					continue;
567 				}
568 				// XXX BeOS R5 multiaudio node bug workaround
569 				if (format.u.raw_audio.channel_count == 1) {
570 					printf("##### WARNING! DefaultManager: ignored mono format\n");
571 					continue;
572 				}
573 				break;
574 
575 			case 1:
576 				printf("DefaultManager: Trying connect in format 1\n");
577 				memset(&format, 0, sizeof(format));
578 				format.type = B_MEDIA_RAW_AUDIO;
579 				format.u.raw_audio.frame_rate = 44100;
580 				format.u.raw_audio.channel_count = 2;
581 				format.u.raw_audio.format = 0x2;
582 				break;
583 
584 			case 2:
585 				printf("DefaultManager: Trying connect in format 2\n");
586 				memset(&format, 0, sizeof(format));
587 				format.type = B_MEDIA_RAW_AUDIO;
588 				format.u.raw_audio.frame_rate = 48000;
589 				format.u.raw_audio.channel_count = 2;
590 				format.u.raw_audio.format = 0x2;
591 				break;
592 
593 			case 3:
594 				printf("DefaultManager: Trying connect in format 3\n");
595 				memset(&format, 0, sizeof(format));
596 				format.type = B_MEDIA_RAW_AUDIO;
597 				break;
598 
599 			case 4:
600 				// BeOS R5 multiaudio node bug workaround
601 				printf("DefaultManager: Trying connect in native format (2)\n");
602 				if (B_OK != roster->GetFormatFor(input, &format)) {
603 					ERROR("DefaultManager: GetFormatFor failed\n");
604 					continue;
605 				}
606 				break;
607 
608 			case 5:
609 				printf("DefaultManager: Trying connect in format 4\n");
610 				memset(&format, 0, sizeof(format));
611 				break;
612 
613 		}
614 		rv = roster->Connect(output.source, input.destination, &format, &newoutput, &newinput);
615 		if (rv == B_OK)
616 			break;
617 	}
618 	if (rv != B_OK) {
619 		ERROR("DefaultManager: connect failed\n");
620 		goto finish;
621 	}
622 
623 	roster->SetRunModeNode(mixer, BMediaNode::B_INCREASE_LATENCY);
624 	roster->SetRunModeNode(soundcard, BMediaNode::B_RECORDING);
625 
626 	roster->GetTimeSource(&timesource);
627 	roster->SetTimeSourceFor(mixer.node, timesource.node);
628 	roster->SetTimeSourceFor(soundcard.node, timesource.node);
629 	roster->PrerollNode(mixer);
630 	roster->PrerollNode(soundcard);
631 
632 	ts = roster->MakeTimeSourceFor(mixer);
633 	start_at = ts->Now() + 50000;
634 	roster->StartNode(mixer, start_at);
635 	roster->StartNode(soundcard, start_at);
636 	ts->Release();
637 
638 finish:
639 	roster->ReleaseNode(mixer);
640 	roster->ReleaseNode(soundcard);
641 	roster->ReleaseNode(timesource);
642 	return rv;
643 }
644 
645 void
646 DefaultManager::Dump()
647 {
648 }
649 
650 void
651 DefaultManager::CleanupTeam(team_id team)
652 {
653 }
654 
655