From 8cf73e3fb4e44567a7afa527a0705acb90033a78 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 7 Apr 2011 19:29:49 -0400 Subject: AUDIO: Split QuickTime audio into a new class Standalone QuickTime files can now be played as an AudioStream --- audio/decoders/quicktime.cpp | 311 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 audio/decoders/quicktime.cpp (limited to 'audio/decoders/quicktime.cpp') 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((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); + size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; + } else { + samples = MIN(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 -- cgit v1.2.3 From 88ebf13077a072ac0b3100e54f2949db46960e5e Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 8 Apr 2011 10:50:16 -0400 Subject: AUDIO: Allow for seeking in a QuickTimeAudioStream --- audio/decoders/quicktime.cpp | 84 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 13 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index af59d8803f..53cce30125 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -43,6 +43,7 @@ QuickTimeAudioDecoder::QuickTimeAudioDecoder() : Common::QuickTimeParser() { } QuickTimeAudioDecoder::~QuickTimeAudioDecoder() { + delete _audStream; } bool QuickTimeAudioDecoder::loadFile(const Common::String &filename) { @@ -245,6 +246,64 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { _curAudioChunk++; } +void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { + if (!_audStream) + return; + + // Re-create the audio stream + delete _audStream; + 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 + Audio::Timestamp curAudioTime(0, _streams[_audioStreamIndex]->time_scale); + uint sample = 0; + bool done = false; + for (int32 i = 0; i < _streams[_audioStreamIndex]->stts_count && !done; i++) { + for (int32 j = 0; j < _streams[_audioStreamIndex]->stts_data[i].count; j++) { + curAudioTime = curAudioTime.addFrames(_streams[_audioStreamIndex]->stts_data[i].duration); + + if (curAudioTime > where) { + done = true; + break; + } + + sample++; + } + } + + // Now to track down what chunk it's in + _curAudioChunk = 0; + uint32 totalSamples = 0; + for (uint32 i = 0; i < _streams[_audioStreamIndex]->chunk_count; i++, _curAudioChunk++) { + int sampleToChunkIndex = -1; + + for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) + if (i >= _streams[_audioStreamIndex]->sample_to_chunk[j].first) + sampleToChunkIndex = j; + + assert(sampleToChunkIndex >= 0); + + totalSamples += _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; + + if (sample < totalSamples) { + totalSamples -= _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; + break; + } + } + + // Reposition the audio stream + 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!) + int16 *tempBuffer = new int16[sample - totalSamples]; + _audStream->readBuffer(tempBuffer, sample - totalSamples); + delete[] tempBuffer; + debug(3, "Skipping %d audio samples", sample - totalSamples); + } +} + QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimeParser::SampleDesc() { channels = 0; sampleRate = 0; @@ -255,13 +314,10 @@ QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimePar /** * A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API */ -class QuickTimeAudioStream : public RewindableAudioStream, public QuickTimeAudioDecoder { +class QuickTimeAudioStream : public SeekableAudioStream, public QuickTimeAudioDecoder { public: QuickTimeAudioStream() {} - - ~QuickTimeAudioStream() { - delete _audStream; - } + ~QuickTimeAudioStream() {} bool loadFile(const Common::String &filename) { return QuickTimeAudioDecoder::loadFile(filename) && _audioStreamIndex >= 0 && _audStream; @@ -285,19 +341,21 @@ public: 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; + // SeekableAudioStream API + bool seek(const Timestamp &where) { + if (where > getLength()) + return false; - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - _audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); + setAudioStreamPos(where); return true; } + + Timestamp getLength() const { + return Timestamp(0, _streams[_audioStreamIndex]->duration, _streams[_audioStreamIndex]->time_scale); + } }; -RewindableAudioStream *makeQuickTimeStream(const Common::String &filename) { +SeekableAudioStream *makeQuickTimeStream(const Common::String &filename) { QuickTimeAudioStream *audioStream = new QuickTimeAudioStream(); if (!audioStream->loadFile(filename)) { -- cgit v1.2.3 From faee277978c54ccb3dcccfedc75ddb31f44e630f Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 8 Apr 2011 17:04:29 -0400 Subject: COMMON: Add a DisposeAfterUse flag to QuickTimeParser --- audio/decoders/quicktime.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 53cce30125..62a6f6e404 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -46,16 +46,16 @@ QuickTimeAudioDecoder::~QuickTimeAudioDecoder() { delete _audStream; } -bool QuickTimeAudioDecoder::loadFile(const Common::String &filename) { - if (!Common::QuickTimeParser::loadFile(filename)) +bool QuickTimeAudioDecoder::loadAudioFile(const Common::String &filename) { + if (!Common::QuickTimeParser::parseFile(filename)) return false; init(); return true; } -bool QuickTimeAudioDecoder::loadStream(Common::SeekableReadStream *stream) { - if (!Common::QuickTimeParser::loadStream(stream)) +bool QuickTimeAudioDecoder::loadAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) { + if (!Common::QuickTimeParser::parseStream(stream, disposeFileHandle)) return false; init(); @@ -319,8 +319,12 @@ public: QuickTimeAudioStream() {} ~QuickTimeAudioStream() {} - bool loadFile(const Common::String &filename) { - return QuickTimeAudioDecoder::loadFile(filename) && _audioStreamIndex >= 0 && _audStream; + bool openFromFile(const Common::String &filename) { + return QuickTimeAudioDecoder::loadAudioFile(filename) && _audioStreamIndex >= 0 && _audStream; + } + + bool openFromStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) { + return QuickTimeAudioDecoder::loadAudioStream(stream, disposeFileHandle) && _audioStreamIndex >= 0 && _audStream; } // AudioStream API @@ -358,7 +362,18 @@ public: SeekableAudioStream *makeQuickTimeStream(const Common::String &filename) { QuickTimeAudioStream *audioStream = new QuickTimeAudioStream(); - if (!audioStream->loadFile(filename)) { + if (!audioStream->openFromFile(filename)) { + delete audioStream; + return 0; + } + + return audioStream; +} + +SeekableAudioStream *makeQuickTimeStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + QuickTimeAudioStream *audioStream = new QuickTimeAudioStream(); + + if (!audioStream->openFromStream(stream, disposeAfterUse)) { delete audioStream; return 0; } -- cgit v1.2.3 From 7c5dfaa04c2d02a22b301823bedb2940c3e590aa Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 8 Apr 2011 22:46:19 -0400 Subject: COMMON: Parse the MPEG-4 esds atom --- audio/decoders/quicktime.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 62a6f6e404..e18ba7c480 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -76,7 +76,7 @@ void QuickTimeAudioDecoder::init() { if (_audioStreamIndex >= 0) { AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - if (checkAudioCodecSupport(entry->codecTag)) { + if (checkAudioCodecSupport(entry->codecTag, _streams[_audioStreamIndex]->objectTypeMP4)) { _audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); _curAudioChunk = 0; @@ -140,7 +140,7 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt return 0; } -bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag) { +bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag, byte objectTypeMP4) { // Check if the codec is a supported codec if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4')) return true; @@ -150,9 +150,18 @@ bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag) { return true; #endif - if (tag == MKID_BE('mp4a')) - warning("No MPEG-4 audio (AAC) support"); - else + if (tag == MKID_BE('mp4a')) { + Common::String audioType; + switch (objectTypeMP4) { + case 0x40: + audioType = "AAC"; + break; + default: + audioType = "Unknown"; + break; + } + warning("No MPEG-4 audio (%s) support", audioType.c_str()); + } else warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); return false; -- cgit v1.2.3 From f9413e4dc26081f59247fa7eae62f50258e92fa4 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 10 Apr 2011 14:51:24 -0400 Subject: AUDIO: Add support for AAC audio --- audio/decoders/quicktime.cpp | 72 ++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 20 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index e18ba7c480..9b69012c7a 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -32,6 +32,7 @@ #include "audio/decoders/quicktime.h" // Codecs +#include "audio/decoders/aac.h" #include "audio/decoders/adpcm.h" #include "audio/decoders/qdm2.h" #include "audio/decoders/raw.h" @@ -153,9 +154,13 @@ bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag, byte objectTypeMP if (tag == MKID_BE('mp4a')) { Common::String audioType; switch (objectTypeMP4) { - case 0x40: + case 0x40: // AAC +#ifdef USE_FAAD + return true; +#else audioType = "AAC"; break; +#endif default: audioType = "Unknown"; break; @@ -190,6 +195,12 @@ AudioStream *QuickTimeAudioDecoder::createAudioStream(Common::SeekableReadStream } 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); + } else if (entry->codecTag == MKID_BE('mp4a')) { + // The 7th Guest iOS uses an MPEG-4 codec +#ifdef USE_FAAD + if (_streams[_audioStreamIndex]->objectTypeMP4 == 0x40) + return makeAACStream(stream, DisposeAfterUse::YES, _streams[_audioStreamIndex]->extradata); +#endif #ifdef AUDIO_QDM2_H } else if (entry->codecTag == MKID_BE('QDM2')) { // Myst ME uses this codec for many videos @@ -225,27 +236,48 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk); assert(sampleCount); - // Then calculate the right sizes - while (sampleCount > 0) { - uint32 samples = 0, size = 0; + if (_streams[_audioStreamIndex]->stts_count == 1 && _streams[_audioStreamIndex]->stts_data[0].duration == 1) { + // Old-style audio demuxing + + // 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((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); + size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; + } else { + samples = MIN(1024, sampleCount); + size = samples * _streams[_audioStreamIndex]->sample_size; + } - if (entry->samplesPerFrame >= 160) { - samples = entry->samplesPerFrame; - size = entry->bytesPerFrame; - } else if (entry->samplesPerFrame > 1) { - samples = MIN((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); - size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; - } else { - samples = MIN(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; + } + } else { + // New-style audio demuxing + + // Find our starting sample + uint32 startSample = 0; + for (uint32 i = 0; i < _curAudioChunk; i++) + startSample += getAudioChunkSampleCount(i); + + for (uint32 i = 0; i < sampleCount; i++) { + uint32 size = (_streams[_audioStreamIndex]->sample_size != 0) ? _streams[_audioStreamIndex]->sample_size : _streams[_audioStreamIndex]->sample_sizes[i + startSample]; + + // 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); } - - // 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 -- cgit v1.2.3 From 08b70fa1a737f325a0114632b3cef8cbf028fd3a Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 12 Apr 2011 14:23:23 -0400 Subject: AUDIO: Fix QuickTime/MPEG-4 seeking MPEG-4 seeking was broken while QuickTime seeking was extremely slow. All is fixed now --- audio/decoders/quicktime.cpp | 58 +++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 9b69012c7a..0888041cfc 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -226,6 +226,11 @@ uint32 QuickTimeAudioDecoder::getAudioChunkSampleCount(uint chunk) { return sampleCount; } +bool QuickTimeAudioDecoder::isOldDemuxing() const { + assert(_audioStreamIndex >= 0); + return _streams[_audioStreamIndex]->stts_count == 1 && _streams[_audioStreamIndex]->stts_data[0].duration == 1; +} + void QuickTimeAudioDecoder::queueNextAudioChunk() { AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); @@ -236,7 +241,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk); assert(sampleCount); - if (_streams[_audioStreamIndex]->stts_count == 1 && _streams[_audioStreamIndex]->stts_data[0].duration == 1) { + if (isOldDemuxing()) { // Old-style audio demuxing // Then calculate the right sizes @@ -297,40 +302,32 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); // First, we need to track down what audio sample we need - Audio::Timestamp curAudioTime(0, _streams[_audioStreamIndex]->time_scale); - uint sample = 0; - bool done = false; - for (int32 i = 0; i < _streams[_audioStreamIndex]->stts_count && !done; i++) { - for (int32 j = 0; j < _streams[_audioStreamIndex]->stts_data[i].count; j++) { - curAudioTime = curAudioTime.addFrames(_streams[_audioStreamIndex]->stts_data[i].duration); - - if (curAudioTime > where) { - done = true; - break; - } - - sample++; + Audio::Timestamp curAudioTime = where; + curAudioTime.convertToFramerate(_streams[_audioStreamIndex]->time_scale); + uint32 sample = curAudioTime.totalNumberOfFrames() / entry->channels; + uint32 seekSample = sample; + + if (!isOldDemuxing()) { + // We shouldn't have audio samples that are a different duration + // That would be quite bad! + if (_streams[_audioStreamIndex]->stts_count != 1) { + warning("Failed seeking"); + return; } + + seekSample /= _streams[_audioStreamIndex]->stts_data[0].duration; } // Now to track down what chunk it's in - _curAudioChunk = 0; uint32 totalSamples = 0; + _curAudioChunk = 0; for (uint32 i = 0; i < _streams[_audioStreamIndex]->chunk_count; i++, _curAudioChunk++) { - int sampleToChunkIndex = -1; + uint32 chunkSampleCount = getAudioChunkSampleCount(i); - for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) - if (i >= _streams[_audioStreamIndex]->sample_to_chunk[j].first) - sampleToChunkIndex = j; - - assert(sampleToChunkIndex >= 0); - - totalSamples += _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; - - if (sample < totalSamples) { - totalSamples -= _streams[_audioStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; + if (seekSample < totalSamples + chunkSampleCount) break; - } + + totalSamples += chunkSampleCount; } // Reposition the audio stream @@ -338,10 +335,11 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { if (sample != totalSamples) { // HACK: Skip a certain amount of samples from the stream // (There's got to be a better way to do this!) - int16 *tempBuffer = new int16[sample - totalSamples]; - _audStream->readBuffer(tempBuffer, sample - totalSamples); + int skipSamples = (sample - totalSamples) * ((AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0])->channels; + + int16 *tempBuffer = new int16[skipSamples]; + _audStream->readBuffer(tempBuffer, skipSamples); delete[] tempBuffer; - debug(3, "Skipping %d audio samples", sample - totalSamples); } } -- cgit v1.2.3 From 76105b29b7f885ed873f6af7321d8e48a5b0a41e Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 14 Apr 2011 10:25:02 -0400 Subject: AUDIO: Split the QuickTimeAudioDecoder into a new header file (Mirroring the new adpcm_intern.h file) --- audio/decoders/quicktime.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index d4cd3a62f2..5115a20218 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -30,6 +30,7 @@ #include "audio/audiostream.h" #include "audio/decoders/quicktime.h" +#include "audio/decoders/quicktime_intern.h" // Codecs #include "audio/decoders/aac.h" -- cgit v1.2.3 From d2f5b91a9a2da6b6d8a31b0bd503dc79d8397f00 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Wed, 11 May 2011 14:48:13 -0400 Subject: AUDIO: Fix usage of Timestamp::convertToFramerate --- audio/decoders/quicktime.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index f82dc05b7c..e80c2b592c 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -304,8 +304,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); // First, we need to track down what audio sample we need - Audio::Timestamp curAudioTime = where; - curAudioTime.convertToFramerate(_streams[_audioStreamIndex]->time_scale); + Audio::Timestamp curAudioTime = where.convertToFramerate(_streams[_audioStreamIndex]->time_scale); uint32 sample = curAudioTime.totalNumberOfFrames() / entry->channels; uint32 seekSample = sample; -- cgit v1.2.3 From bc7ff278281f46a77ba955d6f5fa20dab611bd90 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 13 May 2011 02:22:11 -0400 Subject: AUDIO: Fix QuickTime stereo audio seeking --- audio/decoders/quicktime.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index e80c2b592c..bdde9db883 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -305,7 +305,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { // First, we need to track down what audio sample we need Audio::Timestamp curAudioTime = where.convertToFramerate(_streams[_audioStreamIndex]->time_scale); - uint32 sample = curAudioTime.totalNumberOfFrames() / entry->channels; + uint32 sample = curAudioTime.totalNumberOfFrames(); uint32 seekSample = sample; if (!isOldDemuxing()) { @@ -316,6 +316,8 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { return; } + // Note that duration is in terms of *one* channel + // This eases calculation a bit seekSample /= _streams[_audioStreamIndex]->stts_data[0].duration; } @@ -336,7 +338,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { if (sample != totalSamples) { // HACK: Skip a certain amount of samples from the stream // (There's got to be a better way to do this!) - int skipSamples = (sample - totalSamples) * ((AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0])->channels; + int skipSamples = (sample - totalSamples) * entry->channels; int16 *tempBuffer = new int16[skipSamples]; _audStream->readBuffer(tempBuffer, skipSamples); -- cgit v1.2.3 From 2e066816983935b8e365fc555f953bdce9f64e46 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 2 Jun 2011 18:40:49 -0400 Subject: COMMON: Begin objectifying QuickTimeParser::SampleDesc further This is preparation for multiple video and audio tracks --- audio/decoders/quicktime.cpp | 235 +++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 120 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index bdde9db883..a22f0399ad 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -79,13 +79,13 @@ void QuickTimeAudioDecoder::init() { if (_audioStreamIndex >= 0) { AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - if (checkAudioCodecSupport(entry->codecTag, _streams[_audioStreamIndex]->objectTypeMP4)) { - _audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); + if (entry->isAudioCodecSupported()) { + _audStream = makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2); _curAudioChunk = 0; // Make sure the bits per sample transfers to the sample size - if (entry->codecTag == MKTAG('r', 'a', 'w', ' ') || entry->codecTag == MKTAG('t', 'w', 'o', 's')) - _streams[_audioStreamIndex]->sample_size = (entry->bitsPerSample / 8) * entry->channels; + if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's')) + _streams[_audioStreamIndex]->sample_size = (entry->_bitsPerSample / 8) * entry->_channels; } } } @@ -94,32 +94,31 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt if (st->codec_type == CODEC_TYPE_AUDIO) { debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); - AudioSampleDesc *entry = new AudioSampleDesc(); - entry->codecTag = format; + AudioSampleDesc *entry = new AudioSampleDesc(st, format); uint16 stsdVersion = _fd->readUint16BE(); _fd->readUint16BE(); // revision level _fd->readUint32BE(); // vendor - entry->channels = _fd->readUint16BE(); // channel count - entry->bitsPerSample = _fd->readUint16BE(); // sample size + 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); + 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; + 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); + 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); + 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); @@ -130,12 +129,12 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt // 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 == MKTAG('i', 'm', 'a', '4')) { - entry->samplesPerFrame = 64; - entry->bytesPerFrame = 34 * entry->channels; + entry->_samplesPerFrame = 64; + entry->_bytesPerFrame = 34 * entry->_channels; } - if (entry->sampleRate == 0 && st->time_scale > 1) - entry->sampleRate = st->time_scale; + if (entry->_sampleRate == 0 && st->time_scale > 1) + entry->_sampleRate = st->time_scale; return entry; } @@ -143,91 +142,6 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt return 0; } -bool QuickTimeAudioDecoder::checkAudioCodecSupport(uint32 tag, byte objectTypeMP4) { - // Check if the codec is a supported codec - if (tag == MKTAG('t', 'w', 'o', 's') || tag == MKTAG('r', 'a', 'w', ' ') || tag == MKTAG('i', 'm', 'a', '4')) - return true; - -#ifdef AUDIO_QDM2_H - if (tag == MKTAG('Q', 'D', 'M', '2')) - return true; -#endif - - if (tag == MKTAG('m', 'p', '4', 'a')) { - Common::String audioType; - switch (objectTypeMP4) { - case 0x40: // AAC -#ifdef USE_FAAD - return true; -#else - audioType = "AAC"; - break; -#endif - default: - audioType = "Unknown"; - break; - } - warning("No MPEG-4 audio (%s) support", audioType.c_str()); - } 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 == MKTAG('t', 'w', 'o', 's') || entry->codecTag == MKTAG('r', 'a', 'w', ' ')) { - // Fortunately, most of the audio used in Myst videos is raw... - uint16 flags = 0; - if (entry->codecTag == MKTAG('r', 'a', 'w', ' ')) - 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 == MKTAG('i', 'm', 'a', '4')) { - // Riven uses this codec (as do some Myst ME videos) - return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, entry->sampleRate, entry->channels, 34); - } else if (entry->codecTag == MKTAG('m', 'p', '4', 'a')) { - // The 7th Guest iOS uses an MPEG-4 codec -#ifdef USE_FAAD - if (_streams[_audioStreamIndex]->objectTypeMP4 == 0x40) - return makeAACStream(stream, DisposeAfterUse::YES, _streams[_audioStreamIndex]->extradata); -#endif -#ifdef AUDIO_QDM2_H - } else if (entry->codecTag == MKTAG('Q', 'D', 'M', '2')) { - // 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; -} - bool QuickTimeAudioDecoder::isOldDemuxing() const { assert(_audioStreamIndex >= 0); return _streams[_audioStreamIndex]->stts_count == 1 && _streams[_audioStreamIndex]->stts_data[0].duration == 1; @@ -240,7 +154,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); // First, we have to get the sample count - uint32 sampleCount = getAudioChunkSampleCount(_curAudioChunk); + uint32 sampleCount = entry->getAudioChunkSampleCount(_curAudioChunk); assert(sampleCount); if (isOldDemuxing()) { @@ -250,12 +164,12 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { 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((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount); - size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame; + if (entry->_samplesPerFrame >= 160) { + samples = entry->_samplesPerFrame; + size = entry->_bytesPerFrame; + } else if (entry->_samplesPerFrame > 1) { + samples = MIN((1024 / entry->_samplesPerFrame) * entry->_samplesPerFrame, sampleCount); + size = (samples / entry->_samplesPerFrame) * entry->_bytesPerFrame; } else { samples = MIN(1024, sampleCount); size = samples * _streams[_audioStreamIndex]->sample_size; @@ -274,7 +188,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { // Find our starting sample uint32 startSample = 0; for (uint32 i = 0; i < _curAudioChunk; i++) - startSample += getAudioChunkSampleCount(i); + startSample += entry->getAudioChunkSampleCount(i); for (uint32 i = 0; i < sampleCount; i++) { uint32 size = (_streams[_audioStreamIndex]->sample_size != 0) ? _streams[_audioStreamIndex]->sample_size : _streams[_audioStreamIndex]->sample_sizes[i + startSample]; @@ -288,7 +202,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { } // Now queue the buffer - _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); + _audStream->queueAudioStream(entry->createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); delete wStream; _curAudioChunk++; @@ -301,7 +215,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { // Re-create the audio stream delete _audStream; Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; - _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2); + _audStream = Audio::makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2); // First, we need to track down what audio sample we need Audio::Timestamp curAudioTime = where.convertToFramerate(_streams[_audioStreamIndex]->time_scale); @@ -325,7 +239,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { uint32 totalSamples = 0; _curAudioChunk = 0; for (uint32 i = 0; i < _streams[_audioStreamIndex]->chunk_count; i++, _curAudioChunk++) { - uint32 chunkSampleCount = getAudioChunkSampleCount(i); + uint32 chunkSampleCount = entry->getAudioChunkSampleCount(i); if (seekSample < totalSamples + chunkSampleCount) break; @@ -338,7 +252,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { if (sample != totalSamples) { // HACK: Skip a certain amount of samples from the stream // (There's got to be a better way to do this!) - int skipSamples = (sample - totalSamples) * entry->channels; + int skipSamples = (sample - totalSamples) * entry->_channels; int16 *tempBuffer = new int16[skipSamples]; _audStream->readBuffer(tempBuffer, skipSamples); @@ -346,11 +260,92 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { } } -QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimeParser::SampleDesc() { - channels = 0; - sampleRate = 0; - samplesPerFrame = 0; - bytesPerFrame = 0; +QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser::MOVStreamContext *parentStream, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentStream, codecTag) { + _channels = 0; + _sampleRate = 0; + _samplesPerFrame = 0; + _bytesPerFrame = 0; + _bitsPerSample = 0; +} + +bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const { + // Check if the codec is a supported codec + if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ') || _codecTag == MKTAG('i', 'm', 'a', '4')) + return true; + +#ifdef AUDIO_QDM2_H + if (_codecTag == MKTAG('Q', 'D', 'M', '2')) + return true; +#endif + + if (_codecTag == MKTAG('m', 'p', '4', 'a')) { + Common::String audioType; + switch (_parentStream->objectTypeMP4) { + case 0x40: // AAC +#ifdef USE_FAAD + return true; +#else + audioType = "AAC"; + break; +#endif + default: + audioType = "Unknown"; + break; + } + warning("No MPEG-4 audio (%s) support", audioType.c_str()); + } else + warning("Audio Codec Not Supported: \'%s\'", tag2str(_codecTag)); + + return false; +} + +uint32 QuickTimeAudioDecoder::AudioSampleDesc::getAudioChunkSampleCount(uint chunk) const { + uint32 sampleCount = 0; + + for (uint32 j = 0; j < _parentStream->sample_to_chunk_sz; j++) + if (chunk >= _parentStream->sample_to_chunk[j].first) + sampleCount = _parentStream->sample_to_chunk[j].count; + + return sampleCount; +} + +AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::SeekableReadStream *stream) const { + if (!stream) + return 0; + + if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) { + // Fortunately, most of the audio used in Myst videos is raw... + uint16 flags = 0; + if (_codecTag == MKTAG('r', 'a', 'w', ' ')) + flags |= FLAG_UNSIGNED; + if (_channels == 2) + flags |= FLAG_STEREO; + if (_bitsPerSample == 16) + flags |= FLAG_16BITS; + uint32 dataSize = stream->size(); + byte *data = (byte *)malloc(dataSize); + stream->read(data, dataSize); + delete stream; + return makeRawStream(data, dataSize, _sampleRate, flags); + } else if (_codecTag == MKTAG('i', 'm', 'a', '4')) { + // Riven uses this codec (as do some Myst ME videos) + return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34); + } else if (_codecTag == MKTAG('m', 'p', '4', 'a')) { + // The 7th Guest iOS uses an MPEG-4 codec +#ifdef USE_FAAD + if (_parentStream->objectTypeMP4 == 0x40) + return makeAACStream(stream, DisposeAfterUse::YES, _parentStream->extradata); +#endif +#ifdef AUDIO_QDM2_H + } else if (_codecTag == MKTAG('Q', 'D', 'M', '2')) { + // Myst ME uses this codec for many videos + return makeQDM2Stream(stream, _parentStream->extradata); +#endif + } + + error("Unsupported audio codec"); + + return NULL; } /** -- cgit v1.2.3 From 547fd1bdcabcba0e741eb31100ba99ff73399d24 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 3 Jun 2011 00:54:30 -0400 Subject: COMMON: Cleanup QuickTime variable and struct naming --- audio/decoders/quicktime.cpp | 70 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'audio/decoders/quicktime.cpp') diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index a22f0399ad..0f2e76658b 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -68,16 +68,16 @@ bool QuickTimeAudioDecoder::loadAudioStream(Common::SeekableReadStream *stream, void QuickTimeAudioDecoder::init() { Common::QuickTimeParser::init(); - _audioStreamIndex = -1; + _audioTrackIndex = -1; // Find an audio stream - for (uint32 i = 0; i < _numStreams; i++) - if (_streams[i]->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) - _audioStreamIndex = i; + for (uint32 i = 0; i < _tracks.size(); i++) + if (_tracks[i]->codecType == CODEC_TYPE_AUDIO && _audioTrackIndex < 0) + _audioTrackIndex = i; // Initialize audio, if present - if (_audioStreamIndex >= 0) { - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + if (_audioTrackIndex >= 0) { + AudioSampleDesc *entry = (AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0]; if (entry->isAudioCodecSupported()) { _audStream = makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2); @@ -85,16 +85,16 @@ void QuickTimeAudioDecoder::init() { // Make sure the bits per sample transfers to the sample size if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's')) - _streams[_audioStreamIndex]->sample_size = (entry->_bitsPerSample / 8) * entry->_channels; + _tracks[_audioTrackIndex]->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels; } } } -Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVStreamContext *st, uint32 format) { - if (st->codec_type == CODEC_TYPE_AUDIO) { +Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format) { + if (track->codecType == CODEC_TYPE_AUDIO) { debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); - AudioSampleDesc *entry = new AudioSampleDesc(st, format); + AudioSampleDesc *entry = new AudioSampleDesc(track, format); uint16 stsdVersion = _fd->readUint16BE(); _fd->readUint16BE(); // revision level @@ -133,8 +133,8 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt entry->_bytesPerFrame = 34 * entry->_channels; } - if (entry->_sampleRate == 0 && st->time_scale > 1) - entry->_sampleRate = st->time_scale; + if (entry->_sampleRate == 0 && track->timeScale > 1) + entry->_sampleRate = track->timeScale; return entry; } @@ -143,15 +143,15 @@ Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(MOVSt } bool QuickTimeAudioDecoder::isOldDemuxing() const { - assert(_audioStreamIndex >= 0); - return _streams[_audioStreamIndex]->stts_count == 1 && _streams[_audioStreamIndex]->stts_data[0].duration == 1; + assert(_audioTrackIndex >= 0); + return _tracks[_audioTrackIndex]->timeToSampleCount == 1 && _tracks[_audioTrackIndex]->timeToSample[0].duration == 1; } void QuickTimeAudioDecoder::queueNextAudioChunk() { - AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + AudioSampleDesc *entry = (AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0]; Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); - _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); + _fd->seek(_tracks[_audioTrackIndex]->chunkOffsets[_curAudioChunk]); // First, we have to get the sample count uint32 sampleCount = entry->getAudioChunkSampleCount(_curAudioChunk); @@ -172,7 +172,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { size = (samples / entry->_samplesPerFrame) * entry->_bytesPerFrame; } else { samples = MIN(1024, sampleCount); - size = samples * _streams[_audioStreamIndex]->sample_size; + size = samples * _tracks[_audioTrackIndex]->sampleSize; } // Now, we read in the data for this data and output it @@ -191,7 +191,7 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() { startSample += entry->getAudioChunkSampleCount(i); for (uint32 i = 0; i < sampleCount; i++) { - uint32 size = (_streams[_audioStreamIndex]->sample_size != 0) ? _streams[_audioStreamIndex]->sample_size : _streams[_audioStreamIndex]->sample_sizes[i + startSample]; + uint32 size = (_tracks[_audioTrackIndex]->sampleSize != 0) ? _tracks[_audioTrackIndex]->sampleSize : _tracks[_audioTrackIndex]->sampleSizes[i + startSample]; // Now, we read in the data for this data and output it byte *data = (byte *)malloc(size); @@ -214,31 +214,31 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { // Re-create the audio stream delete _audStream; - Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0]; + Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0]; _audStream = Audio::makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2); // First, we need to track down what audio sample we need - Audio::Timestamp curAudioTime = where.convertToFramerate(_streams[_audioStreamIndex]->time_scale); + Audio::Timestamp curAudioTime = where.convertToFramerate(_tracks[_audioTrackIndex]->timeScale); uint32 sample = curAudioTime.totalNumberOfFrames(); uint32 seekSample = sample; if (!isOldDemuxing()) { // We shouldn't have audio samples that are a different duration // That would be quite bad! - if (_streams[_audioStreamIndex]->stts_count != 1) { + if (_tracks[_audioTrackIndex]->timeToSampleCount != 1) { warning("Failed seeking"); return; } // Note that duration is in terms of *one* channel // This eases calculation a bit - seekSample /= _streams[_audioStreamIndex]->stts_data[0].duration; + seekSample /= _tracks[_audioTrackIndex]->timeToSample[0].duration; } // Now to track down what chunk it's in uint32 totalSamples = 0; _curAudioChunk = 0; - for (uint32 i = 0; i < _streams[_audioStreamIndex]->chunk_count; i++, _curAudioChunk++) { + for (uint32 i = 0; i < _tracks[_audioTrackIndex]->chunkCount; i++, _curAudioChunk++) { uint32 chunkSampleCount = entry->getAudioChunkSampleCount(i); if (seekSample < totalSamples + chunkSampleCount) @@ -260,7 +260,7 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { } } -QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser::MOVStreamContext *parentStream, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentStream, codecTag) { +QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) { _channels = 0; _sampleRate = 0; _samplesPerFrame = 0; @@ -280,7 +280,7 @@ bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const { if (_codecTag == MKTAG('m', 'p', '4', 'a')) { Common::String audioType; - switch (_parentStream->objectTypeMP4) { + switch (_parentTrack->objectTypeMP4) { case 0x40: // AAC #ifdef USE_FAAD return true; @@ -302,9 +302,9 @@ bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const { uint32 QuickTimeAudioDecoder::AudioSampleDesc::getAudioChunkSampleCount(uint chunk) const { uint32 sampleCount = 0; - for (uint32 j = 0; j < _parentStream->sample_to_chunk_sz; j++) - if (chunk >= _parentStream->sample_to_chunk[j].first) - sampleCount = _parentStream->sample_to_chunk[j].count; + for (uint32 j = 0; j < _parentTrack->sampleToChunkCount; j++) + if (chunk >= _parentTrack->sampleToChunk[j].first) + sampleCount = _parentTrack->sampleToChunk[j].count; return sampleCount; } @@ -333,13 +333,13 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S } else if (_codecTag == MKTAG('m', 'p', '4', 'a')) { // The 7th Guest iOS uses an MPEG-4 codec #ifdef USE_FAAD - if (_parentStream->objectTypeMP4 == 0x40) - return makeAACStream(stream, DisposeAfterUse::YES, _parentStream->extradata); + if (_parentTrack->objectTypeMP4 == 0x40) + return makeAACStream(stream, DisposeAfterUse::YES, _parentTrack->extraData); #endif #ifdef AUDIO_QDM2_H } else if (_codecTag == MKTAG('Q', 'D', 'M', '2')) { // Myst ME uses this codec for many videos - return makeQDM2Stream(stream, _parentStream->extradata); + return makeQDM2Stream(stream, _parentTrack->extraData); #endif } @@ -357,11 +357,11 @@ public: ~QuickTimeAudioStream() {} bool openFromFile(const Common::String &filename) { - return QuickTimeAudioDecoder::loadAudioFile(filename) && _audioStreamIndex >= 0 && _audStream; + return QuickTimeAudioDecoder::loadAudioFile(filename) && _audioTrackIndex >= 0 && _audStream; } bool openFromStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) { - return QuickTimeAudioDecoder::loadAudioStream(stream, disposeFileHandle) && _audioStreamIndex >= 0 && _audStream; + return QuickTimeAudioDecoder::loadAudioStream(stream, disposeFileHandle) && _audioTrackIndex >= 0 && _audStream; } // AudioStream API @@ -380,7 +380,7 @@ public: bool isStereo() const { return _audStream->isStereo(); } int getRate() const { return _audStream->getRate(); } - bool endOfData() const { return _curAudioChunk >= _streams[_audioStreamIndex]->chunk_count && _audStream->endOfData(); } + bool endOfData() const { return _curAudioChunk >= _tracks[_audioTrackIndex]->chunkCount && _audStream->endOfData(); } // SeekableAudioStream API bool seek(const Timestamp &where) { @@ -392,7 +392,7 @@ public: } Timestamp getLength() const { - return Timestamp(0, _streams[_audioStreamIndex]->duration, _streams[_audioStreamIndex]->time_scale); + return Timestamp(0, _tracks[_audioTrackIndex]->duration, _tracks[_audioTrackIndex]->timeScale); } }; -- cgit v1.2.3