/*
* Controller.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007-2008 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2007-2009 Fredrik Modéen <[FirstName]@[LastName].se>
*
* Released under the terms of the MIT license.
*/
#include "Controller.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Debug.h>
#include <Path.h>
#include <Window.h> // for debugging only
#include "AutoDeleter.h"
#include "ControllerView.h"
#include "MainApp.h"
#include "PlaybackState.h"
#include "Settings.h"
#include "VideoView.h"
// suppliers
#include "AudioTrackSupplier.h"
#include "MediaTrackAudioSupplier.h"
#include "MediaTrackVideoSupplier.h"
#include "ProxyAudioSupplier.h"
#include "ProxyVideoSupplier.h"
#include "SubTitles.h"
#include "TrackSupplier.h"
#include "VideoTrackSupplier.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaPlayer-Controller"
#define MIN_WIDTH 250
using std::nothrow;
class TrackSupplierReleaser {
public:
TrackSupplierReleaser(PlaylistItemRef& owner)
:
fOwner(owner),
fRelease(true)
{}
virtual ~TrackSupplierReleaser()
{
if (fRelease)
fOwner.Get()->ReleaseTrackSupplier();
}
void Detach()
{
fRelease = false;
}
private:
PlaylistItemRef& fOwner;
bool fRelease;
};
void
HandleError(const char *text, status_t err)
{
if (err != B_OK) {
printf("%s. error 0x%08x (%s)\n",text, (int)err, strerror(err));
fflush(NULL);
exit(1);
}
}
// #pragma mark - Controller::Listener
Controller::Listener::Listener() {}
Controller::Listener::~Listener() {}
void Controller::Listener::FileFinished() {}
void Controller::Listener::FileChanged(PlaylistItem* item, status_t result) {}
void Controller::Listener::VideoTrackChanged(int32) {}
void Controller::Listener::AudioTrackChanged(int32) {}
void Controller::Listener::SubTitleTrackChanged(int32) {}
void Controller::Listener::VideoStatsChanged() {}
void Controller::Listener::AudioStatsChanged() {}
void Controller::Listener::PlaybackStateChanged(uint32) {}
void Controller::Listener::PositionChanged(float) {}
void Controller::Listener::SeekHandled(int64 seekFrame) {}
void Controller::Listener::VolumeChanged(float) {}
void Controller::Listener::MutedChanged(bool) {}
// #pragma mark - Controller
enum {
MSG_SET_TO = 'stto'
};
Controller::Controller()
:
NodeManager(),
fVideoView(NULL),
fVolume(1.0),
fActiveVolume(1.0),
fMuted(false),
fItem(NULL),
fVideoSupplier(new ProxyVideoSupplier()),
fAudioSupplier(new ProxyAudioSupplier(this)),
fVideoTrackSupplier(NULL),
fAudioTrackSupplier(NULL),
fSubTitles(NULL),
fSubTitlesIndex(-1),
fCurrentFrame(0),
fDuration(0),
fVideoFrameRate(25.0),
fPendingSeekRequests(0),
fSeekFrame(-1),
fRequestedSeekFrame(-1),
fGlobalSettingsListener(this),
fListeners(4)
{
Settings::Default()->AddListener(&fGlobalSettingsListener);
_AdoptGlobalSettings();
fAutoplay = fAutoplaySetting;
}
Controller::~Controller()
{
Settings::Default()->RemoveListener(&fGlobalSettingsListener);
SetTo(NULL);
}
// #pragma mark - NodeManager interface
void
Controller::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_OBJECT_CHANGED:
// received from fGlobalSettingsListener
// TODO: find out which object, if we ever watch more than
// the global settings instance...
_AdoptGlobalSettings();
break;
case MSG_SET_TO:
{
PlaylistItem* item;
if (message->FindPointer("item", (void**)&item) == B_OK) {
PlaylistItemRef itemRef(item, true);
// The reference was passed with the message.
SetTo(itemRef);
} else
_NotifyFileChanged(NULL, B_BAD_VALUE);
break;
}
default:
NodeManager::MessageReceived(message);
}
}
int64
Controller::Duration()
{
return _FrameDuration();
}
VideoTarget*
Controller::CreateVideoTarget()
{
return fVideoView;
}
VideoSupplier*
Controller::CreateVideoSupplier()
{
return fVideoSupplier;
}
AudioSupplier*
Controller::CreateAudioSupplier()
{
return fAudioSupplier;
}
// #pragma mark -
status_t
Controller::SetToAsync(const PlaylistItemRef& item)
{
PlaylistItemRef additionalReference(item);
BMessage message(MSG_SET_TO);
status_t ret = message.AddPointer("item", item.Get());
if (ret != B_OK)
return ret;
ret = PostMessage(&message);
if (ret != B_OK)
return ret;
// The additional reference is now passed along with the message...
additionalReference.Detach();
return B_OK;
}
status_t
Controller::SetTo(const PlaylistItemRef& item)
{
BAutolock _(this);
if (fItem == item) {
if (InitCheck() == B_OK) {
if (fAutoplay) {
SetPosition(0.0);
StartPlaying(true);
}
}
return B_OK;
}
fAudioSupplier->SetSupplier(NULL, fVideoFrameRate);
fVideoSupplier->SetSupplier(NULL);
if (fItem != NULL)
TrackSupplierReleaser oldSupplierReleaser(fItem);
fItem = item;
// Do not delete the supplier chain until after we called
// NodeManager::Init() to setup a new media node chain
ObjectDeleter<VideoTrackSupplier> videoSupplierDeleter(
fVideoTrackSupplier);
ObjectDeleter<AudioTrackSupplier> audioSupplierDeleter(
fAudioTrackSupplier);
fVideoTrackSupplier = NULL;
fAudioTrackSupplier = NULL;
fSubTitles = NULL;
fSubTitlesIndex = -1;
fCurrentFrame = 0;
fDuration = 0;
fVideoFrameRate = 25.0;
fPendingSeekRequests = 0;
fSeekFrame = -1;
fRequestedSeekFrame = -1;
if (fItem.Get() == NULL)
return B_BAD_VALUE;
TrackSupplier* trackSupplier = fItem->GetTrackSupplier();
if (trackSupplier == NULL) {
_NotifyFileChanged(item.Get(), B_NO_MEMORY);
return B_NO_MEMORY;
}
TrackSupplierReleaser trackSupplierReleaser(fItem);
status_t err = trackSupplier->InitCheck();
if (err != B_OK) {
printf("Controller::SetTo: InitCheck failed\n");
_NotifyFileChanged(item.Get(), err);
return err;
}
if (trackSupplier->CountAudioTracks() == 0
&& trackSupplier->CountVideoTracks() == 0) {
_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
return B_MEDIA_NO_HANDLER;
}
SelectAudioTrack(0);
SelectVideoTrack(0);
if (fAudioTrackSupplier == NULL && fVideoTrackSupplier == NULL) {
printf("Controller::SetTo: no audio or video tracks found or "
"no decoders\n");
_NotifyFileChanged(item.Get(), B_MEDIA_NO_HANDLER);
return B_MEDIA_NO_HANDLER;
}
trackSupplierReleaser.Detach();
// prevent blocking the creation of new overlay buffers
if (fVideoView)
fVideoView->DisableOverlay();
// get video properties (if there is video at all)
bool useOverlays = fVideoView ? fVideoView->UseOverlays() : true;
int width;
int height;
GetSize(&width, &height);
color_space preferredVideoFormat = B_NO_COLOR_SPACE;
if (fVideoTrackSupplier != NULL) {
const media_format& format = fVideoTrackSupplier->Format();
preferredVideoFormat = format.u.raw_video.display.format;
}
uint32 enabledNodes;
if (!fVideoTrackSupplier)
enabledNodes = AUDIO_ONLY;
else if (!fAudioTrackSupplier)
enabledNodes = VIDEO_ONLY;
else
enabledNodes = AUDIO_AND_VIDEO;
float audioFrameRate = 44100.0f;
uint32 audioChannels = 2;
if (fAudioTrackSupplier != NULL) {
const media_format& audioTrackFormat = fAudioTrackSupplier->Format();
audioFrameRate = audioTrackFormat.u.raw_audio.frame_rate;
audioChannels = audioTrackFormat.u.raw_audio.channel_count;
}
if (InitCheck() != B_OK) {
Init(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
preferredVideoFormat, audioFrameRate, audioChannels, LOOPING_ALL,
false, 1.0, enabledNodes, useOverlays);
} else {
FormatChanged(BRect(0, 0, width - 1, height - 1), fVideoFrameRate,
preferredVideoFormat, audioFrameRate, audioChannels, enabledNodes,
useOverlays);
}
_NotifyFileChanged(item.Get(), B_OK);
if (fAutoplay)
StartPlaying(true);
return B_OK;
}
void
Controller::PlayerActivated(bool active)
{
if (LockWithTimeout(5000) != B_OK)
return;
if (active && gMainApp->PlayerCount() > 1) {
if (fActiveVolume != fVolume)
SetVolume(fActiveVolume);
} else {
fActiveVolume = fVolume;
if (gMainApp->PlayerCount() > 1)
switch (fBackgroundMovieVolumeMode) {
case mpSettings::BG_MOVIES_MUTED:
SetVolume(0.0);
break;
case mpSettings::BG_MOVIES_HALF_VLUME:
SetVolume(fVolume * 0.25);
break;
case mpSettings::BG_MOVIES_FULL_VOLUME:
default:
break;
}
}
Unlock();
}
void
Controller::GetSize(int *width, int *height, int* _widthAspect,
int* _heightAspect)
{
BAutolock _(this);
if (fVideoTrackSupplier) {
media_format format = fVideoTrackSupplier->Format();
*height = format.u.raw_video.display.line_count;
*width = format.u.raw_video.display.line_width;
int widthAspect = 0;
int heightAspect = 0;
// Ignore format aspect when both values are 1. If they have been
// intentionally at 1:1 then no harm is done for quadratic videos,
// only if the video is indeed encoded anamorphotic, but supposed
// to be displayed quadratic... extremely unlikely.
if (format.u.raw_video.pixel_width_aspect
!= format.u.raw_video.pixel_height_aspect
&& format.u.raw_video.pixel_width_aspect != 1) {
widthAspect = format.u.raw_video.pixel_width_aspect;
heightAspect = format.u.raw_video.pixel_height_aspect;
}
if (_widthAspect != NULL)
*_widthAspect = widthAspect;
if (_heightAspect != NULL)
*_heightAspect = heightAspect;
} else {
*height = 0;
*width = 0;
if (_widthAspect != NULL)
*_widthAspect = 1;
if (_heightAspect != NULL)
*_heightAspect = 1;
}
}
int
Controller::AudioTrackCount()
{
BAutolock _(this);
if (fItem != NULL && fItem->HasTrackSupplier())
return fItem->GetTrackSupplier()->CountAudioTracks();
return 0;
}
int
Controller::VideoTrackCount()
{
BAutolock _(this);
if (fItem != NULL && fItem->HasTrackSupplier())
return fItem->GetTrackSupplier()->CountVideoTracks();
return 0;
}
int
Controller::SubTitleTrackCount()
{
BAutolock _(this);
if (fItem != NULL && fItem->HasTrackSupplier())
return fItem->GetTrackSupplier()->CountSubTitleTracks();
return 0;
}
status_t
Controller::SelectAudioTrack(int n)
{
BAutolock _(this);
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
ObjectDeleter<AudioTrackSupplier> deleter(fAudioTrackSupplier);
fAudioTrackSupplier
= fItem->GetTrackSupplier()->CreateAudioTrackForIndex(n);
if (fAudioTrackSupplier == NULL)
return B_BAD_INDEX;
bigtime_t a = fAudioTrackSupplier->Duration();
bigtime_t v = fVideoTrackSupplier != NULL
? fVideoTrackSupplier->Duration() : 0;
fDuration = max_c(a, v);
DurationChanged();
// TODO: notify duration changed!
fAudioSupplier->SetSupplier(fAudioTrackSupplier, fVideoFrameRate);
_NotifyAudioTrackChanged(n);
return B_OK;
}
int
Controller::CurrentAudioTrack()
{
BAutolock _(this);
if (fAudioTrackSupplier == NULL)
return -1;
return fAudioTrackSupplier->TrackIndex();
}
int
Controller::AudioTrackChannelCount()
{
media_format format;
if (GetEncodedAudioFormat(&format) == B_OK)
return format.u.encoded_audio.output.channel_count;
return 2;
}
status_t
Controller::SelectVideoTrack(int n)
{
BAutolock _(this);
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
ObjectDeleter<VideoTrackSupplier> deleter(fVideoTrackSupplier);
fVideoTrackSupplier
= fItem->GetTrackSupplier()->CreateVideoTrackForIndex(n);
if (fVideoTrackSupplier == NULL)
return B_BAD_INDEX;
bigtime_t a = fAudioTrackSupplier ? fAudioTrackSupplier->Duration() : 0;
bigtime_t v = fVideoTrackSupplier->Duration();
fDuration = max_c(a, v);
fVideoFrameRate = fVideoTrackSupplier->Format().u.raw_video.field_rate;
if (fVideoFrameRate <= 0.0) {
printf("Controller::SelectVideoTrack(%d) - invalid video frame rate: %.1f\n",
n, fVideoFrameRate);
fVideoFrameRate = 25.0;
}
DurationChanged();
// TODO: notify duration changed!
fVideoSupplier->SetSupplier(fVideoTrackSupplier);
_NotifyVideoTrackChanged(n);
return B_OK;
}
int
Controller::CurrentVideoTrack()
{
BAutolock _(this);
if (fVideoTrackSupplier == NULL)
return -1;
return fVideoTrackSupplier->TrackIndex();
}
status_t
Controller::SelectSubTitleTrack(int n)
{
BAutolock _(this);
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
fSubTitlesIndex = n;
fSubTitles =
fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
const SubTitle* subTitle = NULL;
if (fSubTitles != NULL)
subTitle = fSubTitles->SubTitleAt(_TimePosition());
if (subTitle != NULL)
fVideoView->SetSubTitle(subTitle->text.String());
else
fVideoView->SetSubTitle(NULL);
_NotifySubTitleTrackChanged(n);
return B_OK;
}
int
Controller::CurrentSubTitleTrack()
{
BAutolock _(this);
if (fSubTitles == NULL)
return -1;
return fSubTitlesIndex;
}
const char*
Controller::SubTitleTrackName(int n)
{
BAutolock _(this);
if (fItem == NULL || !fItem->HasTrackSupplier())
return NULL;
const SubTitles* subTitles
= fItem->GetTrackSupplier()->SubTitleTrackForIndex(n);
if (subTitles == NULL)
return NULL;
return subTitles->Name();
}
// #pragma mark -
void
Controller::Stop()
{
//printf("Controller::Stop\n");
BAutolock _(this);
StopPlaying();
SetPosition(0.0);
fAutoplay = fAutoplaySetting;
}
void
Controller::Play()
{
//printf("Controller::Play\n");
BAutolock _(this);
StartPlaying();
fAutoplay = true;
}
void
Controller::Pause()
{
// printf("Controller::Pause\n");
BAutolock _(this);
PausePlaying();
fAutoplay = fAutoplaySetting;
}
void
Controller::TogglePlaying()
{
// printf("Controller::TogglePlaying\n");
BAutolock _(this);
if (InitCheck() == B_OK) {
NodeManager::TogglePlaying();
fAutoplay = IsPlaying() || fAutoplaySetting;
}
}
uint32
Controller::PlaybackState()
{
BAutolock _(this);
return _PlaybackState(PlaybackManager::PlayMode());
}
bigtime_t
Controller::TimeDuration()
{
BAutolock _(this);
return fDuration;
}
bigtime_t
Controller::TimePosition()
{
BAutolock _(this);
return _TimePosition();
}
status_t
Controller::SaveState(bool reset)
{
if (fItem.Get() == NULL)
return B_OK;
if (reset)
fCurrentFrame = 0;
status_t status = fItem.Get()->SetLastVolume(fVolume);
if (status == B_OK)
status = fItem.Get()->SetLastFrame(fCurrentFrame);
else
fItem.Get()->SetLastFrame(fCurrentFrame);
return status;
}
void
Controller::RestoreState()
{
PlaylistItem *item =fItem.Get();
if (item == NULL)
return;
int lastFrame = item->LastFrame();
float lastVolume = item->LastVolume();
// Don't Pause()/Play() if we have nothing to do.
if (lastFrame <= 0 && lastVolume < 0)
return;
Pause();
if (lastFrame > 0) {
bool resume = fResume == mpSettings::RESUME_ALWAYS;
if (fResume == mpSettings::RESUME_ASK) {
BString label;
int32 time = (int32)((float)lastFrame * TimeDuration()
/ (1000000 * _FrameDuration()));
label.SetToFormat(B_TRANSLATE("Do you want to resume %s at %dm%ds?"),
item->Name().String(), time / 60, time % 60);
BAlert *alert = new BAlert(B_TRANSLATE("Resume?"), label,
B_TRANSLATE("Resume"), B_TRANSLATE("Reset"));
resume = alert->Go() == 0;
}
if (resume)
SetFramePosition(lastFrame);
}
if (lastVolume >= 0)
SetVolume(lastVolume);
Play();
}
void
Controller::SetVolume(float value)
{
// printf("Controller::SetVolume %.4f\n", value);
BAutolock _(this);
value = max_c(0.0, min_c(2.0, value));
if (fVolume != value) {
if (fMuted)
ToggleMute();
fVolume = value;
fAudioSupplier->SetVolume(fVolume);
_NotifyVolumeChanged(fVolume);
}
}
void
Controller::VolumeUp()
{
// TODO: linear <-> exponential
SetVolume(Volume() + 0.05);
}
void
Controller::VolumeDown()
{
// TODO: linear <-> exponential
SetVolume(Volume() - 0.05);
}
void
Controller::ToggleMute()
{
BAutolock _(this);
fMuted = !fMuted;
if (fMuted)
fAudioSupplier->SetVolume(0.0);
else
fAudioSupplier->SetVolume(fVolume);
_NotifyMutedChanged(fMuted);
}
float
Controller::Volume()
{
BAutolock _(this);
return fVolume;
}
int64
Controller::SetPosition(float value)
{
BAutolock _(this);
return SetFramePosition(_FrameDuration() * value);
}
int64
Controller::SetFramePosition(int64 value)
{
BAutolock _(this);
fPendingSeekRequests++;
fRequestedSeekFrame = max_c(0, min_c(_FrameDuration(), value));
fSeekFrame = fRequestedSeekFrame;
int64 currentFrame = CurrentFrame();
// Snap to a video keyframe, since that will be the fastest
// to display and seeking will feel more snappy. Note that we
// don't store this change in fSeekFrame, since we still want
// to report the originally requested seek frame in TimePosition()
// until we could reach that frame.
if (Duration() > 240 && fVideoTrackSupplier != NULL
&& abs(value - currentFrame) > 5) {
fVideoTrackSupplier->FindKeyFrameForFrame(&fSeekFrame);
}
//printf("SetFramePosition(%lld) -> %lld (current: %lld, duration: %lld) "
//"(video: %p)\n", value, fSeekFrame, currentFrame, _FrameDuration(),
//fVideoTrackSupplier);
if (fSeekFrame != currentFrame) {
int64 seekFrame = fSeekFrame;
SetCurrentFrame(fSeekFrame);
// May trigger the notification and reset fSeekFrame,
// if next current frame == seek frame.
return seekFrame;
} else
NotifySeekHandled(fRequestedSeekFrame);
return currentFrame;
}
int64
Controller::SetTimePosition(bigtime_t value)
{
BAutolock _(this);
return SetPosition((float)value / TimeDuration());
}
// #pragma mark -
bool
Controller::HasFile()
{
// you need to hold the data lock
return fItem != NULL && fItem->HasTrackSupplier();
}
status_t
Controller::GetFileFormatInfo(media_file_format* fileFormat)
{
// you need to hold the data lock
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
return fItem->GetTrackSupplier()->GetFileFormatInfo(fileFormat);
}
status_t
Controller::GetCopyright(BString* copyright)
{
// you need to hold the data lock
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
return fItem->GetTrackSupplier()->GetCopyright(copyright);
}
status_t
Controller::GetLocation(BString* location)
{
// you need to hold the data lock
if (fItem.Get() == NULL)
return B_NO_INIT;
*location = fItem->LocationURI();
return B_OK;
}
status_t
Controller::GetName(BString* name)
{
// you need to hold the data lock
if (fItem.Get() == NULL)
return B_NO_INIT;
*name = fItem->Name();
return B_OK;
}
status_t
Controller::GetEncodedVideoFormat(media_format* format)
{
// you need to hold the data lock
if (fVideoTrackSupplier)
return fVideoTrackSupplier->GetEncodedFormat(format);
return B_NO_INIT;
}
status_t
Controller::GetVideoCodecInfo(media_codec_info* info)
{
// you need to hold the data lock
if (fVideoTrackSupplier)
return fVideoTrackSupplier->GetCodecInfo(info);
return B_NO_INIT;
}
status_t
Controller::GetEncodedAudioFormat(media_format* format)
{
// you need to hold the data lock
if (fAudioTrackSupplier)
return fAudioTrackSupplier->GetEncodedFormat(format);
return B_NO_INIT;
}
status_t
Controller::GetAudioCodecInfo(media_codec_info* info)
{
// you need to hold the data lock
if (fAudioTrackSupplier)
return fAudioTrackSupplier->GetCodecInfo(info);
return B_NO_INIT;
}
status_t
Controller::GetMetaData(BMessage* metaData)
{
// you need to hold the data lock
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
return fItem->GetTrackSupplier()->GetMetaData(metaData);
}
status_t
Controller::GetVideoMetaData(int32 index, BMessage* metaData)
{
// you need to hold the data lock
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
return fItem->GetTrackSupplier()->GetVideoMetaData(index, metaData);
}
status_t
Controller::GetAudioMetaData(int32 index, BMessage* metaData)
{
// you need to hold the data lock
if (fItem == NULL || !fItem->HasTrackSupplier())
return B_NO_INIT;
return fItem->GetTrackSupplier()->GetAudioMetaData(index, metaData);
}
// #pragma mark -
void
Controller::SetVideoView(VideoView *view)
{
BAutolock _(this);
fVideoView = view;
}
bool
Controller::IsOverlayActive()
{
if (fVideoView)
return fVideoView->IsOverlayActive();
return false;
}
// #pragma mark -
bool
Controller::AddListener(Listener* listener)
{
BAutolock _(this);
if (listener && !fListeners.HasItem(listener))
return fListeners.AddItem(listener);
return false;
}
void
Controller::RemoveListener(Listener* listener)
{
BAutolock _(this);
fListeners.RemoveItem(listener);
}
// #pragma mark - Private
void
Controller::_AdoptGlobalSettings()
{
mpSettings settings;
Settings::Default()->Get(settings);
fAutoplaySetting = settings.autostart;
// not yet used:
fLoopMovies = settings.loopMovie;
fLoopSounds = settings.loopSound;
fBackgroundMovieVolumeMode = settings.backgroundMovieVolumeMode;
fResume = settings.resume;
}
uint32
Controller::_PlaybackState(int32 playingMode) const
{
uint32 state = 0;
switch (playingMode) {
case MODE_PLAYING_PAUSED_FORWARD:
case MODE_PLAYING_PAUSED_BACKWARD:
state = PLAYBACK_STATE_PAUSED;
break;
case MODE_PLAYING_FORWARD:
case MODE_PLAYING_BACKWARD:
state = PLAYBACK_STATE_PLAYING;
break;
default:
state = PLAYBACK_STATE_STOPPED;
break;
}
return state;
}
bigtime_t
Controller::_TimePosition() const
{
if (fDuration == 0)
return 0;
// Check if we are still waiting to reach the seekframe,
// pass the last pending seek frame back to the caller, so
// that the view of the current frame/time from the outside
// does not depend on the internal latency to reach requested
// frames asynchronously.
int64 frame;
if (fPendingSeekRequests > 0)
frame = fRequestedSeekFrame;
else
frame = fCurrentFrame;
return frame * fDuration / _FrameDuration();
}
int64
Controller::_FrameDuration() const
{
// This should really be total frames (video frames at that)
// TODO: It is not so nice that the MediaPlayer still measures
// in video frames if only playing audio. Here for example, it will
// return a duration of 0 if the audio clip happens to be shorter than
// one video frame at 25 fps.
return (int64)((double)fDuration * fVideoFrameRate / 1000000.0);
}
// #pragma mark - Notifications
void
Controller::_NotifyFileChanged(PlaylistItem* item, status_t result) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->FileChanged(item, result);
}
}
void
Controller::_NotifyFileFinished() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->FileFinished();
}
}
void
Controller::_NotifyVideoTrackChanged(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->VideoTrackChanged(index);
}
}
void
Controller::_NotifyAudioTrackChanged(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->AudioTrackChanged(index);
}
}
void
Controller::_NotifySubTitleTrackChanged(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->SubTitleTrackChanged(index);
}
}
void
Controller::_NotifyVideoStatsChanged() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->VideoStatsChanged();
}
}
void
Controller::_NotifyAudioStatsChanged() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->AudioStatsChanged();
}
}
void
Controller::_NotifyPlaybackStateChanged(uint32 state) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->PlaybackStateChanged(state);
}
}
void
Controller::_NotifyPositionChanged(float position) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->PositionChanged(position);
}
}
void
Controller::_NotifySeekHandled(int64 seekFrame) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->SeekHandled(seekFrame);
}
}
void
Controller::_NotifyVolumeChanged(float volume) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->VolumeChanged(volume);
}
}
void
Controller::_NotifyMutedChanged(bool muted) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->MutedChanged(muted);
}
}
void
Controller::NotifyPlayModeChanged(int32 mode) const
{
uint32 state = _PlaybackState(mode);
if (fVideoView)
fVideoView->SetPlaying(state == PLAYBACK_STATE_PLAYING);
_NotifyPlaybackStateChanged(state);
}
void
Controller::NotifyLoopModeChanged(int32 mode) const
{
}
void
Controller::NotifyLoopingEnabledChanged(bool enabled) const
{
}
void
Controller::NotifyVideoBoundsChanged(BRect bounds) const
{
}
void
Controller::NotifyFPSChanged(float fps) const
{
}
void
Controller::NotifyCurrentFrameChanged(int64 frame) const
{
fCurrentFrame = frame;
bigtime_t timePosition = _TimePosition();
_NotifyPositionChanged((float)timePosition / fDuration);
if (fSubTitles != NULL) {
const SubTitle* subTitle = fSubTitles->SubTitleAt(timePosition);
if (subTitle != NULL)
fVideoView->SetSubTitle(subTitle->text.String());
else
fVideoView->SetSubTitle(NULL);
}
}
void
Controller::NotifySpeedChanged(float speed) const
{
}
void
Controller::NotifyFrameDropped() const
{
// printf("Controller::NotifyFrameDropped()\n");
}
void
Controller::NotifyStopFrameReached() const
{
// Currently, this means we reached the end of the current
// file and should play the next file
_NotifyFileFinished();
}
void
Controller::NotifySeekHandled(int64 seekedFrame) const
{
if (fPendingSeekRequests == 0)
return;
fPendingSeekRequests--;
if (fPendingSeekRequests == 0) {
fSeekFrame = -1;
fRequestedSeekFrame = -1;
}
_NotifySeekHandled(seekedFrame);
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.