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