mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-19 16:33:50 +00:00
VIDEO: Implement multiple video codec support for QuickTime
svn-id: r54840
This commit is contained in:
parent
cf86141ca3
commit
680b0d3172
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user