diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/avi_decoder.cpp | 487 | ||||
-rw-r--r-- | video/avi_decoder.h | 50 | ||||
-rw-r--r-- | video/codecs/cdtoons.cpp | 2 | ||||
-rw-r--r-- | video/codecs/cinepak.cpp | 6 | ||||
-rw-r--r-- | video/codecs/msrle.cpp | 2 | ||||
-rw-r--r-- | video/codecs/msvideo1.cpp | 2 | ||||
-rw-r--r-- | video/codecs/qtrle.cpp | 12 | ||||
-rw-r--r-- | video/codecs/rpza.cpp | 2 | ||||
-rw-r--r-- | video/codecs/smc.cpp | 2 | ||||
-rw-r--r-- | video/codecs/truemotion1.cpp | 5 | ||||
-rw-r--r-- | video/coktel_decoder.cpp | 51 | ||||
-rw-r--r-- | video/dxa_decoder.cpp | 6 | ||||
-rw-r--r-- | video/flic_decoder.cpp | 14 | ||||
-rw-r--r-- | video/smk_decoder.cpp | 6 | ||||
-rw-r--r-- | video/theora_decoder.cpp | 11 | ||||
-rw-r--r-- | video/video_decoder.cpp | 53 | ||||
-rw-r--r-- | video/video_decoder.h | 41 |
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; |