diff options
Diffstat (limited to 'video/video_decoder.cpp')
-rw-r--r-- | video/video_decoder.cpp | 166 |
1 files changed, 144 insertions, 22 deletions
diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp index 5df811008c..217b4c8456 100644 --- a/video/video_decoder.cpp +++ b/video/video_decoder.cpp @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -46,6 +46,8 @@ VideoDecoder::VideoDecoder() { _endTime = 0; _endTimeSet = false; _nextVideoTrack = 0; + _mainAudioTrack = 0; + _canSetDither = true; // Find the best format for output _defaultHighColorFormat = g_system->getScreenFormat(); @@ -62,6 +64,8 @@ void VideoDecoder::close() { delete *it; _tracks.clear(); + _internalTracks.clear(); + _externalTracks.clear(); _dirtyPalette = false; _palette = 0; _startTime = 0; @@ -73,6 +77,8 @@ void VideoDecoder::close() { _endTime = 0; _endTimeSet = false; _nextVideoTrack = 0; + _mainAudioTrack = 0; + _canSetDither = true; } bool VideoDecoder::loadFile(const Common::String &filename) { @@ -167,6 +173,7 @@ Graphics::PixelFormat VideoDecoder::getPixelFormat() const { const Graphics::Surface *VideoDecoder::decodeNextFrame() { _needsUpdate = false; + _canSetDither = false; readNextPacket(); @@ -336,7 +343,12 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) { if (isPlaying()) stopAudio(); - for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) + // Do the actual seeking + if (!seekIntern(time)) + return false; + + // Seek any external track too + for (TrackListIterator it = _externalTracks.begin(); it != _externalTracks.end(); it++) if (!(*it)->seek(time)) return false; @@ -356,12 +368,12 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) { } bool VideoDecoder::seekToFrame(uint frame) { + if (!isSeekable()) + return false; + VideoTrack *track = 0; for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { - if (!(*it)->isSeekable()) - return false; - if ((*it)->getTrackType() == Track::kTrackTypeVideo) { // We only allow seeking by frame when one video track // is present @@ -471,6 +483,31 @@ Audio::Timestamp VideoDecoder::getDuration() const { return maxDuration; } +bool VideoDecoder::seekIntern(const Audio::Timestamp &time) { + for (TrackList::iterator it = _internalTracks.begin(); it != _internalTracks.end(); it++) + if (!(*it)->seek(time)) + return false; + + return true; +} + +bool VideoDecoder::setDitheringPalette(const byte *palette) { + // If a frame was already decoded, we can't set it now. + if (!_canSetDither) + return false; + + bool result = false; + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->canDither()) { + ((VideoTrack *)*it)->setDither(palette); + result = true; + } + } + + return result; +} + VideoDecoder::Track::Track() { _paused = false; } @@ -513,13 +550,14 @@ Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getFrameTime(uint frame) con // (which Audio::Timestamp doesn't support). Common::Rational frameRate = getFrameRate(); - if (frameRate == frameRate.toInt()) // The nice case (a whole number) + // Try to keep it in terms of the frame rate, if the frame rate is a whole + // number. + if (frameRate.getDenominator() == 1) return Audio::Timestamp(0, frame, frameRate.toInt()); - // Just convert to milliseconds. - Common::Rational time = frame * 1000; - time /= frameRate; - return Audio::Timestamp(time.toInt(), 1000); + // Convert as best as possible + Common::Rational time = frameRate.getInverse() * frame; + return Audio::Timestamp(0, time.getNumerator(), time.getDenominator()); } uint VideoDecoder::FixedRateVideoTrack::getFrameAtTime(const Audio::Timestamp &time) const { @@ -529,14 +567,19 @@ uint VideoDecoder::FixedRateVideoTrack::getFrameAtTime(const Audio::Timestamp &t if (frameRate == time.framerate()) return time.totalNumberOfFrames(); - // Default case - return (time.totalNumberOfFrames() * frameRate / time.framerate()).toInt(); + // Create the rational based on the time first to hopefully cancel out + // *something* when multiplying by the frameRate (which can be large in + // some AVI videos). + return (Common::Rational(time.totalNumberOfFrames(), time.framerate()) * frameRate).toInt(); } Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getDuration() const { return getFrameTime(getFrameCount()); } +VideoDecoder::AudioTrack::AudioTrack() : _volume(Audio::Mixer::kMaxChannelVolume), _balance(0), _muted(false) { +} + bool VideoDecoder::AudioTrack::endOfTrack() const { Audio::AudioStream *stream = getAudioStream(); return !stream || !g_system->getMixer()->isSoundHandleActive(_handle) || stream->endOfData(); @@ -546,7 +589,7 @@ void VideoDecoder::AudioTrack::setVolume(byte volume) { _volume = volume; if (g_system->getMixer()->isSoundHandleActive(_handle)) - g_system->getMixer()->setChannelVolume(_handle, _volume); + g_system->getMixer()->setChannelVolume(_handle, _muted ? 0 : _volume); } void VideoDecoder::AudioTrack::setBalance(int8 balance) { @@ -562,7 +605,7 @@ void VideoDecoder::AudioTrack::start() { Audio::AudioStream *stream = getAudioStream(); assert(stream); - g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, getVolume(), getBalance(), DisposeAfterUse::NO); + g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, _muted ? 0 : getVolume(), getBalance(), DisposeAfterUse::NO); // Pause the audio again if we're still paused if (isPaused()) @@ -581,7 +624,7 @@ void VideoDecoder::AudioTrack::start(const Audio::Timestamp &limit) { stream = Audio::makeLimitingAudioStream(stream, limit, DisposeAfterUse::NO); - g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, getVolume(), getBalance(), DisposeAfterUse::YES); + g_system->getMixer()->playStream(getSoundType(), &_handle, stream, -1, _muted ? 0 : getVolume(), getBalance(), DisposeAfterUse::YES); // Pause the audio again if we're still paused if (isPaused()) @@ -595,6 +638,16 @@ uint32 VideoDecoder::AudioTrack::getRunningTime() const { return 0; } +void VideoDecoder::AudioTrack::setMute(bool mute) { + // Update the mute settings, if required + if (_muted != mute) { + _muted = mute; + + if (g_system->getMixer()->isSoundHandleActive(_handle)) + g_system->getMixer()->setChannelVolume(_handle, mute ? 0 : _volume); + } +} + void VideoDecoder::AudioTrack::pauseIntern(bool shouldPause) { if (g_system->getMixer()->isSoundHandleActive(_handle)) g_system->getMixer()->pauseHandle(_handle, shouldPause); @@ -641,13 +694,29 @@ bool VideoDecoder::StreamFileAudioTrack::loadFromFile(const Common::String &base return _stream != 0; } -void VideoDecoder::addTrack(Track *track) { +void VideoDecoder::addTrack(Track *track, bool isExternal) { _tracks.push_back(track); + if (isExternal) + _externalTracks.push_back(track); + else + _internalTracks.push_back(track); + if (track->getTrackType() == Track::kTrackTypeAudio) { // Update volume settings if it's an audio track ((AudioTrack *)track)->setVolume(_audioVolume); ((AudioTrack *)track)->setBalance(_audioBalance); + + if (!isExternal && supportsAudioTrackSwitching()) { + if (_mainAudioTrack) { + // The main audio track has already been found + ((AudioTrack *)track)->setMute(true); + } else { + // First audio track found -> now the main one + _mainAudioTrack = (AudioTrack *)track; + _mainAudioTrack->setMute(false); + } + } } else if (track->getTrackType() == Track::kTrackTypeVideo) { // If this track has a better time, update _nextVideoTrack if (!_nextVideoTrack || ((VideoTrack *)track)->getNextFrameStartTime() < _nextVideoTrack->getNextFrameStartTime()) @@ -673,13 +742,41 @@ bool VideoDecoder::addStreamFileTrack(const Common::String &baseName) { bool result = track->loadFromFile(baseName); if (result) - addTrack(track); + addTrack(track, true); else delete track; return result; } +bool VideoDecoder::setAudioTrack(int index) { + if (!supportsAudioTrackSwitching()) + return false; + + AudioTrack *audioTrack = getAudioTrack(index); + + if (!audioTrack) + return false; + + if (_mainAudioTrack == audioTrack) + return true; + + _mainAudioTrack->setMute(true); + audioTrack->setMute(false); + _mainAudioTrack = audioTrack; + return true; +} + +uint VideoDecoder::getAudioTrackCount() const { + uint count = 0; + + for (TrackList::const_iterator it = _internalTracks.begin(); it != _internalTracks.end(); it++) + if ((*it)->getTrackType() == Track::kTrackTypeAudio) + count++; + + return count; +} + void VideoDecoder::setEndTime(const Audio::Timestamp &endTime) { Audio::Timestamp startTime = 0; @@ -703,18 +800,43 @@ void VideoDecoder::setEndTime(const Audio::Timestamp &endTime) { } } +void VideoDecoder::setEndFrame(uint frame) { + VideoTrack *track = 0; + + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo) { + // We only allow this when one video track is present + if (track) + return; + + track = (VideoTrack *)*it; + } + } + + // If we didn't find a video track, we can't set the final frame (of course) + if (!track) + return; + + Audio::Timestamp time = track->getFrameTime(frame + 1); + + if (time < 0) + return; + + setEndTime(time); +} + VideoDecoder::Track *VideoDecoder::getTrack(uint track) { - if (track > _tracks.size()) + if (track > _internalTracks.size()) return 0; - return _tracks[track]; + return _internalTracks[track]; } const VideoDecoder::Track *VideoDecoder::getTrack(uint track) const { - if (track > _tracks.size()) + if (track > _internalTracks.size()) return 0; - return _tracks[track]; + return _internalTracks[track]; } bool VideoDecoder::endOfVideoTracks() const { |