aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/agos/animation.cpp12
-rw-r--r--engines/agos/animation.h4
-rw-r--r--engines/saga/introproc_saga2.cpp4
-rw-r--r--engines/scumm/he/animation_he.cpp5
-rw-r--r--engines/sword1/animation.cpp9
-rw-r--r--engines/sword2/animation.cpp9
-rw-r--r--engines/toon/movie.cpp35
-rw-r--r--engines/toon/movie.h14
-rw-r--r--engines/toon/toon.cpp2
-rw-r--r--video/smk_decoder.cpp391
-rw-r--r--video/smk_decoder.h117
11 files changed, 338 insertions, 264 deletions
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index 10c01741ae..3e8488d7d5 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -415,7 +415,7 @@ void MoviePlayerDXA::updateBalance() {
MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name)
- : MoviePlayer(vm), SmackerDecoder(vm->_mixer) {
+ : MoviePlayer(vm), SmackerDecoder() {
debug(0, "Creating SMK cutscene player");
memset(baseName, 0, sizeof(baseName));
@@ -431,12 +431,12 @@ bool MoviePlayerSMK::load() {
if (!loadStream(videoStream))
error("Failed to load video stream from file %s", videoName.c_str());
+ start();
+
debug(0, "Playing video %s", videoName.c_str());
CursorMan.showMouse(false);
- _firstFrameOffset = _fileStream->pos();
-
return true;
}
@@ -477,10 +477,8 @@ void MoviePlayerSMK::handleNextFrame() {
}
void MoviePlayerSMK::nextFrame() {
- if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
- _fileStream->seek(_firstFrameOffset);
- _curFrame = -1;
- }
+ if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo())
+ rewind();
if (!endOfVideo()) {
decodeNextFrame();
diff --git a/engines/agos/animation.h b/engines/agos/animation.h
index d1ff074b03..37a666b201 100644
--- a/engines/agos/animation.h
+++ b/engines/agos/animation.h
@@ -67,9 +67,6 @@ protected:
virtual void handleNextFrame();
virtual bool processFrame() = 0;
virtual void startSound() {}
-
-protected:
- uint32 _firstFrameOffset;
};
class MoviePlayerDXA : public MoviePlayer, Video::DXADecoder {
@@ -93,6 +90,7 @@ private:
bool processFrame();
void startSound();
void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch);
+ uint32 _firstFrameOffset;
};
class MoviePlayerSMK : public MoviePlayer, Video::SmackerDecoder {
diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp
index b6470370af..15f7f4dc15 100644
--- a/engines/saga/introproc_saga2.cpp
+++ b/engines/saga/introproc_saga2.cpp
@@ -92,7 +92,7 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) {
}
void Scene::playMovie(const char *filename) {
- Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(_vm->_mixer);
+ Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();
if (!smkDecoder->loadFile(filename))
return;
@@ -101,6 +101,8 @@ void Scene::playMovie(const char *filename) {
uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
bool skipVideo = false;
+ smkDecoder->start();
+
while (!_vm->shouldQuit() && !smkDecoder->endOfVideo() && !skipVideo) {
if (smkDecoder->needsUpdate()) {
const Graphics::Surface *frame = smkDecoder->decodeNextFrame();
diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp
index 40e99c26a8..b37a565aab 100644
--- a/engines/scumm/he/animation_he.cpp
+++ b/engines/scumm/he/animation_he.cpp
@@ -40,7 +40,10 @@ MoviePlayer::MoviePlayer(ScummEngine_v90he *vm, Audio::Mixer *mixer) : _vm(vm) {
_video = new Video::BinkDecoder();
else
#endif
- _video = new Video::SmackerDecoder(mixer);
+ {
+ _video = new Video::SmackerDecoder();
+ ((Video::AdvancedVideoDecoder *)_video)->start();
+ }
_flags = 0;
_wizResNum = 0;
diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
index 49c5ef7312..8f863d1e09 100644
--- a/engines/sword1/animation.cpp
+++ b/engines/sword1/animation.cpp
@@ -179,6 +179,13 @@ bool MoviePlayer::load(uint32 id) {
break;
case kVideoDecoderSMK:
filename = Common::String::format("%s.smk", sequenceList[id]);
+
+ if (_decoder->loadFile(filename)) {
+ ((Video::AdvancedVideoDecoder *)_decoder)->start(); // TODO: Remove after new API is complete
+ return true;
+ } else {
+ return false;
+ }
break;
case kVideoDecoderPSX:
filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]);
@@ -547,7 +554,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *
filename = Common::String::format("%s.smk", sequenceList[id]);
if (Common::File::exists(filename)) {
- Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd);
+ Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();
return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK);
}
diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp
index c1cf33ff09..e257ec9029 100644
--- a/engines/sword2/animation.cpp
+++ b/engines/sword2/animation.cpp
@@ -89,6 +89,13 @@ bool MoviePlayer::load(const char *name) {
break;
case kVideoDecoderSMK:
filename = Common::String::format("%s.smk", name);
+
+ if (_decoder->loadFile(filename)) {
+ ((Video::AdvancedVideoDecoder *)_decoder)->start(); // TODO: Remove after new API is complete
+ return true;
+ } else {
+ return false;
+ }
break;
case kVideoDecoderPSX:
filename = Common::String::format("%s.str", name);
@@ -442,7 +449,7 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s
filename = Common::String::format("%s.smk", name);
if (Common::File::exists(filename)) {
- Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd);
+ Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();
return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK);
}
diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp
index 93e41adf57..d988a3ed60 100644
--- a/engines/toon/movie.cpp
+++ b/engines/toon/movie.cpp
@@ -33,6 +33,10 @@
namespace Toon {
+ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() {
+ _lowRes = false;
+}
+
void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize);
@@ -40,33 +44,21 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui
/* uint16 width = */ _fileStream->readUint16LE();
uint16 height = _fileStream->readUint16LE();
_lowRes = (height == getHeight() / 2);
- } else
+ } else {
Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize);
+ }
}
-bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) {
- debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str());
+bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
+ if (!Video::SmackerDecoder::loadStream(stream))
+ return false;
_lowRes = false;
-
- if (Video::SmackerDecoder::loadFile(filename)) {
- if (_surface->h == 200) {
- if (_surface) {
- _surface->free();
- delete _surface;
- }
- _surface = new Graphics::Surface();
- _surface->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
- _header.flags = 4;
- }
-
- return true;
- }
- return false;
+ return true;
}
-ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Video::SmackerDecoder(mixer, soundType) {
- _lowRes = false;
+Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const {
+ return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, signature);
}
// decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed
@@ -103,6 +95,9 @@ void Movie::play(const Common::String &video, int32 flags) {
bool Movie::playVideo(bool isFirstIntroVideo) {
debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo);
+
+ _decoder->start();
+
while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
if (_decoder->needsUpdate()) {
const Graphics::Surface *frame = _decoder->decodeNextFrame();
diff --git a/engines/toon/movie.h b/engines/toon/movie.h
index 2cd33302f2..e795182cba 100644
--- a/engines/toon/movie.h
+++ b/engines/toon/movie.h
@@ -30,13 +30,17 @@ namespace Toon {
class ToonstruckSmackerDecoder : public Video::SmackerDecoder {
public:
- ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
- virtual ~ToonstruckSmackerDecoder() {}
- void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
- bool loadFile(const Common::String &filename);
+ ToonstruckSmackerDecoder();
+
+ bool loadStream(Common::SeekableReadStream *stream);
bool isLowRes() { return _lowRes; }
+
protected:
- bool _lowRes;
+ void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
+ SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const;
+
+private:
+ bool _lowRes;
};
class Movie {
diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp
index ee427652d8..9fd8415676 100644
--- a/engines/toon/toon.cpp
+++ b/engines/toon/toon.cpp
@@ -51,7 +51,7 @@ void ToonEngine::init() {
_currentScriptRegion = 0;
_resources = new Resources(this);
_animationManager = new AnimationManager(this);
- _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer));
+ _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder());
_hotspots = new Hotspots(this);
_mainSurface = new Graphics::Surface();
diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp
index 359f4cb9bd..d707ad519f 100644
--- a/video/smk_decoder.cpp
+++ b/video/smk_decoder.cpp
@@ -204,8 +204,7 @@ BigHuffmanTree::BigHuffmanTree(Common::BitStream &bs, int allocSize)
delete _hiBytes;
}
-BigHuffmanTree::~BigHuffmanTree()
-{
+BigHuffmanTree::~BigHuffmanTree() {
delete[] _tree;
}
@@ -278,24 +277,17 @@ uint32 BigHuffmanTree::getCode(Common::BitStream &bs) {
return v;
}
-SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType)
- : _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) {
- _surface = 0;
+SmackerDecoder::SmackerDecoder(Audio::Mixer::SoundType soundType) : _soundType(soundType) {
_fileStream = 0;
- _dirtyPalette = false;
+ _firstFrameStart = 0;
+ _frameTypes = 0;
+ _frameSizes = 0;
}
SmackerDecoder::~SmackerDecoder() {
close();
}
-uint32 SmackerDecoder::getTime() const {
- if (_audioStream && _audioStarted)
- return _mixer->getSoundElapsedTime(_audioHandle);
-
- return FixedRateVideoDecoder::getTime();
-}
-
bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
@@ -309,16 +301,17 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
uint32 width = _fileStream->readUint32LE();
uint32 height = _fileStream->readUint32LE();
- _frameCount = _fileStream->readUint32LE();
- int32 frameRate = _fileStream->readSint32LE();
-
- // framerate contains 2 digits after the comma, so 1497 is actually 14.97 fps
- if (frameRate > 0)
- _frameRate = Common::Rational(1000, frameRate);
- else if (frameRate < 0)
- _frameRate = Common::Rational(100000, -frameRate);
+ uint32 frameCount = _fileStream->readUint32LE();
+ int32 frameDelay = _fileStream->readSint32LE();
+
+ // frame rate contains 2 digits after the comma, so 1497 is actually 14.97 fps
+ Common::Rational frameRate;
+ if (frameDelay > 0)
+ frameRate = Common::Rational(1000, frameDelay);
+ else if (frameDelay < 0)
+ frameRate = Common::Rational(100000, -frameDelay);
else
- _frameRate = 1000;
+ frameRate = 1000;
// Flags are determined by which bit is set, which can be one of the following:
// 0 - set to 1 if file contains a ring frame.
@@ -328,6 +321,9 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
// before it is displayed.
_header.flags = _fileStream->readUint32LE();
+ SmackerVideoTrack *videoTrack = createVideoTrack(width, height, frameCount, frameRate, _header.flags, _header.signature);
+ addTrack(videoTrack);
+
// TODO: should we do any extra processing for Smacker files with ring frames?
// TODO: should we do any extra processing for Y-doubled videos? Are they the
@@ -374,92 +370,78 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
warning("Unhandled Smacker v2 audio compression");
if (i == 0)
- _audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo);
+ addTrack(new SmackerAudioTrack(_header.audioInfo[i], _soundType));
}
}
_header.dummy = _fileStream->readUint32LE();
- _frameSizes = new uint32[_frameCount];
- for (i = 0; i < _frameCount; ++i)
+ _frameSizes = new uint32[frameCount];
+ for (i = 0; i < frameCount; ++i)
_frameSizes[i] = _fileStream->readUint32LE();
- _frameTypes = new byte[_frameCount];
- for (i = 0; i < _frameCount; ++i)
+ _frameTypes = new byte[frameCount];
+ for (i = 0; i < frameCount; ++i)
_frameTypes[i] = _fileStream->readByte();
byte *huffmanTrees = (byte *) malloc(_header.treesSize);
_fileStream->read(huffmanTrees, _header.treesSize);
Common::BitStream8LSB bs(new Common::MemoryReadStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), true);
+ videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize);
- _MMapTree = new BigHuffmanTree(bs, _header.mMapSize);
- _MClrTree = new BigHuffmanTree(bs, _header.mClrSize);
- _FullTree = new BigHuffmanTree(bs, _header.fullSize);
- _TypeTree = new BigHuffmanTree(bs, _header.typeSize);
-
- _surface = new Graphics::Surface();
+ _firstFrameStart = _fileStream->pos();
- // Height needs to be doubled if we have flags (Y-interlaced or Y-doubled)
- _surface->create(width, height * (_header.flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8());
-
- memset(_palette, 0, 3 * 256);
return true;
}
void SmackerDecoder::close() {
- if (!_fileStream)
- return;
-
- if (_audioStream) {
- if (_audioStarted) {
- // The mixer will delete the stream.
- _mixer->stopHandle(_audioHandle);
- _audioStarted = false;
- } else {
- delete _audioStream;
- }
- _audioStream = 0;
- }
+ AdvancedVideoDecoder::close();
delete _fileStream;
_fileStream = 0;
- _surface->free();
- delete _surface;
- _surface = 0;
-
- delete _MMapTree;
- delete _MClrTree;
- delete _FullTree;
- delete _TypeTree;
+ delete[] _frameTypes;
+ _frameTypes = 0;
delete[] _frameSizes;
- delete[] _frameTypes;
+ _frameSizes = 0;
+}
- reset();
+bool SmackerDecoder::rewind() {
+ // Call the parent method to rewind the tracks first
+ // In particular, only videos without sound can be rewound
+ if (!AdvancedVideoDecoder::rewind())
+ return false;
+
+ // And seek back to where the first frame begins
+ _fileStream->seek(_firstFrameStart);
+ return true;
}
-const Graphics::Surface *SmackerDecoder::decodeNextFrame() {
+void SmackerDecoder::readNextPacket() {
+ SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0);
+
+ if (videoTrack->endOfTrack())
+ return;
+
+ videoTrack->increaseCurFrame();
+
uint i;
uint32 chunkSize = 0;
uint32 dataSizeUnpacked = 0;
uint32 startPos = _fileStream->pos();
- _curFrame++;
-
// Check if we got a frame with palette data, and
// call back the virtual setPalette function to set
// the current palette
- if (_frameTypes[_curFrame] & 1) {
- unpackPalette();
- _dirtyPalette = true;
- }
+ if (_frameTypes[videoTrack->getCurFrame()] & 1)
+ videoTrack->unpackPalette(_fileStream);
// Load audio tracks
for (i = 0; i < 7; ++i) {
- if (!(_frameTypes[_curFrame] & (2 << i)))
+ if (!(_frameTypes[videoTrack->getCurFrame()] & (2 << i)))
continue;
chunkSize = _fileStream->readUint32LE();
@@ -475,29 +457,109 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {
handleAudioTrack(i, chunkSize, dataSizeUnpacked);
}
- uint32 frameSize = _frameSizes[_curFrame] & ~3;
-// uint32 remainder = _frameSizes[_curFrame] & 3;
+ uint32 frameSize = _frameSizes[videoTrack->getCurFrame()] & ~3;
+// uint32 remainder = _frameSizes[videoTrack->getCurFrame()] & 3;
if (_fileStream->pos() - startPos > frameSize)
error("Smacker actual frame size exceeds recorded frame size");
uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos);
- _frameData = (byte *)malloc(frameDataSize + 1);
+ byte *frameData = (byte *)malloc(frameDataSize + 1);
// Padding to keep the BigHuffmanTrees from reading past the data end
- _frameData[frameDataSize] = 0x00;
+ frameData[frameDataSize] = 0x00;
+
+ _fileStream->read(frameData, frameDataSize);
- _fileStream->read(_frameData, frameDataSize);
+ Common::BitStream8LSB bs(new Common::MemoryReadStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), true);
+ videoTrack->decodeFrame(bs);
- Common::BitStream8LSB bs(new Common::MemoryReadStream(_frameData, frameDataSize + 1, DisposeAfterUse::YES), true);
+ _fileStream->seek(startPos + frameSize);
+}
+void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
+ if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) {
+ // Get the audio track, which start at offset 1 (first track is video)
+ SmackerAudioTrack *audioTrack = (SmackerAudioTrack *)getTrack(track + 1);
+
+ // If it's track 0, play the audio data
+ byte *soundBuffer = (byte *)malloc(chunkSize + 1);
+ // Padding to keep the SmallHuffmanTrees from reading past the data end
+ soundBuffer[chunkSize] = 0x00;
+
+ _fileStream->read(soundBuffer, chunkSize);
+
+ if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) {
+ // TODO: Compressed audio (Bink RDFT/DCT encoded)
+ free(soundBuffer);
+ return;
+ } else if (_header.audioInfo[track].compression == kCompressionDPCM) {
+ // Compressed audio (Huffman DPCM encoded)
+ audioTrack->queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize);
+ free(soundBuffer);
+ } else {
+ // Uncompressed audio (PCM)
+ audioTrack->queuePCM(soundBuffer, chunkSize);
+ }
+ } else {
+ // Ignore the rest of the audio tracks, if they exist
+ // TODO: Are there any Smacker videos with more than one audio stream?
+ // If yes, we should play the rest of the audio streams as well
+ if (chunkSize > 0)
+ _fileStream->skip(chunkSize);
+ }
+}
+
+SmackerDecoder::SmackerVideoTrack::SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) {
+ _surface = new Graphics::Surface();
+ _surface->create(width, height * (flags ? 2 : 1), Graphics::PixelFormat::createFormatCLUT8());
+ _frameCount = frameCount;
+ _frameRate = frameRate;
+ _flags = flags;
+ _signature = signature;
+ _curFrame = -1;
+ _dirtyPalette = false;
+ _MMapTree = _MClrTree = _FullTree = _TypeTree = 0;
+ memset(_palette, 0, 3 * 256);
+}
+
+SmackerDecoder::SmackerVideoTrack::~SmackerVideoTrack() {
+ _surface->free();
+ delete _surface;
+
+ delete _MMapTree;
+ delete _MClrTree;
+ delete _FullTree;
+ delete _TypeTree;
+}
+
+uint16 SmackerDecoder::SmackerVideoTrack::getWidth() const {
+ return _surface->w;
+}
+
+uint16 SmackerDecoder::SmackerVideoTrack::getHeight() const {
+ return _surface->h;
+}
+
+Graphics::PixelFormat SmackerDecoder::SmackerVideoTrack::getPixelFormat() const {
+ return _surface->format;
+}
+
+void SmackerDecoder::SmackerVideoTrack::readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) {
+ _MMapTree = new BigHuffmanTree(bs, mMapSize);
+ _MClrTree = new BigHuffmanTree(bs, mClrSize);
+ _FullTree = new BigHuffmanTree(bs, fullSize);
+ _TypeTree = new BigHuffmanTree(bs, typeSize);
+}
+
+void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) {
_MMapTree->reset();
_MClrTree->reset();
_FullTree->reset();
_TypeTree->reset();
// Height needs to be doubled if we have flags (Y-interlaced or Y-doubled)
- uint doubleY = _header.flags ? 2 : 1;
+ uint doubleY = _flags ? 2 : 1;
uint bw = getWidth() / 4;
uint bh = getHeight() / doubleY / 4;
@@ -508,6 +570,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {
uint type, run, j, mode;
uint32 p1, p2, clr, map;
byte hi, lo;
+ uint i;
while (block < blocks) {
type = _TypeTree->getCode(bs);
@@ -536,7 +599,7 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {
break;
case SMK_BLOCK_FULL:
// Smacker v2 has one mode, Smacker v4 has three
- if (_header.signature == MKTAG('S','M','K','2')) {
+ if (_signature == MKTAG('S','M','K','2')) {
mode = 0;
} else {
// 00 - mode 0
@@ -628,60 +691,75 @@ const Graphics::Surface *SmackerDecoder::decodeNextFrame() {
break;
}
}
+}
- _fileStream->seek(startPos + frameSize);
+void SmackerDecoder::SmackerVideoTrack::unpackPalette(Common::SeekableReadStream *stream) {
+ uint startPos = stream->pos();
+ uint32 len = 4 * stream->readByte();
- if (_curFrame == 0)
- _startTime = g_system->getMillis();
+ byte *chunk = (byte *)malloc(len);
+ stream->read(chunk, len);
+ byte *p = chunk;
- return _surface;
-}
+ byte oldPalette[3 * 256];
+ memcpy(oldPalette, _palette, 3 * 256);
-void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
- if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) {
- // If it's track 0, play the audio data
- byte *soundBuffer = (byte *)malloc(chunkSize + 1);
- // Padding to keep the SmallHuffmanTrees from reading past the data end
- soundBuffer[chunkSize] = 0x00;
+ byte *pal = _palette;
- _fileStream->read(soundBuffer, chunkSize);
+ int sz = 0;
+ byte b0;
+ while (sz < 256) {
+ b0 = *p++;
+ if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000)
+ sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111)
+ pal += 3 * ((b0 & 0x7f) + 1);
+ } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000)
+ byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111)
+ uint s = 3 * *p++;
+ sz += c;
- if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) {
- // TODO: Compressed audio (Bink RDFT/DCT encoded)
- free(soundBuffer);
- return;
- } else if (_header.audioInfo[track].compression == kCompressionDPCM) {
- // Compressed audio (Huffman DPCM encoded)
- queueCompressedBuffer(soundBuffer, chunkSize + 1, unpackedSize, track);
- free(soundBuffer);
- } else {
- // Uncompressed audio (PCM)
- byte flags = 0;
- if (_header.audioInfo[track].is16Bits)
- flags = flags | Audio::FLAG_16BITS;
- if (_header.audioInfo[track].isStereo)
- flags = flags | Audio::FLAG_STEREO;
-
- _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags);
- // The sound buffer will be deleted by QueuingAudioStream
- }
+ while (c--) {
+ *pal++ = oldPalette[s + 0];
+ *pal++ = oldPalette[s + 1];
+ *pal++ = oldPalette[s + 2];
+ s += 3;
+ }
+ } else { // top 2 bits are 00
+ sz++;
+ // get the lower 6 bits for each component (0x3f = 00111111)
+ byte b = b0 & 0x3f;
+ byte g = (*p++) & 0x3f;
+ byte r = (*p++) & 0x3f;
+
+ assert(g < 0xc0 && b < 0xc0);
- if (!_audioStarted) {
- _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance());
- _audioStarted = true;
+ // upscale to full 8-bit color values by multiplying by 4
+ *pal++ = b * 4;
+ *pal++ = g * 4;
+ *pal++ = r * 4;
}
- } else {
- // Ignore the rest of the audio tracks, if they exist
- // TODO: Are there any Smacker videos with more than one audio stream?
- // If yes, we should play the rest of the audio streams as well
- if (chunkSize > 0)
- _fileStream->skip(chunkSize);
}
+
+ stream->seek(startPos + len);
+ free(chunk);
+
+ _dirtyPalette = true;
}
-void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
- uint32 unpackedSize, int streamNum) {
+SmackerDecoder::SmackerAudioTrack::SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType) :
+ _audioInfo(audioInfo), _soundType(soundType) {
+ _audioStream = Audio::makeQueuingAudioStream(_audioInfo.sampleRate, _audioInfo.isStereo);
+}
+SmackerDecoder::SmackerAudioTrack::~SmackerAudioTrack() {
+ delete _audioStream;
+}
+
+Audio::AudioStream *SmackerDecoder::SmackerAudioTrack::getAudioStream() const {
+ return _audioStream;
+}
+
+void SmackerDecoder::SmackerAudioTrack::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize) {
Common::BitStream8LSB audioBS(new Common::MemoryReadStream(buffer, bufferSize), true);
bool dataPresent = audioBS.getBit();
@@ -689,9 +767,9 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
return;
bool isStereo = audioBS.getBit();
- assert(isStereo == _header.audioInfo[streamNum].isStereo);
+ assert(isStereo == _audioInfo.isStereo);
bool is16Bits = audioBS.getBit();
- assert(is16Bits == _header.audioInfo[streamNum].is16Bits);
+ assert(is16Bits == _audioInfo.is16Bits);
int numBytes = 1 * (isStereo ? 2 : 1) * (is16Bits ? 2 : 1);
@@ -759,74 +837,21 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
for (int k = 0; k < numBytes; k++)
delete audioTrees[k];
- byte flags = 0;
- if (_header.audioInfo[0].is16Bits)
- flags = flags | Audio::FLAG_16BITS;
- if (_header.audioInfo[0].isStereo)
- flags = flags | Audio::FLAG_STEREO;
- _audioStream->queueBuffer(unpackedBuffer, unpackedSize, DisposeAfterUse::YES, flags);
- // unpackedBuffer will be deleted by QueuingAudioStream
+ queuePCM(unpackedBuffer, unpackedSize);
}
-void SmackerDecoder::unpackPalette() {
- uint startPos = _fileStream->pos();
- uint32 len = 4 * _fileStream->readByte();
-
- byte *chunk = (byte *)malloc(len);
- _fileStream->read(chunk, len);
- byte *p = chunk;
-
- byte oldPalette[3*256];
- memcpy(oldPalette, _palette, 3 * 256);
-
- byte *pal = _palette;
-
- int sz = 0;
- byte b0;
- while (sz < 256) {
- b0 = *p++;
- if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000)
- sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111)
- pal += 3 * ((b0 & 0x7f) + 1);
- } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000)
- byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111)
- uint s = 3 * *p++;
- sz += c;
-
- while (c--) {
- *pal++ = oldPalette[s + 0];
- *pal++ = oldPalette[s + 1];
- *pal++ = oldPalette[s + 2];
- s += 3;
- }
- } else { // top 2 bits are 00
- sz++;
- // get the lower 6 bits for each component (0x3f = 00111111)
- byte b = b0 & 0x3f;
- byte g = (*p++) & 0x3f;
- byte r = (*p++) & 0x3f;
-
- assert(g < 0xc0 && b < 0xc0);
-
- // upscale to full 8-bit color values by multiplying by 4
- *pal++ = b * 4;
- *pal++ = g * 4;
- *pal++ = r * 4;
- }
- }
-
- _fileStream->seek(startPos + len);
- free(chunk);
-}
+void SmackerDecoder::SmackerAudioTrack::queuePCM(byte *buffer, uint32 bufferSize) {
+ byte flags = 0;
+ if (_audioInfo.is16Bits)
+ flags |= Audio::FLAG_16BITS;
+ if (_audioInfo.isStereo)
+ flags |= Audio::FLAG_STEREO;
-void SmackerDecoder::updateVolume() {
- if (g_system->getMixer()->isSoundHandleActive(_audioHandle))
- g_system->getMixer()->setChannelVolume(_audioHandle, getVolume());
+ _audioStream->queueBuffer(buffer, bufferSize, DisposeAfterUse::YES, flags);
}
-void SmackerDecoder::updateBalance() {
- if (g_system->getMixer()->isSoundHandleActive(_audioHandle))
- g_system->getMixer()->setChannelBalance(_audioHandle, getBalance());
+SmackerDecoder::SmackerVideoTrack *SmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const {
+ return new SmackerVideoTrack(width, height, frameCount, frameRate, flags, signature);
}
} // End of namespace Video
diff --git a/video/smk_decoder.h b/video/smk_decoder.h
index 516882e7c8..78a4ded0fc 100644
--- a/video/smk_decoder.h
+++ b/video/smk_decoder.h
@@ -34,6 +34,7 @@ class QueuingAudioStream;
}
namespace Common {
+class BitStream;
class SeekableReadStream;
}
@@ -56,42 +57,72 @@ class BigHuffmanTree;
* - sword2
* - toon
*/
-class SmackerDecoder : public FixedRateVideoDecoder {
+class SmackerDecoder : public AdvancedVideoDecoder {
public:
- SmackerDecoder(Audio::Mixer *mixer,
- Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
+ SmackerDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
virtual ~SmackerDecoder();
- bool loadStream(Common::SeekableReadStream *stream);
+ virtual bool loadStream(Common::SeekableReadStream *stream);
void close();
- bool isVideoLoaded() const { return _fileStream != 0; }
- uint16 getWidth() const { return _surface->w; }
- uint16 getHeight() const { return _surface->h; }
- uint32 getFrameCount() const { return _frameCount; }
- uint32 getTime() const;
- const Graphics::Surface *decodeNextFrame();
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
- const byte *getPalette() { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
- virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
+ bool rewind();
protected:
- Common::SeekableReadStream *_fileStream;
+ void readNextPacket();
+
+ virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
- // VideoDecoder API
- void updateVolume();
- void updateBalance();
+ class SmackerVideoTrack : public FixedRateVideoTrack {
+ public:
+ SmackerVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature);
+ ~SmackerVideoTrack();
- // FixedRateVideoDecoder API
- Common::Rational getFrameRate() const { return _frameRate; }
+ bool isRewindable() const { return true; }
+ bool rewind() { _curFrame = -1; return true; }
-protected:
- void unpackPalette();
- // Possible runs of blocks
- uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); }
- void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum);
+ uint16 getWidth() const;
+ uint16 getHeight() const;
+ Graphics::PixelFormat getPixelFormat() const;
+ int getCurFrame() const { return _curFrame; }
+ int getFrameCount() const { return _frameCount; }
+ const Graphics::Surface *decodeNextFrame() { return _surface; }
+ const byte *getPalette() const { _dirtyPalette = false; return _palette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+
+ void readTrees(Common::BitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize);
+ void increaseCurFrame() { _curFrame++; }
+ void decodeFrame(Common::BitStream &bs);
+ void unpackPalette(Common::SeekableReadStream *stream);
+
+ protected:
+ Common::Rational getFrameRate() const { return _frameRate; }
+
+ Graphics::Surface *_surface;
+
+ private:
+ Common::Rational _frameRate;
+ uint32 _flags, _signature;
+
+ byte _palette[3 * 256];
+ mutable bool _dirtyPalette;
+
+ int _curFrame;
+ uint32 _frameCount;
+
+ BigHuffmanTree *_MMapTree;
+ BigHuffmanTree *_MClrTree;
+ BigHuffmanTree *_FullTree;
+ BigHuffmanTree *_TypeTree;
+ // Possible runs of blocks
+ static uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); }
+ };
+
+ virtual SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const;
+
+ Common::SeekableReadStream *_fileStream;
+
+private:
enum AudioCompression {
kCompressionNone,
kCompressionDPCM,
@@ -120,6 +151,25 @@ protected:
uint32 dummy;
} _header;
+ class SmackerAudioTrack : public AudioTrack {
+ public:
+ SmackerAudioTrack(const AudioInfo &audioInfo, Audio::Mixer::SoundType soundType);
+ ~SmackerAudioTrack();
+
+ Audio::Mixer::SoundType getSoundType() const { return _soundType; }
+
+ void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize);
+ void queuePCM(byte *buffer, uint32 bufferSize);
+
+ protected:
+ Audio::AudioStream *getAudioStream() const;
+
+ private:
+ Audio::Mixer::SoundType _soundType;
+ Audio::QueuingAudioStream *_audioStream;
+ AudioInfo _audioInfo;
+ };
+
uint32 *_frameSizes;
// The FrameTypes section of a Smacker file contains an array of bytes, where
// the 8 bits of each byte describe the contents of the corresponding frame.
@@ -127,25 +177,10 @@ protected:
// and so on), so there can be up to 7 different audio tracks. When the lowest bit
// (bit 0) is set, it denotes a frame that contains a palette record
byte *_frameTypes;
- byte *_frameData;
- // The RGB palette
- byte _palette[3 * 256];
- bool _dirtyPalette;
- Common::Rational _frameRate;
- uint32 _frameCount;
- Graphics::Surface *_surface;
+ uint32 _firstFrameStart;
Audio::Mixer::SoundType _soundType;
- Audio::Mixer *_mixer;
- bool _audioStarted;
- Audio::QueuingAudioStream *_audioStream;
- Audio::SoundHandle _audioHandle;
-
- BigHuffmanTree *_MMapTree;
- BigHuffmanTree *_MClrTree;
- BigHuffmanTree *_FullTree;
- BigHuffmanTree *_TypeTree;
};
} // End of namespace Video