Files
haiku-beta6/src/apps/mediaplayer/media_node_framework/NodeManager.cpp
T
Axel Dörfler bafcab9265 * Minor cleanup, no functional changes.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36222 a95241bf-73f2-0310-859d-f6bbb57e9c96
2010-04-13 15:11:39 +00:00

754 lines
20 KiB
C++

/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
//! This class controls our media nodes and general playback
#include "NodeManager.h"
#include <stdio.h>
#include <string.h>
#include <MediaRoster.h>
#include <scheduler.h>
#include <TimeSource.h>
#include "AudioProducer.h"
#include "AudioSupplier.h"
#include "VideoConsumer.h"
#include "VideoProducer.h"
#include "VideoSupplier.h"
// debugging
//#define TRACE_NODE_MANAGER
#ifdef TRACE_NODE_MANAGER
# define TRACE(x...) printf(x)
# define ERROR(x...) fprintf(stderr, x)
#else
# define TRACE(x...)
# define ERROR(x...) fprintf(stderr, x)
#endif
#define print_error(str, status) printf(str ", error: %s\n", strerror(status))
NodeManager::Connection::Connection()
:
connected(false)
{
memset(&format, 0, sizeof(media_format));
}
// #pragma mark -
NodeManager::NodeManager()
:
PlaybackManager(),
fMediaRoster(NULL),
fAudioProducer(NULL),
fVideoConsumer(NULL),
fVideoProducer(NULL),
fTimeSource(media_node::null),
fAudioConnection(),
fVideoConnection(),
fPerformanceTimeBase(0),
fStatus(B_NO_INIT),
fVideoTarget(NULL),
fAudioSupplier(NULL),
fVideoSupplier(NULL),
fVideoBounds(0, 0, -1, -1),
fPeakListener(NULL)
{
}
NodeManager::~NodeManager()
{
_StopNodes();
_TearDownNodes();
}
status_t
NodeManager::Init(BRect videoBounds, float videoFrameRate,
color_space preferredVideoFormat, float audioFrameRate,
uint32 audioChannels, int32 loopingMode, bool loopingEnabled,
float speed, uint32 enabledNodes, bool useOverlays)
{
// init base class
PlaybackManager::Init(videoFrameRate, loopingMode, loopingEnabled, speed);
// get some objects from a derived class
if (!fVideoTarget)
fVideoTarget = CreateVideoTarget();
if (!fVideoSupplier)
fVideoSupplier = CreateVideoSupplier();
if (!fAudioSupplier)
fAudioSupplier = CreateAudioSupplier();
return FormatChanged(videoBounds, videoFrameRate, preferredVideoFormat,
audioFrameRate, audioChannels, enabledNodes, useOverlays, true);
}
status_t
NodeManager::InitCheck()
{
return fStatus;
}
void
NodeManager::SetPlayMode(int32 mode, bool continuePlaying)
{
// if (fMediaRoster && fMediaRoster->Lock()) {
// BMediaNode::run_mode runMode = mode > 0 ?
// BMediaNode::B_DROP_DATA : BMediaNode::B_OFFLINE;
// fMediaRoster->SetRunModeNode(fVideoConnection.consumer, runMode);
// fMediaRoster->Unlock();
// }
PlaybackManager::SetPlayMode(mode, continuePlaying);
}
status_t
NodeManager::CleanupNodes()
{
_StopNodes();
return _TearDownNodes(false);
}
status_t
NodeManager::FormatChanged(BRect videoBounds, float videoFrameRate,
color_space preferredVideoFormat, float audioFrameRate,
uint32 audioChannels, uint32 enabledNodes, bool useOverlays, bool force)
{
TRACE("NodeManager::FormatChanged()\n");
if (!force && videoBounds == VideoBounds()
&& videoFrameRate == FramesPerSecond()) {
TRACE(" -> reusing existing nodes\n");
// TODO: if enabledNodes would indicate that audio or video
// is no longer needed, or, worse yet, suddenly needed when
// it wasn't before, then we should not return here!
return B_OK;
}
if (videoFrameRate != FramesPerSecond()) {
TRACE(" -> need to Init()\n");
PlaybackManager::Init(videoFrameRate, LoopMode(), IsLoopingEnabled(),
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
}
_StopNodes();
_TearDownNodes();
SetVideoBounds(videoBounds);
status_t ret = _SetUpNodes(preferredVideoFormat, enabledNodes,
useOverlays, audioFrameRate, audioChannels);
if (ret == B_OK)
_StartNodes();
else
fprintf(stderr, "unable to setup nodes: %s\n", strerror(ret));
return ret;
}
bigtime_t
NodeManager::RealTimeForTime(bigtime_t time) const
{
bigtime_t result = 0;
if (fVideoProducer) {
result = fVideoProducer->TimeSource()->RealTimeFor(
fPerformanceTimeBase + time, 0);
} else if (fAudioProducer) {
result = fAudioProducer->TimeSource()->RealTimeFor(
fPerformanceTimeBase + time, 0);
}
//printf("NodeManager::RealTimeForTime(%lld) -> %lld\n", time, result);
return result;
}
bigtime_t
NodeManager::TimeForRealTime(bigtime_t time) const
{
bigtime_t result = 0;
if (fVideoProducer) {
result = fVideoProducer->TimeSource()->PerformanceTimeFor(time)
- fPerformanceTimeBase;
} else if (fAudioProducer) {
result = fAudioProducer->TimeSource()->PerformanceTimeFor(time)
- fPerformanceTimeBase;
}
return result;
}
void
NodeManager::SetCurrentAudioTime(bigtime_t time)
{
//printf("NodeManager::SetCurrentAudioTime(%lld)\n", time);
PlaybackManager::SetCurrentAudioTime(time);
if (!fVideoProducer) {
// running without video, update video time as well
PlaybackManager::SetCurrentVideoTime(time);
}
}
void
NodeManager::SetVideoBounds(BRect bounds)
{
if (bounds != fVideoBounds) {
fVideoBounds = bounds;
NotifyVideoBoundsChanged(fVideoBounds);
}
}
BRect
NodeManager::VideoBounds() const
{
return fVideoBounds;
}
void
NodeManager::SetVideoTarget(VideoTarget* videoTarget)
{
if (videoTarget != fVideoTarget) {
fVideoTarget = videoTarget;
if (fVideoConsumer)
fVideoConsumer->SetTarget(fVideoTarget);
}
}
VideoTarget*
NodeManager::GetVideoTarget() const
{
return fVideoTarget;
}
void
NodeManager::SetVolume(float percent)
{
// TODO: would be nice to set the volume on the system mixer input of
// our audio node...
}
void
NodeManager::SetPeakListener(BHandler* handler)
{
fPeakListener = handler;
if (fAudioProducer)
fAudioProducer->SetPeakListener(fPeakListener);
}
// #pragma mark -
status_t
NodeManager::_SetUpNodes(color_space preferredVideoFormat, uint32 enabledNodes,
bool useOverlays, float audioFrameRate, uint32 audioChannels)
{
TRACE("NodeManager::_SetUpNodes()\n");
// find the media roster
fStatus = B_OK;
fMediaRoster = BMediaRoster::Roster(&fStatus);
if (fStatus != B_OK) {
print_error("Can't find the media roster", fStatus);
fMediaRoster = NULL;
return fStatus;
}
if (!fMediaRoster->Lock())
return B_ERROR;
// find the time source
fStatus = fMediaRoster->GetTimeSource(&fTimeSource);
if (fStatus != B_OK) {
print_error("Can't get a time source", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
// setup the video nodes
if (enabledNodes != AUDIO_ONLY) {
fStatus = _SetUpVideoNodes(preferredVideoFormat, useOverlays);
if (fStatus != B_OK) {
print_error("Error setting up video nodes", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
} else
printf("running without video node\n");
// setup the audio nodes
if (enabledNodes != VIDEO_ONLY) {
fStatus = _SetUpAudioNodes(audioFrameRate, audioChannels);
if (fStatus != B_OK) {
print_error("Error setting up audio nodes", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
fNoAudio = false;
} else {
fNoAudio = true;
printf("running without audio node\n");
}
// we're done mocking with the media roster
fMediaRoster->Unlock();
return fStatus;
}
status_t
NodeManager::_SetUpVideoNodes(color_space preferredVideoFormat,
bool useOverlays)
{
// create the video producer node
fVideoProducer = new VideoProducer(NULL, "MediaPlayer video out", 0,
this, fVideoSupplier);
// register the producer node
fStatus = fMediaRoster->RegisterNode(fVideoProducer);
if (fStatus != B_OK) {
print_error("Can't register the video producer", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fVideoProducer->Node().node,
// &fVideoConnection.producer);
fVideoConnection.producer = fVideoProducer->Node();
// create the video consumer node
fVideoConsumer = new VideoConsumer("MediaPlayer video in", NULL, 0, this,
fVideoTarget);
// register the consumer node
fStatus = fMediaRoster->RegisterNode(fVideoConsumer);
if (fStatus != B_OK) {
print_error("Can't register the video consumer", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fVideoConsumer->Node().node,
// &fVideoConnection.consumer);
fVideoConnection.consumer = fVideoConsumer->Node();
// find free producer output
media_input videoInput;
media_output videoOutput;
int32 count = 1;
fStatus = fMediaRoster->GetFreeOutputsFor(fVideoConnection.producer,
&videoOutput, 1, &count, B_MEDIA_RAW_VIDEO);
if (fStatus != B_OK || count < 1) {
fStatus = B_RESOURCE_UNAVAILABLE;
print_error("Can't find an available video stream", fStatus);
return fStatus;
}
// find free consumer input
count = 1;
fStatus = fMediaRoster->GetFreeInputsFor(fVideoConnection.consumer,
&videoInput, 1, &count, B_MEDIA_RAW_VIDEO);
if (fStatus != B_OK || count < 1) {
fStatus = B_RESOURCE_UNAVAILABLE;
print_error("Can't find an available connection to the video window",
fStatus);
return fStatus;
}
// connect the nodes
media_format format;
format.type = B_MEDIA_RAW_VIDEO;
media_raw_video_format videoFormat = {
FramesPerSecond(), 1, 0,
fVideoBounds.IntegerWidth(),
B_VIDEO_TOP_LEFT_RIGHT, 1, 1,
{
preferredVideoFormat,
fVideoBounds.IntegerWidth() + 1,
fVideoBounds.IntegerHeight() + 1,
0, 0, 0
}
};
format.u.raw_video = videoFormat;
// connect video producer to consumer (hopefully using overlays)
fVideoConsumer->SetTryOverlay(useOverlays);
fStatus = fMediaRoster->Connect(videoOutput.source, videoInput.destination,
&format, &videoOutput, &videoInput);
if (fStatus != B_OK) {
print_error("Can't connect the video source to the video window... "
"trying without overlays", fStatus);
uint32 flags = 0;
bool supported = bitmaps_support_space(
format.u.raw_video.display.format, &flags);
if (!supported || (flags & B_VIEWS_SUPPORT_DRAW_BITMAP) == 0) {
// cannot create bitmaps with such a color space
// or BViews don't support drawing it, fallback to B_RGB32
format.u.raw_video.display.format = B_RGB32;
printf("NodeManager::_SetupVideoNodes() - falling back to "
"B_RGB32\n");
}
fVideoConsumer->SetTryOverlay(false);
// connect video producer to consumer (not using overlays and using
// a colorspace that BViews support drawing)
fStatus = fMediaRoster->Connect(videoOutput.source,
videoInput.destination, &format, &videoOutput, &videoInput);
}
// bail if second attempt failed too
if (fStatus != B_OK) {
print_error("Can't connect the video source to the video window",
fStatus);
return fStatus;
}
// the inputs and outputs might have been reassigned during the
// nodes' negotiation of the Connect(). That's why we wait until
// after Connect() finishes to save their contents.
fVideoConnection.format = format;
fVideoConnection.source = videoOutput.source;
fVideoConnection.destination = videoInput.destination;
fVideoConnection.connected = true;
// set time sources
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConnection.producer.node,
fTimeSource.node);
if (fStatus != B_OK) {
print_error("Can't set the timesource for the video source", fStatus);
return fStatus;
}
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
fTimeSource.node);
if (fStatus != B_OK) {
print_error("Can't set the timesource for the video window", fStatus);
return fStatus;
}
return fStatus;
}
status_t
NodeManager::_SetUpAudioNodes(float audioFrameRate, uint32 audioChannels)
{
fAudioProducer = new AudioProducer("MediaPlayer audio out", fAudioSupplier);
fAudioProducer->SetPeakListener(fPeakListener);
fStatus = fMediaRoster->RegisterNode(fAudioProducer);
if (fStatus != B_OK) {
print_error("unable to register audio producer node!\n", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fAudioProducer->Node().node,
// &fAudioConnection.producer);
fAudioConnection.producer = fAudioProducer->Node();
// connect to the mixer
fStatus = fMediaRoster->GetAudioMixer(&fAudioConnection.consumer);
if (fStatus != B_OK) {
print_error("unable to get the system mixer", fStatus);
return fStatus;
}
fMediaRoster->SetTimeSourceFor(fAudioConnection.producer.node,
fTimeSource.node);
// got the nodes; now we find the endpoints of the connection
media_input mixerInput;
media_output soundOutput;
int32 count = 1;
fStatus = fMediaRoster->GetFreeOutputsFor(fAudioConnection.producer,
&soundOutput, 1, &count);
if (fStatus != B_OK) {
print_error("unable to get a free output from the producer node",
fStatus);
return fStatus;
}
count = 1;
fStatus = fMediaRoster->GetFreeInputsFor(fAudioConnection.consumer,
&mixerInput, 1, &count);
if (fStatus != B_OK) {
print_error("unable to get a free input to the mixer", fStatus);
return fStatus;
}
// got the endpoints; now we connect it!
media_format audioFormat;
audioFormat.type = B_MEDIA_RAW_AUDIO;
audioFormat.u.raw_audio = media_raw_audio_format::wildcard;
audioFormat.u.raw_audio.frame_rate = audioFrameRate;
audioFormat.u.raw_audio.channel_count = audioChannels;
fStatus = fMediaRoster->Connect(soundOutput.source, mixerInput.destination,
&audioFormat, &soundOutput, &mixerInput);
if (fStatus != B_OK) {
print_error("unable to connect audio nodes", fStatus);
return fStatus;
}
// the inputs and outputs might have been reassigned during the
// nodes' negotiation of the Connect(). That's why we wait until
// after Connect() finishes to save their contents.
fAudioConnection.format = audioFormat;
fAudioConnection.source = soundOutput.source;
fAudioConnection.destination = mixerInput.destination;
fAudioConnection.connected = true;
// Set an appropriate run mode for the producer
fMediaRoster->SetRunModeNode(fAudioConnection.producer,
BMediaNode::B_INCREASE_LATENCY);
return fStatus;
}
status_t
NodeManager::_TearDownNodes(bool disconnect)
{
TRACE("NodeManager::_TearDownNodes()\n");
status_t err = B_OK;
fMediaRoster = BMediaRoster::Roster(&err);
if (err != B_OK) {
fprintf(stderr, "NodeManager::_TearDownNodes() - error getting media "
"roster: %s\n", strerror(err));
fMediaRoster = NULL;
}
// begin mucking with the media roster
bool mediaRosterLocked = false;
if (fMediaRoster && fMediaRoster->Lock())
mediaRosterLocked = true;
if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
// disconnect
if (fMediaRoster) {
TRACE(" disconnecting video...\n");
err = fMediaRoster->Disconnect(fVideoConnection.producer.node,
fVideoConnection.source, fVideoConnection.consumer.node,
fVideoConnection.destination);
if (err < B_OK)
print_error("unable to disconnect video nodes", err);
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
"disconnect video nodes, no media server!\n");
}
fVideoConnection.connected = false;
}
if (fVideoProducer) {
TRACE(" releasing video producer...\n");
fVideoProducer->Release();
fVideoProducer = NULL;
}
if (fVideoConsumer) {
TRACE(" releasing video consumer...\n");
fVideoConsumer->Release();
fVideoConsumer = NULL;
}
if (fAudioProducer) {
disconnect = fAudioConnection.connected;
// Ordinarily we'd stop *all* of the nodes in the chain at this point.
// However, one of the nodes is the System Mixer, and stopping the
// Mixer is a Bad Idea (tm). So, we just disconnect from it, and
// release our references to the nodes that we're using. We *are*
// supposed to do that even for global nodes like the Mixer.
if (fMediaRoster && disconnect) {
TRACE(" disconnecting audio...\n");
err = fMediaRoster->Disconnect(fAudioConnection.producer.node,
fAudioConnection.source, fAudioConnection.consumer.node,
fAudioConnection.destination);
if (err < B_OK) {
print_error("unable to disconnect audio nodes", err);
disconnect = false;
}
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
"disconnect audio nodes, no media server!\n");
}
TRACE(" releasing audio producer...\n");
fAudioProducer->Release();
fAudioProducer = NULL;
fAudioConnection.connected = false;
if (fMediaRoster && disconnect) {
TRACE(" releasing audio consumer...\n");
fMediaRoster->ReleaseNode(fAudioConnection.consumer);
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
"audio consumer (system mixer)!\n");
}
}
// we're done mucking with the media roster
if (mediaRosterLocked && fMediaRoster)
fMediaRoster->Unlock();
TRACE("NodeManager::_TearDownNodes() done\n");
return err;
}
status_t
NodeManager::_StartNodes()
{
status_t status = B_NO_INIT;
if (!fMediaRoster)
return status;
// begin mucking with the media roster
if (!fMediaRoster->Lock())
return B_ERROR;
bigtime_t latency = 0;
bigtime_t initLatency = 0;
if (fVideoProducer && fVideoConsumer) {
// figure out what recording delay to use
status = fMediaRoster->GetLatencyFor(fVideoConnection.producer,
&latency);
if (status < B_OK) {
print_error("error getting latency for video producer",
status);
} else
TRACE("video latency: %Ld\n", latency);
status = fMediaRoster->SetProducerRunModeDelay(
fVideoConnection.producer, latency);
if (status < B_OK) {
print_error("error settings run mode delay for video producer",
status);
}
// start the nodes
status = fMediaRoster->GetInitialLatencyFor(
fVideoConnection.producer, &initLatency);
if (status < B_OK) {
print_error("error getting initial latency for video producer",
status);
}
}
initLatency += estimate_max_scheduling_latency();
if (fAudioProducer) {
// TODO: was this supposed to be added to initLatency?!?
bigtime_t audioLatency = 0;
status = fMediaRoster->GetLatencyFor(fAudioConnection.producer,
&audioLatency);
TRACE("audio latency: %Ld\n", audioLatency);
}
BTimeSource* timeSource;
if (fVideoProducer) {
timeSource = fMediaRoster->MakeTimeSourceFor(
fVideoConnection.producer);
} else {
timeSource = fMediaRoster->MakeTimeSourceFor(
fAudioConnection.producer);
}
bool running = timeSource->IsRunning();
// workaround for people without sound cards
// because the system time source won't be running
bigtime_t real = BTimeSource::RealTime();
if (!running) {
status = fMediaRoster->StartTimeSource(fTimeSource, real);
if (status != B_OK) {
timeSource->Release();
print_error("cannot start time source!", status);
return status;
}
status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real);
if (status != B_OK) {
timeSource->Release();
print_error("cannot seek time source!", status);
return status;
}
}
bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
+ initLatency);
printf("performance time for %lld: %lld\n", real + latency
+ initLatency, perf);
timeSource->Release();
// start the nodes
if (fVideoProducer && fVideoConsumer) {
status = fMediaRoster->StartNode(fVideoConnection.consumer, perf);
if (status != B_OK) {
print_error("Can't start the video consumer", status);
return status;
}
status = fMediaRoster->StartNode(fVideoConnection.producer, perf);
if (status != B_OK) {
print_error("Can't start the video producer", status);
return status;
}
}
if (fAudioProducer) {
status = fMediaRoster->StartNode(fAudioConnection.producer, perf);
if (status != B_OK) {
print_error("Can't start the audio producer", status);
return status;
}
}
fPerformanceTimeBase = perf;
// done mucking with the media roster
fMediaRoster->Unlock();
return status;
}
void
NodeManager::_StopNodes()
{
TRACE("NodeManager::_StopNodes()\n");
fMediaRoster = BMediaRoster::Roster();
if (fMediaRoster != NULL && fMediaRoster->Lock()) {
// begin mucking with the media roster
if (fVideoProducer != NULL) {
TRACE(" stopping video producer...\n");
fMediaRoster->StopNode(fVideoConnection.producer, 0, true);
}
if (fAudioProducer != NULL) {
TRACE(" stopping audio producer...\n");
fMediaRoster->StopNode(fAudioConnection.producer, 0, true);
// synchronous stop
}
if (fVideoConsumer != NULL) {
TRACE(" stopping video consumer...\n");
fMediaRoster->StopNode(fVideoConnection.consumer, 0, true);
}
TRACE(" all nodes stopped\n");
// done mucking with the media roster
fMediaRoster->Unlock();
}
TRACE("NodeManager::_StopNodes() done\n");
}