diff options
Diffstat (limited to 'graphics/video')
-rw-r--r-- | graphics/video/avi_decoder.cpp | 26 | ||||
-rw-r--r-- | graphics/video/avi_decoder.h | 5 | ||||
-rw-r--r-- | graphics/video/codecs/indeo3.cpp | 189 | ||||
-rw-r--r-- | graphics/video/codecs/indeo3.h | 2 | ||||
-rw-r--r-- | graphics/video/codecs/qdm2.cpp | 2 | ||||
-rw-r--r-- | graphics/video/coktel_decoder.cpp | 40 | ||||
-rw-r--r-- | graphics/video/qt_decoder.cpp | 68 | ||||
-rw-r--r-- | graphics/video/qt_decoder.h | 15 | ||||
-rw-r--r-- | graphics/video/smk_decoder.cpp | 94 | ||||
-rw-r--r-- | graphics/video/smk_decoder.h | 5 |
10 files changed, 266 insertions, 180 deletions
diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp index 4973cb3eb0..ceca89b8ee 100644 --- a/graphics/video/avi_decoder.cpp +++ b/graphics/video/avi_decoder.cpp @@ -42,6 +42,20 @@ namespace Graphics { +/* +static byte char2num(char c) { + return (c >= 48 && c <= 57) ? c - 48 : 0; +} + +static byte getStreamNum(uint32 tag) { + return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +} +*/ + +static uint16 getStreamType(uint32 tag) { + return tag & 0xffff; +} + AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) { _soundType = soundType; @@ -418,16 +432,4 @@ Audio::QueuingAudioStream *AviDecoder::createAudioStream() { return NULL; } -byte AviDecoder::char2num(char c) { - return (c >= 48 && c <= 57) ? c - 48 : 0; -} - -byte AviDecoder::getStreamNum(uint32 tag) { - return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); -} - -uint16 AviDecoder::getStreamType(uint32 tag) { - return tag & 0xffff; -} - } // End of namespace Graphics diff --git a/graphics/video/avi_decoder.h b/graphics/video/avi_decoder.h index 72cf2d7ef5..68795149c0 100644 --- a/graphics/video/avi_decoder.h +++ b/graphics/video/avi_decoder.h @@ -221,11 +221,6 @@ private: Audio::SoundHandle *_audHandle; Audio::QueuingAudioStream *_audStream; Audio::QueuingAudioStream *createAudioStream(); - - // Helper functions - static byte char2num(char c); - static byte getStreamNum(uint32 tag); - static uint16 getStreamType(uint32 tag); }; } // End of namespace Graphics diff --git a/graphics/video/codecs/indeo3.cpp b/graphics/video/codecs/indeo3.cpp index f59ae81e81..443340de2f 100644 --- a/graphics/video/codecs/indeo3.cpp +++ b/graphics/video/codecs/indeo3.cpp @@ -69,27 +69,22 @@ PixelFormat Indeo3Decoder::getPixelFormat() const { return _pixelFormat; } -bool Indeo3Decoder::isIndeo3(byte *data, uint32 dataLen) { - // No data, no Indeo 3 - if (!data) - return false; - +bool Indeo3Decoder::isIndeo3(Common::SeekableReadStream &stream) { // Less than 16 bytes? This can't be right - if (dataLen < 16) + if (stream.size() < 16) return false; + uint32 id0 = stream.readUint32LE(); + uint32 id1 = stream.readUint32LE(); + uint32 id2 = stream.readUint32LE(); + uint32 id3 = stream.readUint32LE(); + // Unknown, but according to the docs, always 0 - if (READ_LE_UINT32(data + 4) != 0) + if (id1 != 0) return false; - uint32 id; - id = READ_LE_UINT32(data ); // frame number - id ^= READ_LE_UINT32(data + 4); // unknown - id ^= READ_LE_UINT32(data + 8); // checksum - id ^= READ_LE_UINT32(data + 12); // frame data length - // These 4 uint32s XOR'd need to spell "FRMH" - if (id != MKID_BE('FRMH')) + if ((id0 ^ id1 ^ id2 ^ id3) != MKID_BE('FRMH')) return false; return true; @@ -174,31 +169,23 @@ void Indeo3Decoder::allocFrames() { } Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) { - uint32 dataLen = stream->size(); - - byte *inData = new byte[dataLen]; - - if (stream->read(inData, dataLen) != dataLen) - return 0; - // Not Indeo 3? Fail - if (!isIndeo3(inData, dataLen)) + if (!isIndeo3(*stream)) return 0; - uint32 frameDataLen = READ_LE_UINT32(inData + 12); + stream->seek(12); + uint32 frameDataLen = stream->readUint32LE(); // Less data than the frame should have? Fail - if (dataLen < (frameDataLen - 16)) + if (stream->size() < (int)(frameDataLen - 16)) return 0; - Common::MemoryReadStream frame(inData, dataLen); + stream->seek(16); // Behind header + stream->skip(2); // Unknown - frame.skip(16); // Header - frame.skip(2); // Unknown - - uint16 flags1 = frame.readUint16LE(); - uint32 flags3 = frame.readUint32LE(); - uint8 flags2 = frame.readByte(); + uint16 flags1 = stream->readUint16LE(); + uint32 flags3 = stream->readUint32LE(); + uint8 flags2 = stream->readByte(); // Finding the reference frame if (flags1 & 0x200) { @@ -212,77 +199,153 @@ Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) { if (flags3 == 0x80) return _surface; - frame.skip(3); + stream->skip(3); - uint16 fHeight = frame.readUint16LE(); - uint16 fWidth = frame.readUint16LE(); + uint16 fHeight = stream->readUint16LE(); + uint16 fWidth = stream->readUint16LE(); uint32 chromaHeight = ((fHeight >> 2) + 3) & 0x7FFC; uint32 chromaWidth = ((fWidth >> 2) + 3) & 0x7FFC; uint32 offs; - uint32 offsY = frame.readUint32LE() + 16; - uint32 offsU = frame.readUint32LE() + 16; - uint32 offsV = frame.readUint32LE() + 16; + uint32 offsY = stream->readUint32LE() + 16; + uint32 offsU = stream->readUint32LE() + 16; + uint32 offsV = stream->readUint32LE() + 16; + + stream->skip(4); + + uint32 hPos = stream->pos(); + + if (offsY < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } + if (offsU < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } + if (offsV < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } - frame.skip(4); + uint32 dataSize = stream->size() - hPos; - uint32 hPos = frame.pos(); + byte *inData = new byte[dataSize]; + + if (stream->read(inData, dataSize) != dataSize) { + delete[] inData; + return 0; + } - byte *hdr_pos = inData + hPos; + byte *hdr_pos = inData; byte *buf_pos; // Luminance Y - frame.seek(offsY); - buf_pos = inData + offsY + 4; - offs = frame.readUint32LE(); + stream->seek(offsY); + buf_pos = inData + offsY + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Ybuf, _ref_frame->Ybuf, fWidth, fHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(fWidth, 160)); // Chrominance U - frame.seek(offsU); - buf_pos = inData + offsU + 4; - offs = frame.readUint32LE(); + stream->seek(offsU); + buf_pos = inData + offsU + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Vbuf, _ref_frame->Vbuf, chromaWidth, chromaHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); // Chrominance V - frame.seek(offsV); - buf_pos = inData + offsV + 4; - offs = frame.readUint32LE(); + stream->seek(offsV); + buf_pos = inData + offsV + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Ubuf, _ref_frame->Ubuf, chromaWidth, chromaHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); + delete[] inData; + // Blit the frame onto the surface const byte *srcY = _cur_frame->Ybuf; const byte *srcU = _cur_frame->Ubuf; const byte *srcV = _cur_frame->Vbuf; byte *dest = (byte *)_surface->pixels; + + const byte *srcUP = srcU; + const byte *srcVP = srcV; + const byte *srcUN = srcU + chromaWidth; + const byte *srcVN = srcV + chromaWidth; + + uint32 scaleWidth = _surface->w / fWidth; + uint32 scaleHeight = _surface->h / fHeight; + for (uint32 y = 0; y < fHeight; y++) { byte *rowDest = dest; - for (uint32 x = 0; x < fWidth; x++, rowDest += _surface->bytesPerPixel) { - const byte cY = srcY[x]; - const byte cU = srcU[x >> 2]; - const byte cV = srcV[x >> 2]; + for (uint32 sH = 0; sH < scaleHeight; sH++) { + for (uint32 x = 0; x < fWidth; x++) { + uint32 xP = MAX<int32>((x >> 2) - 1, 0); + uint32 xN = MIN<int32>((x >> 2) + 1, chromaWidth - 1); + + byte cY = srcY[x]; + byte cU = srcU[x >> 2]; + byte cV = srcV[x >> 2]; + + if (((x % 4) == 0) && ((y % 4) == 0)) { + cU = (((uint32) cU) + ((uint32) srcUP[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[xP])) / 2; + } else if (((x % 4) == 3) && ((y % 4) == 0)) { + cU = (((uint32) cU) + ((uint32) srcUP[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[xN])) / 2; + } else if (((x % 4) == 0) && ((y % 4) == 3)) { + cU = (((uint32) cU) + ((uint32) srcUN[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[xP])) / 2; + } else if (((x % 4) == 3) && ((y % 4) == 3)) { + cU = (((uint32) cU) + ((uint32) srcUN[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[xN])) / 2; + } else if ( (x % 4) == 0) { + cU = (((uint32) cU) + ((uint32) srcU[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcV[xP])) / 2; + } else if ( (x % 4) == 3) { + cU = (((uint32) cU) + ((uint32) srcU[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcV[xN])) / 2; + } else if ( (y % 4) == 0) { + cU = (((uint32) cU) + ((uint32) srcUP[x >> 2])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[x >> 2])) / 2; + } else if ( (y % 4) == 3) { + cU = (((uint32) cU) + ((uint32) srcUN[x >> 2])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[x >> 2])) / 2; + } + + byte r = 0, g = 0, b = 0; + YUV2RGB(cY, cU, cV, r, g, b); - byte r = 0, g = 0, b = 0; - YUV2RGB(cY, cU, cV, r, g, b); + const uint32 color = _pixelFormat.RGBToColor(r, g, b); - const uint32 color = _pixelFormat.RGBToColor(r, g, b); + for (uint32 sW = 0; sW < scaleWidth; sW++, rowDest += _surface->bytesPerPixel) { + if (_surface->bytesPerPixel == 1) + *((uint8 *)rowDest) = (uint8)color; + else if (_surface->bytesPerPixel == 2) + *((uint16 *)rowDest) = (uint16)color; + } + } - if (_surface->bytesPerPixel == 1) - *((uint8 *)rowDest) = (uint8)color; - else if (_surface->bytesPerPixel == 2) - *((uint16 *)rowDest) = (uint16)color; + dest += _surface->pitch; } - dest += _surface->pitch; srcY += fWidth; if ((y & 3) == 3) { - srcU += fWidth >> 2; - srcV += fWidth >> 2; + srcU += chromaWidth; + srcV += chromaWidth; + + if (y > 0) { + srcUP += chromaWidth; + srcVP += chromaWidth; + } + if (y < (fHeight - 4U)) { + srcUN += chromaWidth; + srcVN += chromaWidth; + } } } diff --git a/graphics/video/codecs/indeo3.h b/graphics/video/codecs/indeo3.h index 8d50b74b1e..319a8e5d42 100644 --- a/graphics/video/codecs/indeo3.h +++ b/graphics/video/codecs/indeo3.h @@ -49,7 +49,7 @@ public: Surface *decodeImage(Common::SeekableReadStream *stream); PixelFormat getPixelFormat() const; - static bool isIndeo3(byte *data, uint32 dataLen); + static bool isIndeo3(Common::SeekableReadStream &stream); private: Surface *_surface; diff --git a/graphics/video/codecs/qdm2.cpp b/graphics/video/codecs/qdm2.cpp index 9f151b4ba8..0050b256d1 100644 --- a/graphics/video/codecs/qdm2.cpp +++ b/graphics/video/codecs/qdm2.cpp @@ -1775,7 +1775,7 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS tmp = extraData->readUint32BE(); debug(1, "QDM2Stream::QDM2Stream() extraType: %d", tmp); if (tmp == MKID_BE('QDMC')) - warning("QDM2Stream::QDM2Stream() QDMC stream type not supported."); + warning("QDM2Stream::QDM2Stream() QDMC stream type not supported"); else if (tmp != MKID_BE('QDM2')) error("QDM2Stream::QDM2Stream() Unsupported stream type"); diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 0709288091..10be09eb23 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -399,11 +399,11 @@ void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) { rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left * _surface.bytesPerPixel; for (int i = 0; i < rect.height(); i++) { - memcpy(dst, src, rect.width()); + memcpy(dst, src, rect.width() * _surface.bytesPerPixel); - src += srcRect.width(); + src += srcRect.width() * _surface.bytesPerPixel; dst += _surface.pitch; } } @@ -581,7 +581,7 @@ uint32 CoktelDecoder::getTimeToNextFrame() const { // the middle of a long video. if (!hasSound()) - return Common::Rational(1000, _frameRate).toInt(); + return (1000 / _frameRate).toInt(); // If there /is/ audio, we do need to keep video and audio // in sync, though. @@ -1589,12 +1589,6 @@ bool VMDDecoder::load(Common::SeekableReadStream *stream) { if (_version & 4) _bytesPerPixel = handle + 1; - if (_bytesPerPixel != 1) { - warning("TODO: _bytesPerPixel = %d", _bytesPerPixel); - close(); - return false; - } - if (_bytesPerPixel > 3) { warning("VMDDecoder::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version); @@ -1696,6 +1690,11 @@ bool VMDDecoder::assessVideoProperties() { _bytesPerPixel = n; } + if ((_bytesPerPixel > 1) && !_externalCodec) { + warning("VMDDecoder::assessVideoProperties(): TODO: Internal _bytesPerPixel == %d", _bytesPerPixel); + return false; + } + if (_hasVideo) { if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) _frameDataSize = _width * _height + 1000; @@ -1837,7 +1836,7 @@ bool VMDDecoder::readFiles() { break; if (_frames[i].parts[j].type == kPartTypeFile) { - File file;; + File file; file.offset = _stream->pos() + 20; file.size = _frames[i].parts[j].size; @@ -2087,9 +2086,19 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) { return false; if (_externalCodec) { - // TODO - warning("_external codec"); - return false; + if (!_codec) + return false; + + Common::MemoryReadStream frameStream(_frameData, _frameDataLen); + Surface *codecSurf = _codec->decodeImage(&frameStream); + if (!codecSurf) + return false; + + rect = Common::Rect(_x, _y, _x + codecSurf->w, _y + codecSurf->h); + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + + renderBlockWhole((const byte *) codecSurf->pixels, rect); + return true; } if (_blitMode > 0) { @@ -2376,6 +2385,9 @@ byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 inde } PixelFormat VMDDecoder::getPixelFormat() const { + if (_externalCodec && _codec) + return _codec->getPixelFormat(); + return PixelFormat::createFormatCLUT8(); } diff --git a/graphics/video/qt_decoder.cpp b/graphics/video/qt_decoder.cpp index 470441dab8..8437f0af43 100644 --- a/graphics/video/qt_decoder.cpp +++ b/graphics/video/qt_decoder.cpp @@ -67,6 +67,8 @@ QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() { _numStreams = 0; _fd = 0; _scaledSurface = 0; + _scaleFactorX = 1; + _scaleFactorY = 1; _dirtyPalette = false; _resFork = new Common::MacResManager(); @@ -82,14 +84,14 @@ uint16 QuickTimeDecoder::getWidth() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->width / getScaleMode(); + return (Common::Rational(_streams[_videoStreamIndex]->width) / getScaleFactorX()).toInt(); } uint16 QuickTimeDecoder::getHeight() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->height / getScaleMode(); + return (Common::Rational(_streams[_videoStreamIndex]->height) / getScaleFactorY()).toInt(); } uint32 QuickTimeDecoder::getFrameCount() const { @@ -113,11 +115,18 @@ uint32 QuickTimeDecoder::getCodecTag() { return _streams[_videoStreamIndex]->codec_tag; } -ScaleMode QuickTimeDecoder::getScaleMode() const { +Common::Rational QuickTimeDecoder::getScaleFactorX() const { if (_videoStreamIndex < 0) - return kScaleNormal; + return 1; - return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode); + return (_scaleFactorX * _streams[_videoStreamIndex]->scaleFactorX); +} + +Common::Rational QuickTimeDecoder::getScaleFactorY() const { + if (_videoStreamIndex < 0) + return 1; + + return (_scaleFactorY * _streams[_videoStreamIndex]->scaleFactorY); } uint32 QuickTimeDecoder::getFrameDuration() { @@ -231,14 +240,14 @@ Surface *QuickTimeDecoder::decodeNextFrame() { } Surface *QuickTimeDecoder::scaleSurface(Surface *frame) { - if (getScaleMode() == kScaleNormal) + if (getScaleFactorX() == 1 && getScaleFactorY() == 1) return frame; assert(_scaledSurface); - for (uint32 j = 0; j < _scaledSurface->h; j++) - for (uint32 k = 0; k < _scaledSurface->w; k++) - memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel); + for (int32 j = 0; j < _scaledSurface->h; j++) + for (int32 k = 0; k < _scaledSurface->w; k++) + memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr((k * getScaleFactorX()).toInt() , (j * getScaleFactorY()).toInt()), frame->bytesPerPixel); return _scaledSurface; } @@ -376,7 +385,7 @@ void QuickTimeDecoder::init() { if (_videoStreamIndex >= 0) { _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); - if (getScaleMode() != kScaleNormal) { + if (getScaleFactorX() != 1 || getScaleFactorY() != 1) { // We have to initialize the scaled surface _scaledSurface = new Surface(); _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); @@ -593,17 +602,11 @@ int QuickTimeDecoder::readMVHD(MOVatom atom) { uint32 yMod = _fd->readUint32BE(); _fd->skip(16); - if (xMod != yMod) - error("X and Y resolution modifiers differ"); - - if (xMod == 0x8000) - _scaleMode = kScaleHalf; - else if (xMod == 0x4000) - _scaleMode = kScaleQuarter; - else - _scaleMode = kScaleNormal; + _scaleFactorX = Common::Rational(0x10000, xMod); + _scaleFactorY = Common::Rational(0x10000, yMod); - debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode); + _scaleFactorX.debugPrint(1, "readMVHD(): scaleFactorX ="); + _scaleFactorY.debugPrint(1, "readMVHD(): scaleFactorY ="); _fd->readUint32BE(); // preview time _fd->readUint32BE(); // preview duration @@ -688,17 +691,11 @@ int QuickTimeDecoder::readTKHD(MOVatom atom) { uint32 yMod = _fd->readUint32BE(); _fd->skip(16); - if (xMod != yMod) - error("X and Y resolution modifiers differ"); + st->scaleFactorX = Common::Rational(0x10000, xMod); + st->scaleFactorY = Common::Rational(0x10000, yMod); - if (xMod == 0x8000) - st->scaleMode = kScaleHalf; - else if (xMod == 0x4000) - st->scaleMode = kScaleQuarter; - else - st->scaleMode = kScaleNormal; - - debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode); + st->scaleFactorX.debugPrint(1, "readTKHD(): scaleFactorX ="); + st->scaleFactorY.debugPrint(1, "readTKHD(): scaleFactorY ="); // these are fixed-point, 16:16 // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width @@ -814,6 +811,17 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) { _fd->readUint16BE(); // index debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type); + + if (st->codec_tag && st->codec_tag != format) { + // HACK: Multiple FourCC, skip this. FFmpeg does this too and also + // skips it with a TODO. However, we really don't need to support + // multiple codec tags since the only two videos in Riven DVD that + // do this just have a fake second stream (or so it seems). + debug(3, "Multiple FourCC not supported"); + _fd->seek(start_pos + size); + continue; + } + st->codec_tag = format; if (st->codec_type == CODEC_TYPE_VIDEO) { diff --git a/graphics/video/qt_decoder.h b/graphics/video/qt_decoder.h index 196d4c02cb..6dcfc0944d 100644 --- a/graphics/video/qt_decoder.h +++ b/graphics/video/qt_decoder.h @@ -36,6 +36,7 @@ #include "common/scummsys.h" #include "common/queue.h" +#include "common/rational.h" #include "graphics/video/video_decoder.h" #include "graphics/video/codecs/codec.h" @@ -50,11 +51,6 @@ namespace Common { namespace Graphics { -enum ScaleMode { - kScaleNormal = 1, - kScaleHalf = 2, - kScaleQuarter = 4 -}; class QuickTimeDecoder : public RewindableVideoDecoder { public: @@ -217,7 +213,8 @@ protected: uint32 nb_frames; uint32 duration; uint32 start_time; - ScaleMode scaleMode; + Common::Rational scaleFactorX; + Common::Rational scaleFactorY; }; const ParseTable *_parseTable; @@ -230,7 +227,8 @@ protected: MOVStreamContext *_partial; uint32 _numStreams; int _ni; - ScaleMode _scaleMode; + Common::Rational _scaleFactorX; + Common::Rational _scaleFactorY; MOVStreamContext *_streams[20]; byte _palette[256 * 3]; bool _dirtyPalette; @@ -260,7 +258,8 @@ protected: Surface *_scaledSurface; Surface *scaleSurface(Surface *frame); - ScaleMode getScaleMode() const; + Common::Rational getScaleFactorX() const; + Common::Rational getScaleFactorY() const; void pauseVideoIntern(bool pause); diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp index 4d03305cce..71858dd3aa 100644 --- a/graphics/video/smk_decoder.cpp +++ b/graphics/video/smk_decoder.cpp @@ -389,12 +389,13 @@ bool SmackerDecoder::load(Common::SeekableReadStream *stream) { _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 = 1000 / frameRate; + _frameRate = Common::Rational(1000, frameRate); else if (frameRate < 0) - _frameRate = 100000 / (-frameRate); + _frameRate = Common::Rational(100000, -frameRate); else - _frameRate = 10; + _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. @@ -540,53 +541,18 @@ Surface *SmackerDecoder::decodeNextFrame() { chunkSize = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the first 4 bytes (chunk size) - if (_header.audioInfo[i].compression != kCompressionNone) { + if (_header.audioInfo[i].compression == kCompressionNone) { + dataSizeUnpacked = chunkSize; + } else { dataSizeUnpacked = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the next 4 bytes (unpacked data size) - } else { - dataSizeUnpacked = 0; } - if (_header.audioInfo[i].hasAudio && chunkSize > 0 && i == 0) { - // If it's track 0, play the audio data - byte *soundBuffer = (byte *)malloc(chunkSize); - - _fileStream->read(soundBuffer, chunkSize); - - if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT) { - // TODO: Compressed audio (Bink RDFT/DCT encoded) - free(soundBuffer); - continue; - } else if (_header.audioInfo[i].compression == kCompressionDPCM) { - // Compressed audio (Huffman DPCM encoded) - queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i); - free(soundBuffer); - } else { - // Uncompressed audio (PCM) - 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(soundBuffer, chunkSize, DisposeAfterUse::YES, flags); - // The sound buffer will be deleted by QueuingAudioStream - } - - if (!_audioStarted) { - _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255); - _audioStarted = true; - } - } 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); - } + handleAudioTrack(i, chunkSize, dataSizeUnpacked); } uint32 frameSize = _frameSizes[_curFrame] & ~3; +// uint32 remainder = _frameSizes[_curFrame] & 3; if (_fileStream->pos() - startPos > frameSize) error("Smacker actual frame size exceeds recorded frame size"); @@ -746,6 +712,46 @@ Surface *SmackerDecoder::decodeNextFrame() { return _surface; } +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); + + _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) + queueCompressedBuffer(soundBuffer, chunkSize, 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 + } + + if (!_audioStarted) { + _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255); + _audioStarted = true; + } + } 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); + } +} + void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum) { @@ -808,7 +814,7 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) | (audioTrees[k * 2 + 1]->getCode(audioBS) << 8)); - WRITE_BE_UINT16(curPointer, CLIP<int32>(bases[k], -32768, 32767)); + WRITE_BE_UINT16(curPointer, bases[k]); curPointer += 2; curPos += 2; } diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h index 43bb84a4f8..5faeab4343 100644 --- a/graphics/video/smk_decoder.h +++ b/graphics/video/smk_decoder.h @@ -69,12 +69,13 @@ public: PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } byte *getPalette() { _dirtyPalette = false; return _palette; } bool hasDirtyPalette() const { return _dirtyPalette; } + virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); protected: Common::Rational getFrameRate() const { return _frameRate; } Common::SeekableReadStream *_fileStream; -private: +protected: void unpackPalette(); // Possible runs of blocks uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } @@ -120,7 +121,7 @@ private: byte *_palette; bool _dirtyPalette; - uint32 _frameRate; + Common::Rational _frameRate; uint32 _frameCount; Surface *_surface; |