aboutsummaryrefslogtreecommitdiff
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/avi_decoder.cpp487
-rw-r--r--video/avi_decoder.h50
-rw-r--r--video/codecs/cdtoons.cpp2
-rw-r--r--video/codecs/cinepak.cpp6
-rw-r--r--video/codecs/msrle.cpp2
-rw-r--r--video/codecs/msvideo1.cpp2
-rw-r--r--video/codecs/qtrle.cpp12
-rw-r--r--video/codecs/rpza.cpp2
-rw-r--r--video/codecs/smc.cpp2
-rw-r--r--video/codecs/truemotion1.cpp5
-rw-r--r--video/coktel_decoder.cpp51
-rw-r--r--video/dxa_decoder.cpp6
-rw-r--r--video/flic_decoder.cpp14
-rw-r--r--video/smk_decoder.cpp6
-rw-r--r--video/theora_decoder.cpp11
-rw-r--r--video/video_decoder.cpp53
-rw-r--r--video/video_decoder.h41
17 files changed, 541 insertions, 211 deletions
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index ff728a8437..5e4b91d1bd 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -65,8 +65,10 @@ namespace Video {
#define ID_VEDT MKTAG('v','e','d','t')
#define ID_IDX1 MKTAG('i','d','x','1')
#define ID_STRD MKTAG('s','t','r','d')
-//#define ID_INFO MKTAG('I','N','F','O')
+#define ID_INFO MKTAG('I','N','F','O')
#define ID_ISFT MKTAG('I','S','F','T')
+#define ID_DISP MKTAG('D','I','S','P')
+#define ID_PRMI MKTAG('P','R','M','I')
// Codec tags
#define ID_RLE MKTAG('R','L','E',' ')
@@ -78,18 +80,13 @@ namespace Video {
#define ID_DUCK MKTAG('D','U','C','K')
#define ID_MPG2 MKTAG('m','p','g','2')
-static byte char2num(char c) {
- c = tolower((byte)c);
- return (c >= 'a' && c <= 'f') ? c - 'a' + 10 : c - '0';
-}
+// Stream Types
+enum {
+ kStreamTypePaletteChange = MKTAG16('p', 'c'),
+ kStreamTypeRawVideo = MKTAG16('d', 'b'),
+ kStreamTypeAudio = MKTAG16('w', 'b')
+};
-static byte getStreamIndex(uint32 tag) {
- return char2num((tag >> 24) & 0xFF) << 4 | char2num((tag >> 16) & 0xFF);
-}
-
-static uint16 getStreamType(uint32 tag) {
- return tag & 0xffff;
-}
AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType) : _frameRateOverride(0), _soundType(soundType) {
initCommon();
@@ -107,29 +104,32 @@ AVIDecoder::~AVIDecoder() {
void AVIDecoder::initCommon() {
_decodedHeader = false;
_foundMovieList = false;
+ _movieListStart = 0;
_fileStream = 0;
- memset(&_ixInfo, 0, sizeof(_ixInfo));
memset(&_header, 0, sizeof(_header));
}
-void AVIDecoder::runHandle(uint32 tag) {
- assert(_fileStream);
+bool AVIDecoder::isSeekable() const {
+ // Only videos with an index can seek
+ // Anyone else who wants to seek is crazy.
+ return isVideoLoaded() && !_indexEntries.empty();
+}
+
+bool AVIDecoder::parseNextChunk() {
+ uint32 tag = _fileStream->readUint32BE();
+ uint32 size = _fileStream->readUint32LE();
+
if (_fileStream->eos())
- return;
+ return false;
debug(3, "Decoding tag %s", tag2str(tag));
switch (tag) {
- case ID_RIFF:
- /*_filesize = */_fileStream->readUint32LE();
- if (_fileStream->readUint32BE() != ID_AVI)
- error("RIFF file is not an AVI video");
- break;
case ID_LIST:
- handleList();
+ handleList(size);
break;
case ID_AVIH:
- _header.size = _fileStream->readUint32LE();
+ _header.size = size;
_header.microSecondsPerFrame = _fileStream->readUint32LE();
_header.maxBytesPerSecond = _fileStream->readUint32LE();
_header.padding = _fileStream->readUint32LE();
@@ -144,58 +144,74 @@ void AVIDecoder::runHandle(uint32 tag) {
_fileStream->skip(16);
break;
case ID_STRH:
- handleStreamHeader();
+ handleStreamHeader(size);
break;
case ID_STRD: // Extra stream info, safe to ignore
case ID_VEDT: // Unknown, safe to ignore
case ID_JUNK: // Alignment bytes, should be ignored
case ID_ISFT: // Metadata, safe to ignore
- {
- uint32 junkSize = _fileStream->readUint32LE();
- _fileStream->skip(junkSize + (junkSize & 1)); // Alignment
- } break;
+ case ID_DISP: // Metadata, should be safe to ignore
+ skipChunk(size);
+ break;
case ID_IDX1:
- _ixInfo.size = _fileStream->readUint32LE();
- _ixInfo.indices = new OldIndex::Index[_ixInfo.size / 16];
- debug(0, "%d Indices", (_ixInfo.size / 16));
- for (uint32 i = 0; i < (_ixInfo.size / 16); i++) {
- _ixInfo.indices[i].id = _fileStream->readUint32BE();
- _ixInfo.indices[i].flags = _fileStream->readUint32LE();
- _ixInfo.indices[i].offset = _fileStream->readUint32LE();
- _ixInfo.indices[i].size = _fileStream->readUint32LE();
- debug(0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size);
+ debug(0, "%d Indices", size / 16);
+ for (uint32 i = 0; i < size / 16; i++) {
+ OldIndex indexEntry;
+ indexEntry.id = _fileStream->readUint32BE();
+ indexEntry.flags = _fileStream->readUint32LE();
+ indexEntry.offset = _fileStream->readUint32LE() + _movieListStart - 4; // Adjust to absolute
+ indexEntry.size = _fileStream->readUint32LE();
+ _indexEntries.push_back(indexEntry);
+ debug(0, "Index %d == Tag \'%s\', Offset = %d, Size = %d (Flags = %d)", i, tag2str(indexEntry.id), indexEntry.offset, indexEntry.size, indexEntry.flags);
}
break;
default:
error("Unknown tag \'%s\' found", tag2str(tag));
}
+
+ return true;
}
-void AVIDecoder::handleList() {
- uint32 listSize = _fileStream->readUint32LE() - 4; // Subtract away listType's 4 bytes
+void AVIDecoder::skipChunk(uint32 size) {
+ // Make sure we're aligned on a word boundary
+ _fileStream->skip(size + (size & 1));
+}
+
+void AVIDecoder::handleList(uint32 listSize) {
uint32 listType = _fileStream->readUint32BE();
+ listSize -= 4; // Subtract away listType's 4 bytes
uint32 curPos = _fileStream->pos();
debug(0, "Found LIST of type %s", tag2str(listType));
- if (listType == ID_MOVI) {
- // Found the 'movi' list
- // We're done parsing everything
+ switch (listType) {
+ case ID_MOVI: // Movie List
+ // We found the movie block
_foundMovieList = true;
+ _movieListStart = curPos;
+ _fileStream->skip(listSize);
return;
+ case ID_HDRL: // Header List
+ // Mark the header as decoded
+ _decodedHeader = true;
+ break;
+ case ID_INFO: // Metadata
+ case ID_PRMI: // Unknown metadata, should be safe to ignore
+ // Ignore metadata
+ _fileStream->skip(listSize);
+ return;
+ case ID_STRL: // Stream list
+ default: // (Just hope we can parse it!)
+ break;
}
while ((_fileStream->pos() - curPos) < listSize)
- runHandle(_fileStream->readUint32BE());
-
- // We now have all the header data
- if (listType == ID_HDRL)
- _decodedHeader = true;
+ parseNextChunk();
}
-void AVIDecoder::handleStreamHeader() {
+void AVIDecoder::handleStreamHeader(uint32 size) {
AVIStreamHeader sHeader;
- sHeader.size = _fileStream->readUint32LE();
+ sHeader.size = size;
sHeader.streamType = _fileStream->readUint32BE();
if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS)
@@ -247,21 +263,22 @@ void AVIDecoder::handleStreamHeader() {
if (sHeader.streamHandler == 0)
sHeader.streamHandler = bmInfo.compression;
- AVIVideoTrack *track = new AVIVideoTrack(_header.totalFrames, sHeader, bmInfo);
+ byte *initialPalette = 0;
if (bmInfo.bitCount == 8) {
- byte *palette = const_cast<byte *>(track->getPalette());
+ initialPalette = new byte[256 * 3];
+ memset(initialPalette, 0, 256 * 3);
+
+ byte *palette = initialPalette;
for (uint32 i = 0; i < bmInfo.clrUsed; i++) {
palette[i * 3 + 2] = _fileStream->readByte();
palette[i * 3 + 1] = _fileStream->readByte();
palette[i * 3] = _fileStream->readByte();
_fileStream->readByte();
}
-
- track->markPaletteDirty();
}
- addTrack(track);
+ addTrack(new AVIVideoTrack(_header.totalFrames, sHeader, bmInfo, initialPalette));
} else if (sHeader.streamType == ID_AUDS) {
PCMWaveFormat wvInfo;
wvInfo.tag = _fileStream->readUint16LE();
@@ -286,28 +303,41 @@ void AVIDecoder::handleStreamHeader() {
bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
- _fileStream = stream;
- _decodedHeader = false;
- _foundMovieList = false;
+ uint32 riffTag = stream->readUint32BE();
+ if (riffTag != ID_RIFF) {
+ warning("Failed to find RIFF header");
+ return false;
+ }
- // Read chunks until we have decoded the header
- while (!_decodedHeader && _fileStream->pos() < _fileStream->size())
- runHandle(_fileStream->readUint32BE());
+ /* uint32 fileSize = */ stream->readUint32LE();
+ uint32 riffType = stream->readUint32BE();
- if (_fileStream->pos() >= _fileStream->size()) {
- warning("Failed to find AVI header");
+ if (riffType != ID_AVI) {
+ warning("RIFF not an AVI file");
return false;
}
- // Then read until we find the movie list
- while (!_foundMovieList && _fileStream->pos() < _fileStream->size())
- runHandle(_fileStream->readUint32BE());
+ _fileStream = stream;
+
+ // Go through all chunks in the file
+ while (parseNextChunk())
+ ;
+
+ if (!_decodedHeader) {
+ warning("Failed to parse AVI header");
+ close();
+ return false;
+ }
- if (_fileStream->pos() >= _fileStream->size()) {
- warning("Failed to find AVI 'movi' LIST");
+ if (!_foundMovieList) {
+ warning("Failed to find 'MOVI' list");
+ close();
return false;
}
+ // Seek back to the start of the MOVI list
+ _fileStream->seek(_movieListStart);
+
return true;
}
@@ -318,33 +348,35 @@ void AVIDecoder::close() {
_fileStream = 0;
_decodedHeader = false;
_foundMovieList = false;
+ _movieListStart = 0;
- delete[] _ixInfo.indices;
- memset(&_ixInfo, 0, sizeof(_ixInfo));
+ _indexEntries.clear();
memset(&_header, 0, sizeof(_header));
}
void AVIDecoder::readNextPacket() {
uint32 nextTag = _fileStream->readUint32BE();
+ uint32 size = _fileStream->readUint32LE();
if (_fileStream->eos())
return;
if (nextTag == ID_LIST) {
// A list of audio/video chunks
- uint32 listSize = _fileStream->readUint32LE() - 4;
int32 startPos = _fileStream->pos();
if (_fileStream->readUint32BE() != ID_REC)
error("Expected 'rec ' LIST");
+ size -= 4; // subtract list type
+
// Decode chunks in the list
- while (_fileStream->pos() < startPos + (int32)listSize)
+ while (_fileStream->pos() < startPos + (int32)size)
readNextPacket();
return;
} else if (nextTag == ID_JUNK || nextTag == ID_IDX1) {
- runHandle(nextTag);
+ skipChunk(size);
return;
}
@@ -353,16 +385,15 @@ void AVIDecoder::readNextPacket() {
if (!track)
error("Cannot get track from tag '%s'", tag2str(nextTag));
- uint32 chunkSize = _fileStream->readUint32LE();
Common::SeekableReadStream *chunk = 0;
- if (chunkSize != 0) {
- chunk = _fileStream->readStream(chunkSize);
- _fileStream->skip(chunkSize & 1);
+ if (size != 0) {
+ chunk = _fileStream->readStream(size);
+ _fileStream->skip(size & 1);
}
if (track->getTrackType() == Track::kTrackTypeAudio) {
- if (getStreamType(nextTag) != MKTAG16('w', 'b'))
+ if (getStreamType(nextTag) != kStreamTypeAudio)
error("Invalid audio track tag '%s'", tag2str(nextTag));
assert(chunk);
@@ -370,29 +401,10 @@ void AVIDecoder::readNextPacket() {
} else {
AVIVideoTrack *videoTrack = (AVIVideoTrack *)track;
- if (getStreamType(nextTag) == MKTAG16('p', 'c')) {
+ if (getStreamType(nextTag) == kStreamTypePaletteChange) {
// Palette Change
- assert(chunk);
- byte firstEntry = chunk->readByte();
- uint16 numEntries = chunk->readByte();
- chunk->readUint16LE(); // Reserved
-
- // 0 entries means all colors are going to be changed
- if (numEntries == 0)
- numEntries = 256;
-
- byte *palette = const_cast<byte *>(videoTrack->getPalette());
-
- for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) {
- palette[i * 3] = chunk->readByte();
- palette[i * 3 + 1] = chunk->readByte();
- palette[i * 3 + 2] = chunk->readByte();
- chunk->readByte(); // Flags that don't serve us any purpose
- }
-
- delete chunk;
- videoTrack->markPaletteDirty();
- } else if (getStreamType(nextTag) == MKTAG16('d', 'b')) {
+ videoTrack->loadPaletteFromChunk(chunk);
+ } else if (getStreamType(nextTag) == kStreamTypeRawVideo) {
// TODO: Check if this really is uncompressed. Many videos
// falsely put compressed data in here.
error("Uncompressed AVI frame found");
@@ -403,17 +415,220 @@ void AVIDecoder::readNextPacket() {
}
}
-AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader)
- : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader) {
- memset(_palette, 0, sizeof(_palette));
+bool AVIDecoder::rewind() {
+ if (!VideoDecoder::rewind())
+ return false;
+
+ _fileStream->seek(_movieListStart);
+ return true;
+}
+
+bool AVIDecoder::seekIntern(const Audio::Timestamp &time) {
+ // Can't seek beyond the end
+ if (time > getDuration())
+ return false;
+
+ // Track down our video track (optionally audio too).
+ // We only support seeking with one track right now.
+ AVIVideoTrack *videoTrack = 0;
+ AVIAudioTrack *audioTrack = 0;
+ int videoIndex = -1;
+ int audioIndex = -1;
+ uint trackID = 0;
+
+ for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++, trackID++) {
+ if ((*it)->getTrackType() == Track::kTrackTypeVideo) {
+ if (videoTrack) {
+ // Already have one
+ // -> Not supported
+ return false;
+ }
+
+ videoTrack = (AVIVideoTrack *)*it;
+ videoIndex = trackID;
+ } else if ((*it)->getTrackType() == Track::kTrackTypeAudio) {
+ if (audioTrack) {
+ // Already have one
+ // -> Not supported
+ return false;
+ }
+
+ audioTrack = (AVIAudioTrack *)*it;
+ audioIndex = trackID;
+ }
+ }
+
+ // Need a video track to go forwards
+ // If there isn't a video track, why would anyone be using AVI then?
+ if (!videoTrack)
+ return false;
+
+ // If we seek directly to the end, just mark the tracks as over
+ if (time == getDuration()) {
+ videoTrack->setCurFrame(videoTrack->getFrameCount() - 1);
+
+ if (audioTrack)
+ audioTrack->resetStream();
+
+ return true;
+ }
+
+ // Get the frame we should be on at this time
+ uint frame = videoTrack->getFrameAtTime(time);
+
+ // Reset any palette, if necessary
+ videoTrack->useInitialPalette();
+
+ int lastKeyFrame = -1;
+ int frameIndex = -1;
+ int lastRecord = -1;
+ uint curFrame = 0;
+
+ // Go through and figure out where we should be
+ // If there's a palette, we need to find the palette too
+ for (uint32 i = 0; i < _indexEntries.size(); i++) {
+ const OldIndex &index = _indexEntries[i];
+
+ if (index.id == ID_REC) {
+ // Keep track of any records we find
+ lastRecord = i;
+ } else {
+ if (getStreamIndex(index.id) != videoIndex)
+ continue;
+
+ uint16 streamType = getStreamType(index.id);
+
+ if (streamType == kStreamTypePaletteChange) {
+ // We need to handle any palette change we see since there's no
+ // flag to tell if this is a "key" palette.
+ // Decode the palette
+ _fileStream->seek(_indexEntries[i].offset + 8);
+ Common::SeekableReadStream *chunk = 0;
+
+ if (_indexEntries[i].size != 0)
+ chunk = _fileStream->readStream(_indexEntries[i].size);
+
+ videoTrack->loadPaletteFromChunk(chunk);
+ } else {
+ // Check to see if this is a keyframe
+ // The first frame has to be a keyframe
+ if ((_indexEntries[i].flags & AVIIF_INDEX) || curFrame == 0)
+ lastKeyFrame = i;
+
+ // Did we find the target frame?
+ if (frame == curFrame) {
+ frameIndex = i;
+ break;
+ }
+
+ curFrame++;
+ }
+ }
+ }
+
+ if (frameIndex < 0) // This shouldn't happen.
+ return false;
+
+ if (audioTrack) {
+ // We need to find where the start of audio should be.
+ // Which is exactly 'initialFrames' audio chunks back from where
+ // our found frame is.
+
+ // Recreate the audio stream
+ audioTrack->resetStream();
+
+ uint framesNeeded = _header.initialFrames;
+ uint startAudioChunk = 0;
+ int startAudioSearch = (lastRecord < 0) ? (frameIndex - 1) : (lastRecord - 1);
+
+ for (int i = startAudioSearch; i >= 0; i--) {
+ if (getStreamIndex(_indexEntries[i].id) != audioIndex)
+ continue;
+
+ assert(getStreamType(_indexEntries[i].id) == kStreamTypeAudio);
+
+ framesNeeded--;
+
+ if (framesNeeded == 0) {
+ startAudioChunk = i;
+ break;
+ }
+ }
+
+ // Now go forward and queue them all
+ for (int i = startAudioChunk; i <= startAudioSearch; i++) {
+ if (_indexEntries[i].id == ID_REC)
+ continue;
+
+ if (getStreamIndex(_indexEntries[i].id) != audioIndex)
+ continue;
+
+ assert(getStreamType(_indexEntries[i].id) == kStreamTypeAudio);
+
+ _fileStream->seek(_indexEntries[i].offset + 8);
+ Common::SeekableReadStream *chunk = _fileStream->readStream(_indexEntries[i].size);
+ audioTrack->queueSound(chunk);
+ }
+
+ // Skip any audio to bring us to the right time
+ audioTrack->skipAudio(time, videoTrack->getFrameTime(frame));
+ }
+
+ // Decode from keyFrame to curFrame - 1
+ for (int i = lastKeyFrame; i < frameIndex; i++) {
+ if (_indexEntries[i].id == ID_REC)
+ continue;
+
+ if (getStreamIndex(_indexEntries[i].id) != videoIndex)
+ continue;
+
+ uint16 streamType = getStreamType(_indexEntries[i].id);
+
+ // Ignore palettes, they were already handled
+ if (streamType == kStreamTypePaletteChange)
+ continue;
+
+ // Frame, hopefully
+ _fileStream->seek(_indexEntries[i].offset + 8);
+ Common::SeekableReadStream *chunk = 0;
+
+ if (_indexEntries[i].size != 0)
+ chunk = _fileStream->readStream(_indexEntries[i].size);
+
+ videoTrack->decodeFrame(chunk);
+ }
+
+ // Seek to the right spot
+ // To the beginning of the last record, or frame if that doesn't exist
+ if (lastRecord >= 0)
+ _fileStream->seek(_indexEntries[lastRecord].offset);
+ else
+ _fileStream->seek(_indexEntries[frameIndex].offset);
+
+ videoTrack->setCurFrame((int)frame - 1);
+
+ return true;
+}
+
+byte AVIDecoder::getStreamIndex(uint32 tag) const {
+ char string[3];
+ WRITE_BE_UINT16(string, tag >> 16);
+ string[2] = 0;
+ return strtol(string, 0, 16);
+}
+
+AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader, byte *initialPalette)
+ : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader), _initialPalette(initialPalette) {
_videoCodec = createCodec();
- _dirtyPalette = false;
_lastFrame = 0;
_curFrame = -1;
+
+ useInitialPalette();
}
AVIDecoder::AVIVideoTrack::~AVIVideoTrack() {
delete _videoCodec;
+ delete[] _initialPalette;
}
void AVIDecoder::AVIVideoTrack::decodeFrame(Common::SeekableReadStream *stream) {
@@ -436,6 +651,47 @@ Graphics::PixelFormat AVIDecoder::AVIVideoTrack::getPixelFormat() const {
return Graphics::PixelFormat();
}
+void AVIDecoder::AVIVideoTrack::loadPaletteFromChunk(Common::SeekableReadStream *chunk) {
+ assert(chunk);
+ byte firstEntry = chunk->readByte();
+ uint16 numEntries = chunk->readByte();
+ chunk->readUint16LE(); // Reserved
+
+ // 0 entries means all colors are going to be changed
+ if (numEntries == 0)
+ numEntries = 256;
+
+ for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) {
+ _palette[i * 3] = chunk->readByte();
+ _palette[i * 3 + 1] = chunk->readByte();
+ _palette[i * 3 + 2] = chunk->readByte();
+ chunk->readByte(); // Flags that don't serve us any purpose
+ }
+
+ delete chunk;
+ _dirtyPalette = true;
+}
+
+void AVIDecoder::AVIVideoTrack::useInitialPalette() {
+ _dirtyPalette = false;
+
+ if (_initialPalette) {
+ memcpy(_palette, _initialPalette, sizeof(_palette));
+ _dirtyPalette = true;
+ }
+}
+
+bool AVIDecoder::AVIVideoTrack::rewind() {
+ _curFrame = -1;
+
+ useInitialPalette();
+
+ delete _videoCodec;
+ _videoCodec = createCodec();
+ _lastFrame = 0;
+ return true;
+}
+
Codec *AVIDecoder::AVIVideoTrack::createCodec() {
switch (_vidsHeader.streamHandler) {
case ID_CRAM:
@@ -497,6 +753,31 @@ void AVIDecoder::AVIAudioTrack::queueSound(Common::SeekableReadStream *stream) {
}
}
+void AVIDecoder::AVIAudioTrack::skipAudio(const Audio::Timestamp &time, const Audio::Timestamp &frameTime) {
+ Audio::Timestamp timeDiff = time.convertToFramerate(_wvInfo.samplesPerSec) - frameTime.convertToFramerate(_wvInfo.samplesPerSec);
+ int skipFrames = timeDiff.totalNumberOfFrames();
+
+ if (skipFrames <= 0)
+ return;
+
+ if (_audStream->isStereo())
+ skipFrames *= 2;
+
+ int16 *tempBuffer = new int16[skipFrames];
+ _audStream->readBuffer(tempBuffer, skipFrames);
+ delete[] tempBuffer;
+}
+
+void AVIDecoder::AVIAudioTrack::resetStream() {
+ delete _audStream;
+ _audStream = createAudioStream();
+}
+
+bool AVIDecoder::AVIAudioTrack::rewind() {
+ resetStream();
+ return true;
+}
+
Audio::AudioStream *AVIDecoder::AVIAudioTrack::getAudioStream() const {
return _audStream;
}
diff --git a/video/avi_decoder.h b/video/avi_decoder.h
index 6082232464..fffcbfbe16 100644
--- a/video/avi_decoder.h
+++ b/video/avi_decoder.h
@@ -20,10 +20,10 @@
*
*/
-#ifndef VIDEO_AVI_PLAYER_H
-#define VIDEO_AVI_PLAYER_H
+#ifndef VIDEO_AVI_DECODER_H
+#define VIDEO_AVI_DECODER_H
-#include "common/endian.h"
+#include "common/array.h"
#include "common/rational.h"
#include "common/rect.h"
#include "common/str.h"
@@ -66,8 +66,13 @@ public:
uint16 getWidth() const { return _header.width; }
uint16 getHeight() const { return _header.height; }
+ bool rewind();
+ bool isRewindable() const { return true; }
+ bool isSeekable() const;
+
protected:
void readNextPacket();
+ bool seekIntern(const Audio::Timestamp &time);
private:
struct BitmapInfoHeader {
@@ -102,13 +107,10 @@ private:
};
struct OldIndex {
+ uint32 id;
+ uint32 flags;
+ uint32 offset;
uint32 size;
- struct Index {
- uint32 id;
- uint32 flags;
- uint32 offset;
- uint32 size;
- } *indices;
};
// Index Flags
@@ -160,7 +162,7 @@ private:
class AVIVideoTrack : public FixedRateVideoTrack {
public:
- AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader);
+ AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader, byte *initialPalette = 0);
~AVIVideoTrack();
void decodeFrame(Common::SeekableReadStream *stream);
@@ -173,7 +175,12 @@ private:
const Graphics::Surface *decodeNextFrame() { return _lastFrame; }
const byte *getPalette() const { _dirtyPalette = false; return _palette; }
bool hasDirtyPalette() const { return _dirtyPalette; }
- void markPaletteDirty() { _dirtyPalette = true; }
+ void setCurFrame(int frame) { _curFrame = frame; }
+ void loadPaletteFromChunk(Common::SeekableReadStream *chunk);
+ void useInitialPalette();
+
+ bool isRewindable() const { return true; }
+ bool rewind();
protected:
Common::Rational getFrameRate() const { return Common::Rational(_vidsHeader.rate, _vidsHeader.scale); }
@@ -182,6 +189,7 @@ private:
AVIStreamHeader _vidsHeader;
BitmapInfoHeader _bmInfo;
byte _palette[3 * 256];
+ byte *_initialPalette;
mutable bool _dirtyPalette;
int _frameCount, _curFrame;
@@ -197,6 +205,11 @@ private:
void queueSound(Common::SeekableReadStream *stream);
Audio::Mixer::SoundType getSoundType() const { return _soundType; }
+ void skipAudio(const Audio::Timestamp &time, const Audio::Timestamp &frameTime);
+ void resetStream();
+
+ bool isRewindable() const { return true; }
+ bool rewind();
protected:
Audio::AudioStream *getAudioStream() const;
@@ -218,19 +231,24 @@ private:
Audio::QueuingAudioStream *createAudioStream();
};
- OldIndex _ixInfo;
+ Common::Array<OldIndex> _indexEntries;
AVIHeader _header;
Common::SeekableReadStream *_fileStream;
- bool _decodedHeader, _foundMovieList;
+ bool _decodedHeader;
+ bool _foundMovieList;
+ uint32 _movieListStart;
Audio::Mixer::SoundType _soundType;
Common::Rational _frameRateOverride;
void initCommon();
- void runHandle(uint32 tag);
- void handleList();
- void handleStreamHeader();
+ bool parseNextChunk();
+ void skipChunk(uint32 size);
+ void handleList(uint32 listSize);
+ void handleStreamHeader(uint32 size);
+ uint16 getStreamType(uint32 tag) const { return tag & 0xFFFF; }
+ byte getStreamIndex(uint32 tag) const;
};
} // End of namespace Video
diff --git a/video/codecs/cdtoons.cpp b/video/codecs/cdtoons.cpp
index 528cee8094..68925ed0db 100644
--- a/video/codecs/cdtoons.cpp
+++ b/video/codecs/cdtoons.cpp
@@ -298,7 +298,7 @@ Graphics::Surface *CDToonsDecoder::decodeImage(Common::SeekableReadStream *strea
for (uint i = 0; i < actions.size(); i++) {
CDToonsAction &action = actions[i];
if (i == 0 && action.blockId == 0)
- memset(_surface->pixels, backgroundColor, _surface->w * _surface->h);
+ memset(_surface->getPixels(), backgroundColor, _surface->w * _surface->h);
if (!_blocks.contains(action.blockId))
continue;
if (!action.rect.right)
diff --git a/video/codecs/cinepak.cpp b/video/codecs/cinepak.cpp
index bcf0cf1180..a7782f4192 100644
--- a/video/codecs/cinepak.cpp
+++ b/video/codecs/cinepak.cpp
@@ -41,11 +41,11 @@ namespace Video {
byte b = _clipTable[lum + (u << 1)]; \
\
if (_pixelFormat.bytesPerPixel == 2) \
- *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \
+ *((uint16 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
else \
- *((uint32 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \
+ *((uint32 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
} else \
- *((byte *)_curFrame.surface->pixels + offset) = lum
+ *((byte *)_curFrame.surface->getPixels() + offset) = lum
CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec() {
_curFrame.surface = NULL;
diff --git a/video/codecs/msrle.cpp b/video/codecs/msrle.cpp
index fa03a59efd..2f2ac0334f 100644
--- a/video/codecs/msrle.cpp
+++ b/video/codecs/msrle.cpp
@@ -53,7 +53,7 @@ void MSRLEDecoder::decode8(Common::SeekableReadStream *stream) {
int x = 0;
int y = _surface->h - 1;
- byte *data = (byte *) _surface->pixels;
+ byte *data = (byte *) _surface->getPixels();
uint16 width = _surface->w;
uint16 height = _surface->h;
diff --git a/video/codecs/msvideo1.cpp b/video/codecs/msvideo1.cpp
index 06e4640025..409d588ddf 100644
--- a/video/codecs/msvideo1.cpp
+++ b/video/codecs/msvideo1.cpp
@@ -48,7 +48,7 @@ MSVideo1Decoder::~MSVideo1Decoder() {
void MSVideo1Decoder::decode8(Common::SeekableReadStream *stream) {
byte colors[8];
- byte *pixels = (byte *)_surface->pixels;
+ byte *pixels = (byte *)_surface->getPixels();
uint16 stride = _surface->w;
int skipBlocks = 0;
diff --git a/video/codecs/qtrle.cpp b/video/codecs/qtrle.cpp
index d2cdea27de..1f1fee7997 100644
--- a/video/codecs/qtrle.cpp
+++ b/video/codecs/qtrle.cpp
@@ -61,7 +61,7 @@ QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Cod
void QTRLEDecoder::decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
- byte *rgb = (byte *)_surface->pixels;
+ byte *rgb = (byte *)_surface->getPixels();
while (linesToChange) {
CHECK_STREAM_PTR(2);
@@ -105,7 +105,7 @@ void QTRLEDecoder::decode1(Common::SeekableReadStream *stream, uint32 rowPtr, ui
void QTRLEDecoder::decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp) {
uint32 pixelPtr = 0;
- byte *rgb = (byte *)_surface->pixels;
+ byte *rgb = (byte *)_surface->getPixels();
byte numPixels = (bpp == 4) ? 8 : 16;
while (linesToChange--) {
@@ -165,7 +165,7 @@ void QTRLEDecoder::decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr,
void QTRLEDecoder::decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
- byte *rgb = (byte *)_surface->pixels;
+ byte *rgb = (byte *)_surface->getPixels();
while (linesToChange--) {
CHECK_STREAM_PTR(2);
@@ -210,7 +210,7 @@ void QTRLEDecoder::decode8(Common::SeekableReadStream *stream, uint32 rowPtr, ui
void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
- uint16 *rgb = (uint16 *)_surface->pixels;
+ uint16 *rgb = (uint16 *)_surface->getPixels();
while (linesToChange--) {
CHECK_STREAM_PTR(2);
@@ -248,7 +248,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, u
void QTRLEDecoder::decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
- uint32 *rgb = (uint32 *)_surface->pixels;
+ uint32 *rgb = (uint32 *)_surface->getPixels();
while (linesToChange--) {
CHECK_STREAM_PTR(2);
@@ -294,7 +294,7 @@ void QTRLEDecoder::decode24(Common::SeekableReadStream *stream, uint32 rowPtr, u
void QTRLEDecoder::decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
- uint32 *rgb = (uint32 *)_surface->pixels;
+ uint32 *rgb = (uint32 *)_surface->getPixels();
while (linesToChange--) {
CHECK_STREAM_PTR(2);
diff --git a/video/codecs/rpza.cpp b/video/codecs/rpza.cpp
index 0a9f87747e..17a2c53d9b 100644
--- a/video/codecs/rpza.cpp
+++ b/video/codecs/rpza.cpp
@@ -58,7 +58,7 @@ RPZADecoder::~RPZADecoder() {
#define PUT_PIXEL(color) \
if ((int32)blockPtr < _surface->w * _surface->h) \
- WRITE_UINT16((uint16 *)_surface->pixels + blockPtr, color); \
+ WRITE_UINT16((uint16 *)_surface->getPixels() + blockPtr, color); \
blockPtr++
const Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) {
diff --git a/video/codecs/smc.cpp b/video/codecs/smc.cpp
index 2eedb62a0f..c0f8152547 100644
--- a/video/codecs/smc.cpp
+++ b/video/codecs/smc.cpp
@@ -56,7 +56,7 @@ SMCDecoder::~SMCDecoder() {
}
const Graphics::Surface *SMCDecoder::decodeImage(Common::SeekableReadStream *stream) {
- byte *pixels = (byte *)_surface->pixels;
+ byte *pixels = (byte *)_surface->getPixels();
uint32 numBlocks = 0;
uint32 colorFlags = 0;
diff --git a/video/codecs/truemotion1.cpp b/video/codecs/truemotion1.cpp
index e475c8426c..720e86a4ff 100644
--- a/video/codecs/truemotion1.cpp
+++ b/video/codecs/truemotion1.cpp
@@ -400,11 +400,14 @@ void TrueMotion1Decoder::decode16() {
const Graphics::Surface *TrueMotion1Decoder::decodeImage(Common::SeekableReadStream *stream) {
decodeHeader(stream);
- if (compressionTypes[_header.compression].algorithm == ALGO_NOP)
+ if (compressionTypes[_header.compression].algorithm == ALGO_NOP) {
+ delete[] _buf;
return 0;
+ }
if (compressionTypes[_header.compression].algorithm == ALGO_RGB24H) {
warning("Unhandled TrueMotion1 24bpp frame");
+ delete[] _buf;
return 0;
} else
decode16();
diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp
index 4c3b6f8414..024e479bf7 100644
--- a/video/coktel_decoder.cpp
+++ b/video/coktel_decoder.cpp
@@ -97,12 +97,8 @@ void CoktelDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uin
assert(bpp == getPixelFormat().bytesPerPixel);
// Create a surface over this memory
- _surface.w = width;
- _surface.h = height;
- _surface.pitch = width * bpp;
- _surface.pixels = mem;
// TODO: Check whether it is fine to assume we want the setup PixelFormat.
- _surface.format = getPixelFormat();
+ _surface.init(width, height, width * bpp, mem, getPixelFormat());
_ownSurface = false;
}
@@ -122,7 +118,7 @@ const Graphics::Surface *CoktelDecoder::getSurface() const {
}
bool CoktelDecoder::hasSurface() {
- return _surface.pixels != 0;
+ return _surface.getPixels();
}
void CoktelDecoder::createSurface() {
@@ -143,7 +139,7 @@ void CoktelDecoder::freeSurface() {
_surface.w = 0;
_surface.h = 0;
_surface.pitch = 0;
- _surface.pixels = 0;
+ _surface.setPixels(0);
_surface.format = Graphics::PixelFormat();
} else
_surface.free();
@@ -473,7 +469,7 @@ void CoktelDecoder::renderBlockWhole(Graphics::Surface &dstSurf, const byte *src
rect.clip(dstSurf.w, dstSurf.h);
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left * dstSurf.format.bytesPerPixel;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
for (int i = 0; i < rect.height(); i++) {
memcpy(dst, src, rect.width() * dstSurf.format.bytesPerPixel);
@@ -488,7 +484,7 @@ void CoktelDecoder::renderBlockWhole4X(Graphics::Surface &dstSurf, const byte *s
rect.clip(dstSurf.w, dstSurf.h);
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
for (int i = 0; i < rect.height(); i++) {
byte *dstRow = dst;
const byte *srcRow = src;
@@ -515,7 +511,7 @@ void CoktelDecoder::renderBlockWhole2Y(Graphics::Surface &dstSurf, const byte *s
int16 height = rect.height();
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
while (height > 1) {
memcpy(dst , src, rect.width());
memcpy(dst + dstSurf.pitch, src, rect.width());
@@ -535,7 +531,7 @@ void CoktelDecoder::renderBlockSparse(Graphics::Surface &dstSurf, const byte *sr
rect.clip(dstSurf.w, dstSurf.h);
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
for (int i = 0; i < rect.height(); i++) {
byte *dstRow = dst;
int16 pixWritten = 0;
@@ -572,7 +568,7 @@ void CoktelDecoder::renderBlockSparse2Y(Graphics::Surface &dstSurf, const byte *
rect.clip(dstSurf.w, dstSurf.h);
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
for (int i = 0; i < rect.height(); i += 2) {
byte *dstRow = dst;
int16 pixWritten = 0;
@@ -604,7 +600,7 @@ void CoktelDecoder::renderBlockRLE(Graphics::Surface &dstSurf, const byte *src,
rect.clip(dstSurf.w, dstSurf.h);
- byte *dst = (byte *)dstSurf.pixels + (rect.top * dstSurf.pitch) + rect.left;
+ byte *dst = (byte *)dstSurf.getBasePtr(rect.left, rect.top);
for (int i = 0; i < rect.height(); i++) {
byte *dstRow = dst;
int16 pixWritten = 0;
@@ -865,7 +861,7 @@ void PreIMDDecoder::renderFrame() {
uint16 h = CLIP<int32>(_surface.h - _y, 0, _height);
const byte *src = _videoBuffer;
- byte *dst = (byte *)_surface.pixels + (_y * _surface.pitch) + _x;
+ byte *dst = (byte *)_surface.getBasePtr(_x, _y);
uint32 frameDataSize = _videoBufferSize;
@@ -1458,7 +1454,7 @@ bool IMDDecoder::renderFrame(Common::Rect &rect) {
const int offsetY = (_y + rect.top) * _surface.pitch;
const int offset = offsetX + offsetY;
- if (deLZ77((byte *)_surface.pixels + offset, dataPtr, dataSize,
+ if (deLZ77((byte *)_surface.getPixels() + offset, dataPtr, dataSize,
_surface.w * _surface.h * _surface.format.bytesPerPixel - offset))
return true;
}
@@ -1879,11 +1875,8 @@ bool VMDDecoder::assessVideoProperties() {
_videoBuffer[i] = new byte[_videoBufferSize];
memset(_videoBuffer[i], 0, _videoBufferSize);
- _8bppSurface[i].w = _width * _bytesPerPixel;
- _8bppSurface[i].h = _height;
- _8bppSurface[i].pitch = _width * _bytesPerPixel;
- _8bppSurface[i].pixels = _videoBuffer[i];
- _8bppSurface[i].format = Graphics::PixelFormat::createFormatCLUT8();
+ _8bppSurface[i].init(_width * _bytesPerPixel, _height, _width * _bytesPerPixel,
+ _videoBuffer[i], Graphics::PixelFormat::createFormatCLUT8());
}
}
@@ -2277,7 +2270,7 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) {
rect = Common::Rect(_x, _y, _x + codecSurf->w, _y + codecSurf->h);
rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height));
- renderBlockWhole(_surface, (const byte *) codecSurf->pixels, rect);
+ renderBlockWhole(_surface, (const byte *)codecSurf->getPixels(), rect);
return true;
}
@@ -2298,7 +2291,7 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) {
const int offsetY = (_y + rect.top) * _surface.pitch;
const int offset = offsetX + offsetY;
- if (deLZ77((byte *)_surface.pixels + offset, dataPtr, dataSize,
+ if (deLZ77((byte *)_surface.getPixels() + offset, dataPtr, dataSize,
_surface.w * _surface.h * _surface.format.bytesPerPixel - offset))
return true;
}
@@ -2406,10 +2399,11 @@ void VMDDecoder::blit16(const Graphics::Surface &srcSurf, Common::Rect &rect) {
Graphics::PixelFormat pixelFormat = getPixelFormat();
- const byte *src = (byte *)srcSurf.pixels +
+ // We cannot use getBasePtr here because srcSurf.format.bytesPerPixel is
+ // different from _bytesPerPixel.
+ const byte *src = (const byte *)srcSurf.getPixels() +
(srcRect.top * srcSurf.pitch) + srcRect.left * _bytesPerPixel;
- byte *dst = (byte *)_surface.pixels +
- ((_y + rect.top) * _surface.pitch) + (_x + rect.left) * _surface.format.bytesPerPixel;
+ byte *dst = (byte *)_surface.getBasePtr(_x + rect.left, _y + rect.top);
for (int i = 0; i < rect.height(); i++) {
const byte *srcRow = src;
@@ -2446,10 +2440,11 @@ void VMDDecoder::blit24(const Graphics::Surface &srcSurf, Common::Rect &rect) {
Graphics::PixelFormat pixelFormat = getPixelFormat();
- const byte *src = (byte *)srcSurf.pixels +
+ // We cannot use getBasePtr here because srcSurf.format.bytesPerPixel is
+ // different from _bytesPerPixel.
+ const byte *src = (const byte *)srcSurf.getPixels() +
(srcRect.top * srcSurf.pitch) + srcRect.left * _bytesPerPixel;
- byte *dst = (byte *)_surface.pixels +
- ((_y + rect.top) * _surface.pitch) + (_x + rect.left) * _surface.format.bytesPerPixel;
+ byte *dst = (byte *)_surface.getBasePtr(_x + rect.left, _y + rect.top);
for (int i = 0; i < rect.height(); i++) {
const byte *srcRow = src;
diff --git a/video/dxa_decoder.cpp b/video/dxa_decoder.cpp
index 5ac9bd2088..27b1664b07 100644
--- a/video/dxa_decoder.cpp
+++ b/video/dxa_decoder.cpp
@@ -521,17 +521,17 @@ const Graphics::Surface *DXADecoder::DXAVideoTrack::decodeNextFrame() {
memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width);
memset(&_scaledBuffer[((2 * cy) + 1) * _width], 0, _width);
}
- _surface->pixels = _scaledBuffer;
+ _surface->setPixels(_scaledBuffer);
break;
case S_DOUBLE:
for (int cy = 0; cy < _curHeight; cy++) {
memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width);
memcpy(&_scaledBuffer[((2 * cy) + 1) * _width], &_frameBuffer1[cy * _width], _width);
}
- _surface->pixels = _scaledBuffer;
+ _surface->setPixels(_scaledBuffer);
break;
case S_NONE:
- _surface->pixels = _frameBuffer1;
+ _surface->setPixels(_frameBuffer1);
break;
}
diff --git a/video/flic_decoder.cpp b/video/flic_decoder.cpp
index de545366b1..317dc14691 100644
--- a/video/flic_decoder.cpp
+++ b/video/flic_decoder.cpp
@@ -244,7 +244,7 @@ void FlicDecoder::FlicVideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch)
for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
for (int y = (*it).top; y < (*it).bottom; ++y) {
const int x = (*it).left;
- memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x);
+ memcpy(dst + y * pitch + x, (byte *)_surface->getBasePtr(x, y), (*it).right - x);
}
}
@@ -252,7 +252,7 @@ void FlicDecoder::FlicVideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch)
}
void FlicDecoder::FlicVideoTrack::copyFrame(uint8 *data) {
- memcpy((byte *)_surface->pixels, data, getWidth() * getHeight());
+ memcpy((byte *)_surface->getPixels(), data, getWidth() * getHeight());
// Redraw
_dirtyRects.clear();
@@ -260,8 +260,8 @@ void FlicDecoder::FlicVideoTrack::copyFrame(uint8 *data) {
}
void FlicDecoder::FlicVideoTrack::decodeByteRun(uint8 *data) {
- byte *ptr = (byte *)_surface->pixels;
- while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) {
+ byte *ptr = (byte *)_surface->getPixels();
+ while ((int32)(ptr - (byte *)_surface->getPixels()) < (getWidth() * getHeight())) {
int chunks = *data++;
while (chunks--) {
int count = (int8)*data++;
@@ -305,7 +305,7 @@ void FlicDecoder::FlicVideoTrack::decodeDeltaFLC(uint8 *data) {
case OP_UNDEFINED:
break;
case OP_LASTPIXEL:
- *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF);
+ *((byte *)_surface->getBasePtr(getWidth() - 1, currentLine)) = (opcode & 0xFF);
_dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1));
break;
case OP_LINESKIPCOUNT:
@@ -321,14 +321,14 @@ void FlicDecoder::FlicVideoTrack::decodeDeltaFLC(uint8 *data) {
column += *data++;
int rleCount = (int8)*data++;
if (rleCount > 0) {
- memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2);
+ memcpy((byte *)_surface->getBasePtr(column, currentLine), data, rleCount * 2);
data += rleCount * 2;
_dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
} else if (rleCount < 0) {
rleCount = -rleCount;
uint16 dataWord = READ_UINT16(data); data += 2;
for (int i = 0; i < rleCount; ++i) {
- WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord);
+ WRITE_UINT16((byte *)_surface->getBasePtr(column + i * 2, currentLine), dataWord);
}
_dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
} else { // End of cutscene ?
diff --git a/video/smk_decoder.cpp b/video/smk_decoder.cpp
index b622a0ab61..3dbcebcde4 100644
--- a/video/smk_decoder.cpp
+++ b/video/smk_decoder.cpp
@@ -580,7 +580,7 @@ void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) {
while (run-- && block < blocks) {
clr = _MClrTree->getCode(bs);
map = _MMapTree->getCode(bs);
- out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+ out = (byte *)_surface->getPixels() + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
hi = clr >> 8;
lo = clr & 0xff;
for (i = 0; i < 4; i++) {
@@ -613,7 +613,7 @@ void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) {
}
while (run-- && block < blocks) {
- out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+ out = (byte *)_surface->getPixels() + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
switch (mode) {
case 0:
for (i = 0; i < 4; ++i) {
@@ -679,7 +679,7 @@ void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStream &bs) {
uint32 col;
mode = type >> 8;
while (run-- && block < blocks) {
- out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+ out = (byte *)_surface->getPixels() + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
col = mode * 0x01010101;
for (i = 0; i < 4 * doubleY; ++i) {
out[0] = out[1] = out[2] = out[3] = col;
diff --git a/video/theora_decoder.cpp b/video/theora_decoder.cpp
index 63aa93e2f5..a0ee0a36b4 100644
--- a/video/theora_decoder.cpp
+++ b/video/theora_decoder.cpp
@@ -262,11 +262,8 @@ TheoraDecoder::TheoraVideoTrack::TheoraVideoTrack(const Graphics::PixelFormat &f
_surface.create(theoraInfo.frame_width, theoraInfo.frame_height, format);
// Set up a display surface
- _displaySurface.pixels = _surface.getBasePtr(theoraInfo.pic_x, theoraInfo.pic_y);
- _displaySurface.w = theoraInfo.pic_width;
- _displaySurface.h = theoraInfo.pic_height;
- _displaySurface.format = format;
- _displaySurface.pitch = _surface.pitch;
+ _displaySurface.init(theoraInfo.pic_width, theoraInfo.pic_height, _surface.pitch,
+ _surface.getBasePtr(theoraInfo.pic_x, theoraInfo.pic_y), format);
// Set the frame rate
_frameRate = Common::Rational(theoraInfo.fps_numerator, theoraInfo.fps_denominator);
@@ -280,7 +277,7 @@ TheoraDecoder::TheoraVideoTrack::~TheoraVideoTrack() {
th_decode_free(_theoraDecode);
_surface.free();
- _displaySurface.pixels = 0;
+ _displaySurface.setPixels(0);
}
bool TheoraDecoder::TheoraVideoTrack::decodePacket(ogg_packet &oggPacket) {
@@ -338,7 +335,7 @@ TheoraDecoder::VorbisAudioTrack::VorbisAudioTrack(Audio::Mixer::SoundType soundT
vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
info = &vorbisInfo;
- _audStream = Audio::makeQueuingAudioStream(vorbisInfo.rate, vorbisInfo.channels);
+ _audStream = Audio::makeQueuingAudioStream(vorbisInfo.rate, vorbisInfo.channels != 1);
_audioBufferFill = 0;
_audioBuffer = 0;
diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp
index 5df811008c..0ab1478727 100644
--- a/video/video_decoder.cpp
+++ b/video/video_decoder.cpp
@@ -62,6 +62,8 @@ void VideoDecoder::close() {
delete *it;
_tracks.clear();
+ _internalTracks.clear();
+ _externalTracks.clear();
_dirtyPalette = false;
_palette = 0;
_startTime = 0;
@@ -336,7 +338,12 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) {
if (isPlaying())
stopAudio();
- for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++)
+ // Do the actual seeking
+ if (!seekIntern(time))
+ return false;
+
+ // Seek any external track too
+ for (TrackListIterator it = _externalTracks.begin(); it != _externalTracks.end(); it++)
if (!(*it)->seek(time))
return false;
@@ -356,12 +363,12 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) {
}
bool VideoDecoder::seekToFrame(uint frame) {
+ if (!isSeekable())
+ return false;
+
VideoTrack *track = 0;
for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) {
- if (!(*it)->isSeekable())
- return false;
-
if ((*it)->getTrackType() == Track::kTrackTypeVideo) {
// We only allow seeking by frame when one video track
// is present
@@ -471,6 +478,14 @@ Audio::Timestamp VideoDecoder::getDuration() const {
return maxDuration;
}
+bool VideoDecoder::seekIntern(const Audio::Timestamp &time) {
+ for (TrackList::iterator it = _internalTracks.begin(); it != _internalTracks.end(); it++)
+ if (!(*it)->seek(time))
+ return false;
+
+ return true;
+}
+
VideoDecoder::Track::Track() {
_paused = false;
}
@@ -516,10 +531,9 @@ Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getFrameTime(uint frame) con
if (frameRate == frameRate.toInt()) // The nice case (a whole number)
return Audio::Timestamp(0, frame, frameRate.toInt());
- // Just convert to milliseconds.
- Common::Rational time = frame * 1000;
- time /= frameRate;
- return Audio::Timestamp(time.toInt(), 1000);
+ // Convert as best as possible
+ Common::Rational time = frameRate.getInverse() * frame;
+ return Audio::Timestamp(0, time.getNumerator(), time.getDenominator());
}
uint VideoDecoder::FixedRateVideoTrack::getFrameAtTime(const Audio::Timestamp &time) const {
@@ -529,8 +543,10 @@ uint VideoDecoder::FixedRateVideoTrack::getFrameAtTime(const Audio::Timestamp &t
if (frameRate == time.framerate())
return time.totalNumberOfFrames();
- // Default case
- return (time.totalNumberOfFrames() * frameRate / time.framerate()).toInt();
+ // Create the rational based on the time first to hopefully cancel out
+ // *something* when multiplying by the frameRate (which can be large in
+ // some AVI videos).
+ return (Common::Rational(time.totalNumberOfFrames(), time.framerate()) * frameRate).toInt();
}
Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getDuration() const {
@@ -641,9 +657,14 @@ bool VideoDecoder::StreamFileAudioTrack::loadFromFile(const Common::String &base
return _stream != 0;
}
-void VideoDecoder::addTrack(Track *track) {
+void VideoDecoder::addTrack(Track *track, bool isExternal) {
_tracks.push_back(track);
+ if (isExternal)
+ _externalTracks.push_back(track);
+ else
+ _internalTracks.push_back(track);
+
if (track->getTrackType() == Track::kTrackTypeAudio) {
// Update volume settings if it's an audio track
((AudioTrack *)track)->setVolume(_audioVolume);
@@ -673,7 +694,7 @@ bool VideoDecoder::addStreamFileTrack(const Common::String &baseName) {
bool result = track->loadFromFile(baseName);
if (result)
- addTrack(track);
+ addTrack(track, true);
else
delete track;
@@ -704,17 +725,17 @@ void VideoDecoder::setEndTime(const Audio::Timestamp &endTime) {
}
VideoDecoder::Track *VideoDecoder::getTrack(uint track) {
- if (track > _tracks.size())
+ if (track > _internalTracks.size())
return 0;
- return _tracks[track];
+ return _internalTracks[track];
}
const VideoDecoder::Track *VideoDecoder::getTrack(uint track) const {
- if (track > _tracks.size())
+ if (track > _internalTracks.size())
return 0;
- return _tracks[track];
+ return _internalTracks[track];
}
bool VideoDecoder::endOfVideoTracks() const {
diff --git a/video/video_decoder.h b/video/video_decoder.h
index d0a6e08005..ac6586d8dd 100644
--- a/video/video_decoder.h
+++ b/video/video_decoder.h
@@ -168,14 +168,15 @@ public:
/**
* Seek to a given time in the video.
*
- * If the video is playing, it will continue to play. The default
- * implementation will seek each track and must still be called
- * from any other implementation.
+ * If the video is playing, it will continue to play. This calls
+ * seekIntern(), which can be overriden. By default, seekIntern()
+ * will call Track::seek() on all tracks with the time passed to
+ * this function.
*
* @param time The time to seek to
* @return true on success, false otherwise
*/
- virtual bool seek(const Audio::Timestamp &time);
+ bool seek(const Audio::Timestamp &time);
/**
* Seek to a given frame.
@@ -593,17 +594,17 @@ protected:
virtual Audio::Timestamp getDuration() const;
Audio::Timestamp getFrameTime(uint frame) const;
- protected:
- /**
- * Get the rate at which this track is played.
- */
- virtual Common::Rational getFrameRate() const = 0;
-
/**
* Get the frame that should be displaying at the given time. This is
* helpful for someone implementing seek().
*/
uint getFrameAtTime(const Audio::Timestamp &time) const;
+
+ protected:
+ /**
+ * Get the rate at which this track is played.
+ */
+ virtual Common::Rational getFrameRate() const = 0;
};
/**
@@ -760,8 +761,11 @@ protected:
* Define a track to be used by this class.
*
* The pointer is then owned by this base class.
+ *
+ * @param track The track to add
+ * @param isExternal Is this an external track not found by loadStream()?
*/
- void addTrack(Track *track);
+ void addTrack(Track *track, bool isExternal = false);
/**
* Whether or not getTime() will sync with a playing audio track.
@@ -813,16 +817,27 @@ protected:
/**
* Get the begin iterator of the tracks
*/
- TrackListIterator getTrackListBegin() { return _tracks.begin(); }
+ TrackListIterator getTrackListBegin() { return _internalTracks.begin(); }
/**
* Get the end iterator of the tracks
*/
- TrackListIterator getTrackListEnd() { return _tracks.end(); }
+ TrackListIterator getTrackListEnd() { return _internalTracks.end(); }
+
+ /**
+ * The internal seek function that does the actual seeking.
+ *
+ * @see seek()
+ *
+ * @return true on success, false otherwise
+ */
+ virtual bool seekIntern(const Audio::Timestamp &time);
private:
// Tracks owned by this VideoDecoder
TrackList _tracks;
+ TrackList _internalTracks;
+ TrackList _externalTracks;
// Current playback status
bool _needsUpdate;