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 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)size) readNextPacket(); return; } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { skipChunk(size); return; } Track *track = getTrack(getStreamIndex(nextTag)); if (!track) error("Cannot get track from tag '%s'", tag2str(nextTag)); Common::SeekableReadStream *chunk = 0; if (size != 0) { chunk = _fileStream->readStream(size); _fileStream->skip(size & 1); } if (track->getTrackType() == Track::kTrackTypeAudio) { if (getStreamType(nextTag) != kStreamTypeAudio) error("Invalid audio track tag '%s'", tag2str(nextTag)); assert(chunk); ((AVIAudioTrack *)track)->queueSound(chunk); } else { AVIVideoTrack *videoTrack = (AVIVideoTrack *)track; if (getStreamType(nextTag) == kStreamTypePaletteChange) { // Palette Change 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"); } else { // Otherwise, assume it's a compressed frame videoTrack->decodeFrame(chunk); } } }
void AVIDecoder::seekTransparencyFrame(int frame) { TrackStatus &status = _transparencyTrack; AVIVideoTrack *transTrack = static_cast<AVIVideoTrack *>(status.track); // Find the index entry for the frame int indexFrame = frame; OldIndex *entry = nullptr; do { entry = _indexEntries.find(status.index, indexFrame); } while (!entry && indexFrame-- > 0); assert(entry); // Set it's frame number transTrack->setCurFrame(indexFrame - 1); // Read in the frame Common::SeekableReadStream *chunk = nullptr; _fileStream->seek(entry->offset + 8); status.chunkSearchOffset = entry->offset; if (entry->size != 0) chunk = _fileStream->readStream(entry->size); transTrack->decodeFrame(chunk); if (indexFrame < (int)frame) { while (status.chunkSearchOffset < _movieListEnd && indexFrame++ < (int)frame) { // There was no index entry for the desired frame, so an earlier one was decoded. // We now have to sequentially skip frames until we get to the desired frame _fileStream->readUint32BE(); uint32 size = _fileStream->readUint32LE() - 8; _fileStream->skip(size & 1); status.chunkSearchOffset = _fileStream->pos(); } } transTrack->setCurFrame(frame - 1); }
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; }
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 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)size) readNextPacket(); return; } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { skipChunk(size); return; } Track *track = getTrack(getStreamIndex(nextTag)); if (!track) error("Cannot get track from tag '%s'", tag2str(nextTag)); Common::SeekableReadStream *chunk = 0; if (size != 0) { chunk = _fileStream->readStream(size); _fileStream->skip(size & 1); } if (track->getTrackType() == Track::kTrackTypeAudio) { if (getStreamType(nextTag) != MKTAG16('w', 'b')) error("Invalid audio track tag '%s'", tag2str(nextTag)); assert(chunk); ((AVIAudioTrack *)track)->queueSound(chunk); } else { AVIVideoTrack *videoTrack = (AVIVideoTrack *)track; if (getStreamType(nextTag) == MKTAG16('p', 'c')) { // 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')) { // TODO: Check if this really is uncompressed. Many videos // falsely put compressed data in here. error("Uncompressed AVI frame found"); } else { // Otherwise, assume it's a compressed frame videoTrack->decodeFrame(chunk); } } }
bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { // Can't seek beyond the end if (time > getDuration()) return false; // Get our video AVIVideoTrack *videoTrack = (AVIVideoTrack *)_videoTracks[0].track; uint32 videoIndex = _videoTracks[0].index; // If we seek directly to the end, just mark the tracks as over if (time == getDuration()) { videoTrack->setCurFrame(videoTrack->getFrameCount() - 1); for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) if ((*it)->getTrackType() == Track::kTrackTypeAudio) ((AVIAudioTrack *)*it)->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; 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]; // We don't care about RECs if (index.id == ID_REC) continue; // We're only looking at entries for this track 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; // Update all the audio tracks for (uint32 i = 0; i < _audioTracks.size(); i++) { AVIAudioTrack *audioTrack = (AVIAudioTrack *)_audioTracks[i].track; // Recreate the audio stream audioTrack->resetStream(); // Set the chunk index for the track audioTrack->setCurChunk(frame); uint32 chunksFound = 0; for (uint32 j = 0; j < _indexEntries.size(); j++) { const OldIndex &index = _indexEntries[j]; // Continue ignoring RECs if (index.id == ID_REC) continue; if (getStreamIndex(index.id) == _audioTracks[i].index) { if (chunksFound == frame) { _fileStream->seek(index.offset + 8); Common::SeekableReadStream *audioChunk = _fileStream->readStream(index.size); audioTrack->queueSound(audioChunk); _audioTracks[i].chunkSearchOffset = (j == _indexEntries.size() - 1) ? _movieListEnd : _indexEntries[j + 1].offset; break; } chunksFound++; } } // 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); } // Set the video track's frame videoTrack->setCurFrame((int)frame - 1); // Set the video track's search offset to the right spot _videoTracks[0].chunkSearchOffset = _indexEntries[frameIndex].offset; return true; }
void AVIDecoder::handleNextPacket(TrackStatus &status) { // If there's no more to search, bail out if (status.chunkSearchOffset + 8 >= _movieListEnd) { if (status.track->getTrackType() == Track::kTrackTypeVideo) { // Horrible AVI video has a premature end // Force the frame to be the last frame debug(7, "Forcing end of AVI video"); ((AVIVideoTrack *)status.track)->forceTrackEnd(); } return; } // See if audio needs to be buffered and break out if not if (status.track->getTrackType() == Track::kTrackTypeAudio && !shouldQueueAudio(status)) return; // Seek to where we shall start searching _fileStream->seek(status.chunkSearchOffset); for (;;) { // If there's no more to search, bail out if ((uint32)_fileStream->pos() + 8 >= _movieListEnd) { if (status.track->getTrackType() == Track::kTrackTypeVideo) { // Horrible AVI video has a premature end // Force the frame to be the last frame debug(7, "Forcing end of AVI video"); ((AVIVideoTrack *)status.track)->forceTrackEnd(); } break; } uint32 nextTag = _fileStream->readUint32BE(); uint32 size = _fileStream->readUint32LE(); if (nextTag == ID_LIST) { // A list of audio/video chunks if (_fileStream->readUint32BE() != ID_REC) error("Expected 'rec ' LIST"); continue; } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { skipChunk(size); continue; } // Only accept chunks for this stream uint32 streamIndex = getStreamIndex(nextTag); if (streamIndex != status.index) { skipChunk(size); continue; } Common::SeekableReadStream *chunk = 0; if (size != 0) { chunk = _fileStream->readStream(size); _fileStream->skip(size & 1); } if (status.track->getTrackType() == Track::kTrackTypeAudio) { if (getStreamType(nextTag) != kStreamTypeAudio) error("Invalid audio track tag '%s'", tag2str(nextTag)); assert(chunk); ((AVIAudioTrack *)status.track)->queueSound(chunk); // Break out if we have enough audio if (!shouldQueueAudio(status)) break; } else { AVIVideoTrack *videoTrack = (AVIVideoTrack *)status.track; if (getStreamType(nextTag) == kStreamTypePaletteChange) { // Palette Change videoTrack->loadPaletteFromChunk(chunk); } else { // Otherwise, assume it's a compressed frame videoTrack->decodeFrame(chunk); break; } } } // Start us off in this position next time status.chunkSearchOffset = _fileStream->pos(); }
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()) { // Also premature end. forceVideoEnd(); return; } if (nextTag == ID_LIST) { // A list of audio/video chunks 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)size) readNextPacket(); return; } else if (nextTag == ID_JUNK || nextTag == ID_IDX1) { skipChunk(size); return; } Track *track = getTrack(getStreamIndex(nextTag)); if (!track) error("Cannot get track from tag '%s'", tag2str(nextTag)); Common::SeekableReadStream *chunk = 0; if (size != 0) { chunk = _fileStream->readStream(size); _fileStream->skip(size & 1); } if (track->getTrackType() == Track::kTrackTypeAudio) { if (getStreamType(nextTag) != kStreamTypeAudio) error("Invalid audio track tag '%s'", tag2str(nextTag)); assert(chunk); ((AVIAudioTrack *)track)->queueSound(chunk); } else { AVIVideoTrack *videoTrack = (AVIVideoTrack *)track; if (getStreamType(nextTag) == kStreamTypePaletteChange) { // Palette Change videoTrack->loadPaletteFromChunk(chunk); } else { // Otherwise, assume it's a compressed frame videoTrack->decodeFrame(chunk); } } }