TRECISION: Refactor the video decoder classes

Change NightlongVideoDecoder to be a base class. This allows us to
remove it as an intermediate class and cleanly separate the PC Smacker
from the Amiga video decoder
This commit is contained in:
Filippos Karapetis 2022-01-25 17:57:08 +02:00
parent ede8abfd5d
commit 13be0648f0
3 changed files with 193 additions and 215 deletions

View File

@ -66,33 +66,38 @@ AnimManager::~AnimManager() {
}
void AnimManager::playMovie(const Common::String &filename, int startFrame, int endFrame, bool singleChoice) {
NightlongVideoDecoder *smkDecoder = new NightlongVideoDecoder(_vm->isAmiga());
NightlongVideoDecoder *videoDecoder;
if (!smkDecoder->loadFile(filename)) {
if (!_vm->isAmiga())
videoDecoder = new NightlongSmackerDecoder();
else
videoDecoder = new NightlongAmigaDecoder();
if (!videoDecoder->loadFile(filename)) {
warning("playMovie: File %s not found", filename.c_str());
delete smkDecoder;
delete videoDecoder;
_vm->_dialogMgr->afterChoice();
return;
}
Common::Event event;
bool skipVideo = false;
uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
_vm->_drawText._text.clear();
smkDecoder->start();
videoDecoder->start();
// WORKAROUND: If the video has a single choice, and it starts from
// the beginning, ignore the calculated end frame and play all of it
if (singleChoice && startFrame < 10 && endFrame < (int)smkDecoder->getFrameCount() - 1)
endFrame = smkDecoder->getFrameCount() - 1;
if (singleChoice && startFrame < 10 && endFrame < (int)videoDecoder->getFrameCount() - 1)
endFrame = videoDecoder->getFrameCount() - 1;
setVideoRange(smkDecoder, startFrame, endFrame);
setVideoRange(videoDecoder, startFrame, endFrame);
while (!_vm->shouldQuit() && startFrame != endFrame && !smkDecoder->endOfVideo() && !skipVideo) {
if (smkDecoder->needsUpdate()) {
drawFrame(smkDecoder, x, y, true);
while (!_vm->shouldQuit() && startFrame != endFrame && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
drawFrame(videoDecoder, x, y, true);
}
while (_vm->getEventManager()->pollEvent(event)) {
@ -103,7 +108,7 @@ void AnimManager::playMovie(const Common::String &filename, int startFrame, int
g_system->delayMillis(10);
}
delete smkDecoder;
delete videoDecoder;
_vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
_vm->freeKey();
@ -171,7 +176,10 @@ void AnimManager::openSmkAnim(int slot, const Common::String &name) {
}
void AnimManager::openSmk(int slot, Common::SeekableReadStream *stream) {
_animations[slot] = new NightlongVideoDecoder(_vm->isAmiga());
if (!_vm->isAmiga())
_animations[slot] = new NightlongSmackerDecoder();
else
_animations[slot] = new NightlongAmigaDecoder();
if (!_animations[slot]->loadStream(stream)) {
warning("Invalid SMK file");
@ -409,33 +417,33 @@ bool AnimManager::shouldShowAnim(int animation, Common::Rect curRect) {
}
void AnimManager::drawSmkBackgroundFrame(int animation) {
NightlongVideoDecoder *smkDecoder = _animations[kSmackerBackground];
if (smkDecoder == nullptr)
NightlongVideoDecoder *videoDecoder = _animations[kSmackerBackground];
if (videoDecoder == nullptr)
return;
const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (!frame)
return;
const Common::Rect *lastRect = smkDecoder->getNextDirtyRect();
const byte *palette = smkDecoder->getPalette();
const Common::Rect *lastRect = videoDecoder->getNextDirtyRect();
const byte *palette = videoDecoder->getPalette();
if (smkDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
if (videoDecoder->getCurFrame() == 0 && shouldShowAnim(animation, *lastRect) && !_bgAnimRestarted) {
_vm->_graphicsMgr->blitToScreenBuffer(frame, 0, TOP, palette, true);
} else {
while (lastRect) {
if (smkDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
if (videoDecoder->getCurFrame() > 0 && shouldShowAnim(animation, *lastRect)) {
Graphics::Surface anim = frame->getSubArea(*lastRect);
_vm->_graphicsMgr->blitToScreenBuffer(&anim, lastRect->left, lastRect->top + TOP, palette, true);
}
lastRect = smkDecoder->getNextDirtyRect();
lastRect = videoDecoder->getNextDirtyRect();
}
}
}
void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
NightlongVideoDecoder *smkDecoder = _animations[kSmackerIcon];
if (smkDecoder == nullptr)
NightlongVideoDecoder *videoDecoder = _animations[kSmackerIcon];
if (videoDecoder == nullptr)
return;
int stx = ICONMARGSX;
@ -453,11 +461,14 @@ void AnimManager::drawSmkIconFrame(uint16 startIcon, uint16 iconNum) {
if (a == ICONSHOWN)
return;
const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
_vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, smkDecoder->getPalette());
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (!frame)
return;
if (smkDecoder->endOfVideo())
smkDecoder->rewind();
_vm->_graphicsMgr->copyToScreenBuffer(frame, stx, FIRSTLINE, videoDecoder->getPalette());
if (videoDecoder->endOfVideo())
videoDecoder->rewind();
}
void AnimManager::drawSmkActionFrame() {

View File

@ -39,160 +39,6 @@
namespace Trecision {
NightlongVideoDecoder::NightlongVideoDecoder(bool isAmiga) {
_isAmiga = isAmiga;
_smkDecoder = !isAmiga ? new NightlongSmackerDecoder() : nullptr;
_mixer = g_system->getMixer();
}
NightlongVideoDecoder::~NightlongVideoDecoder() {
delete _smkDecoder;
if (_mixer->isSoundHandleActive(_amigaSoundHandle))
_mixer->stopHandle(_amigaSoundHandle);
}
bool NightlongVideoDecoder::loadFile(const Common::Path &filename) {
if (!_isAmiga)
return _smkDecoder->loadFile(filename);
else {
// TODO: Amiga video format
// Load the video's audio track
Common::File *stream = new Common::File();
Common::String file = filename.toString();
stream->open("a" + file);
if (stream->isOpen()) {
Audio::SeekableAudioStream *sound = Audio::makeRawStream(stream, 11025, 0, DisposeAfterUse::YES);
_mixer->playStream(
Audio::Mixer::kSFXSoundType,
&_amigaSoundHandle,
sound);
return true;
} else {
delete stream;
return false;
}
}
}
bool NightlongVideoDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!_isAmiga)
return _smkDecoder->loadStream(stream);
else
return false; // TODO: Amiga videos
}
void NightlongVideoDecoder::muteTrack(uint track, bool mute) {
if (!_isAmiga)
_smkDecoder->muteTrack(track, mute);
// TODO: Amiga videos
}
void NightlongVideoDecoder::setMute(bool mute) {
if (!_isAmiga)
_smkDecoder->setMute(mute);
// TODO: Amiga videos
}
bool NightlongVideoDecoder::forceSeekToFrame(uint frame) {
if (!_isAmiga)
return _smkDecoder->forceSeekToFrame(frame);
else
return false; // TODO: Amiga videos
}
bool NightlongVideoDecoder::endOfFrames() const {
if (!_isAmiga)
return _smkDecoder->endOfFrames();
else
return !_mixer->isSoundHandleActive(_amigaSoundHandle); // HACK, since we only play the audio for now
}
int NightlongVideoDecoder::getCurFrame() const {
if (!_isAmiga)
return _smkDecoder->getCurFrame();
else
return 0; // TODO: Amiga videos
}
uint16 NightlongVideoDecoder::getWidth() const {
if (!_isAmiga)
return _smkDecoder->getWidth();
else
return 0; // TODO: Amiga videos
}
uint16 NightlongVideoDecoder::getHeight() const {
if (!_isAmiga)
return _smkDecoder->getHeight();
else
return 0; // TODO: Amiga videos
}
const Graphics::Surface *NightlongVideoDecoder::decodeNextFrame() {
if (!_isAmiga)
return _smkDecoder->decodeNextFrame();
else
return nullptr; // TODO: Amiga videos
}
uint32 NightlongVideoDecoder::getFrameCount() const {
if (!_isAmiga)
return _smkDecoder->getFrameCount();
else
return 10; // TODO: Amiga videos. Anything > 1 to keep playing till the audio is done
}
const byte *NightlongVideoDecoder::getPalette() {
if (!_isAmiga)
return _smkDecoder->getPalette();
else
return nullptr; // TODO: Amiga videos
}
void NightlongVideoDecoder::start() {
if (!_isAmiga)
_smkDecoder->start();
// TODO: Amiga videos
}
void NightlongVideoDecoder::rewind() {
if (!_isAmiga)
_smkDecoder->rewind();
// TODO: Amiga videos
}
bool NightlongVideoDecoder::needsUpdate() const {
if (!_isAmiga)
return _smkDecoder->needsUpdate();
else
return false; // TODO: Amiga videos
}
void NightlongVideoDecoder::setEndFrame(uint frame) {
if (!_isAmiga)
_smkDecoder->setEndFrame(frame);
// TODO: Amiga videos
}
bool NightlongVideoDecoder::endOfVideo() const {
if (!_isAmiga)
return _smkDecoder->endOfVideo();
else
return false; // TODO: Amiga videos
}
const Common::Rect *NightlongVideoDecoder::getNextDirtyRect() {
if (!_isAmiga)
return _smkDecoder->getNextDirtyRect();
else
return nullptr; // TODO: Amiga videos
}
bool NightlongSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!SmackerDecoder::loadStream(stream))
return false;
@ -271,4 +117,107 @@ bool NightlongSmackerDecoder::endOfFrames() const {
return getCurFrame() >= (int32)getFrameCount() - 1;
}
// ----------------------------------------------------------------------------
NightlongAmigaDecoder::AmigaVideoTrack::AmigaVideoTrack(const Common::String &fileName) {
memset(_palette, 0, sizeof(_palette));
Common::File *stream = new Common::File();
stream->open(fileName);
if (!stream->isOpen())
return;
_curFrame = 0;
_frameCount = 10; // TODO: Anything > 1 to keep playing till the audio is done
// TODO: some videos have more than 256 entries
/*uint16 palEntries = stream->readUint16LE();
stream->skip(2); // unknown
for (uint16 i = 0; i < palEntries; i++) {
_palette[i * 3] = stream->readByte();
_palette[i * 3 + 1] = stream->readByte();
_palette[i * 3 + 2] = stream->readByte();
stream->skip(1); // unused alpha channel
}*/
delete stream;
}
uint16 NightlongAmigaDecoder::AmigaVideoTrack::getWidth() const {
// TODO
return 0;
}
uint16 NightlongAmigaDecoder::AmigaVideoTrack::getHeight() const {
// TODO
return 0;
}
Graphics::PixelFormat NightlongAmigaDecoder::AmigaVideoTrack::getPixelFormat() const {
// TODO
return g_system->getScreenFormat();
}
uint32 NightlongAmigaDecoder::AmigaVideoTrack::getNextFrameStartTime() const {
// TODO
return 0;
}
const Graphics::Surface *NightlongAmigaDecoder::AmigaVideoTrack::decodeNextFrame() {
// TODO
return nullptr;
}
NightlongAmigaDecoder::AmigaAudioTrack::AmigaAudioTrack(const Common::String &fileName) :
AudioTrack(Audio::Mixer::SoundType::kSFXSoundType) {
Common::File *stream = new Common::File();
stream->open(fileName);
_audioStream = Audio::makeRawStream(stream, 11025, 0, DisposeAfterUse::YES);
}
void NightlongAmigaDecoder::readNextPacket() {
AmigaVideoTrack *videoTrack = (AmigaVideoTrack *)getTrack(0);
if (videoTrack->endOfTrack())
return;
// TODO
}
bool NightlongAmigaDecoder::loadStream(Common::SeekableReadStream *stream) {
Common::File *file = dynamic_cast<Common::File *>(stream);
if (!file)
return false;
Common::String fileName = file->getName();
addTrack(new AmigaVideoTrack(fileName));
if (Common::File::exists("a" + fileName))
addTrack(new AmigaAudioTrack("a" + fileName));
return true;
}
void NightlongAmigaDecoder::muteTrack(uint track, bool mute) {
// TODO
}
void NightlongAmigaDecoder::setMute(bool mute) {
// TODO
}
bool NightlongAmigaDecoder::forceSeekToFrame(uint frame) {
// TODO
return false;
}
const Common::Rect *NightlongAmigaDecoder::getNextDirtyRect() {
// TODO
return &_lastDirtyRect;
}
bool NightlongAmigaDecoder::endOfFrames() const {
//return getCurFrame() >= (int32)getFrameCount() - 1;
return true;
}
} // namespace Trecision

View File

@ -32,46 +32,64 @@ namespace Trecision {
class TrecisionEngine;
class NightlongSmackerDecoder : public Video::SmackerDecoder {
class NightlongVideoDecoder : public Video::SmackerDecoder {
public:
bool loadStream(Common::SeekableReadStream *stream) override;
void muteTrack(uint track, bool mute);
void setMute(bool mute);
bool forceSeekToFrame(uint frame);
bool endOfFrames() const;
virtual void muteTrack(uint track, bool mute) {}
virtual void setMute(bool mute) {}
virtual bool forceSeekToFrame(uint frame) { return false; }
virtual bool endOfFrames() const { return false; }
};
class NightlongVideoDecoder {
class NightlongSmackerDecoder : public NightlongVideoDecoder {
public:
NightlongVideoDecoder(bool isAmiga);
~NightlongVideoDecoder();
bool loadStream(Common::SeekableReadStream *stream);
void muteTrack(uint track, bool mute);
void setMute(bool mute);
bool forceSeekToFrame(uint frame);
bool endOfFrames() const;
bool loadStream(Common::SeekableReadStream *stream) override;
void muteTrack(uint track, bool mute) override;
void setMute(bool mute) override;
bool forceSeekToFrame(uint frame) override;
bool endOfFrames() const override;
};
// VideoDecoder functions
int getCurFrame() const;
uint16 getWidth() const;
uint16 getHeight() const;
const Graphics::Surface *decodeNextFrame();
uint32 getFrameCount() const;
const byte *getPalette();
void start();
void rewind();
bool needsUpdate() const;
void setEndFrame(uint frame);
bool endOfVideo() const;
bool loadFile(const Common::Path &filename);
const Common::Rect *getNextDirtyRect();
class NightlongAmigaDecoder : public NightlongVideoDecoder {
public:
bool loadStream(Common::SeekableReadStream *stream) override;
void muteTrack(uint track, bool mute) override;
void setMute(bool mute) override;
bool forceSeekToFrame(uint frame) override;
bool endOfFrames() const override;
const Common::Rect *getNextDirtyRect() override;
private:
bool _isAmiga;
NightlongSmackerDecoder *_smkDecoder;
Audio::SoundHandle _amigaSoundHandle;
Audio::Mixer *_mixer;
Common::Rect _lastDirtyRect;
void readNextPacket() override;
class AmigaVideoTrack : public VideoTrack {
public:
AmigaVideoTrack(const Common::String &fileName);
private:
byte _palette[3 * 256];
int _curFrame;
uint32 _frameCount;
uint16 getWidth() const override;
uint16 getHeight() const override;
Graphics::PixelFormat getPixelFormat() const override;
int getCurFrame() const override { return _curFrame; }
uint32 getNextFrameStartTime() const override;
const Graphics::Surface *decodeNextFrame() override;
int getFrameCount() const override { return _frameCount; }
const byte *getPalette() const override { return _palette; }
bool hasDirtyPalette() const override { return true; }
};
class AmigaAudioTrack : public AudioTrack {
public:
AmigaAudioTrack(const Common::String &fileName);
private:
Audio::AudioStream *getAudioStream() const override { return _audioStream; }
Audio::AudioStream *_audioStream;
};
};
} // End of namespace Trecision