Coverage Report

Created: 2018-09-25 14:53

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