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.
8 //! This class controls our media nodes and general playback
11 #include "NodeManager.h"
16 #include <MediaRoster.h>
17 #include <scheduler.h>
18 #include <TimeSource.h>
20 #include "AudioProducer.h"
21 #include "AudioSupplier.h"
22 #include "VideoConsumer.h"
23 #include "VideoProducer.h"
24 #include "VideoSupplier.h"
28 //#define TRACE_NODE_MANAGER
29 #ifdef TRACE_NODE_MANAGER
30 # define TRACE(x...) printf(x)
31 # define ERROR(x...) fprintf(stderr, x)
34 # define ERROR(x...) fprintf(stderr, x)
37 #define print_error(str, status) printf(str ", error: %s\n", strerror(status))
40 NodeManager::Connection::Connection()
44 memset(&format
, 0, sizeof(media_format
));
51 NodeManager::NodeManager()
58 fTimeSource(media_node::null
),
61 fPerformanceTimeBase(0),
66 fVideoBounds(0, 0, -1, -1),
72 NodeManager::~NodeManager()
80 NodeManager::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
)
86 PlaybackManager::Init(videoFrameRate
, true, loopingMode
, loopingEnabled
,
89 // get some objects from a derived class
90 if (fVideoTarget
== NULL
)
91 fVideoTarget
= CreateVideoTarget();
93 if (fVideoSupplier
== NULL
)
94 fVideoSupplier
= CreateVideoSupplier();
96 if (fAudioSupplier
== NULL
)
97 fAudioSupplier
= CreateAudioSupplier();
99 return FormatChanged(videoBounds
, videoFrameRate
, preferredVideoFormat
,
100 audioFrameRate
, audioChannels
, enabledNodes
, useOverlays
, true);
105 NodeManager::InitCheck()
112 NodeManager::SetPlayMode(int32 mode
, bool continuePlaying
)
114 if (fVideoConsumer
!= NULL
&& fMediaRoster
!= NULL
) {
115 BMediaNode::run_mode runMode
= mode
> 0 ?
116 BMediaNode::B_DROP_DATA
: BMediaNode::B_OFFLINE
;
117 status_t ret
= fMediaRoster
->SetRunModeNode(fVideoConnection
.consumer
,
120 printf("NodeManager::SetPlayMode(%" B_PRId32
"), setting run mode "
121 "failed: %s\n", mode
, strerror(ret
));
125 PlaybackManager::SetPlayMode(mode
, continuePlaying
);
130 NodeManager::CleanupNodes()
133 return _TearDownNodes(false);
138 NodeManager::FormatChanged(BRect videoBounds
, float videoFrameRate
,
139 color_space preferredVideoFormat
, float audioFrameRate
,
140 uint32 audioChannels
, uint32 enabledNodes
, bool useOverlays
, bool force
)
142 TRACE("NodeManager::FormatChanged()\n");
144 if (!force
&& videoBounds
== VideoBounds()
145 && videoFrameRate
== FramesPerSecond()) {
146 TRACE(" -> reusing existing nodes\n");
147 // TODO: if enabledNodes would indicate that audio or video
148 // is no longer needed, or, worse yet, suddenly needed when
149 // it wasn't before, then we should not return here!
150 PlaybackManager::Init(videoFrameRate
, false, LoopMode(),
151 IsLoopingEnabled(), Speed(), MODE_PLAYING_PAUSED_FORWARD
,
159 PlaybackManager::Init(videoFrameRate
, true, LoopMode(), IsLoopingEnabled(),
160 Speed(), MODE_PLAYING_PAUSED_FORWARD
, CurrentFrame());
162 SetVideoBounds(videoBounds
);
164 status_t ret
= _SetUpNodes(preferredVideoFormat
, enabledNodes
,
165 useOverlays
, audioFrameRate
, audioChannels
);
169 fprintf(stderr
, "unable to setup nodes: %s\n", strerror(ret
));
176 NodeManager::RealTimeForTime(bigtime_t time
) const
178 bigtime_t result
= 0;
179 if (fVideoProducer
) {
180 result
= fVideoProducer
->TimeSource()->RealTimeFor(
181 fPerformanceTimeBase
+ time
, 0);
182 } else if (fAudioProducer
) {
183 result
= fAudioProducer
->TimeSource()->RealTimeFor(
184 fPerformanceTimeBase
+ time
, 0);
186 //printf("NodeManager::RealTimeForTime(%lld) -> %lld\n", time, result);
192 NodeManager::TimeForRealTime(bigtime_t time
) const
194 bigtime_t result
= 0;
195 if (fVideoProducer
) {
196 result
= fVideoProducer
->TimeSource()->PerformanceTimeFor(time
)
197 - fPerformanceTimeBase
;
198 } else if (fAudioProducer
) {
199 result
= fAudioProducer
->TimeSource()->PerformanceTimeFor(time
)
200 - fPerformanceTimeBase
;
207 NodeManager::SetCurrentAudioTime(bigtime_t time
)
209 //printf("NodeManager::SetCurrentAudioTime(%lld)\n", time);
210 PlaybackManager::SetCurrentAudioTime(time
);
211 if (!fVideoProducer
) {
212 // running without video, update video time as well
213 PlaybackManager::SetCurrentVideoTime(time
);
219 NodeManager::SetVideoBounds(BRect bounds
)
221 if (bounds
!= fVideoBounds
) {
222 fVideoBounds
= bounds
;
223 NotifyVideoBoundsChanged(fVideoBounds
);
229 NodeManager::VideoBounds() const
236 NodeManager::SetVideoTarget(VideoTarget
* videoTarget
)
238 if (videoTarget
!= fVideoTarget
) {
239 fVideoTarget
= videoTarget
;
241 fVideoConsumer
->SetTarget(fVideoTarget
);
247 NodeManager::GetVideoTarget() const
254 NodeManager::SetVolume(float percent
)
256 // TODO: would be nice to set the volume on the system mixer input of
262 NodeManager::SetPeakListener(BHandler
* handler
)
264 fPeakListener
= handler
;
266 fAudioProducer
->SetPeakListener(fPeakListener
);
274 NodeManager::_SetUpNodes(color_space preferredVideoFormat
, uint32 enabledNodes
,
275 bool useOverlays
, float audioFrameRate
, uint32 audioChannels
)
277 TRACE("NodeManager::_SetUpNodes()\n");
279 // find the media roster
281 fMediaRoster
= BMediaRoster::Roster(&fStatus
);
282 if (fStatus
!= B_OK
) {
283 print_error("Can't find the media roster", fStatus
);
288 // find the time source
289 fStatus
= fMediaRoster
->GetTimeSource(&fTimeSource
);
290 if (fStatus
!= B_OK
) {
291 print_error("Can't get a time source", fStatus
);
295 // setup the video nodes
296 if (enabledNodes
!= AUDIO_ONLY
) {
297 fStatus
= _SetUpVideoNodes(preferredVideoFormat
, useOverlays
);
298 if (fStatus
!= B_OK
) {
299 print_error("Error setting up video nodes", fStatus
);
303 printf("running without video node\n");
305 // setup the audio nodes
306 if (enabledNodes
!= VIDEO_ONLY
) {
307 fStatus
= _SetUpAudioNodes(audioFrameRate
, audioChannels
);
308 if (fStatus
!= B_OK
) {
309 print_error("Error setting up audio nodes", fStatus
);
315 printf("running without audio node\n");
323 NodeManager::_SetUpVideoNodes(color_space preferredVideoFormat
,
326 // create the video producer node
327 fVideoProducer
= new VideoProducer(NULL
, "MediaPlayer video out", 0,
328 this, fVideoSupplier
);
330 // register the producer node
331 fStatus
= fMediaRoster
->RegisterNode(fVideoProducer
);
332 if (fStatus
!= B_OK
) {
333 print_error("Can't register the video producer", fStatus
);
337 // make sure the Media Roster knows that we're using the node
338 // fMediaRoster->GetNodeFor(fVideoProducer->Node().node,
339 // &fVideoConnection.producer);
340 fVideoConnection
.producer
= fVideoProducer
->Node();
342 // create the video consumer node
343 fVideoConsumer
= new VideoConsumer("MediaPlayer video in", NULL
, 0, this,
346 // register the consumer node
347 fStatus
= fMediaRoster
->RegisterNode(fVideoConsumer
);
348 if (fStatus
!= B_OK
) {
349 print_error("Can't register the video consumer", fStatus
);
353 // make sure the Media Roster knows that we're using the node
354 // fMediaRoster->GetNodeFor(fVideoConsumer->Node().node,
355 // &fVideoConnection.consumer);
356 fVideoConnection
.consumer
= fVideoConsumer
->Node();
358 // find free producer output
359 media_input videoInput
;
360 media_output videoOutput
;
362 fStatus
= fMediaRoster
->GetFreeOutputsFor(fVideoConnection
.producer
,
363 &videoOutput
, 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 video stream", fStatus
);
370 // find free consumer input
372 fStatus
= fMediaRoster
->GetFreeInputsFor(fVideoConnection
.consumer
,
373 &videoInput
, 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 connection to the video window",
383 format
.type
= B_MEDIA_RAW_VIDEO
;
384 media_raw_video_format videoFormat
= {
385 FramesPerSecond(), 1, 0,
386 (uint32
)fVideoBounds
.IntegerWidth(),
387 B_VIDEO_TOP_LEFT_RIGHT
, 1, 1,
389 preferredVideoFormat
,
390 (uint32
)(fVideoBounds
.IntegerWidth() + 1),
391 (uint32
)(fVideoBounds
.IntegerHeight() + 1),
395 format
.u
.raw_video
= videoFormat
;
397 // connect video producer to consumer (hopefully using overlays)
398 fVideoConsumer
->SetTryOverlay(useOverlays
);
399 fStatus
= fMediaRoster
->Connect(videoOutput
.source
, videoInput
.destination
,
400 &format
, &videoOutput
, &videoInput
);
402 if (fStatus
!= B_OK
) {
403 print_error("Can't connect the video source to the video window... "
404 "trying without overlays", fStatus
);
407 bool supported
= bitmaps_support_space(
408 format
.u
.raw_video
.display
.format
, &flags
);
409 if (!supported
|| (flags
& B_VIEWS_SUPPORT_DRAW_BITMAP
) == 0) {
410 // cannot create bitmaps with such a color space
411 // or BViews don't support drawing it, fallback to B_RGB32
412 format
.u
.raw_video
.display
.format
= B_RGB32
;
413 printf("NodeManager::_SetupVideoNodes() - falling back to "
417 fVideoConsumer
->SetTryOverlay(false);
418 // connect video producer to consumer (not using overlays and using
419 // a colorspace that BViews support drawing)
420 fStatus
= fMediaRoster
->Connect(videoOutput
.source
,
421 videoInput
.destination
, &format
, &videoOutput
, &videoInput
);
423 // bail if second attempt failed too
424 if (fStatus
!= B_OK
) {
425 print_error("Can't connect the video source to the video window",
430 // the inputs and outputs might have been reassigned during the
431 // nodes' negotiation of the Connect(). That's why we wait until
432 // after Connect() finishes to save their contents.
433 fVideoConnection
.format
= format
;
434 fVideoConnection
.source
= videoOutput
.source
;
435 fVideoConnection
.destination
= videoInput
.destination
;
436 fVideoConnection
.connected
= true;
439 fStatus
= fMediaRoster
->SetTimeSourceFor(fVideoConnection
.producer
.node
,
441 if (fStatus
!= B_OK
) {
442 print_error("Can't set the timesource for the video source", fStatus
);
446 fStatus
= fMediaRoster
->SetTimeSourceFor(fVideoConsumer
->ID(),
448 if (fStatus
!= B_OK
) {
449 print_error("Can't set the timesource for the video window", fStatus
);
458 NodeManager::_SetUpAudioNodes(float audioFrameRate
, uint32 audioChannels
)
460 fAudioProducer
= new AudioProducer("MediaPlayer audio out", fAudioSupplier
);
461 fAudioProducer
->SetPeakListener(fPeakListener
);
462 fStatus
= fMediaRoster
->RegisterNode(fAudioProducer
);
463 if (fStatus
!= B_OK
) {
464 print_error("unable to register audio producer node!\n", fStatus
);
467 // make sure the Media Roster knows that we're using the node
468 // fMediaRoster->GetNodeFor(fAudioProducer->Node().node,
469 // &fAudioConnection.producer);
470 fAudioConnection
.producer
= fAudioProducer
->Node();
472 // connect to the mixer
473 fStatus
= fMediaRoster
->GetAudioMixer(&fAudioConnection
.consumer
);
474 if (fStatus
!= B_OK
) {
475 print_error("unable to get the system mixer", fStatus
);
479 fMediaRoster
->SetTimeSourceFor(fAudioConnection
.producer
.node
,
482 // got the nodes; now we find the endpoints of the connection
483 media_input mixerInput
;
484 media_output soundOutput
;
486 fStatus
= fMediaRoster
->GetFreeOutputsFor(fAudioConnection
.producer
,
487 &soundOutput
, 1, &count
);
488 if (fStatus
!= B_OK
) {
489 print_error("unable to get a free output from the producer node",
494 fStatus
= fMediaRoster
->GetFreeInputsFor(fAudioConnection
.consumer
,
495 &mixerInput
, 1, &count
);
496 if (fStatus
!= B_OK
) {
497 print_error("unable to get a free input to the mixer", fStatus
);
501 // got the endpoints; now we connect it!
502 media_format audioFormat
;
503 audioFormat
.type
= B_MEDIA_RAW_AUDIO
;
504 audioFormat
.u
.raw_audio
= media_raw_audio_format::wildcard
;
505 audioFormat
.u
.raw_audio
.frame_rate
= audioFrameRate
;
506 audioFormat
.u
.raw_audio
.channel_count
= audioChannels
;
507 fStatus
= fMediaRoster
->Connect(soundOutput
.source
, mixerInput
.destination
,
508 &audioFormat
, &soundOutput
, &mixerInput
);
509 if (fStatus
!= B_OK
) {
510 print_error("unable to connect audio nodes", fStatus
);
514 // the inputs and outputs might have been reassigned during the
515 // nodes' negotiation of the Connect(). That's why we wait until
516 // after Connect() finishes to save their contents.
517 fAudioConnection
.format
= audioFormat
;
518 fAudioConnection
.source
= soundOutput
.source
;
519 fAudioConnection
.destination
= mixerInput
.destination
;
520 fAudioConnection
.connected
= true;
522 // Set an appropriate run mode for the producer
523 fMediaRoster
->SetRunModeNode(fAudioConnection
.producer
,
524 BMediaNode::B_INCREASE_LATENCY
);
531 NodeManager::_TearDownNodes(bool disconnect
)
533 TRACE("NodeManager::_TearDownNodes()\n");
535 fMediaRoster
= BMediaRoster::Roster(&err
);
537 fprintf(stderr
, "NodeManager::_TearDownNodes() - error getting media "
538 "roster: %s\n", strerror(err
));
542 if (fVideoConsumer
&& fVideoProducer
&& fVideoConnection
.connected
) {
545 TRACE(" disconnecting video...\n");
546 err
= fMediaRoster
->Disconnect(fVideoConnection
.producer
.node
,
547 fVideoConnection
.source
, fVideoConnection
.consumer
.node
,
548 fVideoConnection
.destination
);
550 print_error("unable to disconnect video nodes", err
);
552 fprintf(stderr
, "NodeManager::_TearDownNodes() - cannot "
553 "disconnect video nodes, no media server!\n");
555 fVideoConnection
.connected
= false;
557 if (fVideoProducer
) {
558 TRACE(" releasing video producer...\n");
559 fVideoProducer
->Release();
560 fVideoProducer
= NULL
;
562 if (fVideoConsumer
) {
563 TRACE(" releasing video consumer...\n");
564 fVideoConsumer
->Release();
565 fVideoConsumer
= NULL
;
567 if (fAudioProducer
) {
568 disconnect
= fAudioConnection
.connected
;
569 // Ordinarily we'd stop *all* of the nodes in the chain at this point.
570 // However, one of the nodes is the System Mixer, and stopping the
571 // Mixer is a Bad Idea (tm). So, we just disconnect from it, and
572 // release our references to the nodes that we're using. We *are*
573 // supposed to do that even for global nodes like the Mixer.
574 if (fMediaRoster
!= NULL
&& disconnect
) {
575 TRACE(" disconnecting audio...\n");
576 err
= fMediaRoster
->Disconnect(fAudioConnection
.producer
.node
,
577 fAudioConnection
.source
, fAudioConnection
.consumer
.node
,
578 fAudioConnection
.destination
);
580 print_error("unable to disconnect audio nodes", err
);
584 fprintf(stderr
, "NodeManager::_TearDownNodes() - cannot "
585 "disconnect audio nodes, no media server!\n");
588 TRACE(" releasing audio producer...\n");
589 fAudioProducer
->Release();
590 fAudioProducer
= NULL
;
591 fAudioConnection
.connected
= false;
593 if (fMediaRoster
!= NULL
&& disconnect
) {
594 TRACE(" releasing audio consumer...\n");
595 fMediaRoster
->ReleaseNode(fAudioConnection
.consumer
);
597 fprintf(stderr
, "NodeManager::_TearDownNodes() - cannot release "
598 "audio consumer (system mixer)!\n");
602 TRACE("NodeManager::_TearDownNodes() done\n");
608 NodeManager::_StartNodes()
610 status_t status
= B_NO_INIT
;
614 bigtime_t latency
= 0;
615 bigtime_t initLatency
= 0;
616 if (fVideoProducer
&& fVideoConsumer
) {
617 // figure out what recording delay to use
618 status
= fMediaRoster
->GetLatencyFor(fVideoConnection
.producer
,
621 print_error("error getting latency for video producer",
624 TRACE("video latency: %Ld\n", latency
);
625 status
= fMediaRoster
->SetProducerRunModeDelay(
626 fVideoConnection
.producer
, latency
);
628 print_error("error settings run mode delay for video producer",
633 status
= fMediaRoster
->GetInitialLatencyFor(
634 fVideoConnection
.producer
, &initLatency
);
636 print_error("error getting initial latency for video producer",
640 initLatency
+= estimate_max_scheduling_latency();
642 if (fAudioProducer
) {
643 // TODO: was this supposed to be added to initLatency?!?
644 bigtime_t audioLatency
= 0;
645 status
= fMediaRoster
->GetLatencyFor(fAudioConnection
.producer
,
647 TRACE("audio latency: %Ld\n", audioLatency
);
650 BTimeSource
* timeSource
;
651 if (fVideoProducer
) {
652 timeSource
= fMediaRoster
->MakeTimeSourceFor(
653 fVideoConnection
.producer
);
655 timeSource
= fMediaRoster
->MakeTimeSourceFor(
656 fAudioConnection
.producer
);
658 bool running
= timeSource
->IsRunning();
660 // workaround for people without sound cards
661 // because the system time source won't be running
662 bigtime_t real
= BTimeSource::RealTime();
664 status
= fMediaRoster
->StartTimeSource(fTimeSource
, real
);
665 if (status
!= B_OK
) {
666 timeSource
->Release();
667 print_error("cannot start time source!", status
);
670 status
= fMediaRoster
->SeekTimeSource(fTimeSource
, 0, real
);
671 if (status
!= B_OK
) {
672 timeSource
->Release();
673 print_error("cannot seek time source!", status
);
678 bigtime_t perf
= timeSource
->PerformanceTimeFor(real
+ latency
681 timeSource
->Release();
684 if (fVideoProducer
&& fVideoConsumer
) {
685 status
= fMediaRoster
->StartNode(fVideoConnection
.consumer
, perf
);
686 if (status
!= B_OK
) {
687 print_error("Can't start the video consumer", status
);
690 status
= fMediaRoster
->StartNode(fVideoConnection
.producer
, perf
);
691 if (status
!= B_OK
) {
692 print_error("Can't start the video producer", status
);
697 if (fAudioProducer
) {
698 status
= fMediaRoster
->StartNode(fAudioConnection
.producer
, perf
);
699 if (status
!= B_OK
) {
700 print_error("Can't start the audio producer", status
);
705 fPerformanceTimeBase
= perf
;
712 NodeManager::_StopNodes()
714 TRACE("NodeManager::_StopNodes()\n");
715 fMediaRoster
= BMediaRoster::Roster();
716 if (fMediaRoster
!= NULL
) {
717 // begin mucking with the media roster
718 if (fVideoProducer
!= NULL
) {
719 TRACE(" stopping video producer...\n");
720 fMediaRoster
->StopNode(fVideoConnection
.producer
, 0, true);
722 if (fAudioProducer
!= NULL
) {
723 TRACE(" stopping audio producer...\n");
724 fMediaRoster
->StopNode(fAudioConnection
.producer
, 0, true);
727 if (fVideoConsumer
!= NULL
) {
728 TRACE(" stopping video consumer...\n");
729 fMediaRoster
->StopNode(fVideoConnection
.consumer
, 0, true);
731 TRACE(" all nodes stopped\n");
733 TRACE("NodeManager::_StopNodes() done\n");