btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / mediaplayer / media_node_framework / NodeManager.cpp
blobe984a57d0c621194a361e7daf77efe8578d5964b
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 */
8 //! This class controls our media nodes and general playback
11 #include "NodeManager.h"
13 #include <stdio.h>
14 #include <string.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"
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
37 #define print_error(str, status) printf(str ", error: %s\n", strerror(status))
40 NodeManager::Connection::Connection()
42 connected(false)
44 memset(&format, 0, sizeof(media_format));
48 // #pragma mark -
51 NodeManager::NodeManager()
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)
72 NodeManager::~NodeManager()
74 _StopNodes();
75 _TearDownNodes();
79 status_t
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)
85 // init base class
86 PlaybackManager::Init(videoFrameRate, true, loopingMode, loopingEnabled,
87 speed);
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);
104 status_t
105 NodeManager::InitCheck()
107 return fStatus;
111 void
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,
118 runMode);
119 if (ret != B_OK) {
120 printf("NodeManager::SetPlayMode(%" B_PRId32 "), setting run mode "
121 "failed: %s\n", mode, strerror(ret));
125 PlaybackManager::SetPlayMode(mode, continuePlaying);
129 status_t
130 NodeManager::CleanupNodes()
132 _StopNodes();
133 return _TearDownNodes(false);
137 status_t
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,
152 CurrentFrame());
153 return B_OK;
156 _StopNodes();
157 _TearDownNodes();
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);
166 if (ret == B_OK)
167 _StartNodes();
168 else
169 fprintf(stderr, "unable to setup nodes: %s\n", strerror(ret));
171 return ret;
175 bigtime_t
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);
187 return result;
191 bigtime_t
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;
202 return result;
206 void
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);
218 void
219 NodeManager::SetVideoBounds(BRect bounds)
221 if (bounds != fVideoBounds) {
222 fVideoBounds = bounds;
223 NotifyVideoBoundsChanged(fVideoBounds);
228 BRect
229 NodeManager::VideoBounds() const
231 return fVideoBounds;
235 void
236 NodeManager::SetVideoTarget(VideoTarget* videoTarget)
238 if (videoTarget != fVideoTarget) {
239 fVideoTarget = videoTarget;
240 if (fVideoConsumer)
241 fVideoConsumer->SetTarget(fVideoTarget);
246 VideoTarget*
247 NodeManager::GetVideoTarget() const
249 return fVideoTarget;
253 void
254 NodeManager::SetVolume(float percent)
256 // TODO: would be nice to set the volume on the system mixer input of
257 // our audio node...
261 void
262 NodeManager::SetPeakListener(BHandler* handler)
264 fPeakListener = handler;
265 if (fAudioProducer)
266 fAudioProducer->SetPeakListener(fPeakListener);
270 // #pragma mark -
273 status_t
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
280 fStatus = B_OK;
281 fMediaRoster = BMediaRoster::Roster(&fStatus);
282 if (fStatus != B_OK) {
283 print_error("Can't find the media roster", fStatus);
284 fMediaRoster = NULL;
285 return 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);
292 return 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);
300 return fStatus;
302 } else
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);
310 return fStatus;
312 fNoAudio = false;
313 } else {
314 fNoAudio = true;
315 printf("running without audio node\n");
318 return fStatus;
322 status_t
323 NodeManager::_SetUpVideoNodes(color_space preferredVideoFormat,
324 bool useOverlays)
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);
334 return 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,
344 fVideoTarget);
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);
350 return 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;
361 int32 count = 1;
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);
367 return fStatus;
370 // find free consumer input
371 count = 1;
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",
377 fStatus);
378 return fStatus;
381 // connect the nodes
382 media_format format;
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),
392 0, 0, 0
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);
406 uint32 flags = 0;
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 "
414 "B_RGB32\n");
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",
426 fStatus);
427 return fStatus;
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;
438 // set time sources
439 fStatus = fMediaRoster->SetTimeSourceFor(fVideoConnection.producer.node,
440 fTimeSource.node);
441 if (fStatus != B_OK) {
442 print_error("Can't set the timesource for the video source", fStatus);
443 return fStatus;
446 fStatus = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
447 fTimeSource.node);
448 if (fStatus != B_OK) {
449 print_error("Can't set the timesource for the video window", fStatus);
450 return fStatus;
453 return fStatus;
457 status_t
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);
465 return 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);
476 return fStatus;
479 fMediaRoster->SetTimeSourceFor(fAudioConnection.producer.node,
480 fTimeSource.node);
482 // got the nodes; now we find the endpoints of the connection
483 media_input mixerInput;
484 media_output soundOutput;
485 int32 count = 1;
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",
490 fStatus);
491 return fStatus;
493 count = 1;
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);
498 return 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);
511 return 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);
526 return fStatus;
530 status_t
531 NodeManager::_TearDownNodes(bool disconnect)
533 TRACE("NodeManager::_TearDownNodes()\n");
534 status_t err = B_OK;
535 fMediaRoster = BMediaRoster::Roster(&err);
536 if (err != B_OK) {
537 fprintf(stderr, "NodeManager::_TearDownNodes() - error getting media "
538 "roster: %s\n", strerror(err));
539 fMediaRoster = NULL;
542 if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
543 // disconnect
544 if (fMediaRoster) {
545 TRACE(" disconnecting video...\n");
546 err = fMediaRoster->Disconnect(fVideoConnection.producer.node,
547 fVideoConnection.source, fVideoConnection.consumer.node,
548 fVideoConnection.destination);
549 if (err < B_OK)
550 print_error("unable to disconnect video nodes", err);
551 } else {
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);
579 if (err < B_OK) {
580 print_error("unable to disconnect audio nodes", err);
581 disconnect = false;
583 } else {
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);
596 } else {
597 fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
598 "audio consumer (system mixer)!\n");
602 TRACE("NodeManager::_TearDownNodes() done\n");
603 return err;
607 status_t
608 NodeManager::_StartNodes()
610 status_t status = B_NO_INIT;
611 if (!fMediaRoster)
612 return status;
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,
619 &latency);
620 if (status < B_OK) {
621 print_error("error getting latency for video producer",
622 status);
623 } else
624 TRACE("video latency: %Ld\n", latency);
625 status = fMediaRoster->SetProducerRunModeDelay(
626 fVideoConnection.producer, latency);
627 if (status < B_OK) {
628 print_error("error settings run mode delay for video producer",
629 status);
632 // start the nodes
633 status = fMediaRoster->GetInitialLatencyFor(
634 fVideoConnection.producer, &initLatency);
635 if (status < B_OK) {
636 print_error("error getting initial latency for video producer",
637 status);
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,
646 &audioLatency);
647 TRACE("audio latency: %Ld\n", audioLatency);
650 BTimeSource* timeSource;
651 if (fVideoProducer) {
652 timeSource = fMediaRoster->MakeTimeSourceFor(
653 fVideoConnection.producer);
654 } else {
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();
663 if (!running) {
664 status = fMediaRoster->StartTimeSource(fTimeSource, real);
665 if (status != B_OK) {
666 timeSource->Release();
667 print_error("cannot start time source!", status);
668 return status;
670 status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real);
671 if (status != B_OK) {
672 timeSource->Release();
673 print_error("cannot seek time source!", status);
674 return status;
678 bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
679 + initLatency);
681 timeSource->Release();
683 // start the nodes
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);
688 return status;
690 status = fMediaRoster->StartNode(fVideoConnection.producer, perf);
691 if (status != B_OK) {
692 print_error("Can't start the video producer", status);
693 return 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);
701 return status;
705 fPerformanceTimeBase = perf;
707 return status;
711 void
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);
725 // synchronous stop
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");