/* 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 . * */ // 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 data; }; Common::Array 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(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