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 #include "nsTArray.h"
9 #include "OggCodecState.h"
10 #include "OpusParser.h"
11 #include "VideoUtils.h"
12 #include "BufferReader.h"
13 #include "mozilla/ResultExtensions.h"
14
15 namespace mozilla {
16
17 #define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
18 #define FLAC_STREAMINFO_SIZE 34
19
20 #define BITMASK(x) ((1ULL << x) - 1)
21
22 enum {
23 FLAC_METADATA_TYPE_STREAMINFO = 0,
24 FLAC_METADATA_TYPE_PADDING,
25 FLAC_METADATA_TYPE_APPLICATION,
26 FLAC_METADATA_TYPE_SEEKTABLE,
27 FLAC_METADATA_TYPE_VORBIS_COMMENT,
28 FLAC_METADATA_TYPE_CUESHEET,
29 FLAC_METADATA_TYPE_PICTURE,
30 FLAC_METADATA_TYPE_INVALID = 127
31 };
32
FlacFrameParser()33 FlacFrameParser::FlacFrameParser()
34 : mMinBlockSize(0),
35 mMaxBlockSize(0),
36 mMinFrameSize(0),
37 mMaxFrameSize(0),
38 mNumFrames(0),
39 mFullMetadata(false),
40 mPacketCount(0) {}
41
~FlacFrameParser()42 FlacFrameParser::~FlacFrameParser() {}
43
HeaderBlockLength(const uint8_t * aPacket) const44 uint32_t FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket) const {
45 uint32_t extra = 4;
46 if (aPacket[0] == 'f') {
47 // This must be the first block read, which contains the fLaC signature.
48 aPacket += 4;
49 extra += 4;
50 }
51 return (BigEndian::readUint32(aPacket) & BITMASK(24)) + extra;
52 }
53
DecodeHeaderBlock(const uint8_t * aPacket,size_t aLength)54 Result<Ok, nsresult> FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket,
55 size_t aLength) {
56 if (aLength < 4 || aPacket[0] == 0xff) {
57 // Not a header block.
58 return Err(NS_ERROR_FAILURE);
59 }
60 BufferReader br(aPacket, aLength);
61
62 mPacketCount++;
63
64 if (aPacket[0] == 'f') {
65 if (mPacketCount != 1 || memcmp(br.Read(4), "fLaC", 4) ||
66 br.Remaining() != FLAC_STREAMINFO_SIZE + 4) {
67 return Err(NS_ERROR_FAILURE);
68 }
69 }
70 uint8_t blockHeader;
71 MOZ_TRY_VAR(blockHeader, 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;
84 MOZ_TRY_VAR(major, br.ReadU8());
85 if (major != 1) {
86 // unsupported version;
87 return Err(NS_ERROR_FAILURE);
88 }
89 MOZ_TRY(br.ReadU8()); // minor version
90 uint32_t header;
91 MOZ_TRY_VAR(header, br.ReadU16());
92 mNumHeaders = Some(header);
93 br.Read(4); // fLaC
94 MOZ_TRY_VAR(blockType, br.ReadU8());
95 blockType &= BITMASK(7);
96 // First METADATA_BLOCK_STREAMINFO
97 if (blockType != FLAC_METADATA_TYPE_STREAMINFO) {
98 // First block must be a stream info.
99 return Err(NS_ERROR_FAILURE);
100 }
101 }
102
103 uint32_t blockDataSize;
104 MOZ_TRY_VAR(blockDataSize, br.ReadU24());
105 const uint8_t* blockDataStart = br.Peek(blockDataSize);
106 if (!blockDataStart) {
107 // Incomplete block.
108 return Err(NS_ERROR_FAILURE);
109 }
110
111 switch (blockType) {
112 case FLAC_METADATA_TYPE_STREAMINFO: {
113 if (mPacketCount != 1 || blockDataSize != FLAC_STREAMINFO_SIZE) {
114 // STREAMINFO must be the first metadata block found, and its size
115 // is constant.
116 return Err(NS_ERROR_FAILURE);
117 }
118
119 MOZ_TRY_VAR(mMinBlockSize, br.ReadU16());
120 MOZ_TRY_VAR(mMaxBlockSize, br.ReadU16());
121 MOZ_TRY_VAR(mMinFrameSize, br.ReadU24());
122 MOZ_TRY_VAR(mMaxFrameSize, br.ReadU24());
123
124 uint64_t blob;
125 MOZ_TRY_VAR(blob, br.ReadU64());
126 uint32_t sampleRate = (blob >> 44) & BITMASK(20);
127 if (!sampleRate) {
128 return Err(NS_ERROR_FAILURE);
129 }
130 uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1;
131 if (numChannels > FLAC_MAX_CHANNELS) {
132 return Err(NS_ERROR_FAILURE);
133 }
134 uint32_t bps = ((blob >> 36) & BITMASK(5)) + 1;
135 if (bps > 24) {
136 return Err(NS_ERROR_FAILURE);
137 }
138 mNumFrames = blob & BITMASK(36);
139
140 mInfo.mMimeType = "audio/flac";
141 mInfo.mRate = sampleRate;
142 mInfo.mChannels = numChannels;
143 mInfo.mBitDepth = bps;
144 mInfo.mCodecSpecificConfig->AppendElements(blockDataStart, blockDataSize);
145 auto duration = FramesToTimeUnit(mNumFrames, sampleRate);
146 mInfo.mDuration = duration.IsValid() ? duration : media::TimeUnit::Zero();
147 mParser = new 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
BlockDuration(const uint8_t * aPacket,size_t aLength) const179 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
IsHeaderBlock(const uint8_t * aPacket,size_t aLength) const192 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;
218 MOZ_TRY_VAR(blockType, br.ReadU8());
219 blockType &= 0x7f;
220 return blockType == FLAC_METADATA_TYPE_STREAMINFO;
221 }
222 char type = aPacket[0] & 0x7f;
223 return type >= 1 && type <= 6;
224 }
225
GetTags() const226 MetadataTags* FlacFrameParser::GetTags() const {
227 MetadataTags* tags;
228
229 tags = new MetadataTags;
230 for (uint32_t i = 0; i < mParser->mTags.Length(); i++) {
231 OggCodecState::AddVorbisComment(tags, mParser->mTags[i].Data(),
232 mParser->mTags[i].Length());
233 }
234
235 return tags;
236 }
237
238 } // namespace mozilla
239