mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-25 05:34:27 +00:00
AUDIO: Allow for seeking in a QuickTimeAudioStream
This commit is contained in:
parent
5c4d7baa06
commit
88ebf13077
@ -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)) {
|
||||
|
@ -80,16 +80,18 @@ protected:
|
||||
int8 _audioStreamIndex;
|
||||
uint _curAudioChunk;
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
|
||||
|
@ -168,58 +168,9 @@ void QuickTimeDecoder::seekToFrame(uint32 frame) {
|
||||
if (_audioStreamIndex >= 0) {
|
||||
_audioStartOffset = curVideoTime;
|
||||
|
||||
// Re-create the audio stream
|
||||
Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_streams[_audioStreamIndex]->sampleDescs[0];
|
||||
_audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2);
|
||||
// Seek to the new audio location
|
||||
setAudioStreamPos(_audioStartOffset);
|
||||
|
||||
// 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
|
||||
startAudio();
|
||||
}
|
||||
@ -282,17 +233,15 @@ Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) {
|
||||
}
|
||||
|
||||
void QuickTimeDecoder::startAudio() {
|
||||
if (_audStream) { // No audio/audio not supported
|
||||
if (_audStream) {
|
||||
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() {
|
||||
if (_audStream) {
|
||||
if (_audStream)
|
||||
g_system->getMixer()->stopHandle(_audHandle);
|
||||
_audStream = NULL; // the mixer automatically frees the stream
|
||||
}
|
||||
}
|
||||
|
||||
void QuickTimeDecoder::pauseVideoIntern(bool pause) {
|
||||
@ -550,9 +499,6 @@ void QuickTimeDecoder::close() {
|
||||
_scaledSurface = 0;
|
||||
}
|
||||
|
||||
// The audio stream is deleted automatically
|
||||
_audStream = NULL;
|
||||
|
||||
Common::QuickTimeParser::close();
|
||||
SeekableVideoDecoder::reset();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user