Bug 1943650 - Command-line --help output misformatted after --dbus-service. r=emilio
[gecko.git] / dom / media / flac / FlacDemuxer.cpp
blobe478e128198a03efbff490469bb441af15fda704
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/. */
7 #include "FlacDemuxer.h"
9 #include "mozilla/Maybe.h"
10 #include "BitReader.h"
11 #include "prenv.h"
12 #include "FlacFrameParser.h"
13 #include "VideoUtils.h"
14 #include "TimeUnits.h"
16 extern mozilla::LazyLogModule gMediaDemuxerLog;
17 #define LOG(msg, ...) \
18 DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__)
19 #define LOGV(msg, ...) \
20 DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, msg, ##__VA_ARGS__)
22 using namespace mozilla::media;
24 namespace mozilla {
25 namespace flac {
27 // flac::FrameHeader - Holds the flac frame header and its parsing
28 // state.
30 class FrameHeader {
31 public:
32 const AudioInfo& Info() const { return mInfo; }
34 uint32_t Size() const { return mSize; }
36 bool IsValid() const { return mValid; }
38 // Return the index (in samples) from the beginning of the track.
39 int64_t Index() const { return mIndex; }
41 // Parse the current packet and check that it made a valid flac frame header.
42 // From https://xiph.org/flac/format.html#frame_header
43 // A valid header is one that can be decoded without error and that has a
44 // valid CRC.
45 bool Parse(const uint8_t* aPacket, size_t aBytes) {
46 BitReader br(aPacket, aBytes * 8);
48 // Frame sync code.
49 if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) {
50 return false;
53 // Variable block size stream code.
54 mVariableBlockSize = br.ReadBit();
56 // Block size and sample rate codes.
57 int bs_code = AssertedCast<int>(br.ReadBits(4));
58 int sr_code = AssertedCast<int>(br.ReadBits(4));
60 // Channels and decorrelation.
61 uint32_t ch_mode = br.ReadBits(4);
62 if (ch_mode < FLAC_MAX_CHANNELS) {
63 mInfo.mChannels = ch_mode + 1;
64 } else if (ch_mode < FLAC_MAX_CHANNELS + FLAC_CHMODE_MID_SIDE) {
65 // This is a special flac channels, we can't handle those yet. Treat it
66 // as stereo.
67 mInfo.mChannels = 2;
68 } else {
69 // invalid channel mode
70 return false;
73 // Bits per sample.
74 int bps_code = AssertedCast<int>(br.ReadBits(3));
75 if (bps_code == 3 || bps_code == 7) {
76 // Invalid sample size code.
77 return false;
79 mInfo.mBitDepth = FlacSampleSizeTable[bps_code];
81 // Reserved bit, must be 0.
82 if (br.ReadBit()) {
83 // Broken stream, invalid padding.
84 return false;
87 // Sample or frame count.
88 uint64_t frame_or_sample_num = br.ReadUTF8();
89 if (frame_or_sample_num == UINT64_MAX) {
90 // Sample/frame number invalid.
91 return false;
94 // Blocksize
95 if (bs_code == 0) {
96 // reserved blocksize code
97 return false;
99 if (bs_code == 6) {
100 mBlocksize = br.ReadBits(8) + 1;
101 } else if (bs_code == 7) {
102 mBlocksize = br.ReadBits(16) + 1;
103 } else {
104 mBlocksize = FlacBlocksizeTable[bs_code];
107 // The sample index is either:
108 // 1- coded sample number if blocksize is variable or
109 // 2- coded frame number if blocksize is known.
110 // A frame is made of Blocksize sample.
111 mIndex = mVariableBlockSize
112 ? AssertedCast<int64_t>(frame_or_sample_num)
113 : AssertedCast<int64_t>(frame_or_sample_num * mBlocksize);
114 mFrameOrSampleNum = static_cast<uint64_t>(frame_or_sample_num);
116 // Sample rate.
117 if (sr_code < 12) {
118 mInfo.mRate = FlacSampleRateTable[sr_code];
119 } else if (sr_code == 12) {
120 mInfo.mRate = br.ReadBits(8) * 1000;
121 } else if (sr_code == 13) {
122 mInfo.mRate = br.ReadBits(16);
123 } else if (sr_code == 14) {
124 mInfo.mRate = br.ReadBits(16) * 10;
125 } else {
126 // Illegal sample rate code.
127 return false;
130 // Header CRC-8 check.
131 uint8_t crc = 0;
132 for (uint32_t i = 0; i < br.BitCount() / 8; i++) {
133 crc = CRC8Table[crc ^ aPacket[i]];
135 mValid =
136 #ifdef FUZZING
137 true;
138 #else
139 crc == br.ReadBits(8);
140 #endif
141 mSize = br.BitCount() / 8;
143 if (mValid) {
144 // Set the mimetype to make it a valid AudioInfo.
145 mInfo.mMimeType = "audio/flac";
146 // Set the codec specific data to flac, but leave it empty since we don't
147 // have METADATA_BLOCK_STREAMINFO in the frame.
148 mInfo.mCodecSpecificConfig =
149 AudioCodecSpecificVariant{FlacCodecSpecificData{}};
152 return mValid;
155 private:
156 friend class Frame;
157 enum {
158 FLAC_CHMODE_INDEPENDENT = 0,
159 FLAC_CHMODE_LEFT_SIDE,
160 FLAC_CHMODE_RIGHT_SIDE,
161 FLAC_CHMODE_MID_SIDE,
163 AudioInfo mInfo;
164 // mFrameOrSampleNum is either:
165 // 1- coded sample number if blocksize is variable or
166 // 2- coded frame number if blocksize is fixed.
167 // A frame is made of Blocksize sample.
168 uint64_t mFrameOrSampleNum = 0;
169 // Index in samples from start;
170 int64_t mIndex = 0;
171 bool mVariableBlockSize = false;
172 uint32_t mBlocksize = 0;
173 uint32_t mSize = 0;
174 bool mValid = false;
176 static const uint32_t FlacSampleRateTable[16];
177 static const uint32_t FlacBlocksizeTable[16];
178 static const uint8_t FlacSampleSizeTable[8];
179 static const uint8_t CRC8Table[256];
182 const uint32_t FrameHeader::FlacSampleRateTable[16] = {
183 0, 88200, 176400, 192000, 8000, 16000, 22050, 24000,
184 32000, 44100, 48000, 96000, 0, 0, 0, 0};
186 const uint32_t FrameHeader::FlacBlocksizeTable[16] = {
187 0, 192, 576 << 0, 576 << 1, 576 << 2, 576 << 3,
188 0, 0, 256 << 0, 256 << 1, 256 << 2, 256 << 3,
189 256 << 4, 256 << 5, 256 << 6, 256 << 7};
191 const uint8_t FrameHeader::FlacSampleSizeTable[8] = {0, 8, 12, 0,
192 16, 20, 24, 0};
194 const uint8_t FrameHeader::CRC8Table[256] = {
195 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31,
196 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
197 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9,
198 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
199 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
200 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
201 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,
202 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
203 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16,
204 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
205 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80,
206 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
207 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8,
208 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
209 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,
210 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
211 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F,
212 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
213 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,
214 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
215 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF,
216 0xFA, 0xFD, 0xF4, 0xF3};
218 // flac::Frame - Frame meta container used to parse and hold a frame
219 // header and side info.
220 class Frame {
221 public:
222 // The FLAC signature is made of 14 bits set to 1; however the 15th bit is
223 // mandatorily set to 0, so we need to find either of 0xfffc or 0xfffd 2-bytes
224 // signature. We first use a bitmask to see if 0xfc or 0xfd is present. And if
225 // so we check for the whole signature.
226 int64_t FindNext(const uint8_t* aData, const uint32_t aLength) {
227 // The non-variable size of a FLAC header is 32 bits followed by variable
228 // size data and a 8 bits CRC.
229 // There's no need to read the last 4 bytes, it can never make a complete
230 // header.
231 if (aLength < 4) {
232 return -1;
234 uint32_t modOffset = aLength % 4;
235 uint32_t i, j;
237 for (i = 0; i < modOffset; i++) {
238 if ((BigEndian::readUint16(aData + i) & 0xfffe) == 0xfff8) {
239 if (mHeader.Parse(aData + i, aLength - i)) {
240 return i;
245 for (; i < aLength - 4; i += 4) {
246 uint32_t x = BigEndian::readUint32(aData + i);
247 if (((x & ~(x + 0x01010101)) & 0x80808080)) {
248 for (j = 0; j < 4; j++) {
249 if ((BigEndian::readUint16(aData + i + j) & 0xfffe) == 0xfff8) {
250 if (mHeader.Parse(aData + i + j, aLength - i - j)) {
251 return i + j;
257 return -1;
260 // Find the next frame start in the current resource.
261 // On exit return true, offset is set and resource points to the frame found.
262 bool FindNext(MediaResourceIndex& aResource) {
263 static const int BUFFER_SIZE = 4096;
265 Reset();
267 nsTArray<char> buffer;
268 uint64_t originalOffset = static_cast<uint64_t>(aResource.Tell());
269 uint64_t offset = originalOffset;
270 uint32_t innerOffset = 0;
272 do {
273 uint32_t read = 0;
274 buffer.SetLength(BUFFER_SIZE + innerOffset);
275 nsresult rv =
276 aResource.Read(buffer.Elements() + innerOffset, BUFFER_SIZE, &read);
277 if (NS_FAILED(rv)) {
278 return false;
281 const size_t bufSize = read + innerOffset;
282 int64_t foundOffset =
283 FindNext(reinterpret_cast<uint8_t*>(buffer.Elements()), bufSize);
285 if (foundOffset >= 0) {
286 SetOffset(aResource, static_cast<uint64_t>(foundOffset) + offset);
287 return true;
290 if (read < BUFFER_SIZE) {
291 // Nothing more to try on as we had reached EOS during the previous
292 // read.
293 mEOS = true;
294 return false;
297 // Scan the next block;
298 // We rewind a bit to re-try what could have been an incomplete packet.
299 // The maximum size of a FLAC header being FLAC_MAX_FRAME_HEADER_SIZE so
300 // we need to retry just after that amount.
301 offset += bufSize - (FLAC_MAX_FRAME_HEADER_SIZE + 1);
302 buffer.RemoveElementsAt(0, bufSize - (FLAC_MAX_FRAME_HEADER_SIZE + 1));
303 innerOffset = buffer.Length();
304 } while (offset - originalOffset < FLAC_MAX_FRAME_SIZE);
306 return false;
309 uint64_t Offset() const { return mOffset; }
311 const AudioInfo& Info() const { return Header().Info(); }
313 void SetEndOffset(uint64_t aOffset) { mSize = aOffset - mOffset; }
315 void SetEndTime(int64_t aIndex) {
316 if (aIndex > Header().mIndex) {
317 mDuration = aIndex - Header().mIndex;
321 void ResetStartTimeIfNeeded(const Frame& aReferenceFrame) {
322 if (Header().mVariableBlockSize ||
323 aReferenceFrame.Header().mVariableBlockSize ||
324 aReferenceFrame.Header().mBlocksize <= Header().mBlocksize) {
325 // Not a fixed size frame, or nothing to adjust.
326 return;
328 mHeader.mIndex = AssertedCast<int64_t>(Header().mFrameOrSampleNum *
329 aReferenceFrame.Header().mBlocksize);
332 uint32_t Size() const { return mSize; }
334 TimeUnit Time() const {
335 if (!IsValid()) {
336 return TimeUnit::Invalid();
338 MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header");
339 return media::TimeUnit(Header().mIndex, Header().Info().mRate);
342 TimeUnit Duration() const {
343 if (!IsValid()) {
344 return TimeUnit();
346 MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header");
347 return media::TimeUnit(mDuration, Header().Info().mRate);
350 // Returns the parsed frame header.
351 const FrameHeader& Header() const { return mHeader; }
353 bool IsValid() const { return mHeader.IsValid(); }
355 bool EOS() const { return mEOS; }
357 void SetRate(uint32_t aRate) { mHeader.mInfo.mRate = aRate; };
359 void SetBitDepth(uint32_t aBitDepth) { mHeader.mInfo.mBitDepth = aBitDepth; }
361 void SetInvalid() { mHeader.mValid = false; }
363 // Resets the frame header and data.
364 void Reset() { *this = Frame(); }
366 private:
367 void SetOffset(MediaResourceIndex& aResource, uint64_t aOffset) {
368 mOffset = aOffset;
369 aResource.Seek(SEEK_SET, AssertedCast<int64_t>(mOffset));
372 // The offset to the start of the header.
373 uint64_t mOffset = 0;
374 uint32_t mSize = 0;
375 uint32_t mDuration = 0;
376 bool mEOS = false;
378 // The currently parsed frame header.
379 FrameHeader mHeader;
382 class FrameParser {
383 public:
384 // Returns the currently parsed frame. Reset via EndFrameSession.
385 const Frame& CurrentFrame() const { return mFrame; }
387 // Returns the first parsed frame.
388 const Frame& FirstFrame() const { return mFirstFrame; }
390 // Clear the last parsed frame to allow for next frame parsing
391 void EndFrameSession() {
392 mNextFrame.Reset();
393 mFrame.Reset();
396 // Attempt to find the next frame.
397 bool FindNextFrame(MediaResourceIndex& aResource) {
398 mFrame = mNextFrame;
399 if (GetNextFrame(aResource)) {
400 if (!mFrame.IsValid()) {
401 mFrame = mNextFrame;
402 // We need two frames to be able to start playing (or have reached EOS).
403 GetNextFrame(aResource);
407 if (mFrame.IsValid()) {
408 if (mNextFrame.EOS()) {
409 mFrame.SetEndOffset(static_cast<uint64_t>(aResource.Tell()));
410 // If the blocksize is fixed, the frame's starting sample number will be
411 // the frame number times the blocksize. However, the last block may
412 // have been incorrectly set as shorter than the stream blocksize.
413 // We recalculate the start time of this last sample using the first
414 // frame blocksize.
415 // TODO: should we use an overall counter of frames instead?
416 mFrame.ResetStartTimeIfNeeded(mFirstFrame);
417 } else if (mNextFrame.IsValid()) {
418 mFrame.SetEndOffset(mNextFrame.Offset());
419 mFrame.SetEndTime(mNextFrame.Header().Index());
423 if (!mFirstFrame.IsValid()) {
424 mFirstFrame = mFrame;
426 return mFrame.IsValid();
429 // Convenience methods to external FlacFrameParser ones.
430 bool IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const {
431 auto res = mParser.IsHeaderBlock(aPacket, aLength);
432 return res.isOk() ? res.unwrap() : false;
435 uint32_t HeaderBlockLength(const uint8_t* aPacket) const {
436 return mParser.HeaderBlockLength(aPacket);
439 bool DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength) {
440 return mParser.DecodeHeaderBlock(aPacket, aLength).isOk();
443 bool HasFullMetadata() const { return mParser.HasFullMetadata(); }
445 AudioInfo Info() const { return mParser.mInfo; }
447 // Return a hash table with tag metadata.
448 UniquePtr<MetadataTags> GetTags() const { return mParser.GetTags(); }
450 private:
451 bool GetNextFrame(MediaResourceIndex& aResource) {
452 while (mNextFrame.FindNext(aResource)) {
453 // Move our offset slightly, so that we don't find the same frame at the
454 // next FindNext call.
455 aResource.Seek(SEEK_CUR, mNextFrame.Header().Size());
456 if (mFrame.IsValid() &&
457 mNextFrame.Offset() - mFrame.Offset() < FLAC_MAX_FRAME_SIZE &&
458 !CheckCRC16AtOffset(AssertedCast<int64_t>(mFrame.Offset()),
459 AssertedCast<int64_t>(mNextFrame.Offset()),
460 aResource)) {
461 // The frame doesn't match its CRC or would be too far, skip it..
462 continue;
464 CheckFrameData();
465 break;
467 return mNextFrame.IsValid();
470 bool CheckFrameData() {
471 if (mNextFrame.Header().Info().mRate == 0 ||
472 mNextFrame.Header().Info().mBitDepth == 0) {
473 if (!Info().IsValid()) {
474 // We can only use the STREAMINFO data if we have one.
475 mNextFrame.SetInvalid();
476 } else {
477 if (mNextFrame.Header().Info().mRate == 0) {
478 mNextFrame.SetRate(Info().mRate);
480 if (mNextFrame.Header().Info().mBitDepth == 0) {
481 mNextFrame.SetBitDepth(Info().mBitDepth);
485 return mNextFrame.IsValid();
488 bool CheckCRC16AtOffset(int64_t aStart, int64_t aEnd,
489 MediaResourceIndex& aResource) const {
490 int64_t size = aEnd - aStart;
491 if (size <= 0) {
492 return false;
494 UniquePtr<char[]> buffer(new char[static_cast<size_t>(size)]);
495 uint32_t read = 0;
496 if (NS_FAILED(aResource.ReadAt(aStart, buffer.get(), size, &read)) ||
497 read != size) {
498 NS_WARNING("Couldn't read frame content");
499 return false;
502 uint16_t crc = 0;
503 uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.get());
504 const uint8_t* end = buf + size;
505 while (buf < end) {
506 crc = CRC16Table[((uint8_t)crc) ^ *buf++] ^ (crc >> 8);
508 #ifdef FUZZING
509 return true;
510 #else
511 return !crc;
512 #endif
515 const uint16_t CRC16Table[256] = {
516 0x0000, 0x0580, 0x0F80, 0x0A00, 0x1B80, 0x1E00, 0x1400, 0x1180, 0x3380,
517 0x3600, 0x3C00, 0x3980, 0x2800, 0x2D80, 0x2780, 0x2200, 0x6380, 0x6600,
518 0x6C00, 0x6980, 0x7800, 0x7D80, 0x7780, 0x7200, 0x5000, 0x5580, 0x5F80,
519 0x5A00, 0x4B80, 0x4E00, 0x4400, 0x4180, 0xC380, 0xC600, 0xCC00, 0xC980,
520 0xD800, 0xDD80, 0xD780, 0xD200, 0xF000, 0xF580, 0xFF80, 0xFA00, 0xEB80,
521 0xEE00, 0xE400, 0xE180, 0xA000, 0xA580, 0xAF80, 0xAA00, 0xBB80, 0xBE00,
522 0xB400, 0xB180, 0x9380, 0x9600, 0x9C00, 0x9980, 0x8800, 0x8D80, 0x8780,
523 0x8200, 0x8381, 0x8601, 0x8C01, 0x8981, 0x9801, 0x9D81, 0x9781, 0x9201,
524 0xB001, 0xB581, 0xBF81, 0xBA01, 0xAB81, 0xAE01, 0xA401, 0xA181, 0xE001,
525 0xE581, 0xEF81, 0xEA01, 0xFB81, 0xFE01, 0xF401, 0xF181, 0xD381, 0xD601,
526 0xDC01, 0xD981, 0xC801, 0xCD81, 0xC781, 0xC201, 0x4001, 0x4581, 0x4F81,
527 0x4A01, 0x5B81, 0x5E01, 0x5401, 0x5181, 0x7381, 0x7601, 0x7C01, 0x7981,
528 0x6801, 0x6D81, 0x6781, 0x6201, 0x2381, 0x2601, 0x2C01, 0x2981, 0x3801,
529 0x3D81, 0x3781, 0x3201, 0x1001, 0x1581, 0x1F81, 0x1A01, 0x0B81, 0x0E01,
530 0x0401, 0x0181, 0x0383, 0x0603, 0x0C03, 0x0983, 0x1803, 0x1D83, 0x1783,
531 0x1203, 0x3003, 0x3583, 0x3F83, 0x3A03, 0x2B83, 0x2E03, 0x2403, 0x2183,
532 0x6003, 0x6583, 0x6F83, 0x6A03, 0x7B83, 0x7E03, 0x7403, 0x7183, 0x5383,
533 0x5603, 0x5C03, 0x5983, 0x4803, 0x4D83, 0x4783, 0x4203, 0xC003, 0xC583,
534 0xCF83, 0xCA03, 0xDB83, 0xDE03, 0xD403, 0xD183, 0xF383, 0xF603, 0xFC03,
535 0xF983, 0xE803, 0xED83, 0xE783, 0xE203, 0xA383, 0xA603, 0xAC03, 0xA983,
536 0xB803, 0xBD83, 0xB783, 0xB203, 0x9003, 0x9583, 0x9F83, 0x9A03, 0x8B83,
537 0x8E03, 0x8403, 0x8183, 0x8002, 0x8582, 0x8F82, 0x8A02, 0x9B82, 0x9E02,
538 0x9402, 0x9182, 0xB382, 0xB602, 0xBC02, 0xB982, 0xA802, 0xAD82, 0xA782,
539 0xA202, 0xE382, 0xE602, 0xEC02, 0xE982, 0xF802, 0xFD82, 0xF782, 0xF202,
540 0xD002, 0xD582, 0xDF82, 0xDA02, 0xCB82, 0xCE02, 0xC402, 0xC182, 0x4382,
541 0x4602, 0x4C02, 0x4982, 0x5802, 0x5D82, 0x5782, 0x5202, 0x7002, 0x7582,
542 0x7F82, 0x7A02, 0x6B82, 0x6E02, 0x6402, 0x6182, 0x2002, 0x2582, 0x2F82,
543 0x2A02, 0x3B82, 0x3E02, 0x3402, 0x3182, 0x1382, 0x1602, 0x1C02, 0x1982,
544 0x0802, 0x0D82, 0x0782, 0x0202,
547 FlacFrameParser mParser;
548 // We keep the first parsed frame around for static info access
549 // and the currently parsed frame.
550 Frame mFirstFrame;
551 Frame mNextFrame;
552 Frame mFrame;
555 } // namespace flac
557 // FlacDemuxer
559 FlacDemuxer::FlacDemuxer(MediaResource* aSource) : mSource(aSource) {
560 DDLINKCHILD("source", aSource);
563 bool FlacDemuxer::InitInternal() {
564 if (!mTrackDemuxer) {
565 mTrackDemuxer = new FlacTrackDemuxer(mSource);
566 DDLINKCHILD("track demuxer", mTrackDemuxer.get());
568 return mTrackDemuxer->Init();
571 RefPtr<FlacDemuxer::InitPromise> FlacDemuxer::Init() {
572 if (!InitInternal()) {
573 LOG("Init() failure: waiting for data");
575 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
576 __func__);
579 LOG("Init() successful");
580 return InitPromise::CreateAndResolve(NS_OK, __func__);
583 uint32_t FlacDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
584 return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
587 already_AddRefed<MediaTrackDemuxer> FlacDemuxer::GetTrackDemuxer(
588 TrackInfo::TrackType aType, uint32_t aTrackNumber) {
589 if (!mTrackDemuxer) {
590 return nullptr;
593 return RefPtr<FlacTrackDemuxer>(mTrackDemuxer).forget();
596 bool FlacDemuxer::IsSeekable() const {
597 return mTrackDemuxer && mTrackDemuxer->IsSeekable();
600 // FlacTrackDemuxer
601 FlacTrackDemuxer::FlacTrackDemuxer(MediaResource* aSource)
602 : mSource(aSource), mParser(new flac::FrameParser()), mTotalFrameLen(0) {
603 DDLINKCHILD("source", aSource);
604 Reset();
607 FlacTrackDemuxer::~FlacTrackDemuxer() = default;
609 bool FlacTrackDemuxer::Init() {
610 static const int BUFFER_SIZE = 4096;
612 // First check if we have a valid Flac start.
613 char buffer[BUFFER_SIZE];
614 const uint8_t* ubuffer = // only needed due to type constraints of ReadAt.
615 reinterpret_cast<uint8_t*>(buffer);
616 int64_t offset = 0;
618 do {
619 uint32_t read = 0;
620 nsresult ret = mSource.ReadAt(offset, buffer, BUFFER_SIZE, &read);
621 if (NS_FAILED(ret)) {
622 return false;
624 if (!mParser->IsHeaderBlock(ubuffer, read)) {
625 // Not a header and we haven't reached the end of the metadata blocks.
626 // Will fall back to using the frames header instead.
627 break;
629 uint32_t sizeHeader = mParser->HeaderBlockLength(ubuffer);
630 RefPtr<MediaByteBuffer> block = mSource.MediaReadAt(offset, sizeHeader);
631 if (!block || block->Length() != sizeHeader) {
632 break;
634 if (!mParser->DecodeHeaderBlock(block->Elements(), sizeHeader)) {
635 break;
637 offset += sizeHeader;
638 } while (!mParser->HasFullMetadata());
640 // First flac frame is found after the metadata.
641 // Can seek there immediately to avoid reparsing it all.
642 mSource.Seek(SEEK_SET, offset);
644 // Find the first frame to fully initialise our parser.
645 if (mParser->FindNextFrame(mSource)) {
646 // Ensure that the next frame returned will be the first.
647 mSource.Seek(SEEK_SET,
648 AssertedCast<int64_t>(mParser->FirstFrame().Offset()));
649 mParser->EndFrameSession();
650 } else if (!mParser->Info().IsValid() || !mParser->FirstFrame().IsValid()) {
651 // We must find at least a frame to determine the metadata.
652 // We can't play this stream.
653 return false;
656 if (!mParser->Info().IsValid() || !mParser->Info().mDuration.IsPositive()) {
657 // Check if we can look at the last frame for the end time to determine the
658 // duration when we don't have any.
659 TimeAtEnd();
662 return true;
665 UniquePtr<TrackInfo> FlacTrackDemuxer::GetInfo() const {
666 if (mParser->Info().IsValid()) {
667 // We have a proper metadata header.
668 UniquePtr<TrackInfo> info = mParser->Info().Clone();
669 UniquePtr<MetadataTags> tags(mParser->GetTags());
670 if (tags) {
671 for (const auto& entry : *tags) {
672 info->mTags.AppendElement(MetadataTag(entry.GetKey(), entry.GetData()));
675 MOZ_ASSERT(info->IsAudio() &&
676 info->GetAsAudioInfo()
677 ->mCodecSpecificConfig.is<FlacCodecSpecificData>(),
678 "Should get flac specific data from parser");
679 return info;
682 if (mParser->FirstFrame().Info().IsValid()) {
683 // Use the first frame header.
684 UniquePtr<TrackInfo> info = mParser->FirstFrame().Info().Clone();
685 info->mDuration = Duration();
686 MOZ_ASSERT(info->IsAudio() &&
687 info->GetAsAudioInfo()
688 ->mCodecSpecificConfig.is<FlacCodecSpecificData>(),
689 "Should get flac specific data from parser");
690 return info;
692 return nullptr;
695 bool FlacTrackDemuxer::IsSeekable() const {
696 // For now we only allow seeking if a STREAMINFO block was found and with
697 // a known number of samples (duration is set).
698 return mParser->Info().IsValid() && mParser->Info().mDuration.IsPositive();
701 RefPtr<FlacTrackDemuxer::SeekPromise> FlacTrackDemuxer::Seek(
702 const TimeUnit& aTime) {
703 // Efficiently seek to the position.
704 FastSeek(aTime);
705 // Correct seek position by scanning the next frames.
706 const TimeUnit seekTime = ScanUntil(aTime);
708 return SeekPromise::CreateAndResolve(seekTime, __func__);
711 TimeUnit FlacTrackDemuxer::FastSeek(const TimeUnit& aTime) {
712 LOG("FastSeek(%f) avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64,
713 aTime.ToSeconds(), AverageFrameLength(),
714 mParsedFramesDuration.ToSeconds(), GetResourceOffset());
716 // Invalidate current frames in the parser.
717 mParser->EndFrameSession();
719 if (!mParser->FirstFrame().IsValid()) {
720 // Something wrong, and there's nothing to seek to anyway, so we can
721 // do whatever here.
722 mSource.Seek(SEEK_SET, 0);
723 return TimeUnit();
726 if (aTime <= mParser->FirstFrame().Time()) {
727 // We're attempting to seek prior the first frame, return the first frame.
728 mSource.Seek(SEEK_SET,
729 AssertedCast<int64_t>(mParser->FirstFrame().Offset()));
730 return mParser->FirstFrame().Time();
733 // We look for the seek position using a bisection search, starting where the
734 // estimated position might be using the average frame length.
735 // Typically, with flac such approximation is typically useless.
737 // Estimate where the position might be.
738 int64_t pivot = AssertedCast<int64_t>(
739 aTime.ToSeconds() * AverageFrameLength() +
740 AssertedCast<double>(mParser->FirstFrame().Offset()));
742 // Time in seconds where we can stop seeking and will continue using
743 // ScanUntil.
744 static const int GAP_THRESHOLD = 5;
745 int64_t first = AssertedCast<int64_t>(mParser->FirstFrame().Offset());
746 int64_t last = mSource.GetLength();
747 Maybe<uint64_t> lastFoundOffset;
748 uint32_t iterations = 0;
749 TimeUnit timeSeekedTo;
751 do {
752 iterations++;
753 mSource.Seek(SEEK_SET, pivot);
754 flac::Frame frame;
755 bool found = frame.FindNext(mSource);
756 if (!found) {
757 NS_WARNING("We should have found a point");
758 break;
760 if (!frame.IsValid()) {
761 NS_WARNING("Invalid frame after seeking and sync on a frame");
762 break;
764 // When the rate is 0, the rate from STREAMINFO is to be used.
765 if (frame.Header().Info().mRate == 0) {
766 frame.SetRate(mParser->FirstFrame().Info().mRate);
768 timeSeekedTo = frame.Time();
770 LOGV("FastSeek: interation:%u found:%f @ %" PRIu64, iterations,
771 timeSeekedTo.ToSeconds(), frame.Offset());
773 if (lastFoundOffset && lastFoundOffset.ref() == frame.Offset()) {
774 // Same frame found twice. We're done.
775 break;
777 lastFoundOffset = Some(frame.Offset());
779 if (frame.Time() == aTime) {
780 break;
782 if (aTime > frame.Time() &&
783 aTime - frame.Time() <= TimeUnit::FromSeconds(GAP_THRESHOLD)) {
784 // We're close enough to the target, experimentation shows that bisection
785 // search doesn't help much after that.
786 break;
788 if (frame.Time() > aTime) {
789 last = pivot;
790 pivot -= (pivot - first) / 2;
791 } else {
792 first = pivot;
793 pivot += (last - pivot) / 2;
795 } while (true);
797 if (lastFoundOffset) {
798 mSource.Seek(SEEK_SET, AssertedCast<int64_t>(lastFoundOffset.ref()));
801 return timeSeekedTo;
804 TimeUnit FlacTrackDemuxer::ScanUntil(const TimeUnit& aTime) {
805 LOG("ScanUntil(%f avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64,
806 aTime.ToSeconds(), AverageFrameLength(),
807 mParsedFramesDuration.ToSeconds(), mParser->CurrentFrame().Offset());
809 if (!mParser->FirstFrame().IsValid() ||
810 aTime <= mParser->FirstFrame().Time()) {
811 return FastSeek(aTime);
814 int64_t previousOffset = 0;
815 TimeUnit previousTime;
816 while (FindNextFrame().IsValid() && mParser->CurrentFrame().Time() < aTime) {
817 previousOffset = AssertedCast<int64_t>(mParser->CurrentFrame().Offset());
818 previousTime = mParser->CurrentFrame().Time();
821 if (!mParser->CurrentFrame().IsValid()) {
822 // We reached EOS.
823 return Duration();
826 // Seek back to the last frame found prior the target.
827 mParser->EndFrameSession();
828 mSource.Seek(SEEK_SET, previousOffset);
829 return previousTime;
832 RefPtr<FlacTrackDemuxer::SamplesPromise> FlacTrackDemuxer::GetSamples(
833 int32_t aNumSamples) {
834 LOGV("GetSamples(%d) Begin offset=%" PRId64
835 " mParsedFramesDuration=%f"
836 " mTotalFrameLen=%" PRIu64,
837 aNumSamples, GetResourceOffset(), mParsedFramesDuration.ToSeconds(),
838 mTotalFrameLen);
840 if (!aNumSamples) {
841 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
842 __func__);
845 RefPtr<SamplesHolder> frames = new SamplesHolder();
847 while (aNumSamples--) {
848 RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
849 if (!frame) break;
850 if (!frame->HasValidTime()) {
851 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
852 __func__);
854 frames->AppendSample(frame);
857 LOGV("GetSamples() End mSamples.Length=%zu aNumSamples=%d offset=%" PRId64
858 " mParsedFramesDuration=%f mTotalFrameLen=%" PRIu64,
859 frames->GetSamples().Length(), aNumSamples, GetResourceOffset(),
860 mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
862 if (frames->GetSamples().IsEmpty()) {
863 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
864 __func__);
867 return SamplesPromise::CreateAndResolve(frames, __func__);
870 void FlacTrackDemuxer::Reset() {
871 LOG("Reset()");
872 MOZ_ASSERT(mParser);
873 if (mParser->FirstFrame().IsValid()) {
874 mSource.Seek(SEEK_SET,
875 AssertedCast<int64_t>(mParser->FirstFrame().Offset()));
876 } else {
877 mSource.Seek(SEEK_SET, 0);
879 mParser->EndFrameSession();
882 RefPtr<FlacTrackDemuxer::SkipAccessPointPromise>
883 FlacTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
884 // Will not be called for audio-only resources.
885 return SkipAccessPointPromise::CreateAndReject(
886 SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
889 int64_t FlacTrackDemuxer::GetResourceOffset() const { return mSource.Tell(); }
891 TimeIntervals FlacTrackDemuxer::GetBuffered() {
892 TimeUnit duration = Duration();
894 if (duration <= TimeUnit()) {
895 return TimeIntervals();
898 // We could simply parse the cached data instead and read the timestamps.
899 // However, for now this will do.
900 AutoPinned<MediaResource> stream(mSource.GetResource());
901 return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
904 const flac::Frame& FlacTrackDemuxer::FindNextFrame() {
905 LOGV("FindNextFrame() Begin offset=%" PRId64
906 " mParsedFramesDuration=%f"
907 " mTotalFrameLen=%" PRIu64,
908 GetResourceOffset(), mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
910 if (mParser->FindNextFrame(mSource)) {
911 // Update our current progress stats.
912 mParsedFramesDuration =
913 std::max(mParsedFramesDuration, mParser->CurrentFrame().Time() -
914 mParser->FirstFrame().Time() +
915 mParser->CurrentFrame().Duration());
916 mTotalFrameLen =
917 std::max<uint64_t>(mTotalFrameLen, mParser->CurrentFrame().Offset() -
918 mParser->FirstFrame().Offset() +
919 mParser->CurrentFrame().Size());
921 LOGV("FindNextFrame() End time=%f offset=%" PRId64
922 " mParsedFramesDuration=%f"
923 " mTotalFrameLen=%" PRIu64,
924 mParser->CurrentFrame().Time().ToSeconds(), GetResourceOffset(),
925 mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
928 return mParser->CurrentFrame();
931 already_AddRefed<MediaRawData> FlacTrackDemuxer::GetNextFrame(
932 const flac::Frame& aFrame) {
933 if (!aFrame.IsValid()) {
934 LOG("GetNextFrame() EOS");
935 return nullptr;
938 LOG("GetNextFrame() Begin(time=%f offset=%" PRId64 " size=%u)",
939 aFrame.Time().ToSeconds(), aFrame.Offset(), aFrame.Size());
941 const uint64_t offset = aFrame.Offset();
942 const uint32_t size = aFrame.Size();
944 RefPtr<MediaRawData> frame = new MediaRawData();
945 frame->mOffset = AssertedCast<int64_t>(offset);
947 UniquePtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
948 if (!frameWriter->SetSize(size)) {
949 LOG("GetNext() Exit failed to allocated media buffer");
950 return nullptr;
953 const uint32_t read = Read(frameWriter->Data(), AssertedCast<int64_t>(offset),
954 AssertedCast<int32_t>(size));
955 if (read != size) {
956 LOG("GetNextFrame() Exit read=%u frame->Size=%zu", read, frame->Size());
957 return nullptr;
960 frame->mTime = aFrame.Time();
961 frame->mDuration = aFrame.Duration();
962 frame->mTimecode = frame->mTime;
963 frame->mOffset = AssertedCast<int64_t>(aFrame.Offset());
964 frame->mKeyframe = true;
966 MOZ_ASSERT(!frame->mTime.IsNegative());
967 MOZ_ASSERT(!frame->mDuration.IsNegative());
969 return frame.forget();
972 uint32_t FlacTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset,
973 int32_t aSize) {
974 uint32_t read = 0;
975 const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
976 static_cast<uint32_t>(aSize), &read);
977 NS_ENSURE_SUCCESS(rv, 0);
978 return read;
981 double FlacTrackDemuxer::AverageFrameLength() const {
982 if (mParsedFramesDuration.ToMicroseconds()) {
983 return AssertedCast<double>(mTotalFrameLen) /
984 mParsedFramesDuration.ToSeconds();
987 return 0.0;
990 TimeUnit FlacTrackDemuxer::Duration() const {
991 return std::max(mParsedFramesDuration, mParser->Info().mDuration);
994 TimeUnit FlacTrackDemuxer::TimeAtEnd() {
995 // Scan the last 128kB if available to determine the last frame.
996 static const int OFFSET_FROM_END = 128 * 1024;
998 // Seek to the end of the file and attempt to find the last frame.
999 MediaResourceIndex source(mSource.GetResource());
1000 TimeUnit previousDuration;
1001 TimeUnit previousTime;
1003 const int64_t streamLen = mSource.GetLength();
1004 if (streamLen < 0) {
1005 return TimeUnit::FromInfinity();
1008 flac::FrameParser parser;
1010 source.Seek(SEEK_SET, std::max<int64_t>(0LL, streamLen - OFFSET_FROM_END));
1011 while (parser.FindNextFrame(source)) {
1012 // FFmpeg flac muxer can generate a last frame with earlier than the others.
1013 previousTime = std::max(previousTime, parser.CurrentFrame().Time());
1014 if (parser.CurrentFrame().Duration() > TimeUnit()) {
1015 // The last frame doesn't have a duration, so only update our duration
1016 // if we do have one.
1017 previousDuration = parser.CurrentFrame().Duration();
1019 if (source.Tell() >= streamLen) {
1020 // Limit the read, in case the length change half-way.
1021 break;
1025 // Update our current progress stats.
1026 mParsedFramesDuration =
1027 previousTime + previousDuration - mParser->FirstFrame().Time();
1029 mTotalFrameLen =
1030 static_cast<uint64_t>(streamLen) - mParser->FirstFrame().Offset();
1032 return mParsedFramesDuration;
1035 /* static */
1036 bool FlacDemuxer::FlacSniffer(const uint8_t* aData, const uint32_t aLength) {
1037 if (aLength < FLAC_MIN_FRAME_SIZE) {
1038 return false;
1041 flac::Frame frame;
1042 return frame.FindNext(aData, aLength) >= 0;
1045 } // namespace mozilla