2021-12-26 21:19:38 +01:00

461 lines
14 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Based on xoreos' ASF code which is in turn
// Largely based on the ASF implementation found in FFmpeg.
#include "common/textconsole.h"
#include "common/stream.h"
#include "common/util.h"
#include "audio/audiostream.h"
#include "audio/decoders/asf.h"
#include "audio/decoders/wma.h"
#include "audio/decoders/wave_types.h"
namespace Audio {
class ASFGUID {
public:
ASFGUID(Common::SeekableReadStream &stream) {
stream.read(id, 16);
}
bool operator==(const byte gid[16]) const {
return !memcmp(gid, id, 16);
}
bool operator!=(const byte gid[16]) const {
return memcmp(gid, id, 16);
}
Common::String toString() const {
return Common::String::format("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]);
}
private:
byte id[16];
};
// GUID's that we need
static const byte s_asfHeader[16] = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C };
static const byte s_asfFileHeader[16] = { 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
static const byte s_asfHead1[16] = { 0xb5, 0x03, 0xbf, 0x5f, 0x2E, 0xA9, 0xCF, 0x11, 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 };
static const byte s_asfComment[16] = { 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
static const byte s_asfStreamHeader[16] = { 0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
static const byte s_asfCodecComment[16] = { 0x40, 0x52, 0xD1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 };
static const byte s_asfDataHeader[16] = { 0x36, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
static const byte s_asfAudioStream[16] = { 0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B };
static const byte s_asfExtendedHeader[16] = { 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 };
static const byte s_asfStreamBitRate[16] = { 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2 };
class ASFStream : public SeekableAudioStream {
public:
ASFStream(Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse);
~ASFStream();
int readBuffer(int16 *buffer, const int numSamples) override;
bool endOfData() const override;
bool isStereo() const override { return _channels == 2; }
int getRate() const override { return _sampleRate; }
Timestamp getLength() const override { return Audio::Timestamp(_duration / 10000, _sampleRate); }
bool seek(const Timestamp &where) override;
bool rewind() override;
private:
// Packet data
struct Packet {
Packet();
~Packet();
byte flags;
byte segmentType;
uint16 packetSize;
uint32 sendTime;
uint16 duration;
struct Segment {
byte streamID;
byte sequenceNumber;
bool isKeyframe;
Common::Array<Common::SeekableReadStream *> data;
};
Common::Array<Segment> segments;
};
Common::SeekableReadStream *_stream;
DisposeAfterUse::Flag _disposeAfterUse;
void parseStreamHeader();
void parseFileHeader();
Packet *readPacket();
Codec *createCodec();
AudioStream *createAudioStream();
uint32 _rewindPos;
uint64 _curPacket;
Packet *_lastPacket;
Codec *_codec;
AudioStream *_curAudioStream;
byte _curSequenceNumber;
// Header object variables
uint64 _packetCount;
uint64 _duration;
uint32 _minPacketSize, _maxPacketSize;
// Stream object variables
uint16 _streamID;
uint16 _compression;
uint16 _channels;
int _sampleRate;
uint32 _bitRate;
uint16 _blockAlign;
uint16 _bitsPerCodedSample;
Common::SeekableReadStream *_extraData;
};
ASFStream::Packet::Packet() {
}
ASFStream::Packet::~Packet() {
for (uint32 i = 0; i < segments.size(); i++)
for (uint32 j = 0; j < segments[i].data.size(); j++)
delete segments[i].data[j];
}
ASFStream::ASFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) {
_extraData = nullptr;
_lastPacket = nullptr;
_curPacket = 0;
_codec = nullptr;
_curAudioStream = nullptr;
_curSequenceNumber = 1; // They always start at one
ASFGUID guid = ASFGUID(*_stream);
if (guid != s_asfHeader)
error("ASFStream: Missing asf header");
_stream->readUint64LE();
_stream->readUint32LE();
_stream->readByte();
_stream->readByte();
for (;;) {
uint64 startPos = _stream->pos();
guid = ASFGUID(*_stream);
uint64 size = _stream->readUint64LE();
if (_stream->eos())
error("ASFStream: Unexpected eos");
// TODO: Parse each chunk
if (guid == s_asfFileHeader) {
parseFileHeader();
} else if (guid == s_asfHead1) {
// Should be safe to ignore
} else if (guid == s_asfComment) {
// Ignored
} else if (guid == s_asfStreamHeader) {
parseStreamHeader();
} else if (guid == s_asfCodecComment) {
// TODO
} else if (guid == s_asfDataHeader) {
// Done parsing the header
break;
} else if (guid == s_asfExtendedHeader) {
// TODO
} else if (guid == s_asfStreamBitRate) {
// Ignored
} else
warning("Found unknown ASF GUID: %s", guid.toString().c_str());
_stream->seek(startPos + size);
}
// Skip to the beginning of the packets
_stream->skip(26);
_rewindPos = _stream->pos();
}
ASFStream::~ASFStream() {
if (_disposeAfterUse)
delete _stream;
delete _lastPacket;
delete _curAudioStream;
delete _codec;
}
void ASFStream::parseFileHeader() {
_stream->skip(16); // skip client GUID
/* uint64 fileSize = */ _stream->readUint64LE();
/* uint64 creationTime = */ _stream->readUint64LE();
_packetCount = _stream->readUint64LE();
/* uint64 endTimestamp = */ _stream->readUint64LE();
_duration = _stream->readUint64LE();
/* uint32 startTimestamp = */ _stream->readUint32LE();
/* uint32 unknown = */ _stream->readUint32LE();
/* uint32 flags = */ _stream->readUint32LE();
_minPacketSize = _stream->readUint32LE();
_maxPacketSize = _stream->readUint32LE();
/* uint32 uncFrameSize = */ _stream->readUint32LE();
// We only know how to support packets of one length
if (_minPacketSize != _maxPacketSize)
error("ASFStream::parseFileHeader(): Mismatched packet sizes: Min = %d, Max = %d", _minPacketSize, _maxPacketSize);
}
void ASFStream::parseStreamHeader() {
ASFGUID guid = ASFGUID(*_stream);
if (guid != s_asfAudioStream)
error("ASFStream::parseStreamHeader(): Found non-audio stream");
_stream->skip(16); // skip a guid
_stream->readUint64LE(); // total size
uint32 typeSpecificSize = _stream->readUint32LE();
_stream->readUint32LE();
_streamID = _stream->readUint16LE();
_stream->readUint32LE();
// Parse the wave header
_compression = _stream->readUint16LE();
_channels = _stream->readUint16LE();
_sampleRate = _stream->readUint32LE();
_bitRate = _stream->readUint32LE() * 8;
_blockAlign = _stream->readUint16LE();
_bitsPerCodedSample = (typeSpecificSize == 14) ? 8 : _stream->readUint16LE();
if (typeSpecificSize >= 18) {
uint32 cbSize = _stream->readUint16LE();
cbSize = MIN<int>(cbSize, typeSpecificSize - 18);
_extraData = _stream->readStream(cbSize);
}
_codec = createCodec();
}
bool ASFStream::seek(const Timestamp &where) {
if (where == 0) {
return rewind();
}
// Seeking is not supported
return false;
}
bool ASFStream::rewind() {
// Seek back to the start of the packets
_stream->seek(_rewindPos);
// Reset our packet counter
_curPacket = 0;
delete _lastPacket; _lastPacket = nullptr;
// Delete a stream if we have one
delete _curAudioStream; _curAudioStream = nullptr;
// Reset this too
_curSequenceNumber = 1;
return true;
}
ASFStream::Packet *ASFStream::readPacket() {
if (_curPacket == _packetCount)
error("ASFStream::readPacket(): Reading too many packets");
uint32 packetStartPos = _stream->pos();
// Read a single ASF packet
if (_stream->readByte() != 0x82)
error("ASFStream::readPacket(): Missing packet header");
if (_stream->readUint16LE() != 0)
error("ASFStream::readPacket(): Unknown is not zero");
Packet *packet = new Packet();
packet->flags = _stream->readByte();
packet->segmentType = _stream->readByte();
packet->packetSize = (packet->flags & 0x40) ? _stream->readUint16LE() : 0;
uint16 paddingSize = 0;
if (packet->flags & 0x10)
paddingSize = _stream->readUint16LE();
else if (packet->flags & 0x08)
paddingSize = _stream->readByte();
packet->sendTime = _stream->readUint32LE();
packet->duration = _stream->readUint16LE();
byte segmentCount = (packet->flags & 0x01) ? _stream->readByte() : 1;
packet->segments.resize(segmentCount & 0x3F);
for (uint32 i = 0; i < packet->segments.size(); i++) {
Packet::Segment &segment = packet->segments[i];
segment.streamID = _stream->readByte();
segment.sequenceNumber = _stream->readByte();
segment.isKeyframe = (segment.streamID & 0x80) != 0;
segment.streamID &= 0x7F;
uint32 fragmentOffset = 0;
if (packet->segmentType == 0x55)
fragmentOffset = _stream->readByte();
else if (packet->segmentType == 0x59)
fragmentOffset = _stream->readUint16LE();
else if (packet->segmentType == 0x5D)
fragmentOffset = _stream->readUint32LE();
else
error("ASFStream::readPacket(): Unknown packet segment type 0x%02x", packet->segmentType);
byte flags = _stream->readByte();
if (flags == 1) {
//uint32 objectStartTime = fragmentOffset; // reused purpose
_stream->readByte(); // unknown
uint32 dataLength = (packet->segments.size() == 1) ? (_maxPacketSize - (_stream->pos() - packetStartPos) - paddingSize) : _stream->readUint16LE();
uint32 startObjectPos = _stream->pos();
while ((uint32)_stream->pos() < dataLength + startObjectPos)
segment.data.push_back(_stream->readStream(_stream->readByte()));
} else if (flags == 8) {
/* uint32 objectLength = */ _stream->readUint32LE();
/* uint32 objectStartTime = */ _stream->readUint32LE();
uint32 dataLength = 0;
if (packet->segments.size() == 1)
dataLength = _maxPacketSize - (_stream->pos() - packetStartPos) - fragmentOffset - paddingSize;
else if (segmentCount & 0x40)
dataLength = _stream->readByte();
else
dataLength = _stream->readUint16LE();
_stream->skip(fragmentOffset);
segment.data.push_back(_stream->readStream(dataLength));
} else
error("ASFStream::readPacket(): Unknown packet flags 0x%02x", flags);
}
// Skip any padding
_stream->skip(paddingSize);
// We just read a packet
_curPacket++;
if ((uint32)_stream->pos() != packetStartPos + _maxPacketSize)
error("ASFStream::readPacket(): Mismatching packet pos: %d (should be %d)", (int)_stream->pos(), _maxPacketSize + packetStartPos);
return packet;
}
Codec *ASFStream::createCodec() {
switch (_compression) {
case kWaveFormatWMAv2:
return new WMACodec(2, _sampleRate, _channels, _bitRate, _blockAlign, _extraData);
default:
error("ASFStream::createAudioStream(): Unknown compression 0x%04x", _compression);
}
return nullptr;
}
AudioStream *ASFStream::createAudioStream() {
delete _lastPacket;
_lastPacket = readPacket();
// TODO
if (_lastPacket->segments.size() != 1)
error("ASFStream::createAudioStream(): Only single segment packets supported");
Packet::Segment &segment = _lastPacket->segments[0];
// We should only have one stream in a ASF audio file
if (segment.streamID != _streamID)
error("ASFStream::createAudioStream(): Packet stream ID mismatch");
// TODO
if (segment.sequenceNumber != _curSequenceNumber)
error("ASFStream::createAudioStream(): Only one sequence number per packet supported");
// This can overflow and needs to overflow!
_curSequenceNumber++;
// TODO
if (segment.data.size() != 1)
error("ASFStream::createAudioStream(): Packet grouping not supported");
Common::SeekableReadStream *stream = segment.data[0];
if (_codec)
return _codec->decodeFrame(*stream);
return nullptr;
}
int ASFStream::readBuffer(int16 *buffer, const int numSamples) {
int samplesDecoded = 0;
for (;;) {
if (_curAudioStream) {
samplesDecoded += _curAudioStream->readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded);
if (_curAudioStream->endOfData()) {
delete _curAudioStream;
_curAudioStream = nullptr;
}
}
if (samplesDecoded == numSamples || endOfData())
break;
if (!_curAudioStream) {
_curAudioStream = createAudioStream();
}
}
return samplesDecoded;
}
bool ASFStream::endOfData() const {
return _curPacket == _packetCount && !_curAudioStream;
}
SeekableAudioStream *makeASFStream(
Common::SeekableReadStream *stream,
DisposeAfterUse::Flag disposeAfterUse) {
SeekableAudioStream *s = new ASFStream(stream, disposeAfterUse);
if (s && s->endOfData()) {
delete s;
return nullptr;
}
return s;
}
} // End of namespace Audio