diff options
Diffstat (limited to 'video/avi_decoder.cpp')
-rw-r--r-- | video/avi_decoder.cpp | 138 |
1 files changed, 110 insertions, 28 deletions
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index 5e4b91d1bd..bae5b7babd 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -36,6 +36,7 @@ // Video Codecs #include "video/codecs/cinepak.h" #include "video/codecs/indeo3.h" +#include "video/codecs/mjpeg.h" #include "video/codecs/mpeg.h" #include "video/codecs/msvideo1.h" #include "video/codecs/msrle.h" @@ -59,6 +60,8 @@ namespace Video { #define ID_MIDS MKTAG('m','i','d','s') #define ID_TXTS MKTAG('t','x','t','s') #define ID_JUNK MKTAG('J','U','N','K') +#define ID_JUNQ MKTAG('J','U','N','Q') +#define ID_DMLH MKTAG('d','m','l','h') #define ID_STRF MKTAG('s','t','r','f') #define ID_MOVI MKTAG('m','o','v','i') #define ID_REC MKTAG('r','e','c',' ') @@ -69,6 +72,7 @@ namespace Video { #define ID_ISFT MKTAG('I','S','F','T') #define ID_DISP MKTAG('D','I','S','P') #define ID_PRMI MKTAG('P','R','M','I') +#define ID_STRN MKTAG('s','t','r','n') // Codec tags #define ID_RLE MKTAG('R','L','E',' ') @@ -79,6 +83,7 @@ namespace Video { #define ID_IV32 MKTAG('i','v','3','2') #define ID_DUCK MKTAG('D','U','C','K') #define ID_MPG2 MKTAG('m','p','g','2') +#define ID_MJPG MKTAG('m','j','p','g') // Stream Types enum { @@ -101,10 +106,15 @@ AVIDecoder::~AVIDecoder() { close(); } +AVIDecoder::AVIAudioTrack *AVIDecoder::createAudioTrack(AVIStreamHeader sHeader, PCMWaveFormat wvInfo) { + return new AVIAudioTrack(sHeader, wvInfo, _soundType); +} + void AVIDecoder::initCommon() { _decodedHeader = false; _foundMovieList = false; _movieListStart = 0; + _movieListEnd = 0; _fileStream = 0; memset(&_header, 0, sizeof(_header)); } @@ -149,21 +159,15 @@ bool AVIDecoder::parseNextChunk() { 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_JUNQ: // Same as JUNK, safe to ignore case ID_ISFT: // Metadata, safe to ignore case ID_DISP: // Metadata, should be safe to ignore + case ID_STRN: // Metadata, safe to ignore + case ID_DMLH: // OpenDML extension, contains an extra total frames field, safe to ignore skipChunk(size); break; case ID_IDX1: - 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); - } + readOldIndex(size); break; default: error("Unknown tag \'%s\' found", tag2str(tag)); @@ -189,6 +193,7 @@ void AVIDecoder::handleList(uint32 listSize) { // We found the movie block _foundMovieList = true; _movieListStart = curPos; + _movieListEnd = _movieListStart + listSize + (listSize & 1); _fileStream->skip(listSize); return; case ID_HDRL: // Header List @@ -293,7 +298,7 @@ void AVIDecoder::handleStreamHeader(uint32 size) { if (wvInfo.channels == 2) sHeader.sampleSize /= 2; - addTrack(new AVIAudioTrack(sHeader, wvInfo, _soundType)); + addTrack(createAudioTrack(sHeader, wvInfo)); } // Ensure that we're at the end of the chunk @@ -349,17 +354,27 @@ void AVIDecoder::close() { _decodedHeader = false; _foundMovieList = false; _movieListStart = 0; + _movieListEnd = 0; _indexEntries.clear(); memset(&_header, 0, sizeof(_header)); } void AVIDecoder::readNextPacket() { + if ((uint32)_fileStream->pos() >= _movieListEnd) { + // Ugh, reached the end premature. + forceVideoEnd(); + return; + } + uint32 nextTag = _fileStream->readUint32BE(); uint32 size = _fileStream->readUint32LE(); - if (_fileStream->eos()) + if (_fileStream->eos()) { + // Also premature end. + forceVideoEnd(); return; + } if (nextTag == ID_LIST) { // A list of audio/video chunks @@ -428,12 +443,10 @@ bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { if (time > getDuration()) return false; - // Track down our video track (optionally audio too). - // We only support seeking with one track right now. + // Track down our video track. + // We only support seeking with one video 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++) { @@ -446,15 +459,6 @@ bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { 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; } } @@ -467,8 +471,9 @@ bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { if (time == getDuration()) { videoTrack->setCurFrame(videoTrack->getFrameCount() - 1); - if (audioTrack) - audioTrack->resetStream(); + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + ((AVIAudioTrack *)*it)->resetStream(); return true; } @@ -529,7 +534,15 @@ bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { if (frameIndex < 0) // This shouldn't happen. return false; - if (audioTrack) { + // Update all the audio tracks + uint audioIndex = 0; + + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++, audioIndex++) { + if ((*it)->getTrackType() != Track::kTrackTypeAudio) + continue; + + AVIAudioTrack *audioTrack = (AVIAudioTrack *)*it; + // We need to find where the start of audio should be. // Which is exactly 'initialFrames' audio chunks back from where // our found frame is. @@ -617,6 +630,69 @@ byte AVIDecoder::getStreamIndex(uint32 tag) const { return strtol(string, 0, 16); } +void AVIDecoder::readOldIndex(uint32 size) { + uint32 entryCount = size / 16; + + debug(0, "Old Index: %d entries", entryCount); + + if (entryCount == 0) + return; + + // Read the first index separately + OldIndex firstEntry; + firstEntry.id = _fileStream->readUint32BE(); + firstEntry.flags = _fileStream->readUint32LE(); + firstEntry.offset = _fileStream->readUint32LE(); + firstEntry.size = _fileStream->readUint32LE(); + + // Check if the offset is already absolute + // If it's absolute, the offset will equal the start of the movie list + bool isAbsolute = firstEntry.offset == _movieListStart; + + debug(1, "Old index is %s", isAbsolute ? "absolute" : "relative"); + + if (!isAbsolute) + firstEntry.offset += _movieListStart - 4; + + debug(0, "Index 0: Tag '%s', Offset = %d, Size = %d (Flags = %d)", tag2str(firstEntry.id), firstEntry.offset, firstEntry.size, firstEntry.flags); + _indexEntries.push_back(firstEntry); + + for (uint32 i = 1; i < entryCount; i++) { + OldIndex indexEntry; + indexEntry.id = _fileStream->readUint32BE(); + indexEntry.flags = _fileStream->readUint32LE(); + indexEntry.offset = _fileStream->readUint32LE(); + indexEntry.size = _fileStream->readUint32LE(); + + // Adjust to absolute, if necessary + if (!isAbsolute) + indexEntry.offset += _movieListStart - 4; + + _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); + } +} + +void AVIDecoder::forceVideoEnd() { + // Horrible AVI video has a premature end + // Force the frame to be the last frame + debug(0, "Forcing end of AVI video"); + + for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeVideo) + ((AVIVideoTrack *)*it)->forceTrackEnd(); +} + +VideoDecoder::AudioTrack *AVIDecoder::getAudioTrack(int index) { + // AVI audio track indexes are relative to the first track + Track *track = getTrack(index); + + if (!track || track->getTrackType() != Track::kTrackTypeAudio) + return 0; + + return (AudioTrack *)track; +} + AVIDecoder::AVIVideoTrack::AVIVideoTrack(int frameCount, const AVIStreamHeader &streamHeader, const BitmapInfoHeader &bitmapInfoHeader, byte *initialPalette) : _frameCount(frameCount), _vidsHeader(streamHeader), _bmInfo(bitmapInfoHeader), _initialPalette(initialPalette) { _videoCodec = createCodec(); @@ -712,6 +788,8 @@ Codec *AVIDecoder::AVIVideoTrack::createCodec() { case ID_MPG2: return new MPEGDecoder(); #endif + case ID_MJPG: + return new MJPEGDecoder(); default: warning("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); } @@ -719,6 +797,10 @@ Codec *AVIDecoder::AVIVideoTrack::createCodec() { return 0; } +void AVIDecoder::AVIVideoTrack::forceTrackEnd() { + _curFrame = _frameCount - 1; +} + AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType) : _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType) { _audStream = createAudioStream(); |