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