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