diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp index ab12cfd9710c..ebe15c5fc174 100644 --- a/video/video_decoder.cpp +++ b/video/video_decoder.cpp @@ -188,6 +188,25 @@ const Graphics::Surface *VideoDecoder::decodeNextFrame() { return frame; } +bool VideoDecoder::setReverse(bool reverse) { + // Can only reverse video-only videos + if (reverse && hasAudio()) + return false; + + // Attempt to make sure all the tracks are in the requested direction + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->isReversed() != reverse) { + if (!((VideoTrack *)*it)->setReverse(reverse)) + return false; + + _needsUpdate = true; // force an update + } + } + + findNextVideoTrack(); + return true; +} + const byte *VideoDecoder::getPalette() { _dirtyPalette = false; return _palette; @@ -218,7 +237,7 @@ uint32 VideoDecoder::getTime() const { return _lastTimeChange.msecs(); if (isPaused()) - return (_playbackRate * (_pauseStartTime - _startTime)).toInt(); + return MAX((_playbackRate * (_pauseStartTime - _startTime)).toInt(), 0); if (useAudioSync()) { for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) { @@ -231,20 +250,29 @@ uint32 VideoDecoder::getTime() const { } } - return (_playbackRate * (g_system->getMillis() - _startTime)).toInt(); + return MAX((_playbackRate * (g_system->getMillis() - _startTime)).toInt(), 0); } uint32 VideoDecoder::getTimeToNextFrame() const { if (endOfVideo() || _needsUpdate || !_nextVideoTrack) return 0; - uint32 elapsedTime = getTime(); + uint32 currentTime = getTime(); uint32 nextFrameStartTime = _nextVideoTrack->getNextFrameStartTime(); - if (nextFrameStartTime <= elapsedTime) + if (_nextVideoTrack->isReversed()) { + // For reversed videos, we need to handle the time difference the opposite way. + if (nextFrameStartTime >= currentTime) + return 0; + + return currentTime - nextFrameStartTime; + } + + // Otherwise, handle it normally. + if (nextFrameStartTime <= currentTime) return 0; - return nextFrameStartTime - elapsedTime; + return nextFrameStartTime - currentTime; } bool VideoDecoder::endOfVideo() const { @@ -402,9 +430,11 @@ void VideoDecoder::setRate(const Common::Rational &rate) { Common::Rational targetRate = rate; - if (rate < 0) { - // TODO: Implement support for this + // Attempt to set the reverse + if (!setReverse(rate < 0)) { + assert(rate < 0); // We shouldn't fail for forward. warning("Cannot set custom rate to backwards"); + setReverse(false); targetRate = 1; if (_playbackRate == targetRate) diff --git a/video/video_decoder.h b/video/video_decoder.h index 5fec52cf4240..d0a6e08005e6 100644 --- a/video/video_decoder.h +++ b/video/video_decoder.h @@ -353,6 +353,17 @@ class VideoDecoder { */ void setDefaultHighColorFormat(const Graphics::PixelFormat &format) { _defaultHighColorFormat = format; } + /** + * Set the video to decode frames in reverse. + * + * By default, VideoDecoder will decode forward. + * + * @note This is used by setRate() + * @note This will not work if an audio track is present + * @param reverse true for reverse, false for forward + * @return true on success, false otherwise + */ + bool setReverse(bool reverse); ///////////////////////////////////////// // Audio Control @@ -551,6 +562,21 @@ class VideoDecoder { * should only be used by VideoDecoder::seekToFrame(). */ virtual Audio::Timestamp getFrameTime(uint frame) const; + + /** + * Set the video track to play in reverse or forward. + * + * By default, a VideoTrack must decode forward. + * + * @param reverse true for reverse, false for forward + * @return true for success, false for failure + */ + virtual bool setReverse(bool reverse) { return !reverse; } + + /** + * Is the video track set to play in reverse? + */ + virtual bool isReversed() const { return false; } }; /**