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