const Graphics::Surface *AVIDecoder::decodeNextFrame() { AVIVideoTrack *track = nullptr; bool isReversed = false; int frameNum = 0; // Check whether the video is playing in revese for (int idx = _videoTracks.size() - 1; idx >= 0; --idx) { track = static_cast<AVIVideoTrack *>(_videoTracks[idx].track); isReversed |= track->isReversed(); } if (isReversed) { // For reverse mode we need to keep seeking to just before the // desired frame prior to actually decoding a frame frameNum = getCurFrame(); seekIntern(track->getFrameTime(frameNum)); } // Decode the next frame const Graphics::Surface *frame = VideoDecoder::decodeNextFrame(); if (isReversed) { // In reverse mode, set next frame to be the prior frame number for (int idx = _videoTracks.size() - 1; idx >= 0; --idx) { track = static_cast<AVIVideoTrack *>(_videoTracks[idx].track); track->setCurFrame(frameNum - 1); findNextVideoTrack(); } } return frame; }
bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { uint frame; // 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 (time == getDuration()) { videoTrack->setCurFrame(videoTrack->getFrameCount() - 1); if (!videoTrack->isReversed()) { // Since we're at the end, just mark the tracks as over for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) if ((*it)->getTrackType() == Track::kTrackTypeAudio) ((AVIAudioTrack *)*it)->resetStream(); return true; } frame = videoTrack->getFrameCount() - 1; } else { // Get the frame we should be on at this time 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); } // Update any transparency track if present if (_transparencyTrack.track) seekTransparencyFrame(frame); // Set the video track's frame videoTrack->setCurFrame(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); bool isReversed = false; AVIVideoTrack *videoTrack = nullptr; 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 { videoTrack = (AVIVideoTrack *)status.track; isReversed = videoTrack->isReversed(); if (getStreamType(nextTag) == kStreamTypePaletteChange) { // Palette Change videoTrack->loadPaletteFromChunk(chunk); } else { // Otherwise, assume it's a compressed frame videoTrack->decodeFrame(chunk); break; } } } if (!isReversed) { // Start us off in this position next time status.chunkSearchOffset = _fileStream->pos(); } }