diff options
Diffstat (limited to 'engines/titanic/support/avi_surface.cpp')
-rw-r--r-- | engines/titanic/support/avi_surface.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/engines/titanic/support/avi_surface.cpp b/engines/titanic/support/avi_surface.cpp new file mode 100644 index 0000000000..c37bd83616 --- /dev/null +++ b/engines/titanic/support/avi_surface.cpp @@ -0,0 +1,361 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "titanic/support/avi_surface.h" +#include "titanic/support/screen_manager.h" +#include "titanic/support/video_surface.h" +#include "common/system.h" +#include "graphics/pixelformat.h" +#include "video/avi_decoder.h" + +namespace Titanic { + +Video::AVIDecoder::AVIVideoTrack &AVIDecoder::getVideoTrack() { + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + return *static_cast<AVIVideoTrack *>(*it); + + error("Could not find video track"); +} + +/** + * Track filter for AVIDecoder that filters out any secondary video track + */ +static bool primaryTrackSelect(bool isVideo, int trackCounter) { + return !isVideo || trackCounter == 0; +} + +/** + * Track filter for AVIDecoder that only accepts the secondary video track + * for a video, if present + */ +static bool secondaryTrackSelect(bool isVideo, int trackCounter) { + return isVideo && trackCounter > 0; +} + +AVISurface::AVISurface(const CResourceKey &key) { + _videoSurface = nullptr; + _streamCount = 0; + _movieFrameSurface[0] = _movieFrameSurface[1] = nullptr; + + // Reset current frame. We need to keep track of frames separately from the decoders, + // since it needs to be able to go beyond the frame count or to negative to allow + // correct detection of when range playbacks have finished + _currentFrame = -1; + _isReversed = false; + + // Create a decoder for the audio (if any) and primary video track + _decoders[0] = new AVIDecoder(Audio::Mixer::kPlainSoundType, primaryTrackSelect); + if (!_decoders[0]->loadFile(key.getString())) + error("Could not open video - %s", key.getString().c_str()); + + _streamCount = 1; + + // Create a decoder for any secondary video track + AVIDecoder *decoder2 = new AVIDecoder(Audio::Mixer::kPlainSoundType, secondaryTrackSelect); + if (decoder2->loadFile(key.getString())) { + _decoders[1] = decoder2; + ++_streamCount; + } else { + delete decoder2; + _decoders[1] = nullptr; + } +} + +AVISurface::~AVISurface() { + if (_videoSurface) + _videoSurface->_transBlitFlag = false; + delete _movieFrameSurface[0]; + delete _movieFrameSurface[1]; + delete _decoders[0]; + delete _decoders[1]; +} + +bool AVISurface::play(uint flags, CGameObject *obj) { + if (flags & MOVIE_REVERSE) + return play(_decoders[0]->getFrameCount() - 1, 0, flags, obj); + else + return play(0, _decoders[0]->getFrameCount() - 1, flags, obj); +} + +bool AVISurface::play(int startFrame, int endFrame, uint flags, CGameObject *obj) { + if (flags & MOVIE_STOP_PREVIOUS) + stop(); + + return play(startFrame, endFrame, -1, flags, obj); +} + +bool AVISurface::play(int startFrame, int endFrame, int initialFrame, uint flags, CGameObject *obj) { + CMovieRangeInfo *info = new CMovieRangeInfo(); + info->_startFrame = startFrame; + info->_endFrame = endFrame; + info->_isReversed = endFrame < startFrame; + info->_initialFrame = 0; + info->_isRepeat = flags & MOVIE_REPEAT; + + if (obj) { + CMovieEvent *me = new CMovieEvent(); + me->_type = MET_MOVIE_END; + me->_startFrame = startFrame; + me->_endFrame = endFrame; + me->_initialFrame = 0; + me->_gameObject = obj; + + info->addEvent(me); + } + + _movieRangeInfo.push_back(info); + + if (_movieRangeInfo.size() == 1) { + // First play call, so start the movie playing + setReversed(info->_isReversed); + return startAtFrame(initialFrame); + } else { + return true; + } +} + +void AVISurface::stop() { + _decoders[0]->stop(); + if (_decoders[1]) + _decoders[1]->stop(); + + _movieRangeInfo.destroyContents(); +} + +bool AVISurface::startAtFrame(int frameNumber) { + if (isPlaying()) + // If it's already playing, then don't allow it + return false; + + if (frameNumber == -1) + // Default to starting frame of first movie range + frameNumber = _movieRangeInfo.front()->_startFrame; + + // Get the initial frame + seekToFrame(frameNumber); + renderFrame(); + + // Start the playback + _decoders[0]->start(); + if (_decoders[1]) + _decoders[1]->start(); + + return true; +} + +void AVISurface::seekToFrame(uint frameNumber) { + if ((int)frameNumber != getFrame()) { + _decoders[0]->seekToFrame(frameNumber); + if (_decoders[1]) + _decoders[1]->seekToFrame(frameNumber); + + _currentFrame = (int)frameNumber; + } + + renderFrame(); +} + +void AVISurface::setReversed(bool isReversed) { + _decoders[0]->setReverse(isReversed); + if (_decoders[1]) + _decoders[1]->setReverse(isReversed); + + _isReversed = isReversed; +} + +bool AVISurface::handleEvents(CMovieEventList &events) { + if (!isPlaying()) + return true; + + CMovieRangeInfo *info = _movieRangeInfo.front(); + _currentFrame += _isReversed ? -1 : 1; + + int newFrame = _currentFrame; + if ((info->_isReversed && newFrame <= info->_endFrame) || + (!info->_isReversed && newFrame >= info->_endFrame)) { + if (info->_isRepeat) { + newFrame = info->_startFrame; + } else { + info->getMovieEnd(events); + _movieRangeInfo.remove(info); + delete info; + + if (_movieRangeInfo.empty()) { + // NO more ranges, so stop playback + stop(); + } else { + // Not empty, so move onto new first one + info = _movieRangeInfo.front(); + newFrame = info->_startFrame; + } + } + } + + if (isPlaying()) { + if (newFrame != getFrame()) { + // The frame has been changed, so move to new position + setReversed(info->_isReversed); + seekToFrame(newFrame); + } + + // Get any events for the given position + info->getMovieFrame(events, newFrame); + return renderFrame(); + } else { + return false; + } +} + +void AVISurface::setVideoSurface(CVideoSurface *surface) { + _videoSurface = surface; + + // Handling for secondary video stream + if (_decoders[1]) { + const Common::String &streamName = _decoders[1]->getVideoTrack().getName(); + + if (streamName == "mask0") { + _videoSurface->_transparencyMode = TRANS_MASK0; + } else if (streamName == "mask255") { + _videoSurface->_transparencyMode = TRANS_MASK255; + } else if (streamName == "alpha0") { + _videoSurface->_transparencyMode = TRANS_ALPHA0; + } else if (streamName == "alpha255") { + _videoSurface->_transparencyMode = TRANS_ALPHA255; + } + } + + setupDecompressor(); +} + +void AVISurface::setupDecompressor() { + for (int idx = 0; idx < 2; ++idx) { + if (!_decoders[idx]) + continue; + AVIDecoder &decoder = *_decoders[idx]; + + // Setup frame surface + _movieFrameSurface[idx] = new Graphics::ManagedSurface(decoder.getWidth(), decoder.getHeight(), + g_system->getScreenFormat()); + + // TODO: See whether this simplified form of original works + if (idx == 1) + _videoSurface->_transBlitFlag = true; + } +} + +uint AVISurface::getWidth() const { + return _decoders[0]->getWidth(); +} + +uint AVISurface::getHeight() const { + return _decoders[0]->getHeight(); +} + +void AVISurface::setFrame(int frameNumber) { + // If playback was in process, stop it + if (isPlaying()) + stop(); + + // Ensure the frame number is valid + if (frameNumber >= (int)_decoders[0]->getFrameCount()) + frameNumber = _decoders[0]->getFrameCount() - 1; + + seekToFrame(frameNumber); + renderFrame(); +} + +bool AVISurface::isNextFrame() const { + return _decoders[0]->getTimeToNextFrame() == 0; +} + +bool AVISurface::renderFrame() { + // Check there's a frame ready for display + if (!_decoders[0]->needsUpdate()) + return false; + + // Decode each decoder's video stream into the appropriate surface + for (int idx = 0; idx < _streamCount; ++idx) { + const Graphics::Surface *frame = _decoders[idx]->decodeNextFrame(); + + if (_movieFrameSurface[idx]->format == frame->format) { + _movieFrameSurface[idx]->blitFrom(*frame); + } else { + // Format mis-match, so we need to convert the frame + Graphics::Surface *s = frame->convertTo(_movieFrameSurface[idx]->format, + _decoders[idx]->getPalette()); + _movieFrameSurface[idx]->blitFrom(*s); + s->free(); + delete s; + } + } + + // Blit the primary video frame onto the main overall surface + _videoSurface->lock(); + _videoSurface->getRawSurface()->blitFrom(*_movieFrameSurface[0]); + _videoSurface->unlock(); + + return false; +} + +bool AVISurface::addEvent(int frameNumber, CGameObject *obj) { + if (!_movieRangeInfo.empty()) { + CMovieRangeInfo *tail = _movieRangeInfo.back(); + if (frameNumber == -1) + frameNumber = tail->_startFrame; + + CMovieEvent *me = new CMovieEvent(); + me->_type = MET_FRAME; + me->_startFrame = 0; + me->_endFrame = 0; + me->_initialFrame = frameNumber; + me->_gameObject = obj; + tail->addEvent(me); + + return _movieRangeInfo.size() == 1 && frameNumber == getFrame(); + } + + return false; +} + +void AVISurface::setFrameRate(double rate) { + _decoders[0]->setRate(Common::Rational(rate)); + if (_decoders[1]) + _decoders[1]->setRate(Common::Rational(rate)); +} + +Graphics::ManagedSurface *AVISurface::getSecondarySurface() { + return _streamCount <= 1 ? nullptr : _movieFrameSurface[1]; +} + +Graphics::ManagedSurface *AVISurface::duplicateSecondaryFrame() const { + if (_streamCount <= 1) { + return nullptr; + } else { + Graphics::ManagedSurface *dest = new Graphics::ManagedSurface(_movieFrameSurface[1]->w, + _movieFrameSurface[1]->h, _movieFrameSurface[1]->format); + dest->blitFrom(*_movieFrameSurface[1]); + return dest; + } +} + +} // End of namespace Titanic |