diff options
-rw-r--r-- | video/avi_decoder.cpp | 153 | ||||
-rw-r--r-- | video/avi_decoder.h | 30 |
2 files changed, 99 insertions, 84 deletions
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index ff728a8437..92a60fcccf 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -67,6 +67,7 @@ namespace Video { #define ID_STRD MKTAG('s','t','r','d') //#define ID_INFO MKTAG('I','N','F','O') #define ID_ISFT MKTAG('I','S','F','T') +#define ID_DISP MKTAG('D','I','S','P') // Codec tags #define ID_RLE MKTAG('R','L','E',' ') @@ -78,18 +79,6 @@ 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'; -} - -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 +96,26 @@ 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::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 +130,64 @@ 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(); + indexEntry.size = _fileStream->readUint32LE(); + _indexEntries.push_back(indexEntry); + debug(0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(indexEntry.id), indexEntry.offset, indexEntry.size); } 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 + // If we found the movie block _foundMovieList = true; + _movieListStart = curPos; + _fileStream->skip(listSize); return; + } else if (listType == ID_HDRL) { + // Mark the header as decoded + _decodedHeader = true; } 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) @@ -286,28 +278,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 (_fileStream->pos() >= _fileStream->size()) { - warning("Failed to find AVI 'movi' LIST"); + if (!_decodedHeader) { + warning("Failed to parse AVI header"); + close(); return false; } + 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 +323,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,12 +360,11 @@ 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) { @@ -403,6 +409,13 @@ void AVIDecoder::readNextPacket() { } } +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) : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader) { memset(_palette, 0, sizeof(_palette)); diff --git a/video/avi_decoder.h b/video/avi_decoder.h index 6082232464..5d52c7c797 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" @@ -102,13 +102,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 @@ -218,19 +215,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 |