* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#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"
#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)
{
}
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)
{
PlaybackManager::Init(videoFrameRate, true, loopingMode, loopingEnabled,
speed);
if (fVideoTarget == NULL)
fVideoTarget = CreateVideoTarget();
if (fVideoSupplier == NULL)
fVideoSupplier = CreateVideoSupplier();
if (fAudioSupplier == NULL)
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 (fVideoConsumer != NULL && fMediaRoster != NULL) {
BMediaNode::run_mode runMode = mode > 0 ?
BMediaNode::B_DROP_DATA : BMediaNode::B_OFFLINE;
status_t ret = fMediaRoster->SetRunModeNode(fVideoConnection.consumer,
runMode);
if (ret != B_OK) {
printf("NodeManager::SetPlayMode(%" B_PRId32 "), setting run mode "
"failed: %s\n", mode, strerror(ret));
}
}
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");
PlaybackManager::Init(videoFrameRate, false, LoopMode(),
IsLoopingEnabled(), Speed(), MODE_PLAYING_PAUSED_FORWARD,
CurrentFrame());
return B_OK;
}
_StopNodes();
_TearDownNodes();
PlaybackManager::Init(videoFrameRate, true, LoopMode(), IsLoopingEnabled(),
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
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);
}
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)
{
PlaybackManager::SetCurrentAudioTime(time);
if (!fVideoProducer) {
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)
{
}
void
NodeManager::SetPeakListener(BHandler* handler)
{
fPeakListener = handler;
if (fAudioProducer)
fAudioProducer->SetPeakListener(fPeakListener);
}
status_t
NodeManager::_SetUpNodes(color_space preferredVideoFormat, uint32 enabledNodes,
bool useOverlays, float audioFrameRate, uint32 audioChannels)
{
TRACE("NodeManager::_SetUpNodes()\n");
fStatus = B_OK;
fMediaRoster = BMediaRoster::Roster(&fStatus);
if (fStatus != B_OK) {
print_error("Can't find the media roster", fStatus);
fMediaRoster = NULL;
return fStatus;
}
fStatus = fMediaRoster->GetTimeSource(&fTimeSource);
if (fStatus != B_OK) {
print_error("Can't get a time source", fStatus);
return fStatus;
}
if (enabledNodes != AUDIO_ONLY) {
fStatus = _SetUpVideoNodes(preferredVideoFormat, useOverlays);
if (fStatus != B_OK) {
print_error("Error setting up video nodes", fStatus);
return fStatus;
}
} else
printf("running without video node\n");
if (enabledNodes != VIDEO_ONLY) {
fStatus = _SetUpAudioNodes(audioFrameRate, audioChannels);
if (fStatus != B_OK) {
print_error("Error setting up audio nodes", fStatus);
return fStatus;
}
fNoAudio = false;
} else {
fNoAudio = true;
printf("running without audio node\n");
}
return fStatus;
}
status_t
NodeManager::_SetUpVideoNodes(color_space preferredVideoFormat,
bool useOverlays)
{
fVideoProducer = new VideoProducer(NULL, "MediaPlayer video out", 0,
this, fVideoSupplier);
fStatus = fMediaRoster->RegisterNode(fVideoProducer);
if (fStatus != B_OK) {
print_error("Can't register the video producer", fStatus);
return fStatus;
}
fVideoConnection.producer = fVideoProducer->Node();
fVideoConsumer = new VideoConsumer("MediaPlayer video in", NULL, 0, this,
fVideoTarget);
fStatus = fMediaRoster->RegisterNode(fVideoConsumer);
if (fStatus != B_OK) {
print_error("Can't register the video consumer", fStatus);
return fStatus;
}
fVideoConnection.consumer = fVideoConsumer->Node();
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;
}
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;
}
media_format format;
format.type = B_MEDIA_RAW_VIDEO;
media_raw_video_format videoFormat = {
FramesPerSecond(), 1, 0,
(uint32)fVideoBounds.IntegerWidth(),
B_VIDEO_TOP_LEFT_RIGHT, 1, 1,
{
preferredVideoFormat,
(uint32)(fVideoBounds.IntegerWidth() + 1),
(uint32)(fVideoBounds.IntegerHeight() + 1),
0, 0, 0
}
};
format.u.raw_video = videoFormat;
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) {
format.u.raw_video.display.format = B_RGB32;
printf("NodeManager::_SetupVideoNodes() - falling back to "
"B_RGB32\n");
}
fVideoConsumer->SetTryOverlay(false);
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",
fStatus);
return fStatus;
}
fVideoConnection.format = format;
fVideoConnection.source = videoOutput.source;
fVideoConnection.destination = videoInput.destination;
fVideoConnection.connected = true;
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;
}
fAudioConnection.producer = fAudioProducer->Node();
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);
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;
}
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;
}
fAudioConnection.format = audioFormat;
fAudioConnection.source = soundOutput.source;
fAudioConnection.destination = mixerInput.destination;
fAudioConnection.connected = true;
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;
}
if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
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;
if (fMediaRoster != NULL && 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 != NULL && disconnect) {
TRACE(" releasing audio consumer...\n");
fMediaRoster->ReleaseNode(fAudioConnection.consumer);
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
"audio consumer (system mixer)!\n");
}
}
TRACE("NodeManager::_TearDownNodes() done\n");
return err;
}
status_t
NodeManager::_StartNodes()
{
status_t status = B_NO_INIT;
if (!fMediaRoster)
return status;
bigtime_t latency = 0;
bigtime_t initLatency = 0;
if (fVideoProducer && fVideoConsumer) {
status = fMediaRoster->GetLatencyFor(fVideoConnection.producer,
&latency);
if (status < B_OK) {
print_error("error getting latency for video producer",
status);
} else
TRACE("video latency: %lld\n", latency);
status = fMediaRoster->SetProducerRunModeDelay(
fVideoConnection.producer, latency);
if (status < B_OK) {
print_error("error settings run mode delay for video producer",
status);
}
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) {
bigtime_t audioLatency = 0;
status = fMediaRoster->GetLatencyFor(fAudioConnection.producer,
&audioLatency);
TRACE("audio latency: %lld\n", audioLatency);
}
BTimeSource* timeSource;
if (fVideoProducer) {
timeSource = fMediaRoster->MakeTimeSourceFor(
fVideoConnection.producer);
} else {
timeSource = fMediaRoster->MakeTimeSourceFor(
fAudioConnection.producer);
}
bool running = timeSource->IsRunning();
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);
timeSource->Release();
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;
return status;
}
void
NodeManager::_StopNodes()
{
TRACE("NodeManager::_StopNodes()\n");
fMediaRoster = BMediaRoster::Roster();
if (fMediaRoster != NULL) {
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);
}
if (fVideoConsumer != NULL) {
TRACE(" stopping video consumer...\n");
fMediaRoster->StopNode(fVideoConnection.consumer, 0, true);
}
TRACE(" all nodes stopped\n");
}
TRACE("NodeManager::_StopNodes() done\n");
}