diff options
author | Matthew Hoops | 2011-04-07 19:29:49 -0400 |
---|---|---|
committer | Matthew Hoops | 2011-04-07 19:40:07 -0400 |
commit | 8cf73e3fb4e44567a7afa527a0705acb90033a78 (patch) | |
tree | fa33a05454d837ebe357399071b10344608722d3 | |
parent | d40874d6f4bce278b4ab3b0ef6fb34d35466b852 (diff) | |
download | scummvm-rg350-8cf73e3fb4e44567a7afa527a0705acb90033a78.tar.gz scummvm-rg350-8cf73e3fb4e44567a7afa527a0705acb90033a78.tar.bz2 scummvm-rg350-8cf73e3fb4e44567a7afa527a0705acb90033a78.zip |
AUDIO: Split QuickTime audio into a new class
Standalone QuickTime files can now be played as an AudioStream
-rw-r--r-- | audio/decoders/qdm2.cpp (renamed from video/codecs/qdm2.cpp) | 14 | ||||
-rw-r--r-- | audio/decoders/qdm2.h (renamed from video/codecs/qdm2.h) | 14 | ||||
-rw-r--r-- | audio/decoders/qdm2data.h (renamed from video/codecs/qdm2data.h) | 8 | ||||
-rw-r--r-- | audio/decoders/quicktime.cpp | 311 | ||||
-rw-r--r-- | audio/decoders/quicktime.h | 96 | ||||
-rw-r--r-- | audio/module.mk | 2 | ||||
-rw-r--r-- | video/module.mk | 1 | ||||
-rw-r--r-- | video/qt_decoder.cpp | 215 | ||||
-rw-r--r-- | video/qt_decoder.h | 19 |
9 files changed, 444 insertions, 236 deletions
diff --git a/video/codecs/qdm2.cpp b/audio/decoders/qdm2.cpp index 762f56b3d7..2bbb608754 100644 --- a/video/codecs/qdm2.cpp +++ b/audio/decoders/qdm2.cpp @@ -26,18 +26,18 @@ // Based off ffmpeg's QDM2 decoder #include "common/scummsys.h" -#include "video/codecs/qdm2.h" +#include "audio/decoders/qdm2.h" -#ifdef VIDEO_CODECS_QDM2_H +#ifdef AUDIO_QDM2_H #include "audio/audiostream.h" -#include "video/codecs/qdm2data.h" +#include "audio/decoders/qdm2data.h" #include "common/array.h" #include "common/stream.h" #include "common/system.h" -namespace Video { +namespace Audio { enum { SOFTCLIP_THRESHOLD = 27600, @@ -152,7 +152,7 @@ struct RDFTContext { FFTContext fft; }; -class QDM2Stream : public Audio::AudioStream { +class QDM2Stream : public AudioStream { public: QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); ~QDM2Stream(); @@ -3272,10 +3272,10 @@ int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) { return decodedSamples; } -Audio::AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { +AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { return new QDM2Stream(stream, extraData); } -} // End of namespace Video +} // End of namespace Audio #endif diff --git a/video/codecs/qdm2.h b/audio/decoders/qdm2.h index a6acf3a83e..3d4b542de1 100644 --- a/video/codecs/qdm2.h +++ b/audio/decoders/qdm2.h @@ -26,18 +26,16 @@ // Only compile if Mohawk is enabled or if we're building dynamic modules #if defined(ENABLE_MOHAWK) || defined(DYNAMIC_MODULES) -#ifndef VIDEO_CODECS_QDM2_H -#define VIDEO_CODECS_QDM2_H +#ifndef AUDIO_QDM2_H +#define AUDIO_QDM2_H namespace Common { class SeekableReadStream; } namespace Audio { - class AudioStream; -} -namespace Video { +class AudioStream; /** * Create a new AudioStream from the QDM2 data in the given stream. @@ -46,9 +44,9 @@ namespace Video { * @param extraData the QuickTime extra data stream * @return a new AudioStream, or NULL, if an error occurred */ -Audio::AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); +AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); -} // End of namespace Video +} // End of namespace Audio -#endif // VIDEO_CODECS_QDM2_H +#endif // AUDIO_QDM2_H #endif // Mohawk/Plugins guard diff --git a/video/codecs/qdm2data.h b/audio/decoders/qdm2data.h index e1a690bd2b..cfe1a3d10e 100644 --- a/video/codecs/qdm2data.h +++ b/audio/decoders/qdm2data.h @@ -23,12 +23,12 @@ * */ -#ifndef VIDEO_CODECS_QDM2DATA_H -#define VIDEO_CODECS_QDM2DATA_H +#ifndef AUDIO_QDM2DATA_H +#define AUDIO_QDM2DATA_H #include "common/scummsys.h" -namespace Video { +namespace Audio { /// VLC TABLES @@ -526,6 +526,6 @@ static const float type34_delta[10] = { // FIXME: covers 8 entries.. 0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f, }; -} // End of namespace Video +} // End of namespace Audio #endif diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp new file mode 100644 index 0000000000..af59d8803f --- /dev/null +++ b/audio/decoders/quicktime.cpp @@ -0,0 +1,311 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/debug.h" +#include "common/util.h" +#include "common/memstream.h" +#include "common/stream.h" + +#include "audio/audiostream.h" +#include "audio/decoders/quicktime.h" + +// Codecs +#include "audio/decoders/adpcm.h" +#include "audio/decoders/qdm2.h" +#include "audio/decoders/raw.h" + +namespace Audio { + +QuickTimeAudioDecoder::QuickTimeAudioDecoder() : Common::QuickTimeParser() { + _audStream = 0; +} + +QuickTimeAudioDecoder::~QuickTimeAudioDecoder() { +} + +bool QuickTimeAudioDecoder::loadFile(const Common::String &filename) { + if (!Common::QuickTimeParser::loadFile(filename)) + return false; + + init(); + return true; +} + +bool QuickTimeAudioDecoder::loadStream(Common::SeekableReadStream *stream) { + if (!Common::QuickTimeParser::loadStream(stream)) + return false; + + init(); + return true; +} + +void QuickTimeAudioDecoder::init() { + Common::QuickTimeParser::init(); + + _audioStreamIndex = -1; + + // Find an audio stream + for (uint32 i = 0; i < _numStreams; i++) + if (_streams[i]->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) + _audioStreamIndex = i; + + // Initialize audio, if present + if (_audioStreamIndex >= 0) { + AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + + if (checkAudioCodecSupport(entry->codecTag)) { + _audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); + _curAudioChunk = 0; + + // Make sure the bits per sample transfers to the sample size + if (entry->codecTag == MKID_BE('raw ') || entry->codecTag == MKID_BE('twos')) + _streams[_audioStreamIndex]->sample_size = (entry->bitsPerSample / 8) * entry->channels; + } + } +} + +Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVStreamContext *st, uint32 format) { + if (st->codec_type == CODEC_TYPE_AUDIO) { + debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); + + AudioSampleDesc *entry = new AudioSampleDesc(); + entry->codecTag = format; + + uint16 stsdVersion = _fd->readUint16BE(); + _fd->readUint16BE(); // revision level + _fd->readUint32BE(); // vendor + + entry->channels = _fd->readUint16BE(); // channel count + entry->bitsPerSample = _fd->readUint16BE(); // sample size + + _fd->readUint16BE(); // compression id = 0 + _fd->readUint16BE(); // packet size = 0 + + entry->sampleRate = (_fd->readUint32BE() >> 16); + + debug(0, "stsd version =%d", stsdVersion); + if (stsdVersion == 0) { + // Not used, except in special cases. See below. + entry->samplesPerFrame = entry->bytesPerFrame = 0; + } else if (stsdVersion == 1) { + // Read QT version 1 fields. In version 0 these dont exist. + entry->samplesPerFrame = _fd->readUint32BE(); + debug(0, "stsd samples_per_frame =%d",entry->samplesPerFrame); + _fd->readUint32BE(); // bytes per packet + entry->bytesPerFrame = _fd->readUint32BE(); + debug(0, "stsd bytes_per_frame =%d", entry->bytesPerFrame); + _fd->readUint32BE(); // bytes per sample + } else { + warning("Unsupported QuickTime STSD audio version %d", stsdVersion); + delete entry; + return 0; + } + + // Version 0 videos (such as the Riven ones) don't have this set, + // but we need it later on. Add it in here. + if (format == MKID_BE('ima4')) { + entry->samplesPerFrame = 64; + entry->bytesPerFrame = 34 * entry->channels; + } + + if (entry->sampleRate == 0 && st->time_scale > 1) + entry->sampleRate = st->time_scale; + + return entry; + } + + return 0; +} + +bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag) { + // Check if the codec is a supported codec + if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4')) + return true; + +#ifdef AUDIO_QDM2_H + if (tag == MKID_BE('QDM2')) + return true; +#endif + + if (tag == MKID_BE('mp4a')) + warning("No MPEG-4 audio (AAC) support"); + else + warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); + + return false; +} + +AudioStream *QuickTimeAudioDecoder::createAudioStream(Common::SeekableReadStream *stream) { + if (!stream || _audioStreamIndex < 0) + return NULL; + + AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + + if (entry->codecTag == MKID_BE('twos') || entry->codecTag == MKID_BE('raw ')) { + // Fortunately, most of the audio used in Myst videos is raw... + uint16 flags = 0; + if (entry->codecTag == MKID_BE('raw ')) + flags |= FLAG_UNSIGNED; + if (entry->channels == 2) + flags |= FLAG_STEREO; + if (entry->bitsPerSample == 16) + flags |= FLAG_16BITS; + uint32 dataSize = stream->size(); + byte *data = (byte *)malloc(dataSize); + stream->read(data, dataSize); + delete stream; + return makeRawStream(data, dataSize, entry->sampleRate, flags); + } else if (entry->codecTag == MKID_BE('ima4')) { + // Riven uses this codec (as do some Myst ME videos) + return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, entry->sampleRate, entry->channels, 34); +#ifdef AUDIO_QDM2_H + } else if (entry->codecTag == MKID_BE('QDM2')) { + // Myst ME uses this codec for many videos + return makeQDM2Stream(stream, _streams[_audioStreamIndex]->extradata); +#endif + } + + error("Unsupported audio codec"); + + return NULL; +} + +uint32 QuickTimeAudioDecoder::getAudioChunkSampleCount(uint chunk) { + if (_audioStreamIndex < 0) + return 0; + + uint32 sampleCount = 0; + + for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) + if (chunk >= _streams[_audioStreamIndex]->sample_to_chunk[j].first) + sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count; + + return sampleCount; +} + +void QuickTimeAudioDecoder::queueNextAudioChunk() { + AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); + + _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); + + // First, we have to get the sample count + uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk); + assert(sampleCount); + + // Then calculate the right sizes + while (sampleCount > 0) { + uint32 samples = 0, size = 0; + + if (entry->samplesPerFrame >= 160) { + samples = entry->samplesPerFrame; + size = entry->bytesPerFrame; + } else if (entry->samplesPerFrame > 1) { + samples = MIN<uint32>((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); + size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; + } else { + samples = MIN<uint32>(1024, sampleCount); + size = samples * _streams[_audioStreamIndex]->sample_size; + } + + // Now, we read in the data for this data and output it + byte *data = (byte *)malloc(size); + _fd->read(data, size); + wStream->write(data, size); + free(data); + sampleCount -= samples; + } + + // Now queue the buffer + _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); + delete wStream; + + _curAudioChunk++; +} + +QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimeParser::SampleDesc() { + channels = 0; + sampleRate = 0; + samplesPerFrame = 0; + bytesPerFrame = 0; +} + +/** + * A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API + */ +class QuickTimeAudioStream : public RewindableAudioStream, public QuickTimeAudioDecoder { +public: + QuickTimeAudioStream() {} + + ~QuickTimeAudioStream() { + delete _audStream; + } + + bool loadFile(const Common::String &filename) { + return QuickTimeAudioDecoder::loadFile(filename) && _audioStreamIndex >= 0 && _audStream; + } + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples) { + int samples = 0; + + while (samples < numSamples && !endOfData()) { + if (_audStream->numQueuedStreams() == 0) + queueNextAudioChunk(); + + samples += _audStream->readBuffer(buffer + samples, numSamples - samples); + } + + return samples; + } + + bool isStereo() const { return _audStream->isStereo(); } + int getRate() const { return _audStream->getRate(); } + bool endOfData() const { return _curAudioChunk >= _streams[_audioStreamIndex]->chunk_count && _audStream->endOfData(); } + + // RewindableAudioStream API + bool rewind() { + // Reset our parent stream + _curAudioChunk = 0; + delete _audStream; + + AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + _audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); + return true; + } +}; + +RewindableAudioStream *makeQuickTimeStream(const Common::String &filename) { + QuickTimeAudioStream *audioStream = new QuickTimeAudioStream(); + + if (!audioStream->loadFile(filename)) { + delete audioStream; + return 0; + } + + return audioStream; +} + +} // End of namespace Audio diff --git a/audio/decoders/quicktime.h b/audio/decoders/quicktime.h new file mode 100644 index 0000000000..eb0f791748 --- /dev/null +++ b/audio/decoders/quicktime.h @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AUDIO_QUICKTIME_H +#define AUDIO_QUICKTIME_H + +#include "common/quicktime.h" +#include "common/scummsys.h" +#include "common/types.h" + +namespace Common { + class SeekableReadStream; + class String; +} + +namespace Audio { + +class AudioStream; +class RewindableAudioStream; +class QueuingAudioStream; + +class QuickTimeAudioDecoder : public Common::QuickTimeParser { +public: + QuickTimeAudioDecoder(); + virtual ~QuickTimeAudioDecoder(); + + /** + * Load a QuickTime audio file + * @param filename the filename to load + */ + bool loadFile(const Common::String &filename); + + /** + * Load a QuickTime audio file from a SeekableReadStream + * @param stream the stream to load + */ + bool loadStream(Common::SeekableReadStream *stream); + +protected: + struct AudioSampleDesc : public Common::QuickTimeParser::SampleDesc { + AudioSampleDesc(); + + uint16 channels; + uint32 sampleRate; + uint32 samplesPerFrame; + uint32 bytesPerFrame; + }; + + // Common::QuickTimeParser API + virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(MOVStreamContext *st, uint32 format); + + AudioStream *createAudioStream(Common::SeekableReadStream *stream); + bool checkAudioCodecSupport(uint32 tag); + void init(); + + void queueNextAudioChunk(); + uint32 getAudioChunkSampleCount(uint chunk); + int8 _audioStreamIndex; + uint _curAudioChunk; + QueuingAudioStream *_audStream; +}; + +/** + * Try to load a QuickTime sound file from the given file name and create a RewindableAudioStream + * from that data. + * + * @param filename the filename of the file from which to read the data + * @return a new RewindableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *makeQuickTimeStream(const Common::String &filename); + +} // End of namespace Audio + +#endif diff --git a/audio/module.mk b/audio/module.mk index a9d9bfc869..a13eb49bb1 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -19,6 +19,8 @@ MODULE_OBJS := \ decoders/iff_sound.o \ decoders/mac_snd.o \ decoders/mp3.o \ + decoders/qdm2.o \ + decoders/quicktime.o \ decoders/raw.o \ decoders/vag.o \ decoders/voc.o \ diff --git a/video/module.mk b/video/module.mk index 0172482dfa..308b344a75 100644 --- a/video/module.mk +++ b/video/module.mk @@ -15,7 +15,6 @@ MODULE_OBJS := \ codecs/mjpeg.o \ codecs/msrle.o \ codecs/msvideo1.o \ - codecs/qdm2.o \ codecs/qtrle.o \ codecs/rpza.o \ codecs/smc.o \ diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp index c25b76a5f2..71f31d6415 100644 --- a/video/qt_decoder.cpp +++ b/video/qt_decoder.cpp @@ -38,11 +38,6 @@ #include "common/memstream.h" #include "common/util.h" -// Audio codecs -#include "audio/decoders/adpcm.h" -#include "audio/decoders/raw.h" -#include "video/codecs/qdm2.h" - // Video codecs #include "video/codecs/cinepak.h" #include "video/codecs/mjpeg.h" @@ -58,7 +53,6 @@ namespace Video { //////////////////////////////////////////// QuickTimeDecoder::QuickTimeDecoder() { - _audStream = NULL; _curFrame = -1; _startTime = _nextFrameStartTime = 0; _audHandle = Audio::SoundHandle(); @@ -175,7 +169,7 @@ void QuickTimeDecoder::seekToFrame(uint32 frame) { _audioStartOffset = curVideoTime; // Re-create the audio stream - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); // First, we need to track down what audio sample we need @@ -216,7 +210,7 @@ void QuickTimeDecoder::seekToFrame(uint32 frame) { } // Reposition the audio stream - readNextAudioChunk(); + queueNextAudioChunk(); if (sample != totalSamples) { // HACK: Skip a certain amount of samples from the stream // (There's got to be a better way to do this!) @@ -232,7 +226,7 @@ void QuickTimeDecoder::seekToFrame(uint32 frame) { } void QuickTimeDecoder::seekToTime(Audio::Timestamp time) { - // TODO: Audio-only seeking (or really, have QuickTime sounds) + // Use makeQuickTimeStream() instead if (_videoStreamIndex < 0) error("Audio-only seeking not supported"); @@ -404,10 +398,7 @@ bool QuickTimeDecoder::loadFile(const Common::String &filename) { if (!Common::QuickTimeParser::loadFile(filename)) return false; - _videoStreamIndex = _audioStreamIndex = -1; - _startTime = 0; init(); - return true; } @@ -415,39 +406,24 @@ bool QuickTimeDecoder::loadStream(Common::SeekableReadStream *stream) { if (!Common::QuickTimeParser::loadStream(stream)) return false; - _videoStreamIndex = _audioStreamIndex = -1; - _startTime = 0; init(); - return true; } void QuickTimeDecoder::init() { - Common::QuickTimeParser::init(); + Audio::QuickTimeAudioDecoder::init(); + + _videoStreamIndex = -1; + _startTime = 0; - // Find audio/video streams - for (uint32 i = 0; i < _numStreams; i++) { + // Find video streams + for (uint32 i = 0; i < _numStreams; i++) if (_streams[i]->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0) _videoStreamIndex = i; - else if (_streams[i]->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) - _audioStreamIndex = i; - } - - // Initialize audio, if present - if (_audioStreamIndex >= 0) { - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - - if (checkAudioCodecSupport(entry->codecTag)) { - _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); - _curAudioChunk = 0; - - // Make sure the bits per sample transfers to the sample size - if (entry->codecTag == MKID_BE('raw ') || entry->codecTag == MKID_BE('twos')) - _streams[_audioStreamIndex]->sample_size = (entry->bitsPerSample / 8) * entry->channels; - - startAudio(); - } + // Start the audio codec if we've got one that we can handle + if (_audStream) { + startAudio(); _audioStartOffset = Audio::Timestamp(0); } @@ -559,56 +535,10 @@ Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(MOVStreamC } return entry; - } else if (st->codec_type == CODEC_TYPE_AUDIO) { - debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); - - AudioSampleDesc *entry = new AudioSampleDesc(); - entry->codecTag = format; - - uint16 stsdVersion = _fd->readUint16BE(); - _fd->readUint16BE(); // revision level - _fd->readUint32BE(); // vendor - - entry->channels = _fd->readUint16BE(); // channel count - entry->bitsPerSample = _fd->readUint16BE(); // sample size - - _fd->readUint16BE(); // compression id = 0 - _fd->readUint16BE(); // packet size = 0 - - entry->sampleRate = (_fd->readUint32BE() >> 16); - - debug(0, "stsd version =%d", stsdVersion); - if (stsdVersion == 0) { - // Not used, except in special cases. See below. - entry->samplesPerFrame = entry->bytesPerFrame = 0; - } else if (stsdVersion == 1) { - // Read QT version 1 fields. In version 0 these dont exist. - entry->samplesPerFrame = _fd->readUint32BE(); - debug(0, "stsd samples_per_frame =%d",entry->samplesPerFrame); - _fd->readUint32BE(); // bytes per packet - entry->bytesPerFrame = _fd->readUint32BE(); - debug(0, "stsd bytes_per_frame =%d", entry->bytesPerFrame); - _fd->readUint32BE(); // bytes per sample - } else { - warning("Unsupported QuickTime STSD audio version %d", stsdVersion); - delete entry; - return 0; - } - - // Version 0 videos (such as the Riven ones) don't have this set, - // but we need it later on. Add it in here. - if (format == MKID_BE('ima4')) { - entry->samplesPerFrame = 64; - entry->bytesPerFrame = 34 * entry->channels; - } - - if (entry->sampleRate == 0 && st->time_scale > 1) - entry->sampleRate = st->time_scale; - - return entry; } - return 0; + // Pass it on up + return Audio::QuickTimeAudioDecoder::readSampleDesc(st, format); } void QuickTimeDecoder::close() { @@ -681,112 +611,6 @@ Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket(uint32 &descId) return _fd->readStream(_streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); } -bool QuickTimeDecoder::checkAudioCodecSupport(uint32 tag) { - // Check if the codec is a supported codec - if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4')) - return true; - -#ifdef VIDEO_CODECS_QDM2_H - if (tag == MKID_BE('QDM2')) - return true; -#endif - - if (tag == MKID_BE('mp4a')) - warning("No MPEG-4 audio (AAC) support"); - else - warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); - - return false; -} - -Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStream *stream) { - if (!stream || _audioStreamIndex < 0) - return NULL; - - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - - if (entry->codecTag == MKID_BE('twos') || entry->codecTag == MKID_BE('raw ')) { - // Fortunately, most of the audio used in Myst videos is raw... - uint16 flags = 0; - if (entry->codecTag == MKID_BE('raw ')) - flags |= Audio::FLAG_UNSIGNED; - if (entry->channels == 2) - flags |= Audio::FLAG_STEREO; - if (entry->bitsPerSample == 16) - flags |= Audio::FLAG_16BITS; - uint32 dataSize = stream->size(); - byte *data = (byte *)malloc(dataSize); - stream->read(data, dataSize); - delete stream; - return Audio::makeRawStream(data, dataSize, entry->sampleRate, flags); - } else if (entry->codecTag == MKID_BE('ima4')) { - // Riven uses this codec (as do some Myst ME videos) - return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, entry->sampleRate, entry->channels, 34); -#ifdef VIDEO_CODECS_QDM2_H - } else if (entry->codecTag == MKID_BE('QDM2')) { - // Several Myst ME videos use this codec - return makeQDM2Stream(stream, _streams[_audioStreamIndex]->extradata); -#endif - } - - error("Unsupported audio codec"); - - return NULL; -} - -uint32 QuickTimeDecoder::getAudioChunkSampleCount(uint chunk) { - if (_audioStreamIndex < 0) - return 0; - - uint32 sampleCount = 0; - - for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) - if (chunk >= _streams[_audioStreamIndex]->sample_to_chunk[j].first) - sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count; - - return sampleCount; -} - -void QuickTimeDecoder::readNextAudioChunk() { - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); - - _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); - - // First, we have to get the sample count - uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk); - assert(sampleCount); - - // Then calculate the right sizes - while (sampleCount > 0) { - uint32 samples = 0, size = 0; - - if (entry->samplesPerFrame >= 160) { - samples = entry->samplesPerFrame; - size = entry->bytesPerFrame; - } else if (entry->samplesPerFrame > 1) { - samples = MIN<uint32>((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); - size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; - } else { - samples = MIN<uint32>(1024, sampleCount); - size = samples * _streams[_audioStreamIndex]->sample_size; - } - - // Now, we read in the data for this data and output it - byte *data = (byte *)malloc(size); - _fd->read(data, size); - wStream->write(data, size); - free(data); - sampleCount -= samples; - } - - // Now queue the buffer - _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); - delete wStream; - - _curAudioChunk++; -} - void QuickTimeDecoder::updateAudioBuffer() { if (!_audStream) return; @@ -797,7 +621,7 @@ void QuickTimeDecoder::updateAudioBuffer() { // If we're on the last frame, make sure all audio remaining is buffered numberOfChunksNeeded = _streams[_audioStreamIndex]->chunk_count; } else { - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; // Calculate the amount of chunks we need in memory until the next frame uint32 timeToNextFrame = getTimeToNextFrame(); @@ -817,7 +641,7 @@ void QuickTimeDecoder::updateAudioBuffer() { // Keep three streams in buffer so that if/when the first two end, it goes right into the next while (_audStream->numQueuedStreams() < numberOfChunksNeeded && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count) - readNextAudioChunk(); + queueNextAudioChunk(); } QuickTimeDecoder::VideoSampleDesc::VideoSampleDesc() : Common::QuickTimeParser::SampleDesc() { @@ -832,11 +656,4 @@ QuickTimeDecoder::VideoSampleDesc::~VideoSampleDesc() { delete videoCodec; } -QuickTimeDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimeParser::SampleDesc() { - channels = 0; - sampleRate = 0; - samplesPerFrame = 0; - bytesPerFrame = 0; -} - } // End of namespace Video diff --git a/video/qt_decoder.h b/video/qt_decoder.h index 65ade40c93..b9c4b9b41d 100644 --- a/video/qt_decoder.h +++ b/video/qt_decoder.h @@ -34,7 +34,6 @@ #ifndef VIDEO_QT_DECODER_H #define VIDEO_QT_DECODER_H -#include "common/quicktime.h" #include "common/scummsys.h" #include "video/video_decoder.h" @@ -42,6 +41,7 @@ #include "audio/audiostream.h" #include "audio/mixer.h" +#include "audio/decoders/quicktime.h" namespace Common { class MacResManager; @@ -56,7 +56,7 @@ namespace Video { * - mohawk * - sci */ -class QuickTimeDecoder : public SeekableVideoDecoder, public Common::QuickTimeParser { +class QuickTimeDecoder : public SeekableVideoDecoder, public Audio::QuickTimeAudioDecoder { public: QuickTimeDecoder(); virtual ~QuickTimeDecoder(); @@ -126,32 +126,17 @@ protected: Codec *videoCodec; }; - struct AudioSampleDesc : public Common::QuickTimeParser::SampleDesc { - AudioSampleDesc(); - - uint16 channels; - uint32 sampleRate; - uint32 samplesPerFrame; - uint32 bytesPerFrame; - }; - Common::QuickTimeParser::SampleDesc *readSampleDesc(MOVStreamContext *st, uint32 format); private: - Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); - bool checkAudioCodecSupport(uint32 tag); Common::SeekableReadStream *getNextFramePacket(uint32 &descId); uint32 getFrameDuration(); void init(); - Audio::QueuingAudioStream *_audStream; void startAudio(); void stopAudio(); void updateAudioBuffer(); void readNextAudioChunk(); - uint32 getAudioChunkSampleCount(uint chunk); - int8 _audioStreamIndex; - uint _curAudioChunk; Audio::SoundHandle _audHandle; Audio::Timestamp _audioStartOffset; |