FlacFrameParser.cpp (7529B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FlacFrameParser.h" 8 9 #include "BufferReader.h" 10 #include "OggCodecState.h" 11 #include "OpusParser.h" 12 #include "VideoUtils.h" 13 #include "mozilla/Try.h" 14 #include "nsTArray.h" 15 16 namespace mozilla { 17 18 #define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F 19 #define FLAC_STREAMINFO_SIZE 34 20 21 #define BITMASK(x) ((1ULL << x) - 1) 22 23 enum { 24 FLAC_METADATA_TYPE_STREAMINFO = 0, 25 FLAC_METADATA_TYPE_PADDING, 26 FLAC_METADATA_TYPE_APPLICATION, 27 FLAC_METADATA_TYPE_SEEKTABLE, 28 FLAC_METADATA_TYPE_VORBIS_COMMENT, 29 FLAC_METADATA_TYPE_CUESHEET, 30 FLAC_METADATA_TYPE_PICTURE, 31 FLAC_METADATA_TYPE_INVALID = 127 32 }; 33 34 FlacFrameParser::FlacFrameParser() 35 : mMinBlockSize(0), 36 mMaxBlockSize(0), 37 mMinFrameSize(0), 38 mMaxFrameSize(0), 39 mNumFrames(0), 40 mFullMetadata(false), 41 mPacketCount(0) {}; 42 43 FlacFrameParser::~FlacFrameParser() = default; 44 45 uint32_t FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket) const { 46 uint32_t extra = 4; 47 if (aPacket[0] == 'f') { 48 // This must be the first block read, which contains the fLaC signature. 49 aPacket += 4; 50 extra += 4; 51 } 52 return (BigEndian::readUint32(aPacket) & BITMASK(24)) + extra; 53 } 54 55 Result<Ok, nsresult> FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket, 56 size_t aLength) { 57 if (aLength < 4 || aPacket[0] == 0xff) { 58 // Not a header block. 59 return Err(NS_ERROR_FAILURE); 60 } 61 BufferReader br(aPacket, aLength); 62 63 mPacketCount++; 64 65 if (aPacket[0] == 'f') { 66 if (mPacketCount != 1 || memcmp(br.Read(4), "fLaC", 4) || 67 br.Remaining() != FLAC_STREAMINFO_SIZE + 4) { 68 return Err(NS_ERROR_FAILURE); 69 } 70 } 71 uint8_t blockHeader = MOZ_TRY(br.ReadU8()); 72 // blockType is a misnomer as it could indicate here either a packet type 73 // should it points to the start of a Flac in Ogg metadata, or an actual 74 // block type as per the flac specification. 75 uint32_t blockType = blockHeader & 0x7f; 76 bool lastBlock = blockHeader & 0x80; 77 78 if (blockType == OGG_FLAC_METADATA_TYPE_STREAMINFO) { 79 if (mPacketCount != 1 || memcmp(br.Read(4), "FLAC", 4) || 80 br.Remaining() != FLAC_STREAMINFO_SIZE + 12) { 81 return Err(NS_ERROR_FAILURE); 82 } 83 uint32_t major = MOZ_TRY(br.ReadU8()); 84 if (major != 1) { 85 // unsupported version; 86 return Err(NS_ERROR_FAILURE); 87 } 88 MOZ_TRY(br.ReadU8()); // minor version 89 uint32_t header = MOZ_TRY(br.ReadU16()); 90 mNumHeaders = Some(header); 91 br.Read(4); // fLaC 92 blockType = MOZ_TRY(br.ReadU8()); 93 blockType &= BITMASK(7); 94 // First METADATA_BLOCK_STREAMINFO 95 if (blockType != FLAC_METADATA_TYPE_STREAMINFO) { 96 // First block must be a stream info. 97 return Err(NS_ERROR_FAILURE); 98 } 99 } 100 101 uint32_t blockDataSize = MOZ_TRY(br.ReadU24()); 102 const uint8_t* blockDataStart = br.Peek(blockDataSize); 103 if (!blockDataStart) { 104 // Incomplete block. 105 return Err(NS_ERROR_FAILURE); 106 } 107 108 switch (blockType) { 109 case FLAC_METADATA_TYPE_STREAMINFO: { 110 if (mPacketCount != 1 || blockDataSize != FLAC_STREAMINFO_SIZE) { 111 // STREAMINFO must be the first metadata block found, and its size 112 // is constant. 113 return Err(NS_ERROR_FAILURE); 114 } 115 116 mMinBlockSize = MOZ_TRY(br.ReadU16()); 117 mMaxBlockSize = MOZ_TRY(br.ReadU16()); 118 mMinFrameSize = MOZ_TRY(br.ReadU24()); 119 mMaxFrameSize = MOZ_TRY(br.ReadU24()); 120 121 uint64_t blob = MOZ_TRY(br.ReadU64()); 122 uint32_t sampleRate = (blob >> 44) & BITMASK(20); 123 if (!sampleRate) { 124 return Err(NS_ERROR_FAILURE); 125 } 126 uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1; 127 if (numChannels > FLAC_MAX_CHANNELS) { 128 return Err(NS_ERROR_FAILURE); 129 } 130 uint32_t bps = ((blob >> 36) & BITMASK(5)) + 1; 131 if (bps > 24) { 132 return Err(NS_ERROR_FAILURE); 133 } 134 mNumFrames = blob & BITMASK(36); 135 136 mInfo.mMimeType = "audio/flac"; 137 mInfo.mRate = sampleRate; 138 mInfo.mChannels = numChannels; 139 mInfo.mBitDepth = bps; 140 FlacCodecSpecificData flacCodecSpecificData; 141 flacCodecSpecificData.mStreamInfoBinaryBlob->AppendElements( 142 blockDataStart, blockDataSize); 143 mInfo.mCodecSpecificConfig = 144 AudioCodecSpecificVariant{std::move(flacCodecSpecificData)}; 145 auto duration = media::TimeUnit(mNumFrames, sampleRate); 146 mInfo.mDuration = duration.IsValid() ? duration : media::TimeUnit::Zero(); 147 mParser = MakeUnique<OpusParser>(); 148 break; 149 } 150 case FLAC_METADATA_TYPE_VORBIS_COMMENT: { 151 if (!mParser) { 152 // We must have seen a valid streaminfo first. 153 return Err(NS_ERROR_FAILURE); 154 } 155 nsTArray<uint8_t> comments(blockDataSize + 8); 156 comments.AppendElements("OpusTags", 8); 157 comments.AppendElements(blockDataStart, blockDataSize); 158 if (!mParser->DecodeTags(comments.Elements(), comments.Length())) { 159 return Err(NS_ERROR_FAILURE); 160 } 161 break; 162 } 163 default: 164 break; 165 } 166 167 if (mNumHeaders && mPacketCount > mNumHeaders.ref() + 1) { 168 // Received too many header block. assuming invalid. 169 return Err(NS_ERROR_FAILURE); 170 } 171 172 if (lastBlock || (mNumHeaders && mNumHeaders.ref() + 1 == mPacketCount)) { 173 mFullMetadata = true; 174 } 175 176 return Ok(); 177 } 178 179 int64_t FlacFrameParser::BlockDuration(const uint8_t* aPacket, 180 size_t aLength) const { 181 if (!mInfo.IsValid()) { 182 return -1; 183 } 184 if (mMinBlockSize == mMaxBlockSize) { 185 // block size is fixed, use this instead of looking at the frame header. 186 return mMinBlockSize; 187 } 188 // TODO 189 return 0; 190 } 191 192 Result<bool, nsresult> FlacFrameParser::IsHeaderBlock(const uint8_t* aPacket, 193 size_t aLength) const { 194 // Ogg Flac header 195 // The one-byte packet type 0x7F 196 // The four-byte ASCII signature "FLAC", i.e. 0x46, 0x4C, 0x41, 0x43 197 198 // Flac header: 199 // "fLaC", the FLAC stream marker in ASCII, meaning byte 0 of the stream is 200 // 0x66, followed by 0x4C 0x61 0x43 201 202 // If we detect either a ogg or plain flac header, then it must be valid. 203 if (aLength < 4 || aPacket[0] == 0xff) { 204 // A header is at least 4 bytes. 205 return false; 206 } 207 if (aPacket[0] == 0x7f) { 208 // Ogg packet 209 BufferReader br(aPacket + 1, aLength - 1); 210 const uint8_t* signature = br.Read(4); 211 return signature && !memcmp(signature, "FLAC", 4); 212 } 213 BufferReader br(aPacket, aLength - 1); 214 const uint8_t* signature = br.Read(4); 215 if (signature && !memcmp(signature, "fLaC", 4)) { 216 // Flac start header, must have STREAMINFO as first metadata block; 217 uint32_t blockType = MOZ_TRY(br.ReadU8()); 218 blockType &= 0x7f; 219 return blockType == FLAC_METADATA_TYPE_STREAMINFO; 220 } 221 char type = aPacket[0] & 0x7f; 222 return type >= 1 && type <= 6; 223 } 224 225 UniquePtr<MetadataTags> FlacFrameParser::GetTags() const { 226 if (!mParser) { 227 return nullptr; 228 } 229 230 auto tags = MakeUnique<MetadataTags>(); 231 for (uint32_t i = 0; i < mParser->mTags.Length(); i++) { 232 OggCodecState::AddVorbisComment(tags, mParser->mTags[i].Data(), 233 mParser->mTags[i].Length()); 234 } 235 236 return tags; 237 } 238 239 } // namespace mozilla