aboutsummaryrefslogtreecommitdiff
path: root/video/avi_decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'video/avi_decoder.cpp')
-rw-r--r--video/avi_decoder.cpp138
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();