PlaybackManager:

* Make sure that the messages which trigger a performance
   time update a) don't pever pile up and b) that we don't
   still receive an event after reinitialization (would
   not have been a problem, at one point I thought it was).
 * Don't compile in support for changing the playback speed
   for the moment.
 * Better support for notifying the reaching of a seek frame,
   In _UpdateStates(), the wrong state (most often out of bounds)
   was checked to be a seek request state. Check if a seek request
   was reached in all other cases where states are removed.

Controller:
 * Simple but important simplification of the problem that
   seeked frames are reached asynchronously and with a latency:
   In TimePosition() simply report the seeked frame, if there
   are still pending seek requests. This allows a consistent
   view from the outside, i.e. after calling SetTimePosition(),
   TimePosition() will not return something different.
 * Use a more robust way to track pending seek requests. A
   new request may have been issued while not having reached
   the previous one yet.
 * Implement a notification for reaching the seek frame, but
   I didn't need it after all, may come in handy later...

MainWin:
 * Change the cursor left/right keys to support winding.
   Cursor up/down change the volume, Cmd-up/down skips
   to the previous/next playlist item, left/right do the
   winding now, as requested in ticket #2495.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38594 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2010-09-09 21:30:36 +00:00
parent bcbde9ce94
commit 0beac2ff04
9 changed files with 370 additions and 162 deletions

View File

@ -77,6 +77,7 @@ 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) {}
@ -108,10 +109,12 @@ Controller::Controller()
fAudioTrackList(4),
fVideoTrackList(2),
fPosition(0),
fCurrentFrame(0),
fDuration(0),
fVideoFrameRate(25.0),
fSeekRequested(false),
fPendingSeekRequests(0),
fSeekFrame(-1),
fGlobalSettingsListener(this),
@ -167,12 +170,7 @@ Controller::MessageReceived(BMessage* message)
int64
Controller::Duration()
{
// 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);
return _FrameDuration();
}
@ -258,11 +256,12 @@ Controller::SetTo(const PlaylistItemRef& item)
fVideoTrackSupplier = NULL;
fAudioTrackSupplier = NULL;
fCurrentFrame = 0;
fDuration = 0;
fVideoFrameRate = 25.0;
fPendingSeekRequests = 0;
fSeekFrame = -1;
fSeekRequested = false;
if (fItem.Get() == NULL)
return B_BAD_VALUE;
@ -641,7 +640,7 @@ Controller::TimePosition()
{
BAutolock _(this);
return fPosition;
return _TimePosition();
}
@ -649,8 +648,7 @@ void
Controller::SetVolume(float value)
{
// printf("Controller::SetVolume %.4f\n", value);
if (!Lock())
return;
BAutolock _(this);
value = max_c(0.0, min_c(2.0, value));
@ -663,8 +661,6 @@ Controller::SetVolume(float value)
_NotifyVolumeChanged(fVolume);
}
Unlock();
}
void
@ -684,8 +680,7 @@ Controller::VolumeDown()
void
Controller::ToggleMute()
{
if (!Lock())
return;
BAutolock _(this);
fMuted = !fMuted;
@ -695,8 +690,6 @@ Controller::ToggleMute()
fAudioSupplier->SetVolume(fVolume);
_NotifyMutedChanged(fMuted);
Unlock();
}
@ -709,40 +702,50 @@ Controller::Volume()
}
void
int64
Controller::SetPosition(float value)
{
BAutolock _(this);
SetFramePosition(Duration() * value);
return SetFramePosition(_FrameDuration() * value);
}
void
Controller::SetFramePosition(int32 value)
int64
Controller::SetFramePosition(int64 value)
{
BAutolock _(this);
int64 seekFrame = max_c(0, min_c(Duration(), value));
int64 currentFrame = CurrentFrame();
fPendingSeekRequests++;
fSeekFrame = max_c(0, min_c(_FrameDuration(), value));
// Snap to video keyframe, since that will be the fastest
// to display and seeking will feel more snappy.
if (Duration() > 240 && fVideoTrackSupplier != NULL)
fVideoTrackSupplier->FindKeyFrameForFrame(&seekFrame);
if (seekFrame != currentFrame) {
fSeekFrame = seekFrame;
fSeekRequested = true;
SetCurrentFrame(seekFrame);
}
fVideoTrackSupplier->FindKeyFrameForFrame(&fSeekFrame);
int64 currentFrame = CurrentFrame();
//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(fSeekFrame);
return currentFrame;
}
void
int64
Controller::SetTimePosition(bigtime_t value)
{
BAutolock _(this);
SetPosition((float)value / TimeDuration());
return SetPosition((float)value / TimeDuration());
}
@ -924,6 +927,39 @@ Controller::_PlaybackState(int32 playingMode) const
}
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 = fSeekFrame;
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
@ -1023,6 +1059,18 @@ Controller::_NotifyPositionChanged(float position) const
}
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
{
@ -1084,20 +1132,8 @@ Controller::NotifyFPSChanged(float fps) const
void
Controller::NotifyCurrentFrameChanged(int64 frame) const
{
// check if we are still waiting to reach the seekframe,
// don't pass the event on to the listeners in that case
if (fSeekRequested && fSeekFrame != frame)
return;
fSeekFrame = -1;
fSeekRequested = false;
float position = 0.0;
double duration = (double)fDuration * fVideoFrameRate / 1000000.0;
if (duration > 0)
position = (float)frame / duration;
fPosition = (bigtime_t)(position * fDuration + 0.5);
_NotifyPositionChanged(position);
fCurrentFrame = frame;
_NotifyPositionChanged((float)_TimePosition() / fDuration);
}
@ -1124,9 +1160,15 @@ Controller::NotifyStopFrameReached() const
void
Controller::NotifySeekHandled() const
Controller::NotifySeekHandled(int64 seekedFrame) const
{
fSeekRequested = false;
fSeekFrame = -1;
if (fPendingSeekRequests == 0)
return;
fPendingSeekRequests--;
if (fPendingSeekRequests == 0)
fSeekFrame = -1;
_NotifySeekHandled(seekedFrame);
}

View File

@ -66,6 +66,7 @@ public:
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void SeekHandled(int64 seekFrame);
virtual void VolumeChanged(float volume);
virtual void MutedChanged(bool muted);
};
@ -117,9 +118,10 @@ public:
void VolumeUp();
void VolumeDown();
void ToggleMute();
void SetPosition(float value);
void SetFramePosition(int32 frame);
void SetTimePosition(bigtime_t position);
int64 SetPosition(float value);
int64 SetFramePosition(int64 frame);
int64 SetTimePosition(bigtime_t position);
bool HasFile();
status_t GetFileFormatInfo(
@ -145,6 +147,8 @@ private:
void _AdoptGlobalSettings();
uint32 _PlaybackState(int32 playingMode) const;
int64 _FrameDuration() const;
bigtime_t _TimePosition() const;
void _NotifyFileChanged(PlaylistItem* item,
status_t result) const;
@ -157,6 +161,7 @@ private:
void _NotifyPlaybackStateChanged(uint32 state) const;
void _NotifyPositionChanged(float position) const;
void _NotifySeekHandled(int64 seekFrame) const;
void _NotifyVolumeChanged(float volume) const;
void _NotifyMutedChanged(bool muted) const;
@ -172,7 +177,7 @@ private:
virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const;
virtual void NotifySeekHandled() const;
virtual void NotifySeekHandled(int64 seekedFrame) const;
VideoView* fVideoView;
@ -191,12 +196,12 @@ private:
BList fAudioTrackList;
BList fVideoTrackList;
mutable bigtime_t fPosition;
mutable int64 fCurrentFrame;
bigtime_t fDuration;
float fVideoFrameRate;
mutable bool fSeekRequested;
mutable int32 fSeekFrame;
mutable int32 fPendingSeekRequests;
mutable int64 fSeekFrame;
ListenerAdapter fGlobalSettingsListener;

View File

@ -133,6 +133,19 @@ ControllerObserver::PositionChanged(float position)
}
void
ControllerObserver::SeekHandled(int64 seekFrame)
{
if (!(fObserveFlags & OBSERVE_POSITION_CHANGES))
return;
BMessage message(MSG_CONTROLLER_SEEK_HANDLED);
message.AddInt64("seek frame", seekFrame);
DeliverMessage(message);
}
void
ControllerObserver::VolumeChanged(float volume)
{

View File

@ -25,6 +25,7 @@ enum {
MSG_CONTROLLER_PLAYBACK_STATE_CHANGED = 'cnps',
MSG_CONTROLLER_POSITION_CHANGED = 'cnpc',
MSG_CONTROLLER_SEEK_HANDLED = 'cnsh',
MSG_CONTROLLER_VOLUME_CHANGED = 'cnvc',
MSG_CONTROLLER_MUTED_CHANGED = 'cnmc'
};
@ -60,6 +61,7 @@ public:
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void SeekHandled(int64 seekFrame);
virtual void VolumeChanged(float volume);
virtual void MutedChanged(bool muted);

View File

@ -81,6 +81,7 @@ enum {
M_VOLUME_DOWN,
M_SKIP_NEXT,
M_SKIP_PREV,
M_WIND,
// The common display aspect ratios
M_ASPECT_SAME_AS_SOURCE,
@ -179,7 +180,8 @@ MainWin::MainWin(bool isFirstWindow, BMessage* message)
fMouseMoveDist(0),
fGlobalSettingsListener(this),
fInitialSeekPosition(0)
fInitialSeekPosition(0),
fAllowWinding(true)
{
// Handle window position and size depending on whether this is the
// first window or not. Use the window size from the window that was
@ -675,9 +677,13 @@ MainWin::MessageReceived(BMessage* msg)
if (msg->FindFloat("position", &position) == B_OK) {
fControls->SetPosition(position, fController->TimePosition(),
fController->TimeDuration());
fAllowWinding = true;
}
break;
}
case MSG_CONTROLLER_SEEK_HANDLED:
break;
case MSG_CONTROLLER_VOLUME_CHANGED:
{
float volume;
@ -782,6 +788,32 @@ MainWin::MessageReceived(BMessage* msg)
fControls->SkipBackward();
break;
case M_WIND:
{
if (!fAllowWinding)
break;
bigtime_t howMuch;
if (msg->FindInt64("how much", &howMuch) != B_OK)
break;
if (fController->Lock()) {
bigtime_t seekTime = fController->TimePosition() + howMuch;
if (seekTime < 0) {
fInitialSeekPosition = seekTime;
PostMessage(M_SKIP_PREV);
} else if (seekTime > fController->TimeDuration()) {
fInitialSeekPosition = 0;
PostMessage(M_SKIP_NEXT);
} else
fController->SetTimePosition(seekTime);
fController->Unlock();
fAllowWinding = false;
}
break;
}
case M_VOLUME_UP:
fController->VolumeUp();
break;
@ -1248,6 +1280,10 @@ MainWin::_PlaylistItemOpened(const PlaylistItemRef& item, status_t result)
fHasAudio = fController->AudioTrackCount() != 0;
SetTitle(item->Name().String());
if (fInitialSeekPosition < 0) {
fInitialSeekPosition
= fController->TimeDuration() + fInitialSeekPosition;
}
fController->SetTimePosition(fInitialSeekPosition);
fInitialSeekPosition = 0;
}
@ -1401,7 +1437,7 @@ MainWin::_CreateMenu()
new BMessage(M_TOGGLE_NO_INTERFACE), 'B');
fSettingsMenu->AddItem(fNoInterfaceMenuItem);
fSettingsMenu->AddItem(new BMenuItem("Always on top",
new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T'));
new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'A'));
fSettingsMenu->AddSeparatorItem();
item = new BMenuItem("Settings"B_UTF8_ELLIPSIS,
new BMessage(M_SETTINGS), 'S');
@ -1872,8 +1908,8 @@ MainWin::_KeyDown(BMessage* msg)
uint32 rawChar = msg->FindInt32("raw_char");
uint32 modifier = msg->FindInt32("modifiers");
printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar,
modifier);
// printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar,
// modifier);
// ignore the system modifier namespace
if ((modifier & (B_CONTROL_KEY | B_COMMAND_KEY))
@ -1923,16 +1959,28 @@ MainWin::_KeyDown(BMessage* msg)
case B_RIGHT_ARROW:
if ((modifier & B_COMMAND_KEY) != 0)
PostMessage(M_VOLUME_UP);
else
PostMessage(M_SKIP_NEXT);
else if (fAllowWinding) {
BMessage windMessage(M_WIND);
if ((modifier & B_SHIFT_KEY) != 0)
windMessage.AddInt64("how much", 30000000LL);
else
windMessage.AddInt64("how much", 5000000LL);
PostMessage(&windMessage);
}
return true;
case B_LEFT_ARROW:
if ((modifier & B_COMMAND_KEY) != 0)
PostMessage(M_VOLUME_DOWN);
else
PostMessage(M_SKIP_PREV);
else if (fAllowWinding) {
BMessage windMessage(M_WIND);
if ((modifier & B_SHIFT_KEY) != 0)
windMessage.AddInt64("how much", -30000000LL);
else
windMessage.AddInt64("how much", -5000000LL);
PostMessage(&windMessage);
}
return true;
case B_PAGE_UP:
@ -1942,6 +1990,19 @@ MainWin::_KeyDown(BMessage* msg)
case B_PAGE_DOWN:
PostMessage(M_SKIP_PREV);
return true;
case B_DELETE:
case 'd': // d for delete
case 't': // t for Trash
if ((modifiers() & B_COMMAND_KEY) != 0) {
BAutolock _(fPlaylist);
BMessage removeMessage(M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH);
removeMessage.AddInt32("playlist index",
fPlaylist->CurrentItemIndex());
fPlaylistWindow->PostMessage(&removeMessage);
return true;
}
break;
}
switch (key) {
@ -1976,19 +2037,6 @@ MainWin::_KeyDown(BMessage* msg)
case 0x48: // numeric keypad left arrow
PostMessage(M_SKIP_PREV);
return true;
case 0x34: // delete button
case 0x3e: // d for delete
case 0x2b: // t for Trash
if ((modifiers() & B_COMMAND_KEY) != 0) {
BAutolock _(fPlaylist);
BMessage removeMessage(M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH);
removeMessage.AddInt32("playlist index",
fPlaylist->CurrentItemIndex());
fPlaylistWindow->PostMessage(&removeMessage);
return true;
}
break;
}
return false;

View File

@ -189,6 +189,7 @@ private:
bool fLoopMovies;
bool fLoopSounds;
bigtime_t fInitialSeekPosition;
bool fAllowWinding;
static int sNoVideoWidth;
};

View File

@ -83,7 +83,8 @@ NodeManager::Init(BRect videoBounds, float videoFrameRate,
float speed, uint32 enabledNodes, bool useOverlays)
{
// init base class
PlaybackManager::Init(videoFrameRate, loopingMode, loopingEnabled, speed);
PlaybackManager::Init(videoFrameRate, true, loopingMode, loopingEnabled,
speed);
// get some objects from a derived class
if (fVideoTarget == NULL)
@ -142,15 +143,18 @@ NodeManager::FormatChanged(BRect videoBounds, float videoFrameRate,
// 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!
PlaybackManager::Init(videoFrameRate, false, LoopMode(),
IsLoopingEnabled(), Speed(), MODE_PLAYING_PAUSED_FORWARD,
CurrentFrame());
return B_OK;
}
PlaybackManager::Init(videoFrameRate, LoopMode(), IsLoopingEnabled(),
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
_StopNodes();
_TearDownNodes();
PlaybackManager::Init(videoFrameRate, true, LoopMode(), IsLoopingEnabled(),
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
SetVideoBounds(videoBounds);
status_t ret = _SetUpNodes(preferredVideoFormat, enabledNodes,

View File

@ -36,6 +36,9 @@ tdebug(const char* str)
}
#define SUPPORT_SPEED_CHANGES 0
struct PlaybackManager::PlayingState {
int64 start_frame;
int64 end_frame;
@ -75,6 +78,7 @@ struct PlaybackManager::PlayingState {
};
#if SUPPORT_SPEED_CHANGES
struct PlaybackManager::SpeedInfo {
int64 activation_frame; // absolute video frame
bigtime_t activation_time; // performance time
@ -82,22 +86,25 @@ struct PlaybackManager::SpeedInfo {
// is 1.0 if not playing
float set_speed; // speed set by the user
};
#endif
// #pragma mark - PlaybackManager
PlaybackManager::PlaybackManager()
: BLooper("playback manager"),
fStates(10),
fSpeeds(10),
fCurrentAudioTime(0),
fCurrentVideoTime(0),
fPerformanceTime(0),
fFrameRate(1.0),
fStopPlayingFrame(-1),
fListeners(),
fNoAudio(false)
:
BLooper("playback manager"),
fStates(10),
fSpeeds(10),
fCurrentAudioTime(0),
fCurrentVideoTime(0),
fPerformanceTime(0),
fPerformanceTimeEvent(NULL),
fFrameRate(1.0),
fStopPlayingFrame(-1),
fListeners(),
fNoAudio(false)
{
Run();
}
@ -110,19 +117,23 @@ PlaybackManager::~PlaybackManager()
void
PlaybackManager::Init(float frameRate, int32 loopingMode, bool loopingEnabled,
float playbackSpeed, int32 playMode, int32 currentFrame)
PlaybackManager::Init(float frameRate, bool initPerformanceTimes,
int32 loopingMode, bool loopingEnabled, float playbackSpeed,
int32 playMode, int32 currentFrame)
{
// cleanup first
Cleanup();
// set the new frame rate
fFrameRate = frameRate;
fCurrentAudioTime = 0;
fCurrentVideoTime = 0;
fPerformanceTime = 0;
if (initPerformanceTimes) {
fCurrentAudioTime = 0;
fCurrentVideoTime = 0;
fPerformanceTime = 0;
}
fStopPlayingFrame = -1;
#if SUPPORT_SPEED_CHANGES
// set up the initial speed
SpeedInfo* speed = new SpeedInfo;
speed->activation_frame = 0;
@ -130,6 +141,7 @@ PlaybackManager::Init(float frameRate, int32 loopingMode, bool loopingEnabled,
speed->speed = playbackSpeed;
speed->set_speed = playbackSpeed;
_PushSpeedInfo(speed);
#endif
// set up the initial state
PlayingState* state = new PlayingState;
@ -165,15 +177,20 @@ PlaybackManager::Init(float frameRate, int32 loopingMode, bool loopingEnabled,
void
PlaybackManager::Cleanup()
{
if (EventQueue::Default().RemoveEvent(fPerformanceTimeEvent))
delete fPerformanceTimeEvent;
fPerformanceTimeEvent = NULL;
// delete states
for (int32 i = 0; PlayingState* state = _StateAt(i); i++)
delete state;
fStates.MakeEmpty();
#if SUPPORT_SPEED_CHANGES
// delete speed infos
for (int32 i = 0; SpeedInfo* speed = _SpeedInfoAt(i); i++)
delete speed;
fSpeeds.MakeEmpty();
#endif
}
@ -182,9 +199,25 @@ PlaybackManager::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_EVENT:
SetPerformanceTime(TimeForRealTime(system_time()));
//TRACE("MSG_EVENT: rt: %lld, pt: %lld\n", system_time(), fPerformanceTime);
{
if (fPerformanceTimeEvent == NULL) {
// Stale event message. There is a natural race
// condition when removing the event from the queue,
// it may have already fired, but we have not processed
// the message yet. Simply ignore the event.
break;
}
// bigtime_t eventTime;
// message->FindInt64("time", &eventTime);
bigtime_t now = system_time();
fPerformanceTimeEvent = NULL;
SetPerformanceTime(TimeForRealTime(now));
//TRACE("MSG_EVENT: rt: %lld, pt: %lld\n", now, fPerformanceTime);
//printf("MSG_EVENT: et: %lld, rt: %lld, pt: %lld\n", eventTime, now, fPerformanceTime);
break;
}
case MSG_PLAYBACK_FORCE_UPDATE:
{
@ -333,9 +366,13 @@ PlaybackManager::CurrentFrame() const
float
PlaybackManager::Speed() const
{
#if SUPPORT_SPEED_CHANGES
if (!_LastState())
return 1.0;
return _LastSpeedInfo()->set_speed;
#else
return 1.0;
#endif
}
@ -384,8 +421,8 @@ PlaybackManager::DurationChanged()
void
PlaybackManager::SetCurrentFrame(int64 frame)
{
if (_LastState()->current_frame == frame) {
NotifySeekHandled();
if (CurrentFrame() == frame) {
NotifySeekHandled(frame);
return;
}
PlayingState* newState = new PlayingState(*_LastState());
@ -465,6 +502,7 @@ PlaybackManager::SetLoopingEnabled(bool enabled, bool continuePlaying)
void
PlaybackManager::SetSpeed(float speed)
{
#if SUPPORT_SPEED_CHANGES
SpeedInfo* lastSpeed = _LastSpeedInfo();
if (speed != lastSpeed->set_speed) {
SpeedInfo* info = new SpeedInfo(*lastSpeed);
@ -476,6 +514,7 @@ PlaybackManager::SetSpeed(float speed)
info->speed = 1.0;
_PushSpeedInfo(info);
}
#endif
}
@ -676,10 +715,12 @@ PlaybackManager::NextChangeFrame(int64 startFrame, int64 endFrame) const
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;
}
@ -693,10 +734,12 @@ PlaybackManager::NextChangeTime(bigtime_t startTime, bigtime_t endTime) const
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;
}
@ -751,7 +794,9 @@ PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
// be greater than necessary, but that doesn't harm.
int64 startFrame = FrameForTime(startTime);
int64 endFrame = FrameForTime(endTime) + 1;
SpeedInfo* info = _SpeedInfoForFrame(startFrame);
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info = _SpeedInfoForFrame(startFrame)->speed;
#endif
// Get the Playlist frame interval that belongs to the frame interval.
int64 xStartFrame;
int64 xEndFrame;
@ -769,33 +814,37 @@ PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
// forward
case 1:
{
// xStartTime = PlaylistTimeForFrame(xStartFrame)
// + startTime - TimeForFrame(startFrame);
// xEndTime = xStartTime + intervalLength;
#if SUPPORT_SPEED_CHANGES
// TODO: The current method does not handle the times the same way.
// It may happen, that for the same performance time different
// Playlist times (within a frame) are returned when passing it
// one time as a start time and another time as an end time.
xStartTime = PlaylistTimeForFrame(xStartFrame)
+ bigtime_t(double(startTime - TimeForFrame(startFrame))
* info->speed);
* info->speed);
xEndTime = xStartTime
+ bigtime_t((double)intervalLength * info->speed);
+ bigtime_t((double)intervalLength * info->speed);
#else
xStartTime = PlaylistTimeForFrame(xStartFrame)
+ startTime - TimeForFrame(startFrame);
xEndTime = xStartTime + intervalLength;
#endif
break;
}
// backward
case -1:
{
// xEndTime = PlaylistTimeForFrame(xEndFrame)
// - startTime + TimeForFrame(startFrame);
// xStartTime = xEndTime - intervalLength;
#if SUPPORT_SPEED_CHANGES
xEndTime = PlaylistTimeForFrame(xEndFrame)
- bigtime_t(double(startTime - TimeForFrame(startFrame))
* info->speed);
* info->speed);
xStartTime = xEndTime
- bigtime_t((double)intervalLength * info->speed);
- bigtime_t((double)intervalLength * info->speed);
#else
xEndTime = PlaylistTimeForFrame(xEndFrame)
- startTime + TimeForFrame(startFrame);
xStartTime = xEndTime - intervalLength;
#endif
break;
}
// not playing
@ -805,7 +854,11 @@ PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
xEndTime = xStartTime;
break;
}
#if SUPPORT_SPEED_CHANGES
playingSpeed = (float)playingDirection * info->speed;
#else
playingSpeed = (float)playingDirection;
#endif
}
@ -815,11 +868,10 @@ int64
PlaybackManager::FrameForTime(bigtime_t time) const
{
//TRACE("PlaybackManager::FrameForTime(%lld)\n", time);
// return (int64)((double)time * (double)fFrameRate / 1000000.0);
// In order to avoid problems caused by rounding errors, we check
// if for the resulting frame holds
// TimeForFrame(frame) <= time < TimeForFrame(frame + 1).
// int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info = _SpeedInfoForTime(time);
if (!info) {
fprintf(stderr, "PlaybackManager::FrameForTime() - no SpeedInfo!\n");
@ -828,6 +880,10 @@ if (!info) {
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)
@ -843,19 +899,18 @@ if (!info) {
bigtime_t
PlaybackManager::TimeForFrame(int64 frame) const
{
// return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
#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;
bigtime_t result = (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
/ ((double)fFrameRate * info->speed)) + info->activation_time;
//fprintf(stderr, "PlaybackManager::TimeForFrame(%lld): %lld\n", frame, result);
return result;
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
}
@ -892,11 +947,13 @@ PlaybackManager::SetCurrentAudioTime(bigtime_t time)
TRACE("PlaybackManager::SetCurrentAudioTime(%lld)\n", time);
bigtime_t lastFrameTime = _TimeForLastFrame();
fCurrentAudioTime = time;
// _UpdateStates();
bigtime_t newLastFrameTime = _TimeForLastFrame();
if (lastFrameTime != newLastFrameTime) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
if (fPerformanceTimeEvent == NULL) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
fPerformanceTimeEvent = new MessageEvent(eventTime, this);
EventQueue::Default().AddEvent(fPerformanceTimeEvent);
}
_CheckStopPlaying();
}
}
@ -917,11 +974,13 @@ PlaybackManager::SetCurrentVideoTime(bigtime_t time)
TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
bigtime_t lastFrameTime = _TimeForLastFrame();
fCurrentVideoTime = time;
// _UpdateStates();
bigtime_t newLastFrameTime = _TimeForLastFrame();
if (lastFrameTime != newLastFrameTime) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
if (fPerformanceTimeEvent == NULL) {
bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
fPerformanceTimeEvent = new MessageEvent(eventTime, this);
EventQueue::Default().AddEvent(fPerformanceTimeEvent);
}
_CheckStopPlaying();
}
}
@ -931,11 +990,11 @@ TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
void
PlaybackManager::SetPerformanceFrame(int64 frame)
{
SetPerformanceFrame(TimeForFrame(frame));
SetPerformanceTime(TimeForFrame(frame));
}
/*! Similar to SetPerformanceTime() just with a time instead of a frame
/*! Similar to SetPerformanceFrame() just with a time instead of a frame
argument. */
void
PlaybackManager::SetPerformanceTime(bigtime_t time)
@ -1089,7 +1148,7 @@ PlaybackManager::NotifyStopFrameReached() const
void
PlaybackManager::NotifySeekHandled() const
PlaybackManager::NotifySeekHandled(int64 frame) const
{
// not currently implemented in PlaybackListener interface
}
@ -1175,6 +1234,7 @@ CurrentFrame(), currentFrame);
fStates.RemoveItem(fStates.CountItems() - 1);
TRACE("deleting last \n");
PrintState(lastState);
_NotifySeekHandledIfNecessary(lastState);
delete lastState;
} else {
// it is -- keep it
@ -1199,6 +1259,7 @@ PrintState(lastState);
fStates.AddItem(state);
PrintState(state);
TRACE("_PushState: state count: %ld\n", fStates.CountItems());
#if SUPPORT_SPEED_CHANGES
// push a new speed info
SpeedInfo* speedInfo = new SpeedInfo(*_LastSpeedInfo());
if (playingDirection == 0)
@ -1207,6 +1268,7 @@ TRACE("_PushState: state count: %ld\n", fStates.CountItems());
speedInfo->speed = speedInfo->set_speed;
speedInfo->activation_frame = state->activation_frame;
_PushSpeedInfo(speedInfo);
#endif
// If the new state is a playing state and looping is turned off,
// determine when playing shall stop.
if (playingDirection != 0 && !state->looping_enabled) {
@ -1252,19 +1314,18 @@ PlaybackManager::_UpdateStates()
// Performance time should always be the least one.
int32 firstActive = _IndexForTime(fPerformanceTime);
//TRACE("firstActive: %ld, numStates: %ld\n", firstActive, fStates.CountItems());
for (int32 i = 0; i < firstActive; i++)
delete _StateAt(i);
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());
}
PlayingState* currentState = _StateAt(firstActive);
if (currentState != NULL && currentState->is_seek_request) {
currentState->is_seek_request = false;
NotifySeekHandled();
}
_NotifySeekHandledIfNecessary(_StateAt(0));
}
@ -1476,7 +1537,7 @@ frameCount);
index = (index % frameCount + frameCount) % frameCount;
// get the frame for the index
int32 frame = startFrame;
int64 frame = startFrame;
switch (state->loop_mode) {
case LOOPING_ALL:
case LOOPING_RANGE:
@ -1512,38 +1573,48 @@ PlaybackManager::_NextFrameInRange(PlayingState* state, int64 frame)
void
PlaybackManager::_PushSpeedInfo(SpeedInfo* info)
{
if (info) {
// check the last state
if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
// set the activation time
info->activation_time = TimeForFrame(info->activation_frame);
// Remove the last state, if it won't be activated.
if (lastSpeed->activation_frame == info->activation_frame) {
#if SUPPORT_SPEED_CHANGES
if (info == NULL)
return;
// check the last state
if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
// set the activation time
info->activation_time = TimeForFrame(info->activation_frame);
// Remove the last state, if it won't be activated.
if (lastSpeed->activation_frame == info->activation_frame) {
//fprintf(stderr, " replacing last speed info\n");
fSpeeds.RemoveItem(lastSpeed);
delete lastSpeed;
}
fSpeeds.RemoveItem(lastSpeed);
delete lastSpeed;
}
fSpeeds.AddItem(info);
}
fSpeeds.AddItem(info);
//fprintf(stderr, " speed info pushed: activation frame: %lld, "
//"activation time: %lld, speed: %f\n", info->activation_frame,
//info->activation_time, info->speed);
// ...
}
// ...
#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
}
@ -1551,6 +1622,7 @@ int32
PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
{
int32 index = 0;
#if SUPPORT_SPEED_CHANGES
SpeedInfo* info;
while (((info = _SpeedInfoAt(index + 1)))
&& info->activation_frame <= frame) {
@ -1558,6 +1630,7 @@ PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
}
//fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForFrame(%lld): %ld\n",
//frame, index);
#endif
return index;
}
@ -1566,6 +1639,7 @@ 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) {
@ -1573,6 +1647,7 @@ PlaybackManager::_SpeedInfoIndexForTime(bigtime_t time) const
}
//fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForTime(%lld): %ld\n",
//time, index);
#endif
return index;
}
@ -1594,12 +1669,14 @@ PlaybackManager::_SpeedInfoForTime(bigtime_t time) const
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);
//fprintf(stderr, " speed infos 0 - %ld removed\n", firstActive);
#endif
}
@ -1635,3 +1712,12 @@ PlaybackManager::_CheckStopPlaying()
}
}
void
PlaybackManager::_NotifySeekHandledIfNecessary(PlayingState* state)
{
if (state->is_seek_request) {
state->is_seek_request = false;
NotifySeekHandled(state->current_frame);
}
}

View File

@ -17,6 +17,7 @@
#include <Rect.h>
class MessageEvent;
class PlaybackListener;
@ -52,11 +53,13 @@ public:
virtual ~PlaybackManager();
void Init(float frameRate,
int32 loopingMode = LOOPING_ALL,
bool loopingEnabled = true,
float speed = 1.0,
int32 playMode = MODE_PLAYING_PAUSED_FORWARD,
int32 currentFrame = 0);
bool initPerformanceTimes,
int32 loopingMode = LOOPING_ALL,
bool loopingEnabled = true,
float speed = 1.0,
int32 playMode
= MODE_PLAYING_PAUSED_FORWARD,
int32 currentFrame = 0);
void Cleanup();
// BHandler interface
@ -132,7 +135,6 @@ public:
int64& xStartFrame, int64& xEndFrame,
int32& playingDirection) const;
// PlaybackManagerInterface
virtual void GetPlaylistTimeInterval(
bigtime_t startTime, bigtime_t& endTime,
bigtime_t& xStartTime, bigtime_t& xEndTime,
@ -169,7 +171,7 @@ public:
virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const;
virtual void NotifySeekHandled() const;
virtual void NotifySeekHandled(int64 seekedFrame) const;
// debugging
void PrintState(PlayingState* state);
@ -215,21 +217,26 @@ public:
bigtime_t _TimeForLastFrame() const;
bigtime_t _TimeForNextFrame() const;
void _NotifySeekHandledIfNecessary(
PlayingState* state);
void _CheckStopPlaying();
private:
private:
BList fStates;
BList fSpeeds;
volatile bigtime_t fCurrentAudioTime;
volatile bigtime_t fCurrentVideoTime;
volatile bigtime_t fPerformanceTime;
MessageEvent* fPerformanceTimeEvent;
volatile float fFrameRate; // video frame rate
volatile bigtime_t fStopPlayingFrame; // frame at which playing
// shall be stopped,
// disabled: -1
BList fListeners;
protected:
protected:
bool fNoAudio;
};