VIDEO: Implement multiple video codec support for QuickTime

svn-id: r54840
This commit is contained in:
Matthew Hoops 2010-12-09 19:59:26 +00:00
parent cf86141ca3
commit 680b0d3172
2 changed files with 169 additions and 156 deletions

View File

@ -61,7 +61,6 @@ namespace Graphics {
QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() { QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() {
_audStream = NULL; _audStream = NULL;
_beginOffset = 0; _beginOffset = 0;
_videoCodec = NULL;
_curFrame = -1; _curFrame = -1;
_startTime = _nextFrameStartTime = 0; _startTime = _nextFrameStartTime = 0;
_audHandle = Audio::SoundHandle(); _audHandle = Audio::SoundHandle();
@ -72,6 +71,7 @@ QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() {
_scaleFactorY = 1; _scaleFactorY = 1;
_dirtyPalette = false; _dirtyPalette = false;
_resFork = new Common::MacResManager(); _resFork = new Common::MacResManager();
_palette = 0;
initParseTable(); initParseTable();
} }
@ -102,20 +102,6 @@ uint32 QuickTimeDecoder::getFrameCount() const {
return _streams[_videoStreamIndex]->nb_frames; return _streams[_videoStreamIndex]->nb_frames;
} }
byte QuickTimeDecoder::getBitsPerPixel() {
if (_videoStreamIndex < 0)
return 0;
return _streams[_videoStreamIndex]->bits_per_sample & 0x1F;
}
uint32 QuickTimeDecoder::getCodecTag() {
if (_videoStreamIndex < 0)
return 0;
return _streams[_videoStreamIndex]->codec_tag;
}
Common::Rational QuickTimeDecoder::getScaleFactorX() const { Common::Rational QuickTimeDecoder::getScaleFactorX() const {
if (_videoStreamIndex < 0) if (_videoStreamIndex < 0)
return 1; return 1;
@ -149,10 +135,12 @@ uint32 QuickTimeDecoder::getFrameDuration() {
} }
PixelFormat QuickTimeDecoder::getPixelFormat() const { PixelFormat QuickTimeDecoder::getPixelFormat() const {
if (!_videoCodec) Codec *codec = findDefaultVideoCodec();
if (!codec)
return PixelFormat::createFormatCLUT8(); return PixelFormat::createFormatCLUT8();
return _videoCodec->getPixelFormat(); return codec->getPixelFormat();
} }
void QuickTimeDecoder::rewind() { void QuickTimeDecoder::rewind() {
@ -163,8 +151,10 @@ void QuickTimeDecoder::rewind() {
stopAudio(); stopAudio();
if (_audioStreamIndex >= 0) { if (_audioStreamIndex >= 0) {
_curAudioChunk = 0; _curAudioChunk = 0;
_audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
_audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2);
} }
startAudio(); startAudio();
} }
@ -219,9 +209,16 @@ void QuickTimeDecoder::pauseVideoIntern(bool pause) {
g_system->getMixer()->pauseHandle(_audHandle, pause); g_system->getMixer()->pauseHandle(_audHandle, pause);
} }
Codec *QuickTimeDecoder::findDefaultVideoCodec() const {
if (_videoStreamIndex < 0 || !_streams[_videoStreamIndex]->stsdEntryCount)
return 0;
return _streams[_videoStreamIndex]->stsdEntries[0].videoCodec;
}
Surface *QuickTimeDecoder::decodeNextFrame() { Surface *QuickTimeDecoder::decodeNextFrame() {
if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1) if (_videoStreamIndex < 0 || _curFrame >= (int32)getFrameCount() - 1)
return NULL; return 0;
if (_startTime == 0) if (_startTime == 0)
_startTime = g_system->getMillis(); _startTime = g_system->getMillis();
@ -229,15 +226,31 @@ Surface *QuickTimeDecoder::decodeNextFrame() {
_curFrame++; _curFrame++;
_nextFrameStartTime += getFrameDuration(); _nextFrameStartTime += getFrameDuration();
Common::SeekableReadStream *frameData = getNextFramePacket(); // Get the next packet
uint32 descId;
Common::SeekableReadStream *frameData = getNextFramePacket(descId);
if (frameData) { if (!frameData || !descId || descId > _streams[_videoStreamIndex]->stsdEntryCount)
Surface *frame = _videoCodec->decodeImage(frameData); return 0;
delete frameData;
return scaleSurface(frame); // Find which video description entry we want
STSDEntry *entry = &_streams[_videoStreamIndex]->stsdEntries[descId - 1];
if (!entry->videoCodec)
return 0;
Surface *frame = entry->videoCodec->decodeImage(frameData);
delete frameData;
// Update the palette in case it changes between video descriptions
byte *palette = entry->palette;
if (palette != _palette) {
_palette = palette;
_dirtyPalette = true;
} }
return NULL; return scaleSurface(frame);
} }
Surface *QuickTimeDecoder::scaleSurface(Surface *frame) { Surface *QuickTimeDecoder::scaleSurface(Surface *frame) {
@ -254,7 +267,7 @@ Surface *QuickTimeDecoder::scaleSurface(Surface *frame) {
} }
bool QuickTimeDecoder::endOfVideo() const { bool QuickTimeDecoder::endOfVideo() const {
return (!_audStream || _audStream->endOfData()) && (!_videoCodec || VideoDecoder::endOfVideo()); return (!_audStream || _audStream->endOfData()) && (!findDefaultVideoCodec() || VideoDecoder::endOfVideo());
} }
uint32 QuickTimeDecoder::getElapsedTime() const { uint32 QuickTimeDecoder::getElapsedTime() const {
@ -339,7 +352,7 @@ bool QuickTimeDecoder::load(Common::SeekableReadStream *stream) {
void QuickTimeDecoder::init() { void QuickTimeDecoder::init() {
// some cleanup : make sure we are on the mdat atom // some cleanup : make sure we are on the mdat atom
if((uint32)_fd->pos() != _mdatOffset) if ((uint32)_fd->pos() != _mdatOffset)
_fd->seek(_mdatOffset, SEEK_SET); _fd->seek(_mdatOffset, SEEK_SET);
for (uint32 i = 0; i < _numStreams;) { for (uint32 i = 0; i < _numStreams;) {
@ -363,28 +376,32 @@ void QuickTimeDecoder::init() {
sc->duration /= sc->time_rate; sc->duration /= sc->time_rate;
sc->ffindex = i;
sc->is_ff_stream = 1;
if (sc->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0) if (sc->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0)
_videoStreamIndex = i; _videoStreamIndex = i;
else if (sc->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) else if (sc->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0)
_audioStreamIndex = i; _audioStreamIndex = i;
} }
if (_audioStreamIndex >= 0 && checkAudioCodecSupport(_streams[_audioStreamIndex]->codec_tag)) { if (_audioStreamIndex >= 0) {
_audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
_curAudioChunk = 0;
// Make sure the bits per sample transfers to the sample size if (checkAudioCodecSupport(entry->codecTag)) {
if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('twos')) _audStream = Audio::makeQueuingAudioStream(entry->sampleRate, entry->channels == 2);
_streams[_audioStreamIndex]->sample_size = (_streams[_audioStreamIndex]->bits_per_sample / 8) * _streams[_audioStreamIndex]->channels; _curAudioChunk = 0;
startAudio(); // 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();
}
} }
if (_videoStreamIndex >= 0) { if (_videoStreamIndex >= 0) {
_videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); for (uint32 i = 0; i < _streams[_videoStreamIndex]->stsdEntryCount; i++) {
STSDEntry *entry = &_streams[_videoStreamIndex]->stsdEntries[i];
entry->videoCodec = createCodec(entry->codecTag, entry->bitsPerSample & 0x1F);
}
if (getScaleFactorX() != 1 || getScaleFactorY() != 1) { if (getScaleFactorX() != 1 || getScaleFactorY() != 1) {
// We have to initialize the scaled surface // We have to initialize the scaled surface
@ -799,9 +816,12 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
_fd->readByte(); // version _fd->readByte(); // version
_fd->readByte(); _fd->readByte(); _fd->readByte(); // flags _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
uint32 entries = _fd->readUint32BE(); st->stsdEntryCount = _fd->readUint32BE();
st->stsdEntries = new STSDEntry[st->stsdEntryCount];
for (uint32 i = 0; i < st->stsdEntryCount; i++) { // Parsing Sample description table
STSDEntry *entry = &st->stsdEntries[i];
while (entries--) { //Parsing Sample description table
MOVatom a = { 0, 0, 0 }; MOVatom a = { 0, 0, 0 };
uint32 start_pos = _fd->pos(); uint32 start_pos = _fd->pos();
int size = _fd->readUint32BE(); // size int size = _fd->readUint32BE(); // size
@ -813,17 +833,7 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type); debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type);
if (st->codec_tag && st->codec_tag != format) { entry->codecTag = format;
// HACK: Multiple FourCC, skip this. FFmpeg does this too and also
// skips it with a TODO. However, we really don't need to support
// multiple codec tags since the only two videos in Riven DVD that
// do this just have a fake second stream (or so it seems).
debug(3, "Multiple FourCC not supported");
_fd->seek(start_pos + size);
continue;
}
st->codec_tag = format;
if (st->codec_type == CODEC_TYPE_VIDEO) { if (st->codec_type == CODEC_TYPE_VIDEO) {
debug(0, "Video Codec FourCC: \'%s\'", tag2str(format)); debug(0, "Video Codec FourCC: \'%s\'", tag2str(format));
@ -834,41 +844,40 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
_fd->readUint32BE(); // temporal quality _fd->readUint32BE(); // temporal quality
_fd->readUint32BE(); // spacial quality _fd->readUint32BE(); // spacial quality
st->width = _fd->readUint16BE(); // width uint16 width = _fd->readUint16BE(); // width
st->height = _fd->readUint16BE(); // height uint16 height = _fd->readUint16BE(); // height
// The width is most likely invalid for entries after the first one
// so only set the overall width if it is not zero here.
if (width)
st->width = width;
if (height)
st->height = height;
_fd->readUint32BE(); // horiz resolution _fd->readUint32BE(); // horiz resolution
_fd->readUint32BE(); // vert resolution _fd->readUint32BE(); // vert resolution
_fd->readUint32BE(); // data size, always 0 _fd->readUint32BE(); // data size, always 0
uint16 frames_per_sample = _fd->readUint16BE(); // frames per samples _fd->readUint16BE(); // frames per samples
debug(0, "frames/samples = %d", frames_per_sample);
byte codec_name[32]; byte codec_name[32];
_fd->read(codec_name, 32); // codec name, pascal string (FIXME: true for mp4?) _fd->read(codec_name, 32); // codec name, pascal string (FIXME: true for mp4?)
if (codec_name[0] <= 31) { if (codec_name[0] <= 31) {
memcpy(st->codec_name, &codec_name[1], codec_name[0]); memcpy(entry->codecName, &codec_name[1], codec_name[0]);
st->codec_name[codec_name[0]] = 0; entry->codecName[codec_name[0]] = 0;
} }
st->bits_per_sample = _fd->readUint16BE(); // depth entry->bitsPerSample = _fd->readUint16BE(); // depth
st->color_table_id = _fd->readUint16BE(); // colortable id entry->colorTableId = _fd->readUint16BE(); // colortable id
// These are set in mov_read_stts and might already be set!
// st->codec->time_base.den = 25;
// st->codec->time_base.num = 1;
// figure out the palette situation // figure out the palette situation
byte colorDepth = st->bits_per_sample & 0x1F; byte colorDepth = entry->bitsPerSample & 0x1F;
bool colorGreyscale = (st->bits_per_sample & 0x20) != 0; bool colorGreyscale = (entry->bitsPerSample & 0x20) != 0;
debug(0, "color depth: %d", colorDepth); debug(0, "color depth: %d", colorDepth);
// if the depth is 2, 4, or 8 bpp, file is palettized // if the depth is 2, 4, or 8 bpp, file is palettized
if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) { if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) {
_dirtyPalette = true;
if (colorGreyscale) { if (colorGreyscale) {
debug(0, "Greyscale palette"); debug(0, "Greyscale palette");
@ -877,35 +886,16 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
int16 colorIndex = 255; int16 colorIndex = 255;
byte colorDec = 256 / (colorCount - 1); byte colorDec = 256 / (colorCount - 1);
for (byte j = 0; j < colorCount; j++) { for (byte j = 0; j < colorCount; j++) {
_palette[j * 3] = _palette[j * 3 + 1] = _palette[j * 3 + 2] = colorIndex; entry->palette[j * 3] = entry->palette[j * 3 + 1] = entry->palette[j * 3 + 2] = colorIndex;
colorIndex -= colorDec; colorIndex -= colorDec;
if (colorIndex < 0) if (colorIndex < 0)
colorIndex = 0; colorIndex = 0;
} }
} else if (st->color_table_id & 0x08) { } else if (entry->colorTableId & 0x08) {
// if flag bit 3 is set, use the default palette // if flag bit 3 is set, use the default palette
//uint16 colorCount = 1 << colorDepth; //uint16 colorCount = 1 << colorDepth;
warning("Predefined palette! %dbpp", colorDepth); warning("Predefined palette! %dbpp", colorDepth);
#if 0
byte *color_table;
byte r, g, b;
if (colorDepth == 2)
color_table = ff_qt_default_palette_4;
else if (colorDepth == 4)
color_table = ff_qt_default_palette_16;
else
color_table = ff_qt_default_palette_256;
for (byte j = 0; j < color_count; j++) {
r = color_table[j * 4 + 0];
g = color_table[j * 4 + 1];
b = color_table[j * 4 + 2];
_palette_control.palette[j] = (r << 16) | (g << 8) | (b);
}
#endif
} else { } else {
debug(0, "Palette from file"); debug(0, "Palette from file");
@ -919,57 +909,56 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
// up front // up front
_fd->readByte(); _fd->readByte();
_fd->readByte(); _fd->readByte();
_palette[j * 3] = _fd->readByte(); entry->palette[j * 3] = _fd->readByte();
_fd->readByte(); _fd->readByte();
_palette[j * 3 + 1] = _fd->readByte(); entry->palette[j * 3 + 1] = _fd->readByte();
_fd->readByte(); _fd->readByte();
_palette[j * 3 + 2] = _fd->readByte(); entry->palette[j * 3 + 2] = _fd->readByte();
_fd->readByte(); _fd->readByte();
} }
} }
st->palettized = true; }
} else
st->palettized = false;
} else if (st->codec_type == CODEC_TYPE_AUDIO) { } else if (st->codec_type == CODEC_TYPE_AUDIO) {
debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format));
st->stsd_version = _fd->readUint16BE(); uint16 stsdVersion = _fd->readUint16BE();
_fd->readUint16BE(); // revision level _fd->readUint16BE(); // revision level
_fd->readUint32BE(); // vendor _fd->readUint32BE(); // vendor
st->channels = _fd->readUint16BE(); // channel count entry->channels = _fd->readUint16BE(); // channel count
st->bits_per_sample = _fd->readUint16BE(); // sample size entry->bitsPerSample = _fd->readUint16BE(); // sample size
// do we need to force to 16 for AMR ?
// handle specific s8 codec
_fd->readUint16BE(); // compression id = 0 _fd->readUint16BE(); // compression id = 0
_fd->readUint16BE(); // packet size = 0 _fd->readUint16BE(); // packet size = 0
st->sample_rate = (_fd->readUint32BE() >> 16); entry->sampleRate = (_fd->readUint32BE() >> 16);
debug(0, "stsd version =%d", st->stsd_version); debug(0, "stsd version =%d", stsdVersion);
if (st->stsd_version == 0) { if (stsdVersion == 0) {
// Not used, except in special cases. See below. // Not used, except in special cases. See below.
st->samples_per_frame = st->bytes_per_frame = 0; entry->samplesPerFrame = entry->bytesPerFrame = 0;
} else if (st->stsd_version == 1) { } else if (stsdVersion == 1) {
// Read QT version 1 fields. In version 0 these dont exist. // Read QT version 1 fields. In version 0 these dont exist.
st->samples_per_frame = _fd->readUint32BE(); entry->samplesPerFrame = _fd->readUint32BE();
debug(0, "stsd samples_per_frame =%d", st->samples_per_frame); debug(0, "stsd samples_per_frame =%d",entry->samplesPerFrame);
_fd->readUint32BE(); // bytes per packet _fd->readUint32BE(); // bytes per packet
st->bytes_per_frame = _fd->readUint32BE(); entry->bytesPerFrame = _fd->readUint32BE();
debug(0, "stsd bytes_per_frame =%d", st->bytes_per_frame); debug(0, "stsd bytes_per_frame =%d", entry->bytesPerFrame);
_fd->readUint32BE(); // bytes per sample _fd->readUint32BE(); // bytes per sample
} else { } else {
warning("Unsupported QuickTime STSD audio version %d", st->stsd_version); warning("Unsupported QuickTime STSD audio version %d", stsdVersion);
return 1; return 1;
} }
// Version 0 videos (such as the Riven ones) don't have this set, // Version 0 videos (such as the Riven ones) don't have this set,
// but we need it later on. Add it in here. // but we need it later on. Add it in here.
if (format == MKID_BE('ima4')) { if (format == MKID_BE('ima4')) {
st->samples_per_frame = 64; entry->samplesPerFrame = 64;
st->bytes_per_frame = 34 * st->channels; entry->bytesPerFrame = 34 * entry->channels;
} }
if (entry->sampleRate == 0 && st->time_scale > 1)
entry->sampleRate = st->time_scale;
} else { } else {
// other codec type, just skip (rtp, mp4s, tmcd ...) // other codec type, just skip (rtp, mp4s, tmcd ...)
_fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR); _fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR);
@ -983,9 +972,6 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
_fd->seek(a.size, SEEK_CUR); _fd->seek(a.size, SEEK_CUR);
} }
if (st->codec_type == CODEC_TYPE_AUDIO && st->sample_rate == 0 && st->time_scale > 1)
st->sample_rate= st->time_scale;
return 0; return 0;
} }
@ -1152,7 +1138,7 @@ int QuickTimeDecoder::readWAVE(MOVatom atom) {
if (atom.size > (1 << 30)) if (atom.size > (1 << 30))
return -1; return -1;
if (st->codec_tag == MKID_BE('QDM2')) // Read extradata for QDM2 if (st->stsdEntries[0].codecTag == MKID_BE('QDM2')) // Read extradata for QDM2
st->extradata = _fd->readStream(atom.size - 8); st->extradata = _fd->readStream(atom.size - 8);
else if (atom.size > 8) else if (atom.size > 8)
return readDefault(atom); return readDefault(atom);
@ -1165,8 +1151,6 @@ int QuickTimeDecoder::readWAVE(MOVatom atom) {
void QuickTimeDecoder::close() { void QuickTimeDecoder::close() {
stopAudio(); stopAudio();
delete _videoCodec; _videoCodec = 0;
for (uint32 i = 0; i < _numStreams; i++) for (uint32 i = 0; i < _numStreams; i++)
delete _streams[i]; delete _streams[i];
@ -1185,7 +1169,7 @@ void QuickTimeDecoder::close() {
VideoDecoder::reset(); VideoDecoder::reset();
} }
Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket() { Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket(uint32 &descId) {
if (_videoStreamIndex < 0) if (_videoStreamIndex < 0)
return NULL; return NULL;
@ -1208,6 +1192,7 @@ Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket() {
if (totalSampleCount > getCurFrame()) { if (totalSampleCount > getCurFrame()) {
actualChunk = i; actualChunk = i;
descId = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].id;
sampleInChunk = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count - totalSampleCount + getCurFrame(); sampleInChunk = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count - totalSampleCount + getCurFrame();
break; break;
} }
@ -1257,25 +1242,27 @@ Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStre
if (!stream || _audioStreamIndex < 0) if (!stream || _audioStreamIndex < 0)
return NULL; return NULL;
if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('twos') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) { STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
if (entry->codecTag == MKID_BE('twos') || entry->codecTag == MKID_BE('raw ')) {
// Fortunately, most of the audio used in Myst videos is raw... // Fortunately, most of the audio used in Myst videos is raw...
uint16 flags = 0; uint16 flags = 0;
if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) if (entry->codecTag == MKID_BE('raw '))
flags |= Audio::FLAG_UNSIGNED; flags |= Audio::FLAG_UNSIGNED;
if (_streams[_audioStreamIndex]->channels == 2) if (entry->channels == 2)
flags |= Audio::FLAG_STEREO; flags |= Audio::FLAG_STEREO;
if (_streams[_audioStreamIndex]->bits_per_sample == 16) if (entry->bitsPerSample == 16)
flags |= Audio::FLAG_16BITS; flags |= Audio::FLAG_16BITS;
uint32 dataSize = stream->size(); uint32 dataSize = stream->size();
byte *data = (byte *)malloc(dataSize); byte *data = (byte *)malloc(dataSize);
stream->read(data, dataSize); stream->read(data, dataSize);
delete stream; delete stream;
return Audio::makeRawStream(data, dataSize, _streams[_audioStreamIndex]->sample_rate, flags); return Audio::makeRawStream(data, dataSize, entry->sampleRate, flags);
} else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('ima4')) { } else if (entry->codecTag == MKID_BE('ima4')) {
// Riven uses this codec (as do some Myst ME videos) // Riven uses this codec (as do some Myst ME videos)
return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, _streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels, 34); return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, entry->sampleRate, entry->channels, 34);
#ifdef GRAPHICS_QDM2_H #ifdef GRAPHICS_QDM2_H
} else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('QDM2')) { } else if (entry->codecTag == MKID_BE('QDM2')) {
// Several Myst ME videos use this codec // Several Myst ME videos use this codec
return makeQDM2Stream(stream, _streams[_audioStreamIndex]->extradata); return makeQDM2Stream(stream, _streams[_audioStreamIndex]->extradata);
#endif #endif
@ -1290,6 +1277,8 @@ void QuickTimeDecoder::updateAudioBuffer() {
if (!_audStream) if (!_audStream)
return; return;
STSDEntry *entry = &_streams[_audioStreamIndex]->stsdEntries[0];
// Keep three streams in buffer so that if/when the first two end, it goes right into the next // Keep three streams in buffer so that if/when the first two end, it goes right into the next
for (; _audStream->numQueuedStreams() < 3 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) { for (; _audStream->numQueuedStreams() < 3 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) {
Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic();
@ -1307,12 +1296,12 @@ void QuickTimeDecoder::updateAudioBuffer() {
while (sampleCount > 0) { while (sampleCount > 0) {
uint32 samples = 0, size = 0; uint32 samples = 0, size = 0;
if (_streams[_audioStreamIndex]->samples_per_frame >= 160) { if (entry->samplesPerFrame >= 160) {
samples = _streams[_audioStreamIndex]->samples_per_frame; samples = entry->samplesPerFrame;
size = _streams[_audioStreamIndex]->bytes_per_frame; size = entry->bytesPerFrame;
} else if (_streams[_audioStreamIndex]->samples_per_frame > 1) { } else if (entry->samplesPerFrame > 1) {
samples = MIN<uint32>((1024 / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->samples_per_frame, sampleCount); samples = MIN<uint32>((1024 / entry->samplesPerFrame) * entry->samplesPerFrame, sampleCount);
size = (samples / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->bytes_per_frame; size = (samples / entry->samplesPerFrame) * entry->bytesPerFrame;
} else { } else {
samples = MIN<uint32>(1024, sampleCount); samples = MIN<uint32>(1024, sampleCount);
size = samples * _streams[_audioStreamIndex]->sample_size; size = samples * _streams[_audioStreamIndex]->sample_size;
@ -1332,6 +1321,23 @@ void QuickTimeDecoder::updateAudioBuffer() {
} }
} }
QuickTimeDecoder::STSDEntry::STSDEntry() {
codecTag = 0;
bitsPerSample = 0;
memset(codecName, 0, 32);
colorTableId = 0;
memset(palette, 0, 256 * 3);
videoCodec = 0;
channels = 0;
sampleRate = 0;
samplesPerFrame = 0;
bytesPerFrame = 0;
}
QuickTimeDecoder::STSDEntry::~STSDEntry() {
delete videoCodec;
}
QuickTimeDecoder::MOVStreamContext::MOVStreamContext() { QuickTimeDecoder::MOVStreamContext::MOVStreamContext() {
// FIXME: Setting all members to 0 via memset is a hack -- it works // FIXME: Setting all members to 0 via memset is a hack -- it works
// because the only non-POD member of MOVStreamContext is of type // because the only non-POD member of MOVStreamContext is of type
@ -1348,6 +1354,7 @@ QuickTimeDecoder::MOVStreamContext::~MOVStreamContext() {
delete[] sample_to_chunk; delete[] sample_to_chunk;
delete[] sample_sizes; delete[] sample_sizes;
delete[] keyframes; delete[] keyframes;
delete[] stsdEntries;
delete extradata; delete extradata;
} }

View File

@ -148,6 +148,26 @@ protected:
uint32 id; uint32 id;
}; };
struct STSDEntry {
STSDEntry();
~STSDEntry();
uint32 codecTag;
uint16 bitsPerSample;
// Video
char codecName[32];
uint16 colorTableId;
byte palette[256 * 3];
Codec *videoCodec;
// Audio
uint16 channels;
uint32 sampleRate;
uint32 samplesPerFrame;
uint32 bytesPerFrame;
};
enum CodecType { enum CodecType {
CODEC_TYPE_MOV_OTHER, CODEC_TYPE_MOV_OTHER,
CODEC_TYPE_VIDEO, CODEC_TYPE_VIDEO,
@ -158,9 +178,6 @@ protected:
MOVStreamContext(); MOVStreamContext();
~MOVStreamContext(); ~MOVStreamContext();
int ffindex; /* the ffmpeg stream id */
int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */
uint32 next_chunk;
uint32 chunk_count; uint32 chunk_count;
uint32 *chunk_offsets; uint32 *chunk_offsets;
int stts_count; int stts_count;
@ -183,24 +200,15 @@ protected:
uint32 *keyframes; uint32 *keyframes;
int32 time_scale; int32 time_scale;
int time_rate; int time_rate;
uint32 current_sample;
uint32 left_in_chunk; /* how many samples before next chunk */
uint16 width; uint16 width;
uint16 height; uint16 height;
int codec_type; int codec_type;
uint32 codec_tag;
char codec_name[32];
uint16 bits_per_sample;
uint16 color_table_id;
bool palettized;
Common::SeekableReadStream *extradata;
uint16 stsd_version; uint32 stsdEntryCount;
uint16 channels; STSDEntry *stsdEntries;
uint16 sample_rate;
uint32 samples_per_frame; Common::SeekableReadStream *extradata;
uint32 bytes_per_frame;
uint32 nb_frames; uint32 nb_frames;
uint32 duration; uint32 duration;
@ -222,7 +230,7 @@ protected:
Common::Rational _scaleFactorX; Common::Rational _scaleFactorX;
Common::Rational _scaleFactorY; Common::Rational _scaleFactorY;
MOVStreamContext *_streams[20]; MOVStreamContext *_streams[20];
byte _palette[256 * 3]; byte *_palette;
bool _dirtyPalette; bool _dirtyPalette;
uint32 _beginOffset; uint32 _beginOffset;
Common::MacResManager *_resFork; Common::MacResManager *_resFork;
@ -230,10 +238,8 @@ protected:
void initParseTable(); void initParseTable();
Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream);
bool checkAudioCodecSupport(uint32 tag); bool checkAudioCodecSupport(uint32 tag);
Common::SeekableReadStream *getNextFramePacket(); Common::SeekableReadStream *getNextFramePacket(uint32 &descId);
uint32 getFrameDuration(); uint32 getFrameDuration();
uint32 getCodecTag();
byte getBitsPerPixel();
void init(); void init();
Audio::QueuingAudioStream *_audStream; Audio::QueuingAudioStream *_audStream;
@ -244,7 +250,7 @@ protected:
Audio::SoundHandle _audHandle; Audio::SoundHandle _audHandle;
Codec *createCodec(uint32 codecTag, byte bitsPerPixel); Codec *createCodec(uint32 codecTag, byte bitsPerPixel);
Codec *_videoCodec; Codec *findDefaultVideoCodec() const;
uint32 _nextFrameStartTime; uint32 _nextFrameStartTime;
int8 _videoStreamIndex; int8 _videoStreamIndex;