Video::AVIDecoder::AVIVideoTrack &AVIDecoder::getVideoTrack() { for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) if ((*it)->getTrackType() == Track::kTrackTypeVideo) return *dynamic_cast<AVIVideoTrack *>(*it); error("Could not find video track"); }
bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) { close(); uint32 riffTag = stream->readUint32BE(); if (riffTag != ID_RIFF) { warning("Failed to find RIFF header"); return false; } int32 fileSize = stream->readUint32LE(); uint32 riffType = stream->readUint32BE(); if (riffType != ID_AVI) { warning("RIFF not an AVI file"); return false; } _fileStream = stream; // Go through all chunks in the file while (_fileStream->pos() < fileSize && parseNextChunk()) ; if (!_decodedHeader) { warning("Failed to parse AVI header"); close(); return false; } if (!_foundMovieList) { warning("Failed to find 'MOVI' list"); close(); return false; } // Create the status entries uint32 index = 0; for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++, index++) { TrackStatus status; status.track = *it; status.index = index; status.chunkSearchOffset = _movieListStart; if ((*it)->getTrackType() == Track::kTrackTypeVideo) _videoTracks.push_back(status); else _audioTracks.push_back(status); } if (_videoTracks.size() != 1) { close(); return false; } // Check if this is a special Duck Truemotion video checkTruemotion1(); return true; }
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(); }
void AVIDecoder::checkTruemotion1() { AVIVideoTrack *track = 0; for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) { if ((*it)->getTrackType() == Track::kTrackTypeVideo) { if (track) { // Multiple tracks; isn't going to be truemotion 1 return; } track = (AVIVideoTrack *)*it; } } // No track found? if (!track) return; // Ignore non-truemotion tracks if (!track->isTruemotion1()) return; // Search for a non-empty frame const Graphics::Surface *frame = 0; for (int i = 0; i < 10 && !frame; i++) frame = decodeNextFrame(); if (!frame) { // Probably shouldn't happen rewind(); return; } // Fill in the width/height based on the frame's width/height _header.width = frame->w; _header.height = frame->h; track->forceDimensions(frame->w, frame->h); // Rewind us back to the beginning rewind(); }
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 QuickTimeDecoder::updateAudioBuffer() { // Updates the audio buffers for all audio tracks for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) if ((*it)->getTrackType() == VideoDecoder::Track::kTrackTypeAudio) ((AudioTrackHandler *)*it)->updateBuffer(); }
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; }
bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) { close(); uint32 riffTag = stream->readUint32BE(); if (riffTag != ID_RIFF) { warning("Failed to find RIFF header"); return false; } int32 fileSize = stream->readUint32LE(); uint32 riffType = stream->readUint32BE(); if (riffType != ID_AVI) { warning("RIFF not an AVI file"); return false; } _fileStream = stream; // Go through all chunks in the file while (_fileStream->pos() < fileSize && parseNextChunk()) ; if (!_decodedHeader) { warning("Failed to parse AVI header"); close(); return false; } if (!_foundMovieList) { warning("Failed to find 'MOVI' list"); close(); return false; } // Create the status entries uint32 index = 0; for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++, index++) { TrackStatus status; status.track = *it; status.index = index; status.chunkSearchOffset = _movieListStart; if ((*it)->getTrackType() == Track::kTrackTypeAudio) { _audioTracks.push_back(status); } else if (_videoTracks.empty()) { _videoTracks.push_back(status); } else { // Secondary video track. For now we assume it will always be a // transparency information track status.chunkSearchOffset = getVideoTrackOffset(index); assert(!_transparencyTrack.track); assert(status.chunkSearchOffset != 0); // Copy the track status information into the transparency track field _transparencyTrack = status; } } // If there is a transparency track, remove it from the video decoder's track list. // This is to stop it being included in calls like getFrameCount if (_transparencyTrack.track) eraseTrack(_transparencyTrack.track); // Check if this is a special Duck Truemotion video checkTruemotion1(); return true; }
void MPEGPSDecoder::readNextPacket() { if (_stream->eos()) return; for (;;) { int32 startCode; uint32 pts, dts; int size = readNextPacketHeader(startCode, pts, dts); if (size < 0) { // End of stream for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) if ((*it)->getTrackType() == Track::kTrackTypeVideo) ((MPEGVideoTrack *)*it)->setEndOfTrack(); return; } MPEGStream *stream = 0; Common::SeekableReadStream *packet = _stream->readStream(size); if (_streamMap.contains(startCode)) { // We already found the stream stream = _streamMap[startCode]; } else { // We haven't seen this before if (startCode == 0x1BD) { // Private stream 1 PrivateStreamType streamType = detectPrivateStreamType(packet); packet->seek(0); switch (streamType) { case kPrivateStreamPS2Audio: { // PS2 Audio stream PS2AudioTrack *audioTrack = new PS2AudioTrack(packet); stream = audioTrack; _streamMap[startCode] = audioTrack; addTrack(audioTrack); break; } default: // Unknown (silently ignore) break; } } if (startCode >= 0x1E0 && startCode <= 0x1EF) { // Video stream // TODO: Multiple video streams } else if (startCode >= 0x1C0 && startCode <= 0x1DF) { #ifdef USE_MAD // MPEG Audio stream MPEGAudioTrack *audioTrack = new MPEGAudioTrack(packet); stream = audioTrack; _streamMap[startCode] = audioTrack; addTrack(audioTrack); #endif } } if (stream) { bool done = stream->sendPacket(packet, pts, dts); if (done && stream->getStreamType() == MPEGStream::kStreamTypeVideo) return; } else { delete packet; } } }