AUDIO: Allow for seeking in a QuickTimeAudioStream
This commit is contained in:
parent
5c4d7baa06
commit
88ebf13077
3 changed files with 82 additions and 76 deletions
|
@ -43,6 +43,7 @@ QuickTimeAudioDecoder::QuickTimeAudioDecoder() : Common::QuickTimeParser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QuickTimeAudioDecoder::~QuickTimeAudioDecoder() {
|
QuickTimeAudioDecoder::~QuickTimeAudioDecoder() {
|
||||||
|
delete _audStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuickTimeAudioDecoder::loadFile(const Common::String &filename) {
|
bool QuickTimeAudioDecoder::loadFile(const Common::String &filename) {
|
||||||
|
@ -245,6 +246,64 @@ void QuickTimeAudioDecoder::queueNextAudioChunk() {
|
||||||
_curAudioChunk++;
|
_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() {
|
QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimeParser::SampleDesc() {
|
||||||
channels = 0;
|
channels = 0;
|
||||||
sampleRate = 0;
|
sampleRate = 0;
|
||||||
|
@ -255,13 +314,10 @@ QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc() : Common::QuickTimePar
|
||||||
/**
|
/**
|
||||||
* A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API
|
* A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API
|
||||||
*/
|
*/
|
||||||
class QuickTimeAudioStream : public RewindableAudioStream, public QuickTimeAudioDecoder {
|
class QuickTimeAudioStream : public SeekableAudioStream, public QuickTimeAudioDecoder {
|
||||||
public:
|
public:
|
||||||
QuickTimeAudioStream() {}
|
QuickTimeAudioStream() {}
|
||||||
|
~QuickTimeAudioStream() {}
|
||||||
~QuickTimeAudioStream() {
|
|
||||||
delete _audStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loadFile(const Common::String &filename) {
|
bool loadFile(const Common::String &filename) {
|
||||||
return QuickTimeAudioDecoder::loadFile(filename) && _audioStreamIndex >= 0 && _audStream;
|
return QuickTimeAudioDecoder::loadFile(filename) && _audioStreamIndex >= 0 && _audStream;
|
||||||
|
@ -285,19 +341,21 @@ public:
|
||||||
int getRate() const { return _audStream->getRate(); }
|
int getRate() const { return _audStream->getRate(); }
|
||||||
bool endOfData() const { return _curAudioChunk >= _streams[_audioStreamIndex]->chunk_count && _audStream->endOfData(); }
|
bool endOfData() const { return _curAudioChunk >= _streams[_audioStreamIndex]->chunk_count && _audStream->endOfData(); }
|
||||||
|
|
||||||
// RewindableAudioStream API
|
// SeekableAudioStream API
|
||||||
bool rewind() {
|
bool seek(const Timestamp &where) {
|
||||||
// Reset our parent stream
|
if (where > getLength())
|
||||||
_curAudioChunk = 0;
|
return false;
|
||||||
delete _audStream;
|
|
||||||
|
|
||||||
AudioSampleDesc *entry = (AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0];
|
setAudioStreamPos(where);
|
||||||
_audStream = makeQueuingAudioStream(entry->sampleRate, entry->channels == 2);
|
|
||||||
return true;
|
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();
|
QuickTimeAudioStream *audioStream = new QuickTimeAudioStream();
|
||||||
|
|
||||||
if (!audioStream->loadFile(filename)) {
|
if (!audioStream->loadFile(filename)) {
|
||||||
|
|
|
@ -80,16 +80,18 @@ protected:
|
||||||
int8 _audioStreamIndex;
|
int8 _audioStreamIndex;
|
||||||
uint _curAudioChunk;
|
uint _curAudioChunk;
|
||||||
QueuingAudioStream *_audStream;
|
QueuingAudioStream *_audStream;
|
||||||
|
|
||||||
|
void setAudioStreamPos(const Timestamp &where);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to load a QuickTime sound file from the given file name and create a RewindableAudioStream
|
* Try to load a QuickTime sound file from the given file name and create a SeekableAudioStream
|
||||||
* from that data.
|
* from that data.
|
||||||
*
|
*
|
||||||
* @param filename the filename of the file from which to read the data
|
* @param filename the filename of the file from which to read the data
|
||||||
* @return a new RewindableAudioStream, or NULL, if an error occurred
|
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||||
*/
|
*/
|
||||||
RewindableAudioStream *makeQuickTimeStream(const Common::String &filename);
|
SeekableAudioStream *makeQuickTimeStream(const Common::String &filename);
|
||||||
|
|
||||||
} // End of namespace Audio
|
} // End of namespace Audio
|
||||||
|
|
||||||
|
|
|
@ -168,57 +168,8 @@ void QuickTimeDecoder::seekToFrame(uint32 frame) {
|
||||||
if (_audioStreamIndex >= 0) {
|
if (_audioStreamIndex >= 0) {
|
||||||
_audioStartOffset = curVideoTime;
|
_audioStartOffset = curVideoTime;
|
||||||
|
|
||||||
// Re-create the audio stream
|
// Seek to the new audio location
|
||||||
Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0];
|
setAudioStreamPos(_audioStartOffset);
|
||||||
_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 > curVideoTime) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart the audio
|
// Restart the audio
|
||||||
startAudio();
|
startAudio();
|
||||||
|
@ -282,17 +233,15 @@ Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickTimeDecoder::startAudio() {
|
void QuickTimeDecoder::startAudio() {
|
||||||
if (_audStream) { // No audio/audio not supported
|
if (_audStream) {
|
||||||
updateAudioBuffer();
|
updateAudioBuffer();
|
||||||
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream);
|
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
||||||
}
|
} // else no audio or the audio compression is not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickTimeDecoder::stopAudio() {
|
void QuickTimeDecoder::stopAudio() {
|
||||||
if (_audStream) {
|
if (_audStream)
|
||||||
g_system->getMixer()->stopHandle(_audHandle);
|
g_system->getMixer()->stopHandle(_audHandle);
|
||||||
_audStream = NULL; // the mixer automatically frees the stream
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickTimeDecoder::pauseVideoIntern(bool pause) {
|
void QuickTimeDecoder::pauseVideoIntern(bool pause) {
|
||||||
|
@ -550,9 +499,6 @@ void QuickTimeDecoder::close() {
|
||||||
_scaledSurface = 0;
|
_scaledSurface = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The audio stream is deleted automatically
|
|
||||||
_audStream = NULL;
|
|
||||||
|
|
||||||
Common::QuickTimeParser::close();
|
Common::QuickTimeParser::close();
|
||||||
SeekableVideoDecoder::reset();
|
SeekableVideoDecoder::reset();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue