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"
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
;
27 // flac::FrameHeader - Holds the flac frame header and its parsing
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
45 bool Parse(const uint8_t* aPacket
, size_t aBytes
) {
46 BitReader
br(aPacket
, aBytes
* 8);
49 if ((br
.ReadBits(15) & 0x7fff) != 0x7ffc) {
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
69 // invalid channel mode
74 int bps_code
= AssertedCast
<int>(br
.ReadBits(3));
75 if (bps_code
== 3 || bps_code
== 7) {
76 // Invalid sample size code.
79 mInfo
.mBitDepth
= FlacSampleSizeTable
[bps_code
];
81 // Reserved bit, must be 0.
83 // Broken stream, invalid padding.
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.
96 // reserved blocksize code
100 mBlocksize
= br
.ReadBits(8) + 1;
101 } else if (bs_code
== 7) {
102 mBlocksize
= br
.ReadBits(16) + 1;
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
);
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;
126 // Illegal sample rate code.
130 // Header CRC-8 check.
132 for (uint32_t i
= 0; i
< br
.BitCount() / 8; i
++) {
133 crc
= CRC8Table
[crc
^ aPacket
[i
]];
139 crc
== br
.ReadBits(8);
141 mSize
= br
.BitCount() / 8;
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
{}};
158 FLAC_CHMODE_INDEPENDENT
= 0,
159 FLAC_CHMODE_LEFT_SIDE
,
160 FLAC_CHMODE_RIGHT_SIDE
,
161 FLAC_CHMODE_MID_SIDE
,
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;
171 bool mVariableBlockSize
= false;
172 uint32_t mBlocksize
= 0;
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,
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.
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
234 uint32_t modOffset
= aLength
% 4;
237 for (i
= 0; i
< modOffset
; i
++) {
238 if ((BigEndian::readUint16(aData
+ i
) & 0xfffe) == 0xfff8) {
239 if (mHeader
.Parse(aData
+ i
, aLength
- 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
)) {
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;
267 nsTArray
<char> buffer
;
268 uint64_t originalOffset
= static_cast<uint64_t>(aResource
.Tell());
269 uint64_t offset
= originalOffset
;
270 uint32_t innerOffset
= 0;
274 buffer
.SetLength(BUFFER_SIZE
+ innerOffset
);
276 aResource
.Read(buffer
.Elements() + innerOffset
, BUFFER_SIZE
, &read
);
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
);
290 if (read
< BUFFER_SIZE
) {
291 // Nothing more to try on as we had reached EOS during the previous
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
);
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.
328 mHeader
.mIndex
= AssertedCast
<int64_t>(Header().mFrameOrSampleNum
*
329 aReferenceFrame
.Header().mBlocksize
);
332 uint32_t Size() const { return mSize
; }
334 TimeUnit
Time() const {
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 {
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(); }
367 void SetOffset(MediaResourceIndex
& aResource
, uint64_t aOffset
) {
369 aResource
.Seek(SEEK_SET
, AssertedCast
<int64_t>(mOffset
));
372 // The offset to the start of the header.
373 uint64_t mOffset
= 0;
375 uint32_t mDuration
= 0;
378 // The currently parsed frame header.
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() {
396 // Attempt to find the next frame.
397 bool FindNextFrame(MediaResourceIndex
& aResource
) {
399 if (GetNextFrame(aResource
)) {
400 if (!mFrame
.IsValid()) {
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
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(); }
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()),
461 // The frame doesn't match its CRC or would be too far, skip it..
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();
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
;
494 UniquePtr
<char[]> buffer(new char[static_cast<size_t>(size
)]);
496 if (NS_FAILED(aResource
.ReadAt(aStart
, buffer
.get(), size
, &read
)) ||
498 NS_WARNING("Couldn't read frame content");
503 uint8_t* buf
= reinterpret_cast<uint8_t*>(buffer
.get());
504 const uint8_t* end
= buf
+ size
;
506 crc
= CRC16Table
[((uint8_t)crc
) ^ *buf
++] ^ (crc
>> 8);
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.
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
,
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
) {
593 return RefPtr
<FlacTrackDemuxer
>(mTrackDemuxer
).forget();
596 bool FlacDemuxer::IsSeekable() const {
597 return mTrackDemuxer
&& mTrackDemuxer
->IsSeekable();
601 FlacTrackDemuxer::FlacTrackDemuxer(MediaResource
* aSource
)
602 : mSource(aSource
), mParser(new flac::FrameParser()), mTotalFrameLen(0) {
603 DDLINKCHILD("source", aSource
);
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
);
620 nsresult ret
= mSource
.ReadAt(offset
, buffer
, BUFFER_SIZE
, &read
);
621 if (NS_FAILED(ret
)) {
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.
629 uint32_t sizeHeader
= mParser
->HeaderBlockLength(ubuffer
);
630 RefPtr
<MediaByteBuffer
> block
= mSource
.MediaReadAt(offset
, sizeHeader
);
631 if (!block
|| block
->Length() != sizeHeader
) {
634 if (!mParser
->DecodeHeaderBlock(block
->Elements(), sizeHeader
)) {
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.
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.
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());
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");
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");
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.
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
722 mSource
.Seek(SEEK_SET
, 0);
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
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
;
753 mSource
.Seek(SEEK_SET
, pivot
);
755 bool found
= frame
.FindNext(mSource
);
757 NS_WARNING("We should have found a point");
760 if (!frame
.IsValid()) {
761 NS_WARNING("Invalid frame after seeking and sync on a frame");
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.
777 lastFoundOffset
= Some(frame
.Offset());
779 if (frame
.Time() == aTime
) {
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.
788 if (frame
.Time() > aTime
) {
790 pivot
-= (pivot
- first
) / 2;
793 pivot
+= (last
- pivot
) / 2;
797 if (lastFoundOffset
) {
798 mSource
.Seek(SEEK_SET
, AssertedCast
<int64_t>(lastFoundOffset
.ref()));
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()) {
826 // Seek back to the last frame found prior the target.
827 mParser
->EndFrameSession();
828 mSource
.Seek(SEEK_SET
, previousOffset
);
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(),
841 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR
,
845 RefPtr
<SamplesHolder
> frames
= new SamplesHolder();
847 while (aNumSamples
--) {
848 RefPtr
<MediaRawData
> frame(GetNextFrame(FindNextFrame()));
850 if (!frame
->HasValidTime()) {
851 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR
,
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
,
867 return SamplesPromise::CreateAndResolve(frames
, __func__
);
870 void FlacTrackDemuxer::Reset() {
873 if (mParser
->FirstFrame().IsValid()) {
874 mSource
.Seek(SEEK_SET
,
875 AssertedCast
<int64_t>(mParser
->FirstFrame().Offset()));
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());
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");
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");
953 const uint32_t read
= Read(frameWriter
->Data(), AssertedCast
<int64_t>(offset
),
954 AssertedCast
<int32_t>(size
));
956 LOG("GetNextFrame() Exit read=%u frame->Size=%zu", read
, frame
->Size());
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
,
975 const nsresult rv
= mSource
.ReadAt(aOffset
, reinterpret_cast<char*>(aBuffer
),
976 static_cast<uint32_t>(aSize
), &read
);
977 NS_ENSURE_SUCCESS(rv
, 0);
981 double FlacTrackDemuxer::AverageFrameLength() const {
982 if (mParsedFramesDuration
.ToMicroseconds()) {
983 return AssertedCast
<double>(mTotalFrameLen
) /
984 mParsedFramesDuration
.ToSeconds();
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.
1025 // Update our current progress stats.
1026 mParsedFramesDuration
=
1027 previousTime
+ previousDuration
- mParser
->FirstFrame().Time();
1030 static_cast<uint64_t>(streamLen
) - mParser
->FirstFrame().Offset();
1032 return mParsedFramesDuration
;
1036 bool FlacDemuxer::FlacSniffer(const uint8_t* aData
, const uint32_t aLength
) {
1037 if (aLength
< FLAC_MIN_FRAME_SIZE
) {
1042 return frame
.FindNext(aData
, aLength
) >= 0;
1045 } // namespace mozilla