Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mp3/MP3FrameParser.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "MP3FrameParser.h"
8
9
#include <algorithm>
10
#include <inttypes.h>
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/EndianUtils.h"
14
#include "mozilla/Pair.h"
15
#include "mozilla/ResultExtensions.h"
16
#include "VideoUtils.h"
17
18
extern mozilla::LazyLogModule gMediaDemuxerLog;
19
#define MP3LOG(msg, ...) \
20
0
  MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
21
#define MP3LOGV(msg, ...) \
22
0
  MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
23
24
namespace mozilla {
25
26
// FrameParser
27
28
namespace frame_header {
29
// FrameHeader mRaw byte offsets.
30
static const int SYNC1 = 0;
31
static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
32
static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
33
static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
34
} // namespace frame_header
35
36
FrameParser::FrameParser()
37
0
{
38
0
}
39
40
void
41
FrameParser::Reset()
42
0
{
43
0
  mID3Parser.Reset();
44
0
  mFrame.Reset();
45
0
}
46
47
void
48
FrameParser::ResetFrameData()
49
0
{
50
0
  mFrame.Reset();
51
0
  mFirstFrame.Reset();
52
0
  mPrevFrame.Reset();
53
0
}
54
55
void
56
FrameParser::EndFrameSession()
57
0
{
58
0
  if (!mID3Parser.Header().IsValid()) {
59
0
    // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
60
0
    mID3Parser.Reset();
61
0
  }
62
0
  mPrevFrame = mFrame;
63
0
  mFrame.Reset();
64
0
}
65
66
const FrameParser::Frame&
67
FrameParser::CurrentFrame() const
68
0
{
69
0
  return mFrame;
70
0
}
71
72
const FrameParser::Frame&
73
FrameParser::PrevFrame() const
74
0
{
75
0
  return mPrevFrame;
76
0
}
77
78
const FrameParser::Frame&
79
FrameParser::FirstFrame() const
80
0
{
81
0
  return mFirstFrame;
82
0
}
83
84
const ID3Parser::ID3Header&
85
FrameParser::ID3Header() const
86
0
{
87
0
  return mID3Parser.Header();
88
0
}
89
90
const FrameParser::VBRHeader&
91
FrameParser::VBRInfo() const
92
0
{
93
0
  return mVBRHeader;
94
0
}
95
96
Result<bool, nsresult>
97
FrameParser::Parse(BufferReader* aReader, uint32_t* aBytesToSkip)
98
0
{
99
0
  MOZ_ASSERT(aReader && aBytesToSkip);
100
0
  *aBytesToSkip = 0;
101
0
102
0
  if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) {
103
0
    // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
104
0
    // ID3v1 tags may only be at file end.
105
0
    // TODO: should we try to read ID3 tags at end of file/mid-stream, too?
106
0
    const size_t prevReaderOffset = aReader->Offset();
107
0
    uint32_t tagSize;
108
0
    MOZ_TRY_VAR(tagSize, mID3Parser.Parse(aReader));
109
0
    if (!!tagSize) {
110
0
      // ID3 tag found, skip past it.
111
0
      const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
112
0
113
0
      if (skipSize > aReader->Remaining()) {
114
0
        // Skipping across the ID3v2 tag would take us past the end of the
115
0
        // buffer, therefore we return immediately and let the calling function
116
0
        // handle skipping the rest of the tag.
117
0
        MP3LOGV("ID3v2 tag detected, size=%d,"
118
0
                " needing to skip %zu bytes past the current buffer",
119
0
                tagSize, skipSize - aReader->Remaining());
120
0
        *aBytesToSkip = skipSize - aReader->Remaining();
121
0
        return false;
122
0
      }
123
0
      MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
124
0
      aReader->Read(skipSize);
125
0
    } else {
126
0
      // No ID3v2 tag found, rewinding reader in order to search for a MPEG
127
0
      // frame header.
128
0
      aReader->Seek(prevReaderOffset);
129
0
    }
130
0
  }
131
0
132
0
  for (auto res = aReader->ReadU8();
133
0
       res.isOk() && !mFrame.ParseNext(res.unwrap()); res = aReader->ReadU8())
134
0
  {}
135
0
136
0
  if (mFrame.Length()) {
137
0
    // MP3 frame found.
138
0
    if (!mFirstFrame.Length()) {
139
0
      mFirstFrame = mFrame;
140
0
    }
141
0
    // Indicate success.
142
0
    return true;
143
0
  }
144
0
  return false;
145
0
}
146
147
// FrameParser::Header
148
149
FrameParser::FrameHeader::FrameHeader()
150
0
{
151
0
  Reset();
152
0
}
153
154
uint8_t
155
FrameParser::FrameHeader::Sync1() const
156
0
{
157
0
  return mRaw[frame_header::SYNC1];
158
0
}
159
160
uint8_t
161
FrameParser::FrameHeader::Sync2() const
162
0
{
163
0
  return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5;
164
0
}
165
166
uint8_t
167
FrameParser::FrameHeader::RawVersion() const
168
0
{
169
0
  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3;
170
0
}
171
172
uint8_t
173
FrameParser::FrameHeader::RawLayer() const
174
0
{
175
0
  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1;
176
0
}
177
178
uint8_t
179
FrameParser::FrameHeader::RawProtection() const
180
0
{
181
0
  return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6;
182
0
}
183
184
uint8_t
185
FrameParser::FrameHeader::RawBitrate() const
186
0
{
187
0
  return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4;
188
0
}
189
190
uint8_t
191
FrameParser::FrameHeader::RawSampleRate() const
192
0
{
193
0
  return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2;
194
0
}
195
196
uint8_t
197
FrameParser::FrameHeader::Padding() const
198
0
{
199
0
  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1;
200
0
}
201
202
uint8_t
203
FrameParser::FrameHeader::Private() const
204
0
{
205
0
  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE];
206
0
}
207
208
uint8_t
209
FrameParser::FrameHeader::RawChannelMode() const
210
0
{
211
0
  return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6;
212
0
}
213
214
int32_t
215
FrameParser::FrameHeader::Layer() const
216
0
{
217
0
  static const uint8_t LAYERS[4] = { 0, 3, 2, 1 };
218
0
219
0
  return LAYERS[RawLayer()];
220
0
}
221
222
int32_t
223
FrameParser::FrameHeader::SampleRate() const
224
0
{
225
0
  // Sample rates - use [version][srate]
226
0
  static const uint16_t SAMPLE_RATE[4][4] = {
227
0
    // clang-format off
228
0
    { 11025, 12000,  8000, 0 }, // MPEG 2.5
229
0
    {     0,     0,     0, 0 }, // Reserved
230
0
    { 22050, 24000, 16000, 0 }, // MPEG 2
231
0
    { 44100, 48000, 32000, 0 }  // MPEG 1
232
0
    // clang-format on
233
0
  };
234
0
235
0
  return SAMPLE_RATE[RawVersion()][RawSampleRate()];
236
0
}
237
238
int32_t
239
FrameParser::FrameHeader::Channels() const
240
0
{
241
0
  // 3 is single channel (mono), any other value is some variant of dual
242
0
  // channel.
243
0
  return RawChannelMode() == 3 ? 1 : 2;
244
0
}
245
246
int32_t
247
FrameParser::FrameHeader::SamplesPerFrame() const
248
0
{
249
0
  // Samples per frame - use [version][layer]
250
0
  static const uint16_t FRAME_SAMPLE[4][4] = {
251
0
    // clang-format off
252
0
    // Layer     3     2     1       Version
253
0
    {      0,  576, 1152,  384 }, // 2.5
254
0
    {      0,    0,    0,    0 }, // Reserved
255
0
    {      0,  576, 1152,  384 }, // 2
256
0
    {      0, 1152, 1152,  384 }  // 1
257
0
    // clang-format on
258
0
  };
259
0
260
0
  return FRAME_SAMPLE[RawVersion()][RawLayer()];
261
0
}
262
263
int32_t
264
FrameParser::FrameHeader::Bitrate() const
265
0
{
266
0
  // Bitrates - use [version][layer][bitrate]
267
0
  static const uint16_t BITRATE[4][4][16] = {
268
0
    // clang-format off
269
0
    { // Version 2.5
270
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
271
0
      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
272
0
      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
273
0
      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
274
0
    },
275
0
    { // Reserved
276
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
277
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
278
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
279
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }  // Invalid
280
0
    },
281
0
    { // Version 2
282
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
283
0
      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
284
0
      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
285
0
      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
286
0
    },
287
0
    { // Version 1
288
0
      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
289
0
      { 0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
290
0
      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
291
0
      { 0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
292
0
    }
293
0
    // clang-format on
294
0
  };
295
0
296
0
  return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()];
297
0
}
298
299
int32_t
300
FrameParser::FrameHeader::SlotSize() const
301
0
{
302
0
  // Slot size (MPEG unit of measurement) - use [layer]
303
0
  static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
304
0
305
0
  return SLOT_SIZE[RawLayer()];
306
0
}
307
308
bool
309
FrameParser::FrameHeader::ParseNext(uint8_t c)
310
0
{
311
0
  if (!Update(c)) {
312
0
    Reset();
313
0
    if (!Update(c)) {
314
0
      Reset();
315
0
    }
316
0
  }
317
0
  return IsValid();
318
0
}
319
320
bool
321
FrameParser::FrameHeader::IsValid(int aPos) const
322
0
{
323
0
  if (aPos >= SIZE) {
324
0
    return true;
325
0
  }
326
0
  if (aPos == frame_header::SYNC1) {
327
0
    return Sync1() == 0xFF;
328
0
  }
329
0
  if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) {
330
0
    return Sync2() == 7 &&
331
0
           RawVersion() != 1 &&
332
0
           Layer() == 3;
333
0
  }
334
0
  if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) {
335
0
    return RawBitrate() != 0xF && RawBitrate() != 0 &&
336
0
           RawSampleRate() != 3;
337
0
  }
338
0
  return true;
339
0
}
340
341
bool
342
FrameParser::FrameHeader::IsValid() const
343
0
{
344
0
  return mPos >= SIZE;
345
0
}
346
347
void
348
FrameParser::FrameHeader::Reset()
349
0
{
350
0
  mPos = 0;
351
0
}
352
353
bool
354
FrameParser::FrameHeader::Update(uint8_t c)
355
0
{
356
0
  if (mPos < SIZE) {
357
0
    mRaw[mPos] = c;
358
0
  }
359
0
  return IsValid(mPos++);
360
0
}
361
362
// FrameParser::VBRHeader
363
364
namespace vbr_header {
365
static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"};
366
static const uint32_t TOC_SIZE = 100;
367
} // namespace vbr_header
368
369
FrameParser::VBRHeader::VBRHeader()
370
  : mType(NONE)
371
0
{
372
0
}
373
374
FrameParser::VBRHeader::VBRHeaderType
375
FrameParser::VBRHeader::Type() const
376
0
{
377
0
  return mType;
378
0
}
379
380
const Maybe<uint32_t>&
381
FrameParser::VBRHeader::NumAudioFrames() const
382
0
{
383
0
  return mNumAudioFrames;
384
0
}
385
386
const Maybe<uint32_t>&
387
FrameParser::VBRHeader::NumBytes() const
388
0
{
389
0
  return mNumBytes;
390
0
}
391
392
const Maybe<uint32_t>&
393
FrameParser::VBRHeader::Scale() const
394
0
{
395
0
  return mScale;
396
0
}
397
398
bool
399
FrameParser::VBRHeader::IsTOCPresent() const
400
0
{
401
0
  return mTOC.size() == vbr_header::TOC_SIZE;
402
0
}
403
404
bool
405
FrameParser::VBRHeader::IsValid() const
406
0
{
407
0
  return mType != NONE;
408
0
}
409
410
bool
411
FrameParser::VBRHeader::IsComplete() const
412
0
{
413
0
  return IsValid() &&
414
0
         mNumAudioFrames.valueOr(0) > 0 &&
415
0
         mNumBytes.valueOr(0) > 0
416
0
         // We don't care about the scale for any computations here.
417
0
         // && mScale < 101
418
0
         ;
419
0
}
420
421
int64_t
422
FrameParser::VBRHeader::Offset(float aDurationFac) const
423
0
{
424
0
  if (!IsTOCPresent()) {
425
0
    return -1;
426
0
  }
427
0
428
0
  // Constrain the duration percentage to [0, 99].
429
0
  const float durationPer =
430
0
    100.0f * std::min(0.99f, std::max(0.0f, aDurationFac));
431
0
  const size_t fullPer = durationPer;
432
0
  const float rest = durationPer - fullPer;
433
0
434
0
  MOZ_ASSERT(fullPer < mTOC.size());
435
0
  int64_t offset = mTOC.at(fullPer);
436
0
437
0
  if (rest > 0.0 && fullPer + 1 < mTOC.size()) {
438
0
    offset += rest * (mTOC.at(fullPer + 1) - offset);
439
0
  }
440
0
441
0
  return offset;
442
0
}
443
444
Result<bool, nsresult>
445
FrameParser::VBRHeader::ParseXing(BufferReader* aReader)
446
0
{
447
0
  static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
448
0
  static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
449
0
450
0
  enum Flags
451
0
  {
452
0
    NUM_FRAMES = 0x01,
453
0
    NUM_BYTES = 0x02,
454
0
    TOC = 0x04,
455
0
    VBR_SCALE = 0x08
456
0
  };
457
0
458
0
  MOZ_ASSERT(aReader);
459
0
  const size_t prevReaderOffset = aReader->Offset();
460
0
461
0
  // We have to search for the Xing header as its position can change.
462
0
  for (auto res = aReader->PeekU32();
463
0
       res.isOk() && res.unwrap() != XING_TAG && res.unwrap() != INFO_TAG;) {
464
0
    aReader->Read(1);
465
0
    res = aReader->PeekU32();
466
0
  }
467
0
468
0
  // Skip across the VBR header ID tag.
469
0
  MOZ_TRY(aReader->ReadU32());
470
0
  mType = XING;
471
0
472
0
  uint32_t flags;
473
0
  MOZ_TRY_VAR(flags, aReader->ReadU32());
474
0
475
0
  if (flags & NUM_FRAMES) {
476
0
    uint32_t frames;
477
0
    MOZ_TRY_VAR(frames, aReader->ReadU32());
478
0
    mNumAudioFrames = Some(frames);
479
0
  }
480
0
  if (flags & NUM_BYTES) {
481
0
    uint32_t bytes;
482
0
    MOZ_TRY_VAR(bytes, aReader->ReadU32());
483
0
    mNumBytes = Some(bytes);
484
0
  }
485
0
  if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
486
0
    if (!mNumBytes) {
487
0
      // We don't have the stream size to calculate offsets, skip the TOC.
488
0
      aReader->Read(vbr_header::TOC_SIZE);
489
0
    } else {
490
0
      mTOC.clear();
491
0
      mTOC.reserve(vbr_header::TOC_SIZE);
492
0
      uint8_t data;
493
0
      for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
494
0
        MOZ_TRY_VAR(data, aReader->ReadU8());
495
0
        mTOC.push_back(1.0f / 256.0f * data * mNumBytes.value());
496
0
      }
497
0
    }
498
0
  }
499
0
  if (flags & VBR_SCALE) {
500
0
    uint32_t scale;
501
0
    MOZ_TRY_VAR(scale, aReader->ReadU32());
502
0
    mScale = Some(scale);
503
0
  }
504
0
505
0
  aReader->Seek(prevReaderOffset);
506
0
  return mType == XING;
507
0
}
508
509
Result<bool, nsresult>
510
FrameParser::VBRHeader::ParseVBRI(BufferReader* aReader)
511
0
{
512
0
  static const uint32_t TAG = BigEndian::readUint32("VBRI");
513
0
  static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
514
0
  static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14;
515
0
  static const uint32_t MIN_FRAME_SIZE = OFFSET + 26;
516
0
517
0
  MOZ_ASSERT(aReader);
518
0
  // ParseVBRI assumes that the ByteReader offset points to the beginning of a
519
0
  // frame, therefore as a simple check, we look for the presence of a frame
520
0
  // sync at that position.
521
0
  auto sync = aReader->PeekU16();
522
0
  if (sync.isOk()) { // To avoid compiler complains 'set but unused'.
523
0
    MOZ_ASSERT((sync.unwrap() & 0xFFE0) == 0xFFE0);
524
0
  }
525
0
  const size_t prevReaderOffset = aReader->Offset();
526
0
527
0
  // VBRI have a fixed relative position, so let's check for it there.
528
0
  if (aReader->Remaining() > MIN_FRAME_SIZE) {
529
0
    aReader->Seek(prevReaderOffset + OFFSET);
530
0
    uint32_t tag, frames;
531
0
    MOZ_TRY_VAR(tag, aReader->ReadU32());
532
0
    if (tag == TAG) {
533
0
      aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
534
0
      MOZ_TRY_VAR(frames, aReader->ReadU32());
535
0
      mNumAudioFrames = Some(frames);
536
0
      mType = VBRI;
537
0
      aReader->Seek(prevReaderOffset);
538
0
      return true;
539
0
    }
540
0
  }
541
0
  aReader->Seek(prevReaderOffset);
542
0
  return false;
543
0
}
544
545
bool
546
FrameParser::VBRHeader::Parse(BufferReader* aReader)
547
0
{
548
0
  auto res = MakePair(ParseVBRI(aReader), ParseXing(aReader));
549
0
  const bool rv = (res.first().isOk() && res.first().unwrap()) ||
550
0
                  (res.second().isOk() && res.second().unwrap());
551
0
  if (rv) {
552
0
    MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s"
553
0
           " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%zu",
554
0
           vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0),
555
0
           NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size());
556
0
  }
557
0
  return rv;
558
0
}
559
560
// FrameParser::Frame
561
562
void
563
FrameParser::Frame::Reset()
564
0
{
565
0
  mHeader.Reset();
566
0
}
567
568
int32_t
569
FrameParser::Frame::Length() const
570
0
{
571
0
  if (!mHeader.IsValid() || !mHeader.SampleRate()) {
572
0
    return 0;
573
0
  }
574
0
575
0
  const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f;
576
0
  const int32_t frameLen = bitsPerSample * mHeader.Bitrate()
577
0
                           / mHeader.SampleRate()
578
0
                           + mHeader.Padding() * mHeader.SlotSize();
579
0
  return frameLen;
580
0
}
581
582
bool
583
FrameParser::Frame::ParseNext(uint8_t c)
584
0
{
585
0
  return mHeader.ParseNext(c);
586
0
}
587
588
const FrameParser::FrameHeader&
589
FrameParser::Frame::Header() const
590
0
{
591
0
  return mHeader;
592
0
}
593
594
bool
595
FrameParser::ParseVBRHeader(BufferReader* aReader)
596
0
{
597
0
  return mVBRHeader.Parse(aReader);
598
0
}
599
600
// ID3Parser
601
602
// Constants
603
namespace id3_header {
604
static const int ID_LEN = 3;
605
static const int VERSION_LEN = 2;
606
static const int FLAGS_LEN = 1;
607
static const int SIZE_LEN = 4;
608
609
static const int ID_END = ID_LEN;
610
static const int VERSION_END = ID_END + VERSION_LEN;
611
static const int FLAGS_END = VERSION_END + FLAGS_LEN;
612
static const int SIZE_END = FLAGS_END + SIZE_LEN;
613
614
static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
615
616
static const uint8_t MIN_MAJOR_VER = 2;
617
static const uint8_t MAX_MAJOR_VER = 4;
618
} // namespace id3_header
619
620
Result<uint32_t, nsresult>
621
ID3Parser::Parse(BufferReader* aReader)
622
0
{
623
0
  MOZ_ASSERT(aReader);
624
0
625
0
  for (auto res = aReader->ReadU8();
626
0
       res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader->ReadU8())
627
0
  {}
628
0
629
0
  return mHeader.TotalTagSize();
630
0
}
631
632
void
633
ID3Parser::Reset()
634
0
{
635
0
  mHeader.Reset();
636
0
}
637
638
const ID3Parser::ID3Header&
639
ID3Parser::Header() const
640
0
{
641
0
  return mHeader;
642
0
}
643
644
// ID3Parser::Header
645
646
ID3Parser::ID3Header::ID3Header()
647
0
{
648
0
  Reset();
649
0
}
650
651
void
652
ID3Parser::ID3Header::Reset()
653
0
{
654
0
  mSize = 0;
655
0
  mPos = 0;
656
0
}
657
658
uint8_t
659
ID3Parser::ID3Header::MajorVersion() const
660
0
{
661
0
  return mRaw[id3_header::ID_END];
662
0
}
663
664
uint8_t
665
ID3Parser::ID3Header::MinorVersion() const
666
0
{
667
0
  return mRaw[id3_header::ID_END + 1];
668
0
}
669
670
uint8_t
671
ID3Parser::ID3Header::Flags() const
672
0
{
673
0
  return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN];
674
0
}
675
676
uint32_t
677
ID3Parser::ID3Header::Size() const
678
0
{
679
0
  if (!IsValid()) {
680
0
    return 0;
681
0
  }
682
0
  return mSize;
683
0
}
684
685
uint8_t
686
ID3Parser::ID3Header::FooterSize() const
687
0
{
688
0
  if (Flags() & (1 << 4)) {
689
0
    return SIZE;
690
0
  }
691
0
  return 0;
692
0
}
693
694
uint32_t
695
ID3Parser::ID3Header::TotalTagSize() const
696
0
{
697
0
  if (IsValid()) {
698
0
    // Header found, return total tag size.
699
0
    return ID3Header::SIZE + Size() + FooterSize();
700
0
  }
701
0
  return 0;
702
0
}
703
704
bool
705
ID3Parser::ID3Header::ParseNext(uint8_t c)
706
0
{
707
0
  if (!Update(c)) {
708
0
    Reset();
709
0
    if (!Update(c)) {
710
0
      Reset();
711
0
    }
712
0
  }
713
0
  return IsValid();
714
0
}
715
716
bool
717
ID3Parser::ID3Header::IsValid(int aPos) const
718
0
{
719
0
  if (aPos >= SIZE) {
720
0
    return true;
721
0
  }
722
0
  const uint8_t c = mRaw[aPos];
723
0
  switch (aPos) {
724
0
    case 0: case 1: case 2:
725
0
      // Expecting "ID3".
726
0
      return id3_header::ID[aPos] == c;
727
0
    case 3:
728
0
      return MajorVersion() >= id3_header::MIN_MAJOR_VER &&
729
0
             MajorVersion() <= id3_header::MAX_MAJOR_VER;
730
0
    case 4:
731
0
      return MinorVersion() < 0xFF;
732
0
    case 5:
733
0
      // Validate flags for supported versions, see bug 949036.
734
0
      return ((0xFF >> MajorVersion()) & c) == 0;
735
0
    case 6: case 7: case 8: case 9:
736
0
      return c < 0x80;
737
0
  }
738
0
  return true;
739
0
}
740
741
bool
742
ID3Parser::ID3Header::IsValid() const
743
0
{
744
0
  return mPos >= SIZE;
745
0
}
746
747
bool
748
ID3Parser::ID3Header::Update(uint8_t c)
749
0
{
750
0
  if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN &&
751
0
      mPos < id3_header::SIZE_END) {
752
0
    mSize <<= 7;
753
0
    mSize |= c;
754
0
  }
755
0
  if (mPos < SIZE) {
756
0
    mRaw[mPos] = c;
757
0
  }
758
0
  return IsValid(mPos++);
759
0
}
760
761
} // namespace mozilla