#include <algorithm>
#include <stdio.h>
#include <Message.h>
#include <OS.h>
#include <Window.h>
#include "EventQueue.h"
#include "MessageEvent.h"
#include "PlaybackListener.h"
#include "PlaybackManager.h"
using namespace std;
#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
void
tdebug(const char* str)
{
TRACE("[%lx, %lld] ", find_thread(NULL), system_time());
TRACE(str);
}
#define SUPPORT_SPEED_CHANGES 0
struct PlaybackManager::PlayingState {
int64 start_frame;
int64 end_frame;
int64 frame_count;
int64 first_visible_frame;
int64 last_visible_frame;
int64 max_frame_count;
int32 play_mode;
int32 loop_mode;
bool looping_enabled;
bool is_seek_request;
int64 current_frame;
int64 range_index;
int64 activation_frame;
PlayingState()
{
}
PlayingState(const PlayingState& other)
:
start_frame(other.start_frame),
end_frame(other.end_frame),
frame_count(other.frame_count),
first_visible_frame(other.first_visible_frame),
last_visible_frame(other.last_visible_frame),
max_frame_count(other.max_frame_count),
play_mode(other.play_mode),
loop_mode(other.loop_mode),
looping_enabled(other.looping_enabled),
is_seek_request(false),
current_frame(other.current_frame),
range_index(other.range_index),
activation_frame(other.activation_frame)
{
}
};
#if SUPPORT_SPEED_CHANGES
struct PlaybackManager::SpeedInfo {
int64 activation_frame;
bigtime_t activation_time;
float speed;
float set_speed;
};
#endif
PlaybackManager::PlaybackManager()
:
BLooper("playback manager"),
fStates(10),
fSpeeds(10),
fCurrentAudioTime(0),
fCurrentVideoTime(0),
fPerformanceTime(0),
fPerformanceTimeEvent(NULL),
fFrameRate(1.0),
fStopPlayingFrame(-1),
fListeners(),
fNoAudio(false)
{
Run();
}
PlaybackManager::~PlaybackManager()
{
Cleanup();
}
void
PlaybackManager::Init(float frameRate, bool initPerformanceTimes,
int32 loopingMode, bool loopingEnabled, float playbackSpeed,
int32 playMode, int32 currentFrame)
{
Cleanup();
fFrameRate = frameRate;
if (initPerformanceTimes) {
fCurrentAudioTime = 0;
fCurrentVideoTime = 0;
fPerformanceTime = 0;
}
fStopPlayingFrame = -1;
#if SUPPORT_SPEED_CHANGES
SpeedInfo* speed = new SpeedInfo;
speed->activation_frame = 0;
speed->activation_time = 0;
speed->speed = playbackSpeed;
speed->set_speed = playbackSpeed;
_PushSpeedInfo(speed);
#endif
PlayingState* state = new PlayingState;
state->frame_count = Duration();
state->start_frame = 0;
state->end_frame = 0;
state->first_visible_frame = 0;
state->last_visible_frame = 0;
state->max_frame_count = state->frame_count;
state->play_mode = MODE_PLAYING_PAUSED_FORWARD;
state->loop_mode = loopingMode;
state->looping_enabled = loopingEnabled;
state->is_seek_request = false;
state->current_frame = currentFrame;
state->activation_frame = 0;
fStates.AddItem(state);
TRACE("initial state pushed: state count: %ld\n", fStates.CountItems());
NotifyPlayModeChanged(PlayMode());
NotifyLoopModeChanged(LoopMode());
NotifyLoopingEnabledChanged(IsLoopingEnabled());
NotifyVideoBoundsChanged(VideoBounds());
NotifyFPSChanged(FramesPerSecond());
NotifySpeedChanged(Speed());
NotifyCurrentFrameChanged(CurrentFrame());
SetPlayMode(playMode);
}
void
PlaybackManager::Cleanup()
{
if (EventQueue::Default().RemoveEvent(fPerformanceTimeEvent))
delete fPerformanceTimeEvent;
fPerformanceTimeEvent = NULL;
for (int32 i = 0; PlayingState* state = _StateAt(i); i++)
delete state;
fStates.MakeEmpty();
#if SUPPORT_SPEED_CHANGES
for (int32 i = 0; SpeedInfo* speed = _SpeedInfoAt(i); i++)
delete speed;
fSpeeds.MakeEmpty();
#endif
}
void
PlaybackManager::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_EVENT:
{
if (fPerformanceTimeEvent == NULL) {
break;
}
bigtime_t eventTime;
message->FindInt64("time", &eventTime);
fPerformanceTimeEvent = NULL;
SetPerformanceTime(TimeForRealTime(eventTime));
break;
}
case MSG_PLAYBACK_FORCE_UPDATE:
{
int64 frame;
if (message->FindInt64("frame", &frame) < B_OK)
frame = CurrentFrame();
SetCurrentFrame(frame);
break;
}
case MSG_PLAYBACK_SET_RANGE:
{
int64 startFrame = _LastState()->start_frame;
int64 endFrame = _LastState()->end_frame;
message->FindInt64("start frame", &startFrame);
message->FindInt64("end frame", &endFrame);
if (startFrame != _LastState()->start_frame
|| endFrame != _LastState()->end_frame) {
PlayingState* state = new PlayingState(*_LastState());
state->start_frame = startFrame;
state->end_frame = endFrame;
_PushState(state, true);
}
break;
}
case MSG_PLAYBACK_SET_VISIBLE:
{
int64 startFrame = _LastState()->first_visible_frame;
int64 endFrame = _LastState()->last_visible_frame;
message->FindInt64("start frame", &startFrame);
message->FindInt64("end frame", &endFrame);
if (startFrame != _LastState()->first_visible_frame
|| endFrame != _LastState()->last_visible_frame) {
PlayingState* state = new PlayingState(*_LastState());
state->first_visible_frame = startFrame;
state->last_visible_frame = endFrame;
_PushState(state, true);
}
break;
}
case MSG_PLAYBACK_SET_LOOP_MODE:
{
int32 loopMode = _LastState()->loop_mode;
message->FindInt32("mode", &loopMode);
if (loopMode != _LastState()->loop_mode) {
PlayingState* state = new PlayingState(*_LastState());
state->loop_mode = loopMode;
_PushState(state, true);
}
break;
}
default:
BLooper::MessageReceived(message);
break;
}
}
void
PlaybackManager::StartPlaying(bool atBeginning)
{
TRACE("PlaybackManager::StartPlaying()\n");
int32 playMode = PlayMode();
if (playMode == MODE_PLAYING_PAUSED_FORWARD)
SetPlayMode(MODE_PLAYING_FORWARD, !atBeginning);
if (playMode == MODE_PLAYING_PAUSED_BACKWARD)
SetPlayMode(MODE_PLAYING_BACKWARD, !atBeginning);
}
void
PlaybackManager::StopPlaying()
{
TRACE("PlaybackManager::StopPlaying()\n");
int32 playMode = PlayMode();
if (playMode == MODE_PLAYING_FORWARD)
SetPlayMode(MODE_PLAYING_PAUSED_FORWARD, true);
if (playMode == MODE_PLAYING_BACKWARD)
SetPlayMode(MODE_PLAYING_PAUSED_BACKWARD, true);
}
void
PlaybackManager::TogglePlaying(bool atBeginning)
{
SetPlayMode(-PlayMode(), !atBeginning);
}
void
PlaybackManager::PausePlaying()
{
if (PlayMode() > 0)
TogglePlaying();
}
bool
PlaybackManager::IsPlaying() const
{
return (PlayMode() > 0);
}
int32
PlaybackManager::PlayMode() const
{
if (!_LastState())
return MODE_PLAYING_PAUSED_FORWARD;
return _LastState()->play_mode;
}
int32
PlaybackManager::LoopMode() const
{
if (!_LastState())
return LOOPING_ALL;
return _LastState()->loop_mode;
}
bool
PlaybackManager::IsLoopingEnabled() const
{
if (!_LastState())
return true;
return _LastState()->looping_enabled;
}
int64
PlaybackManager::CurrentFrame() const
{
return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime));
}
float
PlaybackManager::Speed() const
{
#if SUPPORT_SPEED_CHANGES
if (!_LastState())
return 1.0;
return _LastSpeedInfo()->set_speed;
#else
return 1.0;
#endif
}
void
PlaybackManager::SetFramesPerSecond(float framesPerSecond)
{
if (framesPerSecond != fFrameRate) {
fFrameRate = framesPerSecond;
NotifyFPSChanged(fFrameRate);
}
}
float
PlaybackManager::FramesPerSecond() const
{
return fFrameRate;
}
void
PlaybackManager::FrameDropped() const
{
NotifyFrameDropped();
}
void
PlaybackManager::DurationChanged()
{
if (!_LastState())
return;
int32 frameCount = Duration();
if (frameCount != _LastState()->frame_count) {
PlayingState* state = new PlayingState(*_LastState());
state->frame_count = frameCount;
state->max_frame_count = frameCount;
_PushState(state, true);
}
}
its current frame which is set to /frame/.
The new state will be activated as soon as possible. */
void
PlaybackManager::SetCurrentFrame(int64 frame)
{
if (CurrentFrame() == frame) {
NotifySeekHandled(frame);
return;
}
PlayingState* newState = new PlayingState(*_LastState());
newState->current_frame = frame;
newState->is_seek_request = true;
_PushState(newState, false);
}
its playing mode which is set to /mode/.
The new state will be activated as soon as possible.
If /continuePlaying/ is true and the new state is a `not stopped'
state, playing continues at the frame the last state reaches when the
new state becomes active (or the next frame in the playing range).
If /continuePlaying/ is false, playing starts at the beginning of the
playing range (taking the playing direction into consideration). */
void
PlaybackManager::SetPlayMode(int32 mode, bool continuePlaying)
{
PlayingState* newState = new PlayingState(*_LastState());
newState->play_mode = mode;
if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
newState->current_frame = _PlayingStartFrameFor(newState);
_PushState(newState, continuePlaying);
NotifyPlayModeChanged(mode);
}
its loop mode which is set to /mode/.
The new state will be activated as soon as possible.
If /continuePlaying/ is true and the new state is a `not stopped'
state, playing continues at the frame the last state reaches when the
new state becomes active (or the next frame in the playing range).
If /continuePlaying/ is false, playing starts at the beginning of the
playing range (taking the playing direction into consideration). */
void
PlaybackManager::SetLoopMode(int32 mode, bool continuePlaying)
{
PlayingState* newState = new PlayingState(*_LastState());
newState->loop_mode = mode;
if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
newState->current_frame = _PlayingStartFrameFor(newState);
_PushState(newState, continuePlaying);
NotifyLoopModeChanged(mode);
}
its looping enabled flag which is set to /enabled/.
The new state will be activated as soon as possible.
If /continuePlaying/ is true and the new state is a `not stopped'
state, playing continues at the frame the last state reaches when the
new state becomes active (or the next frame in the playing range).
If /continuePlaying/ is false, playing starts at the beginning of the
playing range (taking the playing direction into consideration). */
void
PlaybackManager::SetLoopingEnabled(bool enabled, bool continuePlaying)
{
PlayingState* newState = new PlayingState(*_LastState());
newState->looping_enabled = enabled;
if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
newState->current_frame = _PlayingStartFrameFor(newState);
_PushState(newState, continuePlaying);
NotifyLoopingEnabledChanged(enabled);
}
void
PlaybackManager::SetSpeed(float speed)
{
#if SUPPORT_SPEED_CHANGES
SpeedInfo* lastSpeed = _LastSpeedInfo();
if (speed != lastSpeed->set_speed) {
SpeedInfo* info = new SpeedInfo(*lastSpeed);
info->activation_frame = NextFrame();
info->set_speed = speed;
if (_PlayingDirectionFor(_StateAtFrame(info->activation_frame)) != 0)
info->speed = info->set_speed;
else
info->speed = 1.0;
_PushSpeedInfo(info);
}
#endif
}
that is the first frame for that neither the audio nor the video producer
have fetched data.*/
int64
PlaybackManager::NextFrame() const
{
if (fNoAudio)
return FrameForTime(fCurrentVideoTime - 1) + 1;
return FrameForTime(max((bigtime_t)fCurrentAudioTime,
(bigtime_t)fCurrentVideoTime) - 1) + 1;
}
int64
PlaybackManager::NextPlaylistFrame() const
{
return PlaylistFrameAtFrame(NextFrame());
}
int64
PlaybackManager::FirstPlaybackRangeFrame()
{
PlayingState* state = _StateAtFrame(CurrentFrame());
switch (state->loop_mode) {
case LOOPING_RANGE:
return state->start_frame;
case LOOPING_SELECTION:
return 0;
case LOOPING_VISIBLE:
return state->first_visible_frame;
case LOOPING_ALL:
default:
return 0;
}
}
int64
PlaybackManager::LastPlaybackRangeFrame()
{
PlayingState* state = _StateAtFrame(CurrentFrame());
switch (state->loop_mode) {
case LOOPING_RANGE:
return state->end_frame;
case LOOPING_SELECTION:
return state->frame_count - 1;
case LOOPING_VISIBLE:
return state->last_visible_frame;
case LOOPING_ALL:
default:
return state->frame_count - 1;
}
}
int64
PlaybackManager::StartFrameAtFrame(int64 frame)
{
return _StateAtFrame(frame)->start_frame;
}
int64
PlaybackManager::StartFrameAtTime(bigtime_t time)
{
return _StateAtTime(time)->start_frame;
}
int64
PlaybackManager::EndFrameAtFrame(int64 frame)
{
return _StateAtTime(frame)->end_frame;
}
int64
PlaybackManager::EndFrameAtTime(bigtime_t time)
{
return _StateAtTime(time)->end_frame;
}
int64
PlaybackManager::FrameCountAtFrame(int64 frame)
{
return _StateAtTime(frame)->frame_count;
}
int64
PlaybackManager::FrameCountAtTime(bigtime_t time)
{
return _StateAtTime(time)->frame_count;
}
int32
PlaybackManager::PlayModeAtFrame(int64 frame)
{
return _StateAtTime(frame)->play_mode;
}
int32
PlaybackManager::PlayModeAtTime(bigtime_t time)
{
return _StateAtTime(time)->play_mode;
}
int32
PlaybackManager::LoopModeAtFrame(int64 frame)
{
return _StateAtTime(frame)->loop_mode;
}
int32
PlaybackManager::LoopModeAtTime(bigtime_t time)
{
return _StateAtTime(time)->loop_mode;
}
frame /frame/. Additionally the playing direction (0, 1, -1) is returned,
as well as if a new playing state becomes active with this frame.
A new playing state is installed, if either some data directly concerning
the playing (play mode, loop mode, playing ranges, selection...) or the
Playlist has changed. */
int64
PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection,
bool& newState) const
{
PlayingState* state = _StateAtFrame(frame);
newState = (state->activation_frame == frame);
playingDirection = _PlayingDirectionFor(state);
int64 result = state->current_frame;
if (playingDirection != 0) {
int64 index = state->range_index
+ (frame - state->activation_frame) * playingDirection;
result = _FrameForRangeFrame(state, index);
}
return result;
}
int64
PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection) const
{
bool newState;
return PlaylistFrameAtFrame(frame, playingDirection, newState);
}
int64
PlaybackManager::PlaylistFrameAtFrame(int64 frame) const
{
bool newState;
int32 playingDirection;
return PlaylistFrameAtFrame(frame, playingDirection, newState);
}
playing state or speed change occurs or /endFrame/, if this happens to be
earlier. */
int64
PlaybackManager::NextChangeFrame(int64 startFrame, int64 endFrame) const
{
int32 startIndex = _IndexForFrame(startFrame);
int32 endIndex = _IndexForFrame(endFrame);
if (startIndex < endIndex)
endFrame = _StateAt(startIndex + 1)->activation_frame;
#if SUPPORT_SPEED_CHANGES
startIndex = _SpeedInfoIndexForFrame(startFrame);
endIndex = _SpeedInfoIndexForFrame(endFrame);
if (startIndex < endIndex)
endFrame = _SpeedInfoAt(startIndex + 1)->activation_frame;
#endif
return endFrame;
}
speed change occurs or /endTime/, if this happens to be earlier. */
bigtime_t
PlaybackManager::NextChangeTime(bigtime_t startTime, bigtime_t endTime) const
{
int32 startIndex = _IndexForTime(startTime);
int32 endIndex = _IndexForTime(endTime);
if (startIndex < endIndex)
endTime = TimeForFrame(_StateAt(startIndex + 1)->activation_frame);
#if SUPPORT_SPEED_CHANGES
startIndex = _SpeedInfoIndexForTime(startTime);
endIndex = _SpeedInfoIndexForTime(endTime);
if (startIndex < endIndex)
endTime = TimeForFrame(_SpeedInfoAt(startIndex + 1)->activation_frame);
#endif
return endTime;
}
The returned interval may be smaller than the supplied one. Therefore
the supplied /endFrame/ is adjusted.
The value written to /xEndFrame/ is the first frame that doesn't belong
to the interval. /playingDirection/ may either be -1 for backward,
1 for forward or 0 for not playing. */
void
PlaybackManager::GetPlaylistFrameInterval(int64 startFrame, int64& endFrame,
int64& xStartFrame, int64& xEndFrame, int32& playingDirection) const
{
endFrame = NextChangeFrame(startFrame, endFrame);
xStartFrame = PlaylistFrameAtFrame(startFrame);
xEndFrame = xStartFrame;
playingDirection = _PlayingDirectionFor(_StateAtFrame(startFrame));
bool endOfInterval = false;
int64 intervalLength = 0;
while (startFrame < endFrame && !endOfInterval) {
intervalLength++;
startFrame++;
xEndFrame += playingDirection;
endOfInterval = (PlaylistFrameAtFrame(startFrame) != xEndFrame);
}
if (xStartFrame > xEndFrame) {
swap(xStartFrame, xEndFrame);
xStartFrame++;
xEndFrame++;
}
}
intervals. Note, that /startTime/ and /endTime/ measure
performance times whereas /xStartTime/ and /xEndTime/ specifies an
Playlist time interval. In general it does not hold
xEndTime - xStartTime == endTime - startTime, even if playing (think
of a playing speed != 1.0). */
void
PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
bigtime_t& endTime, bigtime_t& xStartTime, bigtime_t& xEndTime,
float& playingSpeed) const
{
int64 startFrame = FrameForTime(startTime);
int64 endFrame = FrameForTime(endTime) + 1;
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info = _SpeedInfoForFrame(startFrame)->speed;
#endif
int64 xStartFrame;
int64 xEndFrame;
int32 playingDirection;
GetPlaylistFrameInterval(startFrame, endFrame, xStartFrame, xEndFrame,
playingDirection);
bigtime_t endTimeForFrame = TimeForFrame(endFrame);
endTime = min(endTime, endTimeForFrame);
bigtime_t intervalLength = endTime - startTime;
switch (playingDirection) {
case 1:
{
#if SUPPORT_SPEED_CHANGES
xStartTime = PlaylistTimeForFrame(xStartFrame)
+ bigtime_t(double(startTime - TimeForFrame(startFrame))
* info->speed);
xEndTime = xStartTime
+ bigtime_t((double)intervalLength * info->speed);
#else
xStartTime = PlaylistTimeForFrame(xStartFrame)
+ startTime - TimeForFrame(startFrame);
xEndTime = xStartTime + intervalLength;
#endif
break;
}
case -1:
{
#if SUPPORT_SPEED_CHANGES
xEndTime = PlaylistTimeForFrame(xEndFrame)
- bigtime_t(double(startTime - TimeForFrame(startFrame))
* info->speed);
xStartTime = xEndTime
- bigtime_t((double)intervalLength * info->speed);
#else
xEndTime = PlaylistTimeForFrame(xEndFrame)
- startTime + TimeForFrame(startFrame);
xStartTime = xEndTime - intervalLength;
#endif
break;
}
case 0:
default:
xStartTime = PlaylistTimeForFrame(xStartFrame);
xEndTime = xStartTime;
break;
}
#if SUPPORT_SPEED_CHANGES
playingSpeed = (float)playingDirection * info->speed;
#else
playingSpeed = (float)playingDirection;
#endif
}
It holds TimeForFrame(frame) <= time < TimeForFrame(frame + 1). */
int64
PlaybackManager::FrameForTime(bigtime_t time) const
{
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info = _SpeedInfoForTime(time);
if (!info) {
fprintf(stderr, "PlaybackManager::FrameForTime() - no SpeedInfo!\n");
return 0;
}
int64 frame = (int64)(((double)time - info->activation_time)
* (double)fFrameRate * info->speed / 1000000.0)
+ info->activation_frame;
#else
int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
#endif
if (TimeForFrame(frame) > time)
frame--;
else if (TimeForFrame(frame + 1) <= time)
frame++;
return frame;
}
to be performed). */
bigtime_t
PlaybackManager::TimeForFrame(int64 frame) const
{
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info = _SpeedInfoForFrame(frame);
if (!info) {
fprintf(stderr, "PlaybackManager::TimeForFrame() - no SpeedInfo!\n");
return 0;
}
return (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
/ ((double)fFrameRate * info->speed))
+ info->activation_time;
#else
return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
#endif
}
It holds PlaylistTimeForFrame(frame) <= time <
PlaylistTimeForFrame(frame + 1). */
int64
PlaybackManager::PlaylistFrameForTime(bigtime_t time) const
{
int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
if (TimeForFrame(frame) > time)
frame--;
else if (TimeForFrame(frame + 1) <= time)
frame++;
return frame;
}
bigtime_t
PlaybackManager::PlaylistTimeForFrame(int64 frame) const
{
return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
}
void
PlaybackManager::SetCurrentAudioTime(bigtime_t time)
{
TRACE("PlaybackManager::SetCurrentAudioTime(%lld)\n", time);
bigtime_t lastFrameTime = _TimeForLastFrame();
fCurrentAudioTime = time;
bigtime_t newLastFrameTime = _TimeForLastFrame();
if (lastFrameTime != newLastFrameTime) {
if (fPerformanceTimeEvent == NULL) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
fPerformanceTimeEvent = new MessageEvent(eventTime, this);
EventQueue::Default().AddEvent(fPerformanceTimeEvent);
}
_CheckStopPlaying();
}
}
void
PlaybackManager::SetCurrentVideoFrame(int64 frame)
{
SetCurrentVideoTime(TimeForFrame(frame));
}
void
PlaybackManager::SetCurrentVideoTime(bigtime_t time)
{
TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
bigtime_t lastFrameTime = _TimeForLastFrame();
fCurrentVideoTime = time;
bigtime_t newLastFrameTime = _TimeForLastFrame();
if (lastFrameTime != newLastFrameTime) {
if (fPerformanceTimeEvent == NULL) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
fPerformanceTimeEvent = new MessageEvent(eventTime, this);
EventQueue::Default().AddEvent(fPerformanceTimeEvent);
}
_CheckStopPlaying();
}
}
void
PlaybackManager::SetPerformanceFrame(int64 frame)
{
SetPerformanceTime(TimeForFrame(frame));
}
argument. */
void
PlaybackManager::SetPerformanceTime(bigtime_t time)
{
int64 oldCurrentFrame = CurrentFrame();
fPerformanceTime = time;
_UpdateStates();
_UpdateSpeedInfos();
int64 currentFrame = CurrentFrame();
if (currentFrame != oldCurrentFrame)
NotifyCurrentFrameChanged(currentFrame);
}
void
PlaybackManager::AddListener(PlaybackListener* listener)
{
if (!listener)
return;
if (!Lock())
return;
if (!fListeners.HasItem(listener) && fListeners.AddItem(listener)) {
if (_LastState()) {
listener->PlayModeChanged(PlayMode());
listener->LoopModeChanged(LoopMode());
listener->LoopingEnabledChanged(IsLoopingEnabled());
listener->VideoBoundsChanged(VideoBounds());
listener->FramesPerSecondChanged(FramesPerSecond());
listener->SpeedChanged(Speed());
listener->CurrentFrameChanged(CurrentFrame());
}
}
Unlock();
}
void
PlaybackManager::RemoveListener(PlaybackListener* listener)
{
if (listener && Lock()) {
fListeners.RemoveItem(listener);
Unlock();
}
}
void
PlaybackManager::NotifyPlayModeChanged(int32 mode) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->PlayModeChanged(mode);
}
}
void
PlaybackManager::NotifyLoopModeChanged(int32 mode) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->LoopModeChanged(mode);
}
}
void
PlaybackManager::NotifyLoopingEnabledChanged(bool enabled) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->LoopingEnabledChanged(enabled);
}
}
void
PlaybackManager::NotifyVideoBoundsChanged(BRect bounds) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->VideoBoundsChanged(bounds);
}
}
void
PlaybackManager::NotifyFPSChanged(float fps) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->FramesPerSecondChanged(fps);
}
}
void
PlaybackManager::NotifyCurrentFrameChanged(int64 frame) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->CurrentFrameChanged(frame);
}
}
void
PlaybackManager::NotifySpeedChanged(float speed) const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->SpeedChanged(speed);
}
}
void
PlaybackManager::NotifyFrameDropped() const
{
for (int32 i = 0;
PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
i++) {
listener->FrameDropped();
}
}
void
PlaybackManager::NotifyStopFrameReached() const
{
}
void
PlaybackManager::NotifySeekHandled(int64 frame) const
{
}
void
PlaybackManager::PrintState(PlayingState* state)
{
TRACE("state: activation frame: %lld, current frame: %lld, "
"start frame: %lld, end frame: %lld, frame count: %lld, "
"first visible: %lld, last visible: %lld, selection (...), "
"play mode: %ld, loop mode: %ld\n",
state->activation_frame,
state->current_frame,
state->start_frame,
state->end_frame,
state->frame_count,
state->first_visible_frame,
state->last_visible_frame,
state->play_mode,
state->loop_mode);
}
void
PlaybackManager::PrintStateAtFrame(int64 frame)
{
TRACE("frame %lld: ", frame);
PrintState(_StateAtFrame(frame));
}
become active at the same time as _LastState() the latter is removed
and deleted. However, the activation time for the new state is adjusted,
so that it is >= that of the last state and >= the current audio and
video time. If the additional parameter /adjustCurrentFrame/ is true,
the new state's current frame is tried to be set to the frame that is
reached at the time the state will become active. In every case
it is ensured that the current frame lies within the playing range
(if playing). */
void
PlaybackManager::_PushState(PlayingState* state, bool adjustCurrentFrame)
{
TRACE("PlaybackManager::_PushState()\n");
if (state == NULL)
return;
int64 oldStopPlayingFrame = fStopPlayingFrame;
fStopPlayingFrame = -1;
PlayingState* lastState = _LastState();
int64 activationFrame = max(max(state->activation_frame,
lastState->activation_frame),
NextFrame());
int64 currentFrame = 0;
if (adjustCurrentFrame) {
currentFrame = PlaylistFrameAtFrame(activationFrame);
if (currentFrame == CurrentFrame()) {
currentFrame++;
}
}
TRACE(" state activation frame: %lld, last state activation frame: %lld, "
"NextFrame(): %lld, currentFrame: %lld, next currentFrame: %lld\n",
state->activation_frame, lastState->activation_frame, NextFrame(),
CurrentFrame(), currentFrame);
if (lastState->activation_frame >= NextFrame()) {
fStates.RemoveItem(fStates.CountItems() - 1);
TRACE("deleting last \n");
PrintState(lastState);
_NotifySeekHandledIfNecessary(lastState);
delete lastState;
} else {
}
if (adjustCurrentFrame)
state->current_frame = currentFrame;
int32 playingDirection = _PlayingDirectionFor(state);
if (playingDirection != 0) {
state->current_frame
= _NextFrameInRange(state, state->current_frame);
} else {
if (state->current_frame >= state->max_frame_count)
state->current_frame = state->max_frame_count - 1;
if (state->current_frame < 0)
state->current_frame = 0;
}
state->range_index = _RangeFrameForFrame(state, state->current_frame);
state->activation_frame = activationFrame;
fStates.AddItem(state);
PrintState(state);
TRACE("_PushState: state count: %ld\n", fStates.CountItems());
#if SUPPORT_SPEED_CHANGES
SpeedInfo* speedInfo = new SpeedInfo(*_LastSpeedInfo());
if (playingDirection == 0)
speedInfo->speed = 1.0;
else
speedInfo->speed = speedInfo->set_speed;
speedInfo->activation_frame = state->activation_frame;
_PushSpeedInfo(speedInfo);
#endif
if (playingDirection != 0 && !state->looping_enabled) {
int64 startFrame, endFrame, frameCount;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
if (playingDirection == -1)
swap(startFrame, endFrame);
if (oldStopPlayingFrame != state->activation_frame
&& state->current_frame == endFrame && frameCount > 1) {
state->current_frame = startFrame;
state->range_index
= _RangeFrameForFrame(state, state->current_frame);
}
if (playingDirection == 1) {
fStopPlayingFrame = state->activation_frame
+ frameCount - state->range_index - 1;
} else {
fStopPlayingFrame = state->activation_frame
+ state->range_index;
}
_CheckStopPlaying();
}
TRACE("PlaybackManager::_PushState() done\n");
}
being active at or before the current audio and video time. */
void
PlaybackManager::_UpdateStates()
{
int32 firstActive = _IndexForTime(fPerformanceTime);
for (int32 i = 0; i < firstActive; i++) {
PlayingState* state = _StateAt(i);
_NotifySeekHandledIfNecessary(state);
delete state;
}
if (firstActive > 0)
{
fStates.RemoveItems(0, firstActive);
TRACE("_UpdateStates: states removed: %ld, state count: %ld\n",
firstActive, fStates.CountItems());
}
_NotifySeekHandledIfNecessary(_StateAt(0));
}
The index of the first frame (0) is returned, if /frame/ is even less
than the frame at which this state becomes active. */
int32
PlaybackManager::_IndexForFrame(int64 frame) const
{
int32 index = 0;
PlayingState* state;
while (((state = _StateAt(index + 1))) && state->activation_frame <= frame)
index++;
return index;
}
The index of the first frame (0) is returned, if /time/ is even less
than the time at which this state becomes active. */
int32
PlaybackManager::_IndexForTime(bigtime_t time) const
{
return _IndexForFrame(FrameForTime(time));
}
PlaybackManager::PlayingState*
PlaybackManager::_LastState() const
{
return _StateAt(fStates.CountItems() - 1);
}
PlaybackManager::PlayingState*
PlaybackManager::_StateAt(int32 index) const
{
return (PlayingState*)fStates.ItemAt(index);
}
PlaybackManager::PlayingState*
PlaybackManager::_StateAtFrame(int64 frame) const
{
return _StateAt(_IndexForFrame(frame));
}
PlaybackManager::PlayingState*
PlaybackManager::_StateAtTime(bigtime_t time) const
{
return _StateAt(_IndexForTime(time));
}
int32
PlaybackManager::_PlayingDirectionFor(int32 playingMode)
{
int32 direction = 0;
switch (playingMode) {
case MODE_PLAYING_FORWARD:
direction = 1;
break;
case MODE_PLAYING_BACKWARD:
direction = -1;
break;
case MODE_PLAYING_PAUSED_FORWARD:
case MODE_PLAYING_PAUSED_BACKWARD:
break;
}
return direction;
}
int32
PlaybackManager::_PlayingDirectionFor(PlayingState* state)
{
return _PlayingDirectionFor(state->play_mode);
}
state.
\a startFrame is the lower and \a endFrame the upper bound of the range,
and \a frameCount the number of frames in the range; it is guaranteed to
be >= 1, even for an empty selection. */
void
PlaybackManager::_GetPlayingBoundsFor(PlayingState* state, int64& startFrame,
int64& endFrame, int64& frameCount)
{
switch (state->loop_mode) {
case LOOPING_ALL:
startFrame = 0;
endFrame = max(startFrame, state->frame_count - 1);
frameCount = endFrame - startFrame + 1;
break;
case LOOPING_RANGE:
startFrame = state->start_frame;
endFrame = state->end_frame;
frameCount = endFrame - startFrame + 1;
break;
case LOOPING_VISIBLE:
startFrame = state->first_visible_frame;
endFrame = state->last_visible_frame;
frameCount = endFrame - startFrame + 1;
break;
}
}
state. If the playing mode for the supplied state specifies a stopped
or undefined mode, the result is the state's current frame. */
int64
PlaybackManager::_PlayingStartFrameFor(PlayingState* state)
{
int64 startFrame, endFrame, frameCount, frame = 0;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
switch (_PlayingDirectionFor(state)) {
case -1:
frame = endFrame;
break;
case 1:
frame = startFrame;
break;
default:
frame = state->current_frame;
break;
}
return frame;
}
state. If the playing mode for the supplied state specifies a stopped
or undefined mode, the result is the state's current frame. */
int64
PlaybackManager::_PlayingEndFrameFor(PlayingState* state)
{
int64 startFrame, endFrame, frameCount, frame = 0;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
switch (_PlayingDirectionFor(state)) {
case -1:
frame = startFrame;
break;
case 1:
frame = endFrame;
break;
default:
frame = state->current_frame;
break;
}
return frame;
}
If the state specifies a not-playing mode, 0 is returned. The supplied
frame has to lie within the bounds of the playing range, but it doesn't
need to be contained, e.g. if the range is not contiguous. In this case
the index of the next frame within the range (in playing direction) is
returned. */
int64
PlaybackManager::_RangeFrameForFrame(PlayingState* state, int64 frame)
{
TRACE("PlaybackManager::_RangeFrameForFrame(%lld)\n", frame);
int64 startFrame, endFrame, frameCount;
int64 index = 0;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
TRACE(" start frame: %lld, end frame: %lld, frame count: %lld\n",
startFrame, endFrame, frameCount);
switch (state->loop_mode) {
case LOOPING_ALL:
case LOOPING_RANGE:
case LOOPING_VISIBLE:
index = frame - startFrame;
break;
}
TRACE("PlaybackManager::_RangeFrameForFrame() done: %lld\n", index);
return index;
}
to be in the playing range -- it is mapped into. */
int64
PlaybackManager::_FrameForRangeFrame(PlayingState* state, int64 index)
{
TRACE("PlaybackManager::_FrameForRangeFrame(%lld)\n", index);
int64 startFrame, endFrame, frameCount;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
TRACE(" frame range: %lld - %lld, count: %lld\n", startFrame, endFrame,
frameCount);
if (frameCount > 1)
index = (index % frameCount + frameCount) % frameCount;
int64 frame = startFrame;
switch (state->loop_mode) {
case LOOPING_ALL:
case LOOPING_RANGE:
case LOOPING_VISIBLE:
frame = startFrame + index;
break;
}
TRACE("PlaybackManager::_FrameForRangeFrame() done: %" PRId64 "\n", frame);
return frame;
}
the playing range for the supplied playing state. */
int64
PlaybackManager::_NextFrameInRange(PlayingState* state, int64 frame)
{
int64 newFrame = frame;
int64 startFrame, endFrame, frameCount;
_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
if (frame < startFrame || frame > endFrame)
newFrame = _PlayingStartFrameFor(state);
else {
int64 index = _RangeFrameForFrame(state, frame);
newFrame = _FrameForRangeFrame(state, index);
if (newFrame > frame && _PlayingDirectionFor(state) == -1)
newFrame = _FrameForRangeFrame(state, index - 1);
}
return newFrame;
}
void
PlaybackManager::_PushSpeedInfo(SpeedInfo* info)
{
#if SUPPORT_SPEED_CHANGES
if (info == NULL)
return;
if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
info->activation_time = TimeForFrame(info->activation_frame);
if (lastSpeed->activation_frame == info->activation_frame) {
fSpeeds.RemoveItem(lastSpeed);
delete lastSpeed;
}
}
fSpeeds.AddItem(info);
#endif
}
PlaybackManager::SpeedInfo*
PlaybackManager::_LastSpeedInfo() const
{
#if SUPPORT_SPEED_CHANGES
return (SpeedInfo*)fSpeeds.ItemAt(fSpeeds.CountItems() - 1);
#else
return NULL;
#endif
}
PlaybackManager::SpeedInfo*
PlaybackManager::_SpeedInfoAt(int32 index) const
{
#if SUPPORT_SPEED_CHANGES
return (SpeedInfo*)fSpeeds.ItemAt(index);
#else
return NULL;
#endif
}
int32
PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
{
int32 index = 0;
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info;
while (((info = _SpeedInfoAt(index + 1)))
&& info->activation_frame <= frame) {
index++;
}
#endif
return index;
}
int32
PlaybackManager::_SpeedInfoIndexForTime(bigtime_t time) const
{
int32 index = 0;
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info;
while (((info = _SpeedInfoAt(index + 1)))
&& info->activation_time <= time) {
index++;
}
#endif
return index;
}
PlaybackManager::SpeedInfo*
PlaybackManager::_SpeedInfoForFrame(int64 frame) const
{
return _SpeedInfoAt(_SpeedInfoIndexForFrame(frame));
}
PlaybackManager::SpeedInfo*
PlaybackManager::_SpeedInfoForTime(bigtime_t time) const
{
return _SpeedInfoAt(_SpeedInfoIndexForTime(time));
}
void
PlaybackManager::_UpdateSpeedInfos()
{
#if SUPPORT_SPEED_CHANGES
int32 firstActive = _SpeedInfoIndexForTime(fPerformanceTime);
for (int32 i = 0; i < firstActive; i++)
delete _SpeedInfoAt(i);
if (firstActive > 0)
fSpeeds.RemoveItems(0, firstActive);
#endif
}
audio producer both have completely processed. */
bigtime_t
PlaybackManager::_TimeForLastFrame() const
{
if (fNoAudio)
return TimeForFrame(FrameForTime(fCurrentVideoTime));
return TimeForFrame(FrameForTime(min((bigtime_t)fCurrentAudioTime,
(bigtime_t)fCurrentVideoTime)));
}
neither the video nor the audio producer have fetched data. */
bigtime_t
PlaybackManager::_TimeForNextFrame() const
{
return TimeForFrame(NextFrame());
}
void
PlaybackManager::_CheckStopPlaying()
{
if (fStopPlayingFrame > 0 && fStopPlayingFrame <= NextFrame()) {
StopPlaying();
NotifyStopFrameReached();
}
}
void
PlaybackManager::_NotifySeekHandledIfNecessary(PlayingState* state)
{
if (state->is_seek_request) {
state->is_seek_request = false;
NotifySeekHandled(state->current_frame);
}
}