aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorMatthew Hoops2010-05-23 18:33:55 +0000
committerMatthew Hoops2010-05-23 18:33:55 +0000
commit2f31b05651baf87ea07bc00cced798955a5fa6be (patch)
treeeedade6bd746422fe45e8c48c76d1dd667a45b20 /graphics
parente2a388e2f59f689cd89995013236e90a1b004264 (diff)
downloadscummvm-rg350-2f31b05651baf87ea07bc00cced798955a5fa6be.tar.gz
scummvm-rg350-2f31b05651baf87ea07bc00cced798955a5fa6be.tar.bz2
scummvm-rg350-2f31b05651baf87ea07bc00cced798955a5fa6be.zip
Move Mohawk's QuickTime code to graphics/ (and QDM2 to sound, disabled when Mohawk is not enabled) so SCI can use the code.
svn-id: r49165
Diffstat (limited to 'graphics')
-rw-r--r--graphics/conversion.h7
-rw-r--r--graphics/module.mk6
-rw-r--r--graphics/video/avi_decoder.cpp3
-rw-r--r--graphics/video/codecs/cinepak.cpp287
-rw-r--r--graphics/video/codecs/cinepak.h81
-rw-r--r--graphics/video/codecs/mjpeg.cpp73
-rw-r--r--graphics/video/codecs/mjpeg.h58
-rw-r--r--graphics/video/codecs/qtrle.cpp420
-rw-r--r--graphics/video/codecs/qtrle.h58
-rw-r--r--graphics/video/codecs/rpza.cpp208
-rw-r--r--graphics/video/codecs/rpza.h49
-rw-r--r--graphics/video/codecs/smc.cpp385
-rw-r--r--graphics/video/codecs/smc.h59
-rw-r--r--graphics/video/qt_decoder.cpp1279
-rw-r--r--graphics/video/qt_decoder.h282
15 files changed, 3248 insertions, 7 deletions
diff --git a/graphics/conversion.h b/graphics/conversion.h
index 149d7204c6..b6d230612e 100644
--- a/graphics/conversion.h
+++ b/graphics/conversion.h
@@ -45,13 +45,6 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) {
v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b * 83) >> 10) + 128, 0, 255);
}
-/** Converting a color from YUV to RGB colorspace, Cinepak style. */
-inline static void CPYUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) {
- r = CLIP<int>(y + 2 * (v - 128), 0, 255);
- g = CLIP<int>(y - (u - 128) / 2 - (v - 128), 0, 255);
- b = CLIP<int>(y + 2 * (u - 128), 0, 255);
-}
-
// TODO: generic YUV to RGB blit
/**
diff --git a/graphics/module.mk b/graphics/module.mk
index ff6d7f8f60..8f916a5bcc 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -25,10 +25,16 @@ MODULE_OBJS := \
video/dxa_decoder.o \
video/flic_decoder.o \
video/mpeg_player.o \
+ video/qt_decoder.o \
video/smk_decoder.o \
video/video_decoder.o \
+ video/codecs/cinepak.o \
+ video/codecs/mjpeg.o \
video/codecs/msrle.o \
video/codecs/msvideo1.o \
+ video/codecs/qtrle.o \
+ video/codecs/rpza.o \
+ video/codecs/smc.o \
video/coktelvideo/indeo3.o \
video/coktelvideo/coktelvideo.o
diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp
index 944c9700bd..f19ca4aa28 100644
--- a/graphics/video/avi_decoder.cpp
+++ b/graphics/video/avi_decoder.cpp
@@ -35,6 +35,7 @@
#include "graphics/video/avi_decoder.h"
// Codecs
+#include "graphics/video/codecs/cinepak.h"
#include "graphics/video/codecs/msvideo1.h"
#include "graphics/video/codecs/msrle.h"
@@ -379,6 +380,8 @@ Codec *AviDecoder::createCodec() {
return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount);
case ID_RLE :
return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount);
+ case ID_CVID:
+ return new CinepakDecoder();
default:
warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler));
}
diff --git a/graphics/video/codecs/cinepak.cpp b/graphics/video/codecs/cinepak.cpp
new file mode 100644
index 0000000000..d3448bb8f7
--- /dev/null
+++ b/graphics/video/codecs/cinepak.cpp
@@ -0,0 +1,287 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "graphics/video/codecs/cinepak.h"
+
+#include "common/system.h"
+
+// Code here partially based off of ffmpeg ;)
+
+namespace Graphics {
+
+// Convert a color from YUV to RGB colorspace, Cinepak style.
+inline static void CPYUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) {
+ r = CLIP<int>(y + 2 * (v - 128), 0, 255);
+ g = CLIP<int>(y - (u - 128) / 2 - (v - 128), 0, 255);
+ b = CLIP<int>(y + 2 * (u - 128), 0, 255);
+}
+
+#define PUT_PIXEL(offset, lum, u, v) \
+ CPYUV2RGB(lum, u, v, r, g, b); \
+ if (_pixelFormat.bytesPerPixel == 2) \
+ *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \
+ else \
+ *((uint32 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b)
+
+CinepakDecoder::CinepakDecoder() : Codec() {
+ _curFrame.surface = NULL;
+ _curFrame.strips = NULL;
+ _y = 0;
+ _pixelFormat = g_system->getScreenFormat();
+}
+
+CinepakDecoder::~CinepakDecoder() {
+ if (_curFrame.surface)
+ _curFrame.surface->free();
+ delete[] _curFrame.strips;
+}
+
+Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) {
+ _curFrame.flags = stream->readByte();
+ _curFrame.length = (stream->readByte() << 16) + stream->readUint16BE();
+ _curFrame.width = stream->readUint16BE();
+ _curFrame.height = stream->readUint16BE();
+ _curFrame.stripCount = stream->readUint16BE();
+
+ if (_curFrame.strips == NULL)
+ _curFrame.strips = new CinepakStrip[_curFrame.stripCount];
+
+ debug (4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount);
+
+#if 0
+ // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless).
+ // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;)
+ if (_curFrame.length != (uint32)stream->size()) {
+ if (stream->readUint16BE() == 0xFE00)
+ stream->readUint32BE();
+ }
+#endif
+
+ if (!_curFrame.surface) {
+ _curFrame.surface = new Surface();
+ _curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat.bytesPerPixel);
+ }
+
+ // Reset the y variable.
+ _y = 0;
+
+ for (uint16 i = 0; i < _curFrame.stripCount; i++) {
+ if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip
+ for (uint16 j = 0; j < 256; j++) {
+ _curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j];
+ _curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j];
+ }
+ }
+
+ _curFrame.strips[i].id = stream->readUint16BE();
+ _curFrame.strips[i].length = stream->readUint16BE() - 12; // Subtract the 12 byte header
+ _curFrame.strips[i].rect.top = _y; stream->readUint16BE(); // Ignore, substitute with our own.
+ _curFrame.strips[i].rect.left = 0; stream->readUint16BE(); // Ignore, substitute with our own
+ _curFrame.strips[i].rect.bottom = _y + stream->readUint16BE();
+ _curFrame.strips[i].rect.right = _curFrame.width; stream->readUint16BE(); // Ignore, substitute with our own
+
+ //printf ("Left = %d, Top = %d, Right = %d, Bottom = %d\n", _curFrame.strips[i].rect.left, _curFrame.strips[i].rect.top, _curFrame.strips[i].rect.right, _curFrame.strips[i].rect.bottom);
+
+ // Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4.
+ assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4));
+
+ uint32 pos = stream->pos();
+
+ while ((uint32)stream->pos() < (pos + _curFrame.strips[i].length) && !stream->eos()) {
+ byte chunkID = stream->readByte();
+
+ if (stream->eos())
+ break;
+
+ // Chunk Size is 24-bit, ignore the first 4 bytes
+ uint32 chunkSize = stream->readByte() << 16;
+ chunkSize += stream->readUint16BE() - 4;
+
+ int32 startPos = stream->pos();
+
+ switch (chunkID) {
+ case 0x20:
+ case 0x21:
+ case 0x24:
+ case 0x25:
+ loadCodebook(stream, i, 4, chunkID, chunkSize);
+ break;
+ case 0x22:
+ case 0x23:
+ case 0x26:
+ case 0x27:
+ loadCodebook(stream, i, 1, chunkID, chunkSize);
+ break;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ decodeVectors(stream, i, chunkID, chunkSize);
+ break;
+ default:
+ warning("Unknown Cinepak chunk ID %02x", chunkID);
+ return _curFrame.surface;
+ }
+
+ if (stream->pos() != startPos + (int32)chunkSize)
+ stream->seek(startPos + chunkSize);
+ }
+
+ _y = _curFrame.strips[i].rect.bottom;
+ }
+
+ return _curFrame.surface;
+}
+
+void CinepakDecoder::loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize) {
+ CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook;
+
+ int32 startPos = stream->pos();
+ uint32 flag = 0, mask = 0;
+
+ for (uint16 i = 0; i < 256; i++) {
+ if ((chunkID & 0x01) && !(mask >>= 1)) {
+ if ((stream->pos() - startPos + 4) > (int32)chunkSize)
+ break;
+
+ flag = stream->readUint32BE();
+ mask = 0x80000000;
+ }
+
+ if (!(chunkID & 0x01) || (flag & mask)) {
+ byte n = (chunkID & 0x04) ? 4 : 6;
+ if ((stream->pos() - startPos + n) > (int32)chunkSize)
+ break;
+
+ for (byte j = 0; j < 4; j++)
+ codebook[i].y[j] = stream->readByte();
+
+ if (n == 6) {
+ codebook[i].u = stream->readByte() + 128;
+ codebook[i].v = stream->readByte() + 128;
+ } else {
+ /* this codebook type indicates either greyscale or
+ * palettized video; if palettized, U & V components will
+ * not be used so it is safe to set them to 128 for the
+ * benefit of greyscale rendering in YUV420P */
+ codebook[i].u = 128;
+ codebook[i].v = 128;
+ }
+ }
+ }
+}
+
+void CinepakDecoder::decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize) {
+ uint32 flag = 0, mask = 0;
+ uint32 iy[4];
+ int32 startPos = stream->pos();
+ byte r = 0, g = 0, b = 0;
+
+ for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) {
+ iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width;
+ iy[1] = iy[0] + _curFrame.width;
+ iy[2] = iy[1] + _curFrame.width;
+ iy[3] = iy[2] + _curFrame.width;
+
+ for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) {
+ if ((chunkID & 0x01) && !(mask >>= 1)) {
+ if ((stream->pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ flag = stream->readUint32BE();
+ mask = 0x80000000;
+ }
+
+ if (!(chunkID & 0x01) || (flag & mask)) {
+ if (!(chunkID & 0x02) && !(mask >>= 1)) {
+ if ((stream->pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ flag = stream->readUint32BE();
+ mask = 0x80000000;
+ }
+
+ if ((chunkID & 0x02) || (~flag & mask)) {
+ if ((stream->pos() - startPos + 1) > (int32)chunkSize)
+ return;
+
+ // Get the codebook
+ CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream->readByte()];
+
+ PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v);
+
+ PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v);
+
+ PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v);
+
+ PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v);
+ PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
+ } else if (flag & mask) {
+ if ((stream->pos() - startPos + 4) > (int32)chunkSize)
+ return;
+
+ CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()];
+ PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v);
+
+ codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()];
+ PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v);
+
+ codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()];
+ PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v);
+
+ codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()];
+ PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v);
+ PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v);
+ PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
+ }
+ }
+
+ for (byte i = 0; i < 4; i++)
+ iy[i] += 4;
+ }
+ }
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/cinepak.h b/graphics/video/codecs/cinepak.h
new file mode 100644
index 0000000000..92351cdba8
--- /dev/null
+++ b/graphics/video/codecs/cinepak.h
@@ -0,0 +1,81 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GRAPHICS_CINEPAK_H
+#define GRAPHICS_CINEPAK_H
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+#include "graphics/pixelformat.h"
+
+#include "graphics/video/codecs/codec.h"
+
+namespace Graphics {
+
+struct CinepakCodebook {
+ byte y[4];
+ byte u, v;
+};
+
+struct CinepakStrip {
+ uint16 id;
+ uint16 length;
+ Common::Rect rect;
+ CinepakCodebook v1_codebook[256], v4_codebook[256];
+};
+
+struct CinepakFrame {
+ byte flags;
+ uint32 length;
+ uint16 width;
+ uint16 height;
+ uint16 stripCount;
+ CinepakStrip *strips;
+
+ Surface *surface;
+};
+
+class CinepakDecoder : public Codec {
+public:
+ CinepakDecoder();
+ ~CinepakDecoder();
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return _pixelFormat; }
+
+private:
+ CinepakFrame _curFrame;
+ int32 _y;
+ PixelFormat _pixelFormat;
+
+ void loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
+ void decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize);
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/video/codecs/mjpeg.cpp b/graphics/video/codecs/mjpeg.cpp
new file mode 100644
index 0000000000..76363036ee
--- /dev/null
+++ b/graphics/video/codecs/mjpeg.cpp
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/conversion.h" // For YUV2RGB
+
+#include "graphics/video/codecs/mjpeg.h"
+
+namespace Graphics {
+
+JPEGDecoder::JPEGDecoder() : Codec() {
+ _jpeg = new JPEG();
+ _pixelFormat = g_system->getScreenFormat();
+ _surface = NULL;
+}
+
+JPEGDecoder::~JPEGDecoder() {
+ delete _jpeg;
+
+ if (_surface) {
+ _surface->free();
+ delete _surface;
+ }
+}
+
+Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) {
+ _jpeg->read(stream);
+ Surface *ySurface = _jpeg->getComponent(1);
+ Surface *uSurface = _jpeg->getComponent(2);
+ Surface *vSurface = _jpeg->getComponent(3);
+
+ if (!_surface) {
+ _surface = new Surface();
+ _surface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel);
+ }
+
+ for (uint16 i = 0; i < _surface->h; i++) {
+ for (uint16 j = 0; j < _surface->w; j++) {
+ byte r = 0, g = 0, b = 0;
+ YUV2RGB(*((byte *)ySurface->getBasePtr(j, i)), *((byte *)uSurface->getBasePtr(j, i)), *((byte *)vSurface->getBasePtr(j, i)), r, g, b);
+ if (_pixelFormat.bytesPerPixel == 2)
+ *((uint16 *)_surface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
+ else
+ *((uint32 *)_surface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
+ }
+ }
+
+ return _surface;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/mjpeg.h b/graphics/video/codecs/mjpeg.h
new file mode 100644
index 0000000000..ab364fb5be
--- /dev/null
+++ b/graphics/video/codecs/mjpeg.h
@@ -0,0 +1,58 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GRAPHICS_MJPEG_H
+#define GRAPHICS_MJPEG_H
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+
+#include "graphics/video/codecs/codec.h"
+#include "graphics/jpeg.h"
+#include "graphics/pixelformat.h"
+
+namespace Graphics {
+
+// Motion JPEG Decoder
+// Basically a wrapper around JPEG which converts to RGB and also functions
+// as a Codec.
+
+class JPEGDecoder : public Codec {
+public:
+ JPEGDecoder();
+ ~JPEGDecoder();
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return _pixelFormat; }
+
+private:
+ PixelFormat _pixelFormat;
+ JPEG *_jpeg;
+ Surface *_surface;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/video/codecs/qtrle.cpp b/graphics/video/codecs/qtrle.cpp
new file mode 100644
index 0000000000..3e3fd4cfce
--- /dev/null
+++ b/graphics/video/codecs/qtrle.cpp
@@ -0,0 +1,420 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// QuickTime RLE Decoder
+// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson)
+
+#include "graphics/video/codecs/qtrle.h"
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+namespace Graphics {
+
+QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
+ _bitsPerPixel = bitsPerPixel;
+ _pixelFormat = g_system->getScreenFormat();
+
+ // We need to increase the surface size to a multiple of 4
+ uint16 wMod = width % 4;
+ if(wMod != 0)
+ width += 4 - wMod;
+
+ debug(2, "QTRLE corrected width: %d", width);
+
+ _surface = new Surface();
+ _surface->create(width, height, _bitsPerPixel <= 8 ? 1 : _pixelFormat.bytesPerPixel);
+}
+
+#define CHECK_STREAM_PTR(n) \
+ if ((stream->pos() + n) > stream->size()) { \
+ warning ("Problem: stream out of bounds (%d >= %d)", stream->pos() + n, stream->size()); \
+ return; \
+ }
+
+#define CHECK_PIXEL_PTR(n) \
+ if ((int32)pixelPtr + n > _surface->w * _surface->h) { \
+ warning ("Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \
+ return; \
+ } \
+
+void QTRLEDecoder::decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ byte *rgb = (byte *)_surface->pixels;
+
+ while (linesToChange) {
+ CHECK_STREAM_PTR(2);
+ byte skip = stream->readByte();
+ int8 rleCode = stream->readSByte();
+
+ if (rleCode == 0)
+ break;
+
+ if (skip & 0x80) {
+ linesToChange--;
+ rowPtr += _surface->w;
+ pixelPtr = rowPtr + 2 * (skip & 0x7f);
+ } else
+ pixelPtr += 2 * skip;
+
+ if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+ // get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */
+ CHECK_STREAM_PTR(2);
+ byte pi0 = stream->readByte();
+ byte pi1 = stream->readByte();
+ CHECK_PIXEL_PTR(rleCode * 2);
+
+ while (rleCode--) {
+ rgb[pixelPtr++] = pi0;
+ rgb[pixelPtr++] = pi1;
+ }
+ } else {
+ // copy the same pixel directly to output 2 times
+ rleCode *= 2;
+ CHECK_STREAM_PTR(rleCode);
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--)
+ rgb[pixelPtr++] = stream->readByte();
+ }
+ }
+}
+
+void QTRLEDecoder::decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp) {
+ uint32 pixelPtr = 0;
+ byte *rgb = (byte *)_surface->pixels;
+ byte numPixels = (bpp == 4) ? 8 : 16;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+ pixelPtr = rowPtr + (numPixels * (stream->readByte() - 1));
+
+ for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += (numPixels * (stream->readByte() - 1));
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+
+ // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */
+ CHECK_STREAM_PTR(4);
+
+ byte pi[16]; // 16 palette indices
+
+ for (int8 i = numPixels - 1; i >= 0; i--) {
+ pi[numPixels - 1 - i] = (stream->readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1);
+
+ // FIXME: Is this right?
+ //stream_ptr += ((i & ((num_pixels>>2)-1)) == 0);
+ if ((i & ((numPixels >> 2) - 1)) == 0)
+ stream->readByte();
+ }
+
+ CHECK_PIXEL_PTR(rleCode * numPixels);
+
+ while (rleCode--)
+ for (byte i = 0; i < numPixels; i++)
+ rgb[pixelPtr++] = pi[i];
+ } else {
+ // copy the same pixel directly to output 4 times
+ rleCode *= 4;
+ CHECK_STREAM_PTR(rleCode);
+ CHECK_PIXEL_PTR(rleCode * (numPixels >> 2));
+
+ while (rleCode--) {
+ byte temp = stream->readByte();
+ if (bpp == 4) {
+ rgb[pixelPtr++] = (temp >> 4) & 0x0f;
+ rgb[pixelPtr++] = temp & 0x0f;
+ } else {
+ rgb[pixelPtr++] = (temp >> 6) & 0x03;
+ rgb[pixelPtr++] = (temp >> 4) & 0x03;
+ rgb[pixelPtr++] = (temp >> 2) & 0x03;
+ rgb[pixelPtr++] = temp & 0x03;
+ }
+ }
+ }
+ }
+
+ rowPtr += _surface->w;
+ }
+}
+
+void QTRLEDecoder::decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ byte *rgb = (byte *)_surface->pixels;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+ pixelPtr = rowPtr + 4 * (stream->readByte() - 1);
+
+ for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += 4 * (stream->readByte() - 1);
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+
+ // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times
+ CHECK_STREAM_PTR(4);
+
+ byte pi[4]; // 4 palette indexes
+
+ for (byte i = 0; i < 4; i++)
+ pi[i] = stream->readByte();
+
+ CHECK_PIXEL_PTR(rleCode * 4);
+
+ while (rleCode--)
+ for (byte i = 0; i < 4; i++)
+ rgb[pixelPtr++] = pi[i];
+ } else {
+ // copy the same pixel directly to output 4 times
+ rleCode *= 4;
+ CHECK_STREAM_PTR(rleCode);
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--)
+ rgb[pixelPtr++] = stream->readByte();
+ }
+ }
+
+ rowPtr += _surface->w;
+ }
+}
+
+void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ OverlayColor *rgb = (OverlayColor *)_surface->pixels;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+ pixelPtr = rowPtr + stream->readByte() - 1;
+
+ for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream->readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+ CHECK_STREAM_PTR(2);
+
+ uint16 rgb16 = stream->readUint16BE();
+
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--) {
+ // Convert from RGB555 to the format specified by the Overlay
+ byte r = 0, g = 0, b = 0;
+ colorToRGB<ColorMasks<555> >(rgb16, r, g, b);
+ rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b);
+ }
+ } else {
+ CHECK_STREAM_PTR(rleCode * 2);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ uint16 rgb16 = stream->readUint16BE();
+
+ // Convert from RGB555 to the format specified by the Overlay
+ byte r = 0, g = 0, b = 0;
+ colorToRGB<ColorMasks<555> >(rgb16, r, g, b);
+ rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b);
+ }
+ }
+ }
+
+ rowPtr += _surface->w;
+ }
+}
+
+void QTRLEDecoder::decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ OverlayColor *rgb = (OverlayColor *)_surface->pixels;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+ pixelPtr = rowPtr + stream->readByte() - 1;
+
+ for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream->readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+
+ CHECK_STREAM_PTR(3);
+
+ byte r = stream->readByte();
+ byte g = stream->readByte();
+ byte b = stream->readByte();
+
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--)
+ rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b);
+ } else {
+ CHECK_STREAM_PTR(rleCode * 3);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ byte r = stream->readByte();
+ byte g = stream->readByte();
+ byte b = stream->readByte();
+ rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b);
+ }
+ }
+ }
+
+ rowPtr += _surface->w;
+ }
+}
+
+void QTRLEDecoder::decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ OverlayColor *rgb = (OverlayColor *)_surface->pixels;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+ pixelPtr = rowPtr + stream->readByte() - 1;
+
+ for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream->readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+
+ CHECK_STREAM_PTR(4);
+
+ byte a = stream->readByte();
+ byte r = stream->readByte();
+ byte g = stream->readByte();
+ byte b = stream->readByte();
+
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--)
+ rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b);
+ } else {
+ CHECK_STREAM_PTR(rleCode * 4);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ byte a = stream->readByte();
+ byte r = stream->readByte();
+ byte g = stream->readByte();
+ byte b = stream->readByte();
+ rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b);
+ }
+ }
+ }
+
+ rowPtr += _surface->w;
+ }
+}
+
+Surface *QTRLEDecoder::decodeImage(Common::SeekableReadStream *stream) {
+ uint16 start_line = 0;
+ uint16 height = _surface->h;
+
+ // check if this frame is even supposed to change
+ if (stream->size() < 8)
+ return _surface;
+
+ // start after the chunk size
+ stream->readUint32BE();
+
+ // fetch the header
+ uint16 header = stream->readUint16BE();
+
+ // if a header is present, fetch additional decoding parameters
+ if (header & 8) {
+ if(stream->size() < 14)
+ return _surface;
+ start_line = stream->readUint16BE();
+ stream->readUint16BE(); // Unknown
+ height = stream->readUint16BE();
+ stream->readUint16BE(); // Unknown
+ }
+
+ uint32 row_ptr = _surface->w * start_line;
+
+ switch (_bitsPerPixel) {
+ case 1:
+ case 33:
+ decode1(stream, row_ptr, height);
+ break;
+ case 2:
+ case 34:
+ decode2_4(stream, row_ptr, height, 2);
+ break;
+ case 4:
+ case 36:
+ decode2_4(stream, row_ptr, height, 4);
+ break;
+ case 8:
+ case 40:
+ decode8(stream, row_ptr, height);
+ break;
+ case 16:
+ decode16(stream, row_ptr, height);
+ break;
+ case 24:
+ decode24(stream, row_ptr, height);
+ break;
+ case 32:
+ decode32(stream, row_ptr, height);
+ break;
+ default:
+ error ("Unsupported bits per pixel %d", _bitsPerPixel);
+ }
+
+ return _surface;
+}
+
+QTRLEDecoder::~QTRLEDecoder() {
+ _surface->free();
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/qtrle.h b/graphics/video/codecs/qtrle.h
new file mode 100644
index 0000000000..efbef14411
--- /dev/null
+++ b/graphics/video/codecs/qtrle.h
@@ -0,0 +1,58 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GRAPHICS_VIDEO_QTRLE_H
+#define GRAPHICS_VIDEO_QTRLE_H
+
+#include "graphics/pixelformat.h"
+#include "graphics/video/codecs/codec.h"
+
+namespace Graphics {
+
+class QTRLEDecoder : public Codec {
+public:
+ QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel);
+ ~QTRLEDecoder();
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return _pixelFormat; }
+
+private:
+ byte _bitsPerPixel;
+
+ Surface *_surface;
+ PixelFormat _pixelFormat;
+
+ void decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange);
+ void decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp);
+ void decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange);
+ void decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange);
+ void decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange);
+ void decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange);
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/video/codecs/rpza.cpp b/graphics/video/codecs/rpza.cpp
new file mode 100644
index 0000000000..f0ed72e730
--- /dev/null
+++ b/graphics/video/codecs/rpza.cpp
@@ -0,0 +1,208 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+ // Based off ffmpeg's RPZA decoder
+
+#include "graphics/video/codecs/rpza.h"
+
+#include "common/system.h"
+#include "graphics/colormasks.h"
+
+namespace Graphics {
+
+RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() {
+ _pixelFormat = g_system->getScreenFormat();
+
+ // We need to increase the surface size to a multiple of 4
+ uint16 wMod = width % 4;
+ if(wMod != 0)
+ width += 4 - wMod;
+
+ debug(2, "RPZA corrected width: %d", width);
+
+ _surface = new Surface();
+ _surface->create(width, height, _pixelFormat.bytesPerPixel);
+}
+
+#define ADVANCE_BLOCK() \
+ pixelPtr += 4; \
+ if (pixelPtr >= _surface->w) { \
+ pixelPtr = 0; \
+ rowPtr += _surface->w * 4; \
+ } \
+ totalBlocks--; \
+ if (totalBlocks < 0) \
+ error("block counter just went negative (this should not happen)") \
+
+// Convert from RGB555 to the format specified by the screen
+#define PUT_PIXEL(color) \
+ if ((int32)blockPtr < _surface->w * _surface->h) { \
+ byte r = 0, g = 0, b = 0; \
+ colorToRGB<ColorMasks<555> >(color, r, g, b); \
+ if (_pixelFormat.bytesPerPixel == 2) \
+ *((uint16 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \
+ else \
+ *((uint32 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \
+ } \
+ blockPtr++
+
+Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) {
+ uint16 colorA = 0, colorB = 0;
+ uint16 color4[4];
+
+ uint32 rowPtr = 0;
+ uint32 pixelPtr = 0;
+ uint32 blockPtr = 0;
+ uint32 rowInc = _surface->w - 4;
+ uint16 ta;
+ uint16 tb;
+
+ // First byte is always 0xe1. Warn if it's different
+ byte firstByte = stream->readByte();
+ if (firstByte != 0xe1)
+ warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte);
+
+ // Get chunk size, ingnoring first byte
+ uint32 chunkSize = stream->readUint16BE() << 8;
+ chunkSize += stream->readByte();
+
+ // If length mismatch use size from MOV file and try to decode anyway
+ if (chunkSize != (uint32)stream->size()) {
+ warning("MOV chunk size != encoded chunk size; using MOV chunk size");
+ chunkSize = stream->size();
+ }
+
+ // Number of 4x4 blocks in frame
+ int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
+
+ // Process chunk data
+ while ((uint32)stream->pos() < chunkSize) {
+ byte opcode = stream->readByte(); // Get opcode
+ byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode
+
+ // If opcode MSbit is 0, we need more data to decide what to do
+ if ((opcode & 0x80) == 0) {
+ colorA = (opcode << 8) | stream->readByte();
+ opcode = 0;
+ if (stream->readByte() & 0x80) {
+ // Must behave as opcode 110xxxxx, using colorA computed
+ // above. Use fake opcode 0x20 to enter switch block at
+ // the right place
+ opcode = 0x20;
+ numBlocks = 1;
+ }
+ stream->seek(-1, SEEK_CUR);
+ }
+
+ switch (opcode & 0xe0) {
+ case 0x80: // Skip blocks
+ while (numBlocks--) {
+ ADVANCE_BLOCK();
+ }
+ break;
+ case 0xa0: // Fill blocks with one color
+ colorA = stream->readUint16BE();
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+ for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
+ for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
+ PUT_PIXEL(colorA);
+ }
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // Fill blocks with 4 colors
+ case 0xc0:
+ colorA = stream->readUint16BE();
+ case 0x20:
+ colorB = stream->readUint16BE();
+
+ // Sort out the colors
+ color4[0] = colorB;
+ color4[1] = 0;
+ color4[2] = 0;
+ color4[3] = colorA;
+
+ // Red components
+ ta = (colorA >> 10) & 0x1F;
+ tb = (colorB >> 10) & 0x1F;
+ color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
+ color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
+
+ // Green components
+ ta = (colorA >> 5) & 0x1F;
+ tb = (colorB >> 5) & 0x1F;
+ color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
+ color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
+
+ // Blue components
+ ta = colorA & 0x1F;
+ tb = colorB & 0x1F;
+ color4[1] |= ((11 * ta + 21 * tb) >> 5);
+ color4[2] |= ((21 * ta + 11 * tb) >> 5);
+
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+ for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
+ byte index = stream->readByte();
+ for (byte pixel_x = 0; pixel_x < 4; pixel_x++){
+ byte idx = (index >> (2 * (3 - pixel_x))) & 0x03;
+ PUT_PIXEL(color4[idx]);
+ }
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // Fill block with 16 colors
+ case 0x00:
+ blockPtr = rowPtr + pixelPtr;
+ for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
+ for (byte pixel_x = 0; pixel_x < 4; pixel_x++){
+ // We already have color of upper left pixel
+ if (pixel_y != 0 || pixel_x != 0)
+ colorA = stream->readUint16BE();
+
+ PUT_PIXEL(colorA);
+ }
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ break;
+
+ // Unknown opcode
+ default:
+ error("Unknown opcode %02x in rpza chunk", opcode);
+ }
+ }
+
+ return _surface;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/rpza.h b/graphics/video/codecs/rpza.h
new file mode 100644
index 0000000000..e6d32feb72
--- /dev/null
+++ b/graphics/video/codecs/rpza.h
@@ -0,0 +1,49 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GRAPHICS_VIDEO_RPZA_H
+#define GRAPHICS_VIDEO_RPZA_H
+
+#include "graphics/pixelformat.h"
+#include "graphics/video/codecs/codec.h"
+
+namespace Graphics {
+
+class RPZADecoder : public Codec {
+public:
+ RPZADecoder(uint16 width, uint16 height);
+ ~RPZADecoder() { delete _surface; }
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return _pixelFormat; }
+
+private:
+ Surface *_surface;
+ PixelFormat _pixelFormat;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/video/codecs/smc.cpp b/graphics/video/codecs/smc.cpp
new file mode 100644
index 0000000000..4661e3dc84
--- /dev/null
+++ b/graphics/video/codecs/smc.cpp
@@ -0,0 +1,385 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based off ffmpeg's SMC decoder
+
+#include "graphics/video/codecs/smc.h"
+
+namespace Graphics {
+
+#define GET_BLOCK_COUNT() \
+ (opcode & 0x10) ? (1 + stream->readByte()) : 1 + (opcode & 0x0F);
+
+#define ADVANCE_BLOCK() \
+{ \
+ pixelPtr += 4; \
+ if (pixelPtr >= _surface->w) { \
+ pixelPtr = 0; \
+ rowPtr += _surface->w * 4; \
+ } \
+ totalBlocks--; \
+ if (totalBlocks < 0) { \
+ warning("block counter just went negative (this should not happen)"); \
+ return _surface; \
+ } \
+}
+
+SMCDecoder::SMCDecoder(uint16 width, uint16 height) {
+ _surface = new Graphics::Surface();
+ _surface->create(width, height, 1);
+}
+
+Graphics::Surface *SMCDecoder::decodeImage(Common::SeekableReadStream *stream) {
+ byte *pixels = (byte *)_surface->pixels;
+
+ uint32 numBlocks = 0;
+ uint32 colorFlags = 0;
+ uint32 colorFlagsA = 0;
+ uint32 colorFlagsB = 0;
+
+ const uint16 rowInc = _surface->w - 4;
+ int32 rowPtr = 0;
+ int32 pixelPtr = 0;
+ uint32 blockPtr = 0;
+ uint32 prevBlockPtr = 0;
+ uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0;
+ byte prevBlockFlag = false;
+ byte pixel = 0;
+
+ uint32 colorPairIndex = 0;
+ uint32 colorQuadIndex = 0;
+ uint32 colorOctetIndex = 0;
+ uint32 colorTableIndex = 0; // indices to color pair, quad, or octet tables
+
+ int32 chunkSize = stream->readUint32BE() & 0x00FFFFFF;
+ if (chunkSize != stream->size())
+ warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, stream->size());
+
+ int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
+
+ // traverse through the blocks
+ while (totalBlocks != 0) {
+ // sanity checks
+
+ // make sure stream ptr hasn't gone out of bounds
+ if (stream->pos() > stream->size()) {
+ warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", stream->pos(), stream->size());
+ return _surface;
+ }
+
+ // make sure the row pointer hasn't gone wild
+ if (rowPtr >= _surface->w * _surface->h) {
+ warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h);
+ return _surface;
+ }
+
+ byte opcode = stream->readByte();
+
+ switch (opcode & 0xF0) {
+ // skip n blocks
+ case 0x00:
+ case 0x10:
+ numBlocks = GET_BLOCK_COUNT();
+ while (numBlocks--) {
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // repeat last block n times
+ case 0x20:
+ case 0x30:
+ numBlocks = GET_BLOCK_COUNT();
+
+ // sanity check
+ if (rowPtr == 0 && pixelPtr == 0) {
+ warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0);
+ break;
+ }
+
+ // figure out where the previous block started
+ if (pixelPtr == 0)
+ prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4;
+ else
+ prevBlockPtr1 = rowPtr + pixelPtr - 4;
+
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+ prevBlockPtr = prevBlockPtr1;
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++)
+ pixels[blockPtr++] = pixels[prevBlockPtr++];
+ blockPtr += rowInc;
+ prevBlockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // repeat previous pair of blocks n times
+ case 0x40:
+ case 0x50:
+ numBlocks = GET_BLOCK_COUNT();
+ numBlocks *= 2;
+
+ // sanity check
+ if (rowPtr == 0 && pixelPtr < 2 * 4) {
+ warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0);
+ break;
+ }
+
+ // figure out where the previous 2 blocks started
+ if (pixelPtr == 0)
+ prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2;
+ else if (pixelPtr == 4)
+ prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc;
+ else
+ prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2;
+
+ if (pixelPtr == 0)
+ prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc;
+ else
+ prevBlockPtr2 = rowPtr + pixelPtr - 4;
+
+ prevBlockFlag = 0;
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+
+ if (prevBlockFlag)
+ prevBlockPtr = prevBlockPtr2;
+ else
+ prevBlockPtr = prevBlockPtr1;
+
+ prevBlockFlag = !prevBlockFlag;
+
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++)
+ pixels[blockPtr++] = pixels[prevBlockPtr++];
+
+ blockPtr += rowInc;
+ prevBlockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // 1-color block encoding
+ case 0x60:
+ case 0x70:
+ numBlocks = GET_BLOCK_COUNT();
+ pixel = stream->readByte();
+
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++)
+ pixels[blockPtr++] = pixel;
+
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // 2-color block encoding
+ case 0x80:
+ case 0x90:
+ numBlocks = (opcode & 0x0F) + 1;
+
+ // figure out which color pair to use to paint the 2-color block
+ if ((opcode & 0xF0) == 0x80) {
+ // fetch the next 2 colors from bytestream and store in next
+ // available entry in the color pair table
+ for (byte i = 0; i < CPAIR; i++) {
+ pixel = stream->readByte();
+ colorTableIndex = CPAIR * colorPairIndex + i;
+ _colorPairs[colorTableIndex] = pixel;
+ }
+
+ // this is the base index to use for this block
+ colorTableIndex = CPAIR * colorPairIndex;
+ colorPairIndex++;
+
+ // wraparound
+ if (colorPairIndex == COLORS_PER_TABLE)
+ colorPairIndex = 0;
+ } else
+ colorTableIndex = CPAIR * stream->readByte();
+
+ while (numBlocks--) {
+ colorFlags = stream->readUint16BE();
+ uint16 flagMask = 0x8000;
+ blockPtr = rowPtr + pixelPtr;
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++) {
+ if (colorFlags & flagMask)
+ pixel = colorTableIndex + 1;
+ else
+ pixel = colorTableIndex;
+
+ flagMask >>= 1;
+ pixels[blockPtr++] = _colorPairs[pixel];
+ }
+
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // 4-color block encoding
+ case 0xA0:
+ case 0xB0:
+ numBlocks = (opcode & 0x0F) + 1;
+
+ // figure out which color quad to use to paint the 4-color block
+ if ((opcode & 0xF0) == 0xA0) {
+ // fetch the next 4 colors from bytestream and store in next
+ // available entry in the color quad table
+ for (byte i = 0; i < CQUAD; i++) {
+ pixel = stream->readByte();
+ colorTableIndex = CQUAD * colorQuadIndex + i;
+ _colorQuads[colorTableIndex] = pixel;
+ }
+
+ // this is the base index to use for this block
+ colorTableIndex = CQUAD * colorQuadIndex;
+ colorQuadIndex++;
+
+ // wraparound
+ if (colorQuadIndex == COLORS_PER_TABLE)
+ colorQuadIndex = 0;
+ } else
+ colorTableIndex = CQUAD * stream->readByte();
+
+ while (numBlocks--) {
+ colorFlags = stream->readUint32BE();
+
+ // flag mask actually acts as a bit shift count here
+ byte flagMask = 30;
+ blockPtr = rowPtr + pixelPtr;
+
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++) {
+ pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03);
+ flagMask -= 2;
+ pixels[blockPtr++] = _colorQuads[pixel];
+ }
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // 8-color block encoding
+ case 0xC0:
+ case 0xD0:
+ numBlocks = (opcode & 0x0F) + 1;
+
+ // figure out which color octet to use to paint the 8-color block
+ if ((opcode & 0xF0) == 0xC0) {
+ // fetch the next 8 colors from bytestream and store in next
+ // available entry in the color octet table
+ for (byte i = 0; i < COCTET; i++) {
+ pixel = stream->readByte();
+ colorTableIndex = COCTET * colorOctetIndex + i;
+ _colorOctets[colorTableIndex] = pixel;
+ }
+
+ // this is the base index to use for this block
+ colorTableIndex = COCTET * colorOctetIndex;
+ colorOctetIndex++;
+
+ // wraparound
+ if (colorOctetIndex == COLORS_PER_TABLE)
+ colorOctetIndex = 0;
+ } else
+ colorTableIndex = COCTET * stream->readByte();
+
+ while (numBlocks--) {
+ /*
+ For this input of 6 hex bytes:
+ 01 23 45 67 89 AB
+ Mangle it to this output:
+ flags_a = xx012456, flags_b = xx89A37B
+ */
+
+ // build the color flags
+ byte flagData[6];
+ stream->read(flagData, 6);
+
+ colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4);
+ colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) |
+ ((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf);
+
+ colorFlags = colorFlagsA;
+
+ // flag mask actually acts as a bit shift count here
+ byte flagMask = 21;
+ blockPtr = rowPtr + pixelPtr;
+ for (byte y = 0; y < 4; y++) {
+ // reload flags at third row (iteration y == 2)
+ if (y == 2) {
+ colorFlags = colorFlagsB;
+ flagMask = 21;
+ }
+
+ for (byte x = 0; x < 4; x++) {
+ pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07);
+ flagMask -= 3;
+ pixels[blockPtr++] = _colorOctets[pixel];
+ }
+
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ // 16-color block encoding (every pixel is a different color)
+ case 0xE0:
+ numBlocks = (opcode & 0x0F) + 1;
+
+ while (numBlocks--) {
+ blockPtr = rowPtr + pixelPtr;
+ for (byte y = 0; y < 4; y++) {
+ for (byte x = 0; x < 4; x++)
+ pixels[blockPtr++] = stream->readByte();
+
+ blockPtr += rowInc;
+ }
+ ADVANCE_BLOCK();
+ }
+ break;
+
+ case 0xF0:
+ warning("0xF0 opcode seen in SMC chunk (contact the developers)");
+ break;
+ }
+ }
+
+ return _surface;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/smc.h b/graphics/video/codecs/smc.h
new file mode 100644
index 0000000000..2d4355a83e
--- /dev/null
+++ b/graphics/video/codecs/smc.h
@@ -0,0 +1,59 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GRAPHICS_VIDEO_SMC_H
+#define GRAPHICS_VIDEO_SMC_H
+
+#include "graphics/video/codecs/codec.h"
+
+namespace Graphics {
+
+enum {
+ CPAIR = 2,
+ CQUAD = 4,
+ COCTET = 8,
+ COLORS_PER_TABLE = 256
+};
+
+class SMCDecoder : public Codec {
+public:
+ SMCDecoder(uint16 width, uint16 height);
+ ~SMCDecoder() { delete _surface; }
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
+
+private:
+ Surface *_surface;
+
+ // SMC color tables
+ byte _colorPairs[COLORS_PER_TABLE * CPAIR];
+ byte _colorQuads[COLORS_PER_TABLE * CQUAD];
+ byte _colorOctets[COLORS_PER_TABLE * COCTET];
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/video/qt_decoder.cpp b/graphics/video/qt_decoder.cpp
new file mode 100644
index 0000000000..5351b9b676
--- /dev/null
+++ b/graphics/video/qt_decoder.cpp
@@ -0,0 +1,1279 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+//
+// Heavily based on ffmpeg code.
+//
+// Copyright (c) 2001 Fabrice Bellard.
+// First version by Francois Revol revol@free.fr
+// Seek function by Gael Chardon gael.dev@4now.net
+//
+
+#include "graphics/video/qt_decoder.h"
+
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/util.h"
+#include "common/zlib.h"
+
+// Audio codecs
+#include "sound/decoders/adpcm.h"
+#include "sound/decoders/raw.h"
+#include "sound/decoders/qdm2.h"
+
+// Video codecs
+#include "graphics/video/codecs/cinepak.h"
+#include "graphics/video/codecs/mjpeg.h"
+#include "graphics/video/codecs/qtrle.h"
+#include "graphics/video/codecs/rpza.h"
+#include "graphics/video/codecs/smc.h"
+
+namespace Graphics {
+
+////////////////////////////////////////////
+// QuickTimeDecoder
+////////////////////////////////////////////
+
+QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() {
+ _audStream = NULL;
+ _beginOffset = 0;
+ _videoCodec = NULL;
+ _curFrame = -1;
+ _startTime = _nextFrameStartTime = 0;
+ _audHandle = Audio::SoundHandle();
+ _numStreams = 0;
+ _fd = 0;
+ _scaledSurface = 0;
+ _dirtyPalette = false;
+}
+
+QuickTimeDecoder::~QuickTimeDecoder() {
+ close();
+}
+
+uint16 QuickTimeDecoder::getWidth() const {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ return _streams[_videoStreamIndex]->width / getScaleMode();
+}
+
+uint16 QuickTimeDecoder::getHeight() const {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ return _streams[_videoStreamIndex]->height / getScaleMode();
+}
+
+uint32 QuickTimeDecoder::getFrameCount() const {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ return _streams[_videoStreamIndex]->nb_frames;
+}
+
+byte QuickTimeDecoder::getBitsPerPixel() {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ return _streams[_videoStreamIndex]->bits_per_sample & 0x1F;
+}
+
+uint32 QuickTimeDecoder::getCodecTag() {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ return _streams[_videoStreamIndex]->codec_tag;
+}
+
+ScaleMode QuickTimeDecoder::getScaleMode() const {
+ if (_videoStreamIndex < 0)
+ return kScaleNormal;
+
+ return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode);
+}
+
+uint32 QuickTimeDecoder::getFrameDuration() {
+ if (_videoStreamIndex < 0)
+ return 0;
+
+ uint32 curFrameIndex = 0;
+ for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count; i++) {
+ curFrameIndex += _streams[_videoStreamIndex]->stts_data[i].count;
+ if ((uint32)_curFrame < curFrameIndex) {
+ // Ok, now we have what duration this frame has.
+ return _streams[_videoStreamIndex]->stts_data[i].duration;
+ }
+ }
+
+ // This should never occur
+ error ("Cannot find duration for frame %d", _curFrame);
+ return 0;
+}
+
+PixelFormat QuickTimeDecoder::getPixelFormat() const {
+ if (!_videoCodec)
+ return PixelFormat::createFormatCLUT8();
+
+ return _videoCodec->getPixelFormat();
+}
+
+void QuickTimeDecoder::rewind() {
+ delete _videoCodec; _videoCodec = NULL;
+ _curFrame = -1;
+ _startTime = _nextFrameStartTime = 0;
+
+ // Restart the audio too
+ stopAudio();
+ if (_audioStreamIndex >= 0) {
+ _curAudioChunk = 0;
+ _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2);
+ }
+ startAudio();
+}
+
+Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) {
+ if (codecTag == MKID_BE('cvid')) {
+ // Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this.
+ return new CinepakDecoder();
+ } else if (codecTag == MKID_BE('rpza')) {
+ // Apple Video ("Road Pizza"): Used by some Myst videos.
+ return new RPZADecoder(getWidth(), getHeight());
+ } else if (codecTag == MKID_BE('rle ')) {
+ // QuickTime RLE: Used by some Myst ME videos.
+ return new QTRLEDecoder(getWidth(), getHeight(), bitsPerPixel);
+ } else if (codecTag == MKID_BE('smc ')) {
+ // Apple SMC: Used by some Myst videos.
+ return new SMCDecoder(getWidth(), getHeight());
+ } else if (codecTag == MKID_BE('SVQ1')) {
+ // Sorenson Video 1: Used by some Myst ME videos.
+ warning ("Sorenson Video 1 not yet supported");
+ } else if (codecTag == MKID_BE('SVQ3')) {
+ // Sorenson Video 3: Used by some Myst ME videos.
+ warning ("Sorenson Video 3 not yet supported");
+ } else if (codecTag == MKID_BE('jpeg')) {
+ // Motion JPEG: Used by some Myst ME 10th Anniversary videos.
+ return new JPEGDecoder();
+ } else if (codecTag == MKID_BE('QkBk')) {
+ // CDToons: Used by most of the Broderbund games. This is an unknown format so far.
+ warning ("CDToons not yet supported");
+ } else {
+ warning ("Unsupported codec \'%s\'", tag2str(codecTag));
+ }
+
+ return NULL;
+}
+
+void QuickTimeDecoder::startAudio() {
+ if (_audStream) // No audio/audio not supported
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream);
+}
+
+void QuickTimeDecoder::stopAudio() {
+ if (_audStream) {
+ g_system->getMixer()->stopHandle(_audHandle);
+ _audStream = NULL; // the mixer automatically frees the stream
+ }
+}
+
+void QuickTimeDecoder::pauseVideoIntern(bool pause) {
+ if (_audStream)
+ g_system->getMixer()->pauseHandle(_audHandle, pause);
+}
+
+Surface *QuickTimeDecoder::decodeNextFrame() {
+ if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1)
+ return NULL;
+
+ if (_startTime == 0)
+ _startTime = g_system->getMillis();
+
+ _curFrame++;
+ _nextFrameStartTime += getFrameDuration();
+
+ Common::SeekableReadStream *frameData = getNextFramePacket();
+
+ if (frameData) {
+ Surface *frame = _videoCodec->decodeImage(frameData);
+ delete frameData;
+ return scaleSurface(frame);
+ }
+
+ return NULL;
+}
+
+Surface *QuickTimeDecoder::scaleSurface(Surface *frame) {
+ if (getScaleMode() == kScaleNormal)
+ 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);
+
+ return _scaledSurface;
+}
+
+bool QuickTimeDecoder::endOfVideo() const {
+ return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1);
+}
+
+bool QuickTimeDecoder::needsUpdate() const {
+ return !endOfVideo() && getTimeToNextFrame() == 0;
+}
+
+uint32 QuickTimeDecoder::getElapsedTime() const {
+ if (_audStream)
+ return g_system->getMixer()->getSoundElapsedTime(_audHandle);
+
+ return g_system->getMillis() - _startTime;
+}
+
+uint32 QuickTimeDecoder::getTimeToNextFrame() const {
+ if (endOfVideo() || _curFrame < 0)
+ return 0;
+
+ // Convert from the Sega FILM base to 1000
+ uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _streams[_videoStreamIndex]->time_scale;
+ uint32 elapsedTime = getElapsedTime();
+
+ if (nextFrameStartTime <= elapsedTime)
+ return 0;
+
+ return nextFrameStartTime - elapsedTime;
+}
+
+bool QuickTimeDecoder::load(Common::SeekableReadStream &stream) {
+ _fd = &stream;
+ _foundMOOV = _foundMDAT = false;
+ _numStreams = 0;
+ _partial = 0;
+ _videoStreamIndex = _audioStreamIndex = -1;
+ _startTime = 0;
+
+ initParseTable();
+
+ MOVatom atom = { 0, 0, 0xffffffff };
+
+ if (readDefault(atom) < 0 || (!_foundMOOV && !_foundMDAT))
+ return false;
+
+ debug(0, "on_parse_exit_offset=%d", _fd->pos());
+
+ // some cleanup : make sure we are on the mdat atom
+ if((uint32)_fd->pos() != _mdatOffset)
+ _fd->seek(_mdatOffset, SEEK_SET);
+
+ _next_chunk_offset = _mdatOffset; // initialise reading
+
+ for (uint32 i = 0; i < _numStreams;) {
+ if (_streams[i]->codec_type == CODEC_TYPE_MOV_OTHER) {// not audio, not video, delete
+ delete _streams[i];
+ for (uint32 j = i + 1; j < _numStreams; j++)
+ _streams[j - 1] = _streams[j];
+ _numStreams--;
+ } else
+ i++;
+ }
+
+ for (uint32 i = 0; i < _numStreams; i++) {
+ MOVStreamContext *sc = _streams[i];
+
+ if(!sc->time_rate)
+ sc->time_rate = 1;
+
+ if(!sc->time_scale)
+ sc->time_scale = _timeScale;
+
+ //av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale);
+
+ sc->duration /= sc->time_rate;
+
+ sc->ffindex = i;
+ sc->is_ff_stream = 1;
+
+ if (sc->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0)
+ _videoStreamIndex = i;
+ else if (sc->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0)
+ _audioStreamIndex = i;
+ }
+
+ if (_audioStreamIndex >= 0 && checkAudioCodecSupport(_streams[_audioStreamIndex]->codec_tag)) {
+ _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2);
+ _curAudioChunk = 0;
+
+ // Make sure the bits per sample transfers to the sample size
+ if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('twos'))
+ _streams[_audioStreamIndex]->sample_size = (_streams[_audioStreamIndex]->bits_per_sample / 8) * _streams[_audioStreamIndex]->channels;
+
+ startAudio();
+ }
+
+ if (_videoStreamIndex >= 0) {
+ _videoCodec = createCodec(getCodecTag(), getBitsPerPixel());
+
+ if (getScaleMode() != kScaleNormal) {
+ // We have to initialize the scaled surface
+ _scaledSurface = new Surface();
+ _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel);
+ }
+ }
+
+ return true;
+}
+
+void QuickTimeDecoder::initParseTable() {
+ static const ParseTable p[] = {
+ { MKID_BE('dinf'), &QuickTimeDecoder::readDefault },
+ { MKID_BE('dref'), &QuickTimeDecoder::readLeaf },
+ { MKID_BE('edts'), &QuickTimeDecoder::readDefault },
+ { MKID_BE('elst'), &QuickTimeDecoder::readELST },
+ { MKID_BE('hdlr'), &QuickTimeDecoder::readHDLR },
+ { MKID_BE('mdat'), &QuickTimeDecoder::readMDAT },
+ { MKID_BE('mdhd'), &QuickTimeDecoder::readMDHD },
+ { MKID_BE('mdia'), &QuickTimeDecoder::readDefault },
+ { MKID_BE('minf'), &QuickTimeDecoder::readDefault },
+ { MKID_BE('moov'), &QuickTimeDecoder::readMOOV },
+ { MKID_BE('mvhd'), &QuickTimeDecoder::readMVHD },
+ { MKID_BE('smhd'), &QuickTimeDecoder::readLeaf },
+ { MKID_BE('stbl'), &QuickTimeDecoder::readDefault },
+ { MKID_BE('stco'), &QuickTimeDecoder::readSTCO },
+ { MKID_BE('stsc'), &QuickTimeDecoder::readSTSC },
+ { MKID_BE('stsd'), &QuickTimeDecoder::readSTSD },
+ { MKID_BE('stss'), &QuickTimeDecoder::readSTSS },
+ { MKID_BE('stsz'), &QuickTimeDecoder::readSTSZ },
+ { MKID_BE('stts'), &QuickTimeDecoder::readSTTS },
+ { MKID_BE('tkhd'), &QuickTimeDecoder::readTKHD },
+ { MKID_BE('trak'), &QuickTimeDecoder::readTRAK },
+ { MKID_BE('udta'), &QuickTimeDecoder::readLeaf },
+ { MKID_BE('vmhd'), &QuickTimeDecoder::readLeaf },
+ { MKID_BE('cmov'), &QuickTimeDecoder::readCMOV },
+ { MKID_BE('wave'), &QuickTimeDecoder::readWAVE },
+ { 0, 0 }
+ };
+
+ _parseTable = p;
+}
+
+int QuickTimeDecoder::readDefault(MOVatom atom) {
+ uint32 total_size = 0;
+ MOVatom a;
+ int err = 0;
+
+ a.offset = atom.offset;
+
+ while(((total_size + 8) < atom.size) && !_fd->eos() && !err) {
+ a.size = atom.size;
+ a.type = 0;
+
+ if (atom.size >= 8) {
+ a.size = _fd->readUint32BE();
+ a.type = _fd->readUint32BE();
+ }
+
+ total_size += 8;
+ a.offset += 8;
+ debug(4, "type: %08x %.4s sz: %x %x %x", a.type, tag2str(a.type), a.size, atom.size, total_size);
+
+ if (a.size == 1) { // 64 bit extended size
+ warning("64 bit extended size is not supported in QuickTime");
+ return -1;
+ }
+
+ if (a.size == 0) {
+ a.size = atom.size - total_size;
+ if (a.size <= 8)
+ break;
+ }
+
+ uint32 i = 0;
+
+ for (; _parseTable[i].type != 0 && _parseTable[i].type != a.type; i++)
+ // empty;
+
+ if (a.size < 8)
+ break;
+
+ a.size -= 8;
+
+ if (_parseTable[i].type == 0) { // skip leaf atoms data
+ debug(0, ">>> Skipped [%s]", tag2str(a.type));
+
+ _fd->seek(a.size, SEEK_CUR);
+ } else {
+ uint32 start_pos = _fd->pos();
+ err = (this->*_parseTable[i].func)(a);
+
+ uint32 left = a.size - _fd->pos() + start_pos;
+
+ if (left > 0) // skip garbage at atom end
+ _fd->seek(left, SEEK_CUR);
+ }
+
+ a.offset += a.size;
+ total_size += a.size;
+ }
+
+ if (!err && total_size < atom.size)
+ _fd->seek(atom.size - total_size, SEEK_SET);
+
+ return err;
+}
+
+int QuickTimeDecoder::readLeaf(MOVatom atom) {
+ if (atom.size > 1)
+ _fd->seek(atom.size, SEEK_SET);
+
+ return 0;
+}
+
+int QuickTimeDecoder::readMOOV(MOVatom atom) {
+ if (readDefault(atom) < 0)
+ return -1;
+
+ // we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat'
+ // so we don't parse the whole file if over a network
+ _foundMOOV = true;
+
+ if(_foundMDAT)
+ return 1; // found both, just go
+
+ return 0; // now go for mdat
+}
+
+int QuickTimeDecoder::readCMOV(MOVatom atom) {
+#ifdef USE_ZLIB
+ // Read in the dcom atom
+ _fd->readUint32BE();
+ if (_fd->readUint32BE() != MKID_BE('dcom'))
+ return -1;
+ if (_fd->readUint32BE() != MKID_BE('zlib')) {
+ warning("Unknown cmov compression type");
+ return -1;
+ }
+
+ // Read in the cmvd atom
+ uint32 compressedSize = _fd->readUint32BE() - 12;
+ if (_fd->readUint32BE() != MKID_BE('cmvd'))
+ return -1;
+ uint32 uncompressedSize = _fd->readUint32BE();
+
+ // Read in data
+ byte *compressedData = (byte *)malloc(compressedSize);
+ _fd->read(compressedData, compressedSize);
+
+ // Create uncompressed stream
+ byte *uncompressedData = (byte *)malloc(uncompressedSize);
+
+ // Uncompress the data
+ unsigned long dstLen = uncompressedSize;
+ if (!Common::uncompress(uncompressedData, &dstLen, compressedData, compressedSize)) {
+ warning ("Could not uncompress cmov chunk");
+ return -1;
+ }
+
+ // Load data into a new MemoryReadStream and assign _fd to be that
+ Common::SeekableReadStream *oldStream = _fd;
+ _fd = new Common::MemoryReadStream(uncompressedData, uncompressedSize, DisposeAfterUse::YES);
+
+ // Read the contents of the uncompressed data
+ MOVatom a = { MKID_BE('moov'), 0, uncompressedSize };
+ int err = readDefault(a);
+
+ // Assign the file handle back to the original handle
+ free(compressedData);
+ delete _fd;
+ _fd = oldStream;
+
+ return err;
+#else
+ warning ("zlib not found, cannot read QuickTime cmov atom");
+ return -1;
+#endif
+}
+
+int QuickTimeDecoder::readMVHD(MOVatom atom) {
+ byte version = _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ if (version == 1) {
+ warning("QuickTime version 1");
+ _fd->readUint32BE(); _fd->readUint32BE();
+ _fd->readUint32BE(); _fd->readUint32BE();
+ } else {
+ _fd->readUint32BE(); // creation time
+ _fd->readUint32BE(); // modification time
+ }
+
+ _timeScale = _fd->readUint32BE(); // time scale
+ debug(0, "time scale = %i\n", _timeScale);
+
+ // duration
+ _duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE();
+ _fd->readUint32BE(); // preferred scale
+
+ _fd->readUint16BE(); // preferred volume
+
+ _fd->seek(10, SEEK_CUR); // reserved
+
+ // We only need two values from the movie display matrix. Most of the values are just
+ // skipped. xMod and yMod are 16:16 fixed point numbers, the last part of the 3x3 matrix
+ // is 2:30.
+ uint32 xMod = _fd->readUint32BE();
+ _fd->skip(12);
+ 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;
+
+ debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode);
+
+ _fd->readUint32BE(); // preview time
+ _fd->readUint32BE(); // preview duration
+ _fd->readUint32BE(); // poster time
+ _fd->readUint32BE(); // selection time
+ _fd->readUint32BE(); // selection duration
+ _fd->readUint32BE(); // current time
+ _fd->readUint32BE(); // next track ID
+
+ return 0;
+}
+
+int QuickTimeDecoder::readTRAK(MOVatom atom) {
+ MOVStreamContext *sc = new MOVStreamContext();
+
+ if (!sc)
+ return -1;
+
+ sc->sample_to_chunk_index = -1;
+ sc->codec_type = CODEC_TYPE_MOV_OTHER;
+ sc->start_time = 0; // XXX: check
+ _streams[_numStreams++] = sc;
+
+ return readDefault(atom);
+}
+
+// this atom contains actual media data
+int QuickTimeDecoder::readMDAT(MOVatom atom) {
+ if (atom.size == 0) // wrong one (MP4)
+ return 0;
+
+ _foundMDAT = true;
+
+ _mdatOffset = atom.offset;
+ _mdatSize = atom.size;
+
+ if (_foundMOOV)
+ return 1; // found both, just go
+
+ _fd->seek(atom.size, SEEK_CUR);
+
+ return 0; // now go for moov
+}
+
+int QuickTimeDecoder::readTKHD(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+ byte version = _fd->readByte();
+
+ _fd->readByte(); _fd->readByte();
+ _fd->readByte(); // flags
+ //
+ //MOV_TRACK_ENABLED 0x0001
+ //MOV_TRACK_IN_MOVIE 0x0002
+ //MOV_TRACK_IN_PREVIEW 0x0004
+ //MOV_TRACK_IN_POSTER 0x0008
+ //
+
+ if (version == 1) {
+ _fd->readUint32BE(); _fd->readUint32BE();
+ _fd->readUint32BE(); _fd->readUint32BE();
+ } else {
+ _fd->readUint32BE(); // creation time
+ _fd->readUint32BE(); // modification time
+ }
+
+ /* st->id = */_fd->readUint32BE(); // track id (NOT 0 !)
+ _fd->readUint32BE(); // reserved
+ //st->start_time = 0; // check
+ (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase
+ _fd->readUint32BE(); // reserved
+ _fd->readUint32BE(); // reserved
+
+ _fd->readUint16BE(); // layer
+ _fd->readUint16BE(); // alternate group
+ _fd->readUint16BE(); // volume
+ _fd->readUint16BE(); // reserved
+
+ // We only need the two values from the displacement matrix for a track.
+ // See readMVHD() for more information.
+ uint32 xMod = _fd->readUint32BE();
+ _fd->skip(12);
+ uint32 yMod = _fd->readUint32BE();
+ _fd->skip(16);
+
+ if (xMod != yMod)
+ error("X and Y resolution modifiers differ");
+
+ if (xMod == 0x8000)
+ st->scaleMode = kScaleHalf;
+ else if (xMod == 0x4000)
+ st->scaleMode = kScaleQuarter;
+ else
+ st->scaleMode = kScaleNormal;
+
+ debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode);
+
+ // these are fixed-point, 16:16
+ // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width
+ // uint32 tkHeight = _fd->readUint32BE() >> 16; // track height
+
+ return 0;
+}
+
+// edit list atom
+int QuickTimeDecoder::readELST(MOVatom atom) {
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+ uint32 editCount = _streams[_numStreams - 1]->edit_count = _fd->readUint32BE(); // entries
+
+ for (uint32 i = 0; i < editCount; i++){
+ _fd->readUint32BE(); // Track duration
+ _fd->readUint32BE(); // Media time
+ _fd->readUint32BE(); // Media rate
+ }
+
+ debug(0, "track[%i].edit_count = %i", _numStreams - 1, _streams[_numStreams - 1]->edit_count);
+
+ if (editCount != 1)
+ warning("Multiple edit list entries. Things may go awry");
+
+ return 0;
+}
+
+int QuickTimeDecoder::readHDLR(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ // component type
+ uint32 ctype = _fd->readUint32LE();
+ uint32 type = _fd->readUint32BE(); // component subtype
+
+ debug(0, "ctype= %s (0x%08lx)", tag2str(ctype), (long)ctype);
+ debug(0, "stype= %s", tag2str(type));
+
+ if(ctype == MKID_BE('mhlr')) // MOV
+ debug(0, "MOV detected");
+ else if(ctype == 0) {
+ warning("MP4 streams are not supported");
+ return -1;
+ }
+
+ if (type == MKID_BE('vide'))
+ st->codec_type = CODEC_TYPE_VIDEO;
+ else if (type == MKID_BE('soun'))
+ st->codec_type = CODEC_TYPE_AUDIO;
+
+ _fd->readUint32BE(); // component manufacture
+ _fd->readUint32BE(); // component flags
+ _fd->readUint32BE(); // component flags mask
+
+ if (atom.size <= 24)
+ return 0; // nothing left to read
+
+ // .mov: PASCAL string
+ byte len = _fd->readByte();
+ _fd->seek(len, SEEK_CUR);
+
+ _fd->seek(atom.size - (_fd->pos() - atom.offset), SEEK_CUR);
+
+ return 0;
+}
+
+int QuickTimeDecoder::readMDHD(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+ byte version = _fd->readByte();
+
+ if (version > 1)
+ return 1; // unsupported
+
+ _fd->readByte(); _fd->readByte();
+ _fd->readByte(); // flags
+
+ if (version == 1) {
+ _fd->readUint32BE(); _fd->readUint32BE();
+ _fd->readUint32BE(); _fd->readUint32BE();
+ } else {
+ _fd->readUint32BE(); // creation time
+ _fd->readUint32BE(); // modification time
+ }
+
+ st->time_scale = _fd->readUint32BE();
+ st->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration
+
+ _fd->readUint16BE(); // language
+ _fd->readUint16BE(); // quality
+
+ return 0;
+}
+
+int QuickTimeDecoder::readSTSD(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ uint32 entries = _fd->readUint32BE();
+
+ while (entries--) { //Parsing Sample description table
+ MOVatom a = { 0, 0, 0 };
+ uint32 start_pos = _fd->pos();
+ int size = _fd->readUint32BE(); // size
+ uint32 format = _fd->readUint32BE(); // data format
+
+ _fd->readUint32BE(); // reserved
+ _fd->readUint16BE(); // reserved
+ _fd->readUint16BE(); // index
+
+ debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type);
+ st->codec_tag = format;
+
+ if (st->codec_type == CODEC_TYPE_VIDEO) {
+ debug(0, "Video Codec FourCC: \'%s\'", tag2str(format));
+
+ _fd->readUint16BE(); // version
+ _fd->readUint16BE(); // revision level
+ _fd->readUint32BE(); // vendor
+ _fd->readUint32BE(); // temporal quality
+ _fd->readUint32BE(); // spacial quality
+
+ st->width = _fd->readUint16BE(); // width
+ st->height = _fd->readUint16BE(); // height
+
+ _fd->readUint32BE(); // horiz resolution
+ _fd->readUint32BE(); // vert resolution
+ _fd->readUint32BE(); // data size, always 0
+ uint16 frames_per_sample = _fd->readUint16BE(); // frames per samples
+
+ debug(0, "frames/samples = %d", frames_per_sample);
+
+ byte codec_name[32];
+ _fd->read(codec_name, 32); // codec name, pascal string (FIXME: true for mp4?)
+ if (codec_name[0] <= 31) {
+ memcpy(st->codec_name, &codec_name[1], codec_name[0]);
+ st->codec_name[codec_name[0]] = 0;
+ }
+
+ st->bits_per_sample = _fd->readUint16BE(); // depth
+ st->color_table_id = _fd->readUint16BE(); // colortable id
+
+// These are set in mov_read_stts and might already be set!
+// st->codec->time_base.den = 25;
+// st->codec->time_base.num = 1;
+
+
+ // figure out the palette situation
+ byte colorDepth = st->bits_per_sample & 0x1F;
+ bool colorGreyscale = (st->bits_per_sample & 0x20) != 0;
+
+ debug(0, "color depth: %d", colorDepth);
+
+ // if the depth is 2, 4, or 8 bpp, file is palettized
+ if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) {
+ _dirtyPalette = true;
+
+ if (colorGreyscale) {
+ debug(0, "Greyscale palette");
+
+ // compute the greyscale palette
+ uint16 colorCount = 1 << colorDepth;
+ int16 colorIndex = 255;
+ byte colorDec = 256 / (colorCount - 1);
+ for (byte j = 0; j < colorCount; j++) {
+ _palette[j * 3] = _palette[j * 3 + 1] = _palette[j * 3 + 2] = colorIndex;
+ colorIndex -= colorDec;
+ if (colorIndex < 0)
+ colorIndex = 0;
+ }
+ } else if (st->color_table_id & 0x08) {
+ // if flag bit 3 is set, use the default palette
+ //uint16 colorCount = 1 << colorDepth;
+
+ warning("Predefined palette! %dbpp", colorDepth);
+#if 0
+ byte *color_table;
+ byte r, g, b;
+
+ if (colorDepth == 2)
+ color_table = ff_qt_default_palette_4;
+ else if (colorDepth == 4)
+ color_table = ff_qt_default_palette_16;
+ else
+ color_table = ff_qt_default_palette_256;
+
+ for (byte j = 0; j < color_count; j++) {
+ r = color_table[j * 4 + 0];
+ g = color_table[j * 4 + 1];
+ b = color_table[j * 4 + 2];
+ _palette_control.palette[j] = (r << 16) | (g << 8) | (b);
+ }
+#endif
+
+ } else {
+ debug(0, "Palette from file");
+
+ // load the palette from the file
+ uint32 colorStart = _fd->readUint32BE();
+ /* uint16 colorCount = */ _fd->readUint16BE();
+ uint16 colorEnd = _fd->readUint16BE();
+ for (uint32 j = colorStart; j <= colorEnd; j++) {
+ // each R, G, or B component is 16 bits;
+ // only use the top 8 bits; skip alpha bytes
+ // up front
+ _fd->readByte();
+ _fd->readByte();
+ _palette[j * 3] = _fd->readByte();
+ _fd->readByte();
+ _palette[j * 3 + 1] = _fd->readByte();
+ _fd->readByte();
+ _palette[j * 3 + 2] = _fd->readByte();
+ _fd->readByte();
+ }
+ }
+ st->palettized = true;
+ } else
+ st->palettized = false;
+ } else if (st->codec_type == CODEC_TYPE_AUDIO) {
+ debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format));
+
+ st->stsd_version = _fd->readUint16BE();
+ _fd->readUint16BE(); // revision level
+ _fd->readUint32BE(); // vendor
+
+ st->channels = _fd->readUint16BE(); // channel count
+ st->bits_per_sample = _fd->readUint16BE(); // sample size
+ // do we need to force to 16 for AMR ?
+
+ // handle specific s8 codec
+ _fd->readUint16BE(); // compression id = 0
+ _fd->readUint16BE(); // packet size = 0
+
+ st->sample_rate = (_fd->readUint32BE() >> 16);
+
+ debug(0, "stsd version =%d", st->stsd_version);
+ if (st->stsd_version == 0) {
+ // Not used, except in special cases. See below.
+ st->samples_per_frame = st->bytes_per_frame = 0;
+ } else if (st->stsd_version == 1) {
+ // Read QT version 1 fields. In version 0 these dont exist.
+ st->samples_per_frame = _fd->readUint32BE();
+ debug(0, "stsd samples_per_frame =%d", st->samples_per_frame);
+ _fd->readUint32BE(); // bytes per packet
+ st->bytes_per_frame = _fd->readUint32BE();
+ debug(0, "stsd bytes_per_frame =%d", st->bytes_per_frame);
+ _fd->readUint32BE(); // bytes per sample
+ } else {
+ warning("Unsupported QuickTime STSD audio version %d", st->stsd_version);
+ return 1;
+ }
+
+ // Version 0 videos (such as the Riven ones) don't have this set,
+ // but we need it later on. Add it in here.
+ if (format == MKID_BE('ima4')) {
+ st->samples_per_frame = 64;
+ st->bytes_per_frame = 34 * st->channels;
+ }
+ } else {
+ // other codec type, just skip (rtp, mp4s, tmcd ...)
+ _fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR);
+ }
+
+ // this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...)
+ a.size = size - (_fd->pos() - start_pos);
+ if (a.size > 8)
+ readDefault(a);
+ else if (a.size > 0)
+ _fd->seek(a.size, SEEK_CUR);
+ }
+
+ if (st->codec_type == CODEC_TYPE_AUDIO && st->sample_rate == 0 && st->time_scale > 1)
+ st->sample_rate= st->time_scale;
+
+ return 0;
+}
+
+int QuickTimeDecoder::readSTSC(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ st->sample_to_chunk_sz = _fd->readUint32BE();
+
+ debug(0, "track[%i].stsc.entries = %i", _numStreams - 1, st->sample_to_chunk_sz);
+
+ st->sample_to_chunk = new MOVstsc[st->sample_to_chunk_sz];
+
+ if (!st->sample_to_chunk)
+ return -1;
+
+ for (uint32 i = 0; i < st->sample_to_chunk_sz; i++) {
+ st->sample_to_chunk[i].first = _fd->readUint32BE();
+ st->sample_to_chunk[i].count = _fd->readUint32BE();
+ st->sample_to_chunk[i].id = _fd->readUint32BE();
+ //printf ("Sample to Chunk[%d]: First = %d, Count = %d\n", i, st->sample_to_chunk[i].first, st->sample_to_chunk[i].count);
+ }
+
+ return 0;
+}
+
+int QuickTimeDecoder::readSTSS(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ st->keyframe_count = _fd->readUint32BE();
+
+ debug(0, "keyframe_count = %d", st->keyframe_count);
+
+ st->keyframes = new uint32[st->keyframe_count];
+
+ if (!st->keyframes)
+ return -1;
+
+ for (uint32 i = 0; i < st->keyframe_count; i++) {
+ st->keyframes[i] = _fd->readUint32BE();
+ debug(6, "keyframes[%d] = %d", i, st->keyframes[i]);
+
+ }
+ return 0;
+}
+
+int QuickTimeDecoder::readSTSZ(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ st->sample_size = _fd->readUint32BE();
+ st->sample_count = _fd->readUint32BE();
+
+ debug(5, "sample_size = %d sample_count = %d", st->sample_size, st->sample_count);
+
+ if (st->sample_size)
+ return 0; // there isn't any table following
+
+ st->sample_sizes = new uint32[st->sample_count];
+
+ if (!st->sample_sizes)
+ return -1;
+
+ for(uint32 i = 0; i < st->sample_count; i++) {
+ st->sample_sizes[i] = _fd->readUint32BE();
+ debug(6, "sample_sizes[%d] = %d", i, st->sample_sizes[i]);
+ }
+
+ return 0;
+}
+
+static uint32 ff_gcd(uint32 a, uint32 b) {
+ if(b) return ff_gcd(b, a%b);
+ else return a;
+}
+
+int QuickTimeDecoder::readSTTS(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+ uint32 duration = 0;
+ uint32 total_sample_count = 0;
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ st->stts_count = _fd->readUint32BE();
+ st->stts_data = new MOVstts[st->stts_count];
+
+ debug(0, "track[%i].stts.entries = %i", _numStreams - 1, st->stts_count);
+
+ st->time_rate = 0;
+
+ for (int32 i = 0; i < st->stts_count; i++) {
+ int sample_duration;
+ int sample_count;
+
+ sample_count = _fd->readUint32BE();
+ sample_duration = _fd->readUint32BE();
+ st->stts_data[i].count = sample_count;
+ st->stts_data[i].duration = sample_duration;
+
+ st->time_rate = ff_gcd(st->time_rate, sample_duration);
+
+ debug(0, "sample_count=%d, sample_duration=%d", sample_count, sample_duration);
+
+ duration += sample_duration * sample_count;
+ total_sample_count += sample_count;
+ }
+
+ st->nb_frames = total_sample_count;
+
+ if (duration)
+ st->duration = duration;
+
+ return 0;
+}
+
+int QuickTimeDecoder::readSTCO(MOVatom atom) {
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ _fd->readByte(); // version
+ _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
+
+ st->chunk_count = _fd->readUint32BE();
+ st->chunk_offsets = new uint32[st->chunk_count];
+
+ if (!st->chunk_offsets)
+ return -1;
+
+ for (uint32 i = 0; i < st->chunk_count; i++) {
+ // WORKAROUND/HACK: The offsets in Riven videos (ones inside the Mohawk archives themselves)
+ // have offsets relative to the archive and not the video. This is quite nasty. We subtract
+ // the initial offset of the stream to get the correct value inside of the stream.
+ st->chunk_offsets[i] = _fd->readUint32BE() - _beginOffset;
+ }
+
+ for (uint32 i = 0; i < _numStreams; i++) {
+ MOVStreamContext *sc2 = _streams[i];
+
+ if(sc2 && sc2->chunk_offsets){
+ uint32 first = sc2->chunk_offsets[0];
+ uint32 last = sc2->chunk_offsets[sc2->chunk_count - 1];
+
+ if(first >= st->chunk_offsets[st->chunk_count - 1] || last <= st->chunk_offsets[0])
+ _ni = 1;
+ }
+ }
+
+ return 0;
+}
+
+int QuickTimeDecoder::readWAVE(MOVatom atom) {
+ if (_numStreams < 1)
+ return 0;
+
+ MOVStreamContext *st = _streams[_numStreams - 1];
+
+ if (atom.size > (1 << 30))
+ return -1;
+
+ if (st->codec_tag == MKID_BE('QDM2')) // Read extradata for QDM2
+ st->extradata = _fd->readStream(atom.size - 8);
+ else if (atom.size > 8)
+ return readDefault(atom);
+ else
+ _fd->skip(atom.size);
+
+ return 0;
+}
+
+void QuickTimeDecoder::close() {
+ stopAudio();
+
+ delete _videoCodec; _videoCodec = 0;
+
+ for (uint32 i = 0; i < _numStreams; i++)
+ delete _streams[i];
+
+ delete _fd;
+
+ if (_scaledSurface) {
+ _scaledSurface->free();
+ delete _scaledSurface;
+ _scaledSurface = 0;
+ }
+
+ // The audio stream is deleted automatically
+ _audStream = NULL;
+
+ VideoDecoder::reset();
+}
+
+Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket() {
+ if (_videoStreamIndex < 0)
+ return NULL;
+
+ // First, we have to track down which chunk holds the sample and which sample in the chunk contains the frame we are looking for.
+ int32 totalSampleCount = 0;
+ int32 sampleInChunk = 0;
+ int32 actualChunk = -1;
+
+ for (uint32 i = 0; i < _streams[_videoStreamIndex]->chunk_count; i++) {
+ int32 sampleToChunkIndex = -1;
+
+ for (uint32 j = 0; j < _streams[_videoStreamIndex]->sample_to_chunk_sz; j++)
+ if (i >= _streams[_videoStreamIndex]->sample_to_chunk[j].first - 1)
+ sampleToChunkIndex = j;
+
+ if (sampleToChunkIndex < 0)
+ error("This chunk (%d) is imaginary", sampleToChunkIndex);
+
+ totalSampleCount += _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count;
+
+ if (totalSampleCount > getCurFrame()) {
+ actualChunk = i;
+ sampleInChunk = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count - totalSampleCount + getCurFrame();
+ break;
+ }
+ }
+
+ if (actualChunk < 0) {
+ warning ("Could not find data for frame %d", getCurFrame());
+ return NULL;
+ }
+
+ // Next seek to that frame
+ _fd->seek(_streams[_videoStreamIndex]->chunk_offsets[actualChunk]);
+
+ // Then, if the chunk holds more than one frame, seek to where the frame we want is located
+ for (int32 i = getCurFrame() - sampleInChunk; i < getCurFrame(); i++) {
+ if (_streams[_videoStreamIndex]->sample_size != 0)
+ _fd->skip(_streams[_videoStreamIndex]->sample_size);
+ else
+ _fd->skip(_streams[_videoStreamIndex]->sample_sizes[i]);
+ }
+
+ // Finally, read in the raw data for the frame
+ //printf ("Frame Data[%d]: Offset = %d, Size = %d\n", getCurFrame(), _fd->pos(), _streams[_videoStreamIndex]->sample_sizes[getCurFrame()]);
+
+ if (_streams[_videoStreamIndex]->sample_size != 0)
+ return _fd->readStream(_streams[_videoStreamIndex]->sample_size);
+
+ return _fd->readStream(_streams[_videoStreamIndex]->sample_sizes[getCurFrame()]);
+}
+
+bool QuickTimeDecoder::checkAudioCodecSupport(uint32 tag) {
+ // Check if the codec is a supported codec
+ if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4'))
+ return true;
+
+#ifdef SOUND_QDM2_H
+ if (tag == MKID_BE('QDM2'))
+ return true;
+#endif
+
+ warning("Audio Codec Not Supported: \'%s\'", tag2str(tag));
+
+ return false;
+}
+
+Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStream *stream) {
+ if (!stream || _audioStreamIndex < 0)
+ return NULL;
+
+ if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('twos') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) {
+ // Fortunately, most of the audio used in Myst videos is raw...
+ uint16 flags = 0;
+ if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw '))
+ flags |= Audio::FLAG_UNSIGNED;
+ if (_streams[_audioStreamIndex]->channels == 2)
+ flags |= Audio::FLAG_STEREO;
+ if (_streams[_audioStreamIndex]->bits_per_sample == 16)
+ flags |= Audio::FLAG_16BITS;
+ uint32 dataSize = stream->size();
+ byte *data = (byte *)malloc(dataSize);
+ stream->read(data, dataSize);
+ delete stream;
+ return Audio::makeRawStream(data, dataSize, _streams[_audioStreamIndex]->sample_rate, flags);
+ } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('ima4')) {
+ // Riven uses this codec (as do some Myst ME videos)
+ return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, _streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels, 34);
+#ifdef SOUND_QDM2_H
+ } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('QDM2')) {
+ // Several Myst ME videos use this codec
+ return new Audio::QDM2Stream(stream, _streams[_audioStreamIndex]->extradata);
+#endif
+ }
+
+ error("Unsupported audio codec");
+
+ return NULL;
+}
+
+void QuickTimeDecoder::updateAudioBuffer() {
+ if (!_audStream)
+ return;
+
+ // Keep three streams in buffer so that if/when the first two end, it goes right into the next
+ for (; _audStream->numQueuedStreams() < 3 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) {
+ Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic();
+
+ _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]);
+
+ // First, we have to get the sample count
+ uint32 sampleCount = 0;
+ for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++)
+ if (_curAudioChunk >= (_streams[_audioStreamIndex]->sample_to_chunk[j].first - 1))
+ sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count;
+ assert(sampleCount);
+
+ // Then calculate the right sizes
+ while (sampleCount > 0) {
+ uint32 samples = 0, size = 0;
+
+ if (_streams[_audioStreamIndex]->samples_per_frame >= 160) {
+ samples = _streams[_audioStreamIndex]->samples_per_frame;
+ size = _streams[_audioStreamIndex]->bytes_per_frame;
+ } else if (_streams[_audioStreamIndex]->samples_per_frame > 1) {
+ samples = MIN<uint32>((1024 / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->samples_per_frame, sampleCount);
+ size = (samples / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->bytes_per_frame;
+ } else {
+ samples = MIN<uint32>(1024, sampleCount);
+ size = samples * _streams[_audioStreamIndex]->sample_size;
+ }
+
+ // Now, we read in the data for this data and output it
+ byte *data = (byte *)malloc(size);
+ _fd->read(data, size);
+ wStream->write(data, size);
+ free(data);
+ sampleCount -= samples;
+ }
+
+ // Now queue the buffer
+ _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES)));
+ delete wStream;
+ }
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/qt_decoder.h b/graphics/video/qt_decoder.h
new file mode 100644
index 0000000000..dc0557b388
--- /dev/null
+++ b/graphics/video/qt_decoder.h
@@ -0,0 +1,282 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+//
+// Heavily based on ffmpeg code.
+//
+// Copyright (c) 2001 Fabrice Bellard.
+// First version by Francois Revol revol@free.fr
+// Seek function by Gael Chardon gael.dev@4now.net
+//
+
+#ifndef GRAPHICS_QT_DECODER_H
+#define GRAPHICS_QT_DECODER_H
+
+#include "common/scummsys.h"
+#include "common/queue.h"
+
+#include "graphics/video/video_decoder.h"
+#include "graphics/video/codecs/codec.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Common {
+ class File;
+}
+
+namespace Graphics {
+
+enum ScaleMode {
+ kScaleNormal = 1,
+ kScaleHalf = 2,
+ kScaleQuarter = 4
+};
+
+class QuickTimeDecoder : public RewindableVideoDecoder {
+public:
+ QuickTimeDecoder();
+ virtual ~QuickTimeDecoder();
+
+ /**
+ * Returns the width of the video
+ * @return the width of the video
+ */
+ uint16 getWidth() const;
+
+ /**
+ * Returns the height of the video
+ * @return the height of the video
+ */
+ uint16 getHeight() const;
+
+ /**
+ * Returns the amount of frames in the video
+ * @return the amount of frames in the video
+ */
+ uint32 getFrameCount() const;
+
+ /**
+ * Load a QuickTime video file from a SeekableReadStream
+ * @param stream the stream to load
+ */
+ bool load(Common::SeekableReadStream &stream);
+
+ /**
+ * Close a QuickTime encoded video file
+ */
+ void close();
+
+ /**
+ * Returns the palette of the video
+ * @return the palette of the video
+ */
+ byte *getPalette() { _dirtyPalette = false; return _palette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+
+ /**
+ * Set the beginning offset of the video so we can modify the offsets in the stco
+ * atom of videos inside the Mohawk archives
+ * @param the beginning offset of the video
+ */
+ void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; }
+
+ bool isVideoLoaded() const { return _fd != 0; }
+ Surface *decodeNextFrame();
+ bool needsUpdate() const;
+ bool endOfVideo() const;
+ uint32 getElapsedTime() const;
+ uint32 getTimeToNextFrame() const;
+ PixelFormat getPixelFormat() const;
+
+ // RewindableVideoDecoder API
+ void rewind();
+
+ // TODO: This audio function need to be removed from the public and/or added to
+ // the VideoDecoder API directly. I plan on replacing this function with something
+ // that can figure out how much audio is needed instead of constantly keeping two
+ // chunks in memory.
+ void updateAudioBuffer();
+
+protected:
+ // This is the file handle from which data is read from. It can be the actual file handle or a decompressed stream.
+ Common::SeekableReadStream *_fd;
+
+ struct MOVatom {
+ uint32 type;
+ uint32 offset;
+ uint32 size;
+ };
+
+ struct ParseTable {
+ uint32 type;
+ int (QuickTimeDecoder::*func)(MOVatom atom);
+ };
+
+ struct MOVstts {
+ int count;
+ int duration;
+ };
+
+ struct MOVstsc {
+ uint32 first;
+ uint32 count;
+ uint32 id;
+ };
+
+ enum CodecType {
+ CODEC_TYPE_MOV_OTHER,
+ CODEC_TYPE_VIDEO,
+ CODEC_TYPE_AUDIO
+ };
+
+ struct MOVStreamContext {
+ MOVStreamContext() { memset(this, 0, sizeof(MOVStreamContext)); }
+ ~MOVStreamContext() {
+ delete[] chunk_offsets;
+ delete[] stts_data;
+ delete[] ctts_data;
+ delete[] sample_to_chunk;
+ delete[] sample_sizes;
+ delete[] keyframes;
+ delete extradata;
+ }
+
+ int ffindex; /* the ffmpeg stream id */
+ int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */
+ uint32 next_chunk;
+ uint32 chunk_count;
+ uint32 *chunk_offsets;
+ int stts_count;
+ MOVstts *stts_data;
+ int ctts_count;
+ MOVstts *ctts_data;
+ int edit_count; /* number of 'edit' (elst atom) */
+ uint32 sample_to_chunk_sz;
+ MOVstsc *sample_to_chunk;
+ int32 sample_to_chunk_index;
+ int sample_to_time_index;
+ uint32 sample_to_time_sample;
+ uint32 sample_to_time_time;
+ int sample_to_ctime_index;
+ int sample_to_ctime_sample;
+ uint32 sample_size;
+ uint32 sample_count;
+ uint32 *sample_sizes;
+ uint32 keyframe_count;
+ uint32 *keyframes;
+ int32 time_scale;
+ int time_rate;
+ uint32 current_sample;
+ uint32 left_in_chunk; /* how many samples before next chunk */
+
+ uint16 width;
+ uint16 height;
+ int codec_type;
+ uint32 codec_tag;
+ char codec_name[32];
+ uint16 bits_per_sample;
+ uint16 color_table_id;
+ bool palettized;
+ Common::SeekableReadStream *extradata;
+
+ uint16 stsd_version;
+ uint16 channels;
+ uint16 sample_rate;
+ uint32 samples_per_frame;
+ uint32 bytes_per_frame;
+
+ uint32 nb_frames;
+ uint32 duration;
+ uint32 start_time;
+ ScaleMode scaleMode;
+ };
+
+ const ParseTable *_parseTable;
+ bool _foundMOOV;
+ bool _foundMDAT;
+ uint32 _timeScale;
+ uint32 _duration;
+ uint32 _mdatOffset;
+ uint32 _mdatSize;
+ uint32 _next_chunk_offset;
+ MOVStreamContext *_partial;
+ uint32 _numStreams;
+ int _ni;
+ ScaleMode _scaleMode;
+ MOVStreamContext *_streams[20];
+ byte _palette[256 * 3];
+ bool _dirtyPalette;
+ uint32 _beginOffset;
+
+ void initParseTable();
+ Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream);
+ bool checkAudioCodecSupport(uint32 tag);
+ Common::SeekableReadStream *getNextFramePacket();
+ uint32 getFrameDuration();
+ uint32 getCodecTag();
+ byte getBitsPerPixel();
+
+ Audio::QueuingAudioStream *_audStream;
+ void startAudio();
+ void stopAudio();
+ int8 _audioStreamIndex;
+ uint _curAudioChunk;
+ Audio::SoundHandle _audHandle;
+
+ Codec *createCodec(uint32 codecTag, byte bitsPerPixel);
+ Codec *_videoCodec;
+ uint32 _nextFrameStartTime;
+ int8 _videoStreamIndex;
+
+ Surface *_scaledSurface;
+ Surface *scaleSurface(Surface *frame);
+ ScaleMode getScaleMode() const;
+
+ void pauseVideoIntern(bool pause);
+
+ int readDefault(MOVatom atom);
+ int readLeaf(MOVatom atom);
+ int readELST(MOVatom atom);
+ int readHDLR(MOVatom atom);
+ int readMDAT(MOVatom atom);
+ int readMDHD(MOVatom atom);
+ int readMOOV(MOVatom atom);
+ int readMVHD(MOVatom atom);
+ int readTKHD(MOVatom atom);
+ int readTRAK(MOVatom atom);
+ int readSTCO(MOVatom atom);
+ int readSTSC(MOVatom atom);
+ int readSTSD(MOVatom atom);
+ int readSTSS(MOVatom atom);
+ int readSTSZ(MOVatom atom);
+ int readSTTS(MOVatom atom);
+ int readCMOV(MOVatom atom);
+ int readWAVE(MOVatom atom);
+};
+
+} // End of namespace Graphics
+
+#endif