Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/wave/WaveDemuxer.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 "WaveDemuxer.h"
8
9
#include <inttypes.h>
10
#include <algorithm>
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/EndianUtils.h"
14
#include "BufferReader.h"
15
#include "VideoUtils.h"
16
#include "TimeUnits.h"
17
18
using mozilla::media::TimeUnit;
19
using mozilla::media::TimeIntervals;
20
21
namespace mozilla {
22
23
// WAVDemuxer
24
25
WAVDemuxer::WAVDemuxer(MediaResource* aSource)
26
  : mSource(aSource)
27
0
{
28
0
  DDLINKCHILD("source", aSource);
29
0
}
30
31
bool
32
WAVDemuxer::InitInternal()
33
0
{
34
0
  if (!mTrackDemuxer) {
35
0
    mTrackDemuxer = new WAVTrackDemuxer(mSource.GetResource());
36
0
    DDLINKCHILD("track demuxer", mTrackDemuxer.get());
37
0
  }
38
0
  return mTrackDemuxer->Init();
39
0
}
40
41
RefPtr<WAVDemuxer::InitPromise>
42
WAVDemuxer::Init()
43
0
{
44
0
  if (!InitInternal()) {
45
0
    return InitPromise::CreateAndReject(
46
0
      NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
47
0
  }
48
0
  return InitPromise::CreateAndResolve(NS_OK, __func__);
49
0
}
50
51
uint32_t
52
WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
53
0
{
54
0
  return aType == TrackInfo::kAudioTrack ? 1u : 0u;
55
0
}
56
57
already_AddRefed<MediaTrackDemuxer>
58
WAVDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
59
0
{
60
0
  if (!mTrackDemuxer) {
61
0
    return nullptr;
62
0
  }
63
0
  return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget();
64
0
}
65
66
bool
67
WAVDemuxer::IsSeekable() const
68
0
{
69
0
  return true;
70
0
}
71
72
// WAVTrackDemuxer
73
74
WAVTrackDemuxer::WAVTrackDemuxer(MediaResource* aSource)
75
  : mSource(aSource)
76
  , mOffset(0)
77
  , mFirstChunkOffset(0)
78
  , mNumParsedChunks(0)
79
  , mChunkIndex(0)
80
  , mDataLength(0)
81
  , mTotalChunkLen(0)
82
  , mSamplesPerChunk(0)
83
  , mSamplesPerSecond(0)
84
  , mChannels(0)
85
  , mSampleFormat(0)
86
0
{
87
0
  DDLINKCHILD("source", aSource);
88
0
  Reset();
89
0
}
90
91
bool
92
WAVTrackDemuxer::Init()
93
0
{
94
0
  Reset();
95
0
  FastSeek(TimeUnit());
96
0
97
0
  if (!mInfo) {
98
0
    mInfo = MakeUnique<AudioInfo>();
99
0
  }
100
0
101
0
  if (!RIFFParserInit()) {
102
0
    return false;
103
0
  }
104
0
105
0
  while (true) {
106
0
    if (!HeaderParserInit()) {
107
0
      return false;
108
0
    }
109
0
110
0
    uint32_t aChunkName = mHeaderParser.GiveHeader().ChunkName();
111
0
    uint32_t aChunkSize = mHeaderParser.GiveHeader().ChunkSize();
112
0
113
0
    if (aChunkName == FRMT_CODE) {
114
0
      if (!FmtChunkParserInit()) {
115
0
        return false;
116
0
      }
117
0
    } else if (aChunkName == LIST_CODE) {
118
0
      mHeaderParser.Reset();
119
0
      uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + aChunkSize;
120
0
      if (endOfListChunk > UINT32_MAX) {
121
0
        return false;
122
0
      }
123
0
      if (!ListChunkParserInit(aChunkSize)) {
124
0
        mOffset = endOfListChunk;
125
0
      }
126
0
    } else if (aChunkName == DATA_CODE) {
127
0
      mDataLength = aChunkSize;
128
0
      if (mFirstChunkOffset != mOffset) {
129
0
        mFirstChunkOffset = mOffset;
130
0
      }
131
0
      break;
132
0
    } else {
133
0
      mOffset += aChunkSize; // Skip other irrelevant chunks.
134
0
    }
135
0
    if (mOffset & 1) {
136
0
      // Wave files are 2-byte aligned so we need to round up
137
0
      mOffset += 1;
138
0
    }
139
0
    mHeaderParser.Reset();
140
0
  }
141
0
142
0
  if (mDataLength > StreamLength() - mFirstChunkOffset) {
143
0
    mDataLength = StreamLength() - mFirstChunkOffset;
144
0
  }
145
0
146
0
  mSamplesPerSecond = mFmtParser.FmtChunk().SampleRate();
147
0
  mChannels = mFmtParser.FmtChunk().Channels();
148
0
  mSampleFormat = mFmtParser.FmtChunk().SampleFormat();
149
0
  if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
150
0
    return false;
151
0
  }
152
0
  mSamplesPerChunk = DATA_CHUNK_SIZE * 8 / mChannels / mSampleFormat;
153
0
154
0
  mInfo->mRate = mSamplesPerSecond;
155
0
  mInfo->mChannels = mChannels;
156
0
  mInfo->mBitDepth = mSampleFormat;
157
0
  mInfo->mProfile = mFmtParser.FmtChunk().WaveFormat() & 0x00FF;
158
0
  mInfo->mExtendedProfile = (mFmtParser.FmtChunk().WaveFormat() & 0xFF00) >> 8;
159
0
  mInfo->mMimeType = "audio/wave; codecs=";
160
0
  mInfo->mMimeType.AppendInt(mFmtParser.FmtChunk().WaveFormat());
161
0
  mInfo->mDuration = Duration();
162
0
163
0
  return mInfo->mDuration.IsPositive();
164
0
}
165
166
bool
167
WAVTrackDemuxer::RIFFParserInit()
168
0
{
169
0
  RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader());
170
0
  if (!riffHeader) {
171
0
    return false;
172
0
  }
173
0
  BufferReader RIFFReader(riffHeader->Data(), 12);
174
0
  Unused << mRIFFParser.Parse(RIFFReader);
175
0
  return mRIFFParser.RiffHeader().IsValid(11);
176
0
}
177
178
bool
179
WAVTrackDemuxer::HeaderParserInit()
180
0
{
181
0
  RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader());
182
0
  if (!header) {
183
0
    return false;
184
0
  }
185
0
  BufferReader HeaderReader(header->Data(), 8);
186
0
  Unused << mHeaderParser.Parse(HeaderReader);
187
0
  return true;
188
0
}
189
190
bool
191
WAVTrackDemuxer::FmtChunkParserInit()
192
0
{
193
0
  RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk());
194
0
  if (!fmtChunk) {
195
0
    return false;
196
0
  }
197
0
  BufferReader fmtReader(fmtChunk->Data(),
198
0
                         mHeaderParser.GiveHeader().ChunkSize());
199
0
  Unused << mFmtParser.Parse(fmtReader);
200
0
  return true;
201
0
}
202
203
bool
204
WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize)
205
0
{
206
0
  uint32_t bytesRead = 0;
207
0
208
0
  RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag());
209
0
  if (!infoTag) {
210
0
    return false;
211
0
  }
212
0
213
0
  BufferReader infoTagReader(infoTag->Data(), 4);
214
0
  auto res = infoTagReader.ReadU32();
215
0
  if (res.isErr() || (res.isOk() && res.unwrap() != INFO_CODE)) {
216
0
    return false;
217
0
  }
218
0
219
0
  bytesRead += 4;
220
0
221
0
  while (bytesRead < aChunkSize) {
222
0
    if (!HeaderParserInit()) {
223
0
      return false;
224
0
    }
225
0
226
0
    bytesRead += 8;
227
0
228
0
    uint32_t id = mHeaderParser.GiveHeader().ChunkName();
229
0
    uint32_t length = mHeaderParser.GiveHeader().ChunkSize();
230
0
231
0
    // SubChunk Length Cannot Exceed List Chunk length.
232
0
    if (length > aChunkSize - bytesRead) {
233
0
      length = aChunkSize - bytesRead;
234
0
    }
235
0
236
0
    MediaByteRange mRange = { mOffset, mOffset + length };
237
0
    RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
238
0
    if (!mChunkData) {
239
0
      return false;
240
0
    }
241
0
242
0
    const char* rawData = reinterpret_cast<const char*>(mChunkData->Data());
243
0
244
0
    nsCString val(rawData, length);
245
0
    if (length > 0 && val[length - 1] == '\0') {
246
0
      val.SetLength(length - 1);
247
0
    }
248
0
249
0
    if (length % 2) {
250
0
      mOffset += 1;
251
0
      length += length % 2;
252
0
    }
253
0
254
0
    bytesRead += length;
255
0
256
0
    if (!IsUTF8(val)) {
257
0
      mHeaderParser.Reset();
258
0
      continue;
259
0
    }
260
0
261
0
    switch (id) {
262
0
      case 0x49415254:                // IART
263
0
        mInfo->mTags.AppendElement(
264
0
          MetadataTag(NS_LITERAL_CSTRING("artist"), val));
265
0
        break;
266
0
      case 0x49434d54:                // ICMT
267
0
        mInfo->mTags.AppendElement(
268
0
          MetadataTag(NS_LITERAL_CSTRING("comments"), val));
269
0
        break;
270
0
      case 0x49474e52:                // IGNR
271
0
        mInfo->mTags.AppendElement(
272
0
          MetadataTag(NS_LITERAL_CSTRING("genre"), val));
273
0
        break;
274
0
      case 0x494e414d:                // INAM
275
0
        mInfo->mTags.AppendElement(
276
0
          MetadataTag(NS_LITERAL_CSTRING("name"), val));
277
0
        break;
278
0
    }
279
0
280
0
    mHeaderParser.Reset();
281
0
  }
282
0
  return true;
283
0
}
284
285
media::TimeUnit
286
WAVTrackDemuxer::SeekPosition() const
287
0
{
288
0
  TimeUnit pos = Duration(mChunkIndex);
289
0
  if (Duration() > TimeUnit()) {
290
0
    pos = std::min(Duration(), pos);
291
0
  }
292
0
  return pos;
293
0
}
294
295
RefPtr<MediaRawData>
296
WAVTrackDemuxer::DemuxSample()
297
0
{
298
0
  return GetNextChunk(FindNextChunk());
299
0
}
300
301
UniquePtr<TrackInfo>
302
WAVTrackDemuxer::GetInfo() const
303
0
{
304
0
  return mInfo->Clone();
305
0
}
306
307
RefPtr<WAVTrackDemuxer::SeekPromise>
308
WAVTrackDemuxer::Seek(const TimeUnit& aTime)
309
0
{
310
0
  FastSeek(aTime);
311
0
  const TimeUnit seekTime = ScanUntil(aTime);
312
0
  return SeekPromise::CreateAndResolve(seekTime, __func__);
313
0
}
314
315
TimeUnit
316
WAVTrackDemuxer::FastSeek(const TimeUnit& aTime)
317
0
{
318
0
  if (aTime.ToMicroseconds()) {
319
0
    mChunkIndex = ChunkIndexFromTime(aTime);
320
0
  } else {
321
0
    mChunkIndex = 0;
322
0
  }
323
0
324
0
  mOffset = OffsetFromChunkIndex(mChunkIndex);
325
0
326
0
  if (mOffset > mFirstChunkOffset && StreamLength() > 0) {
327
0
    mOffset = std::min(StreamLength() - 1, mOffset);
328
0
  }
329
0
330
0
  return Duration(mChunkIndex);
331
0
}
332
333
TimeUnit
334
WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime)
335
0
{
336
0
  if (!aTime.ToMicroseconds()) {
337
0
    return FastSeek(aTime);
338
0
  }
339
0
340
0
  if (Duration(mChunkIndex) > aTime) {
341
0
    FastSeek(aTime);
342
0
  }
343
0
344
0
  return SeekPosition();
345
0
}
346
347
RefPtr<WAVTrackDemuxer::SamplesPromise>
348
WAVTrackDemuxer::GetSamples(int32_t aNumSamples)
349
0
{
350
0
  MOZ_ASSERT(aNumSamples);
351
0
352
0
  RefPtr<SamplesHolder> datachunks = new SamplesHolder();
353
0
354
0
  while (aNumSamples--) {
355
0
    RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk());
356
0
    if (!datachunk) {
357
0
      break;
358
0
    }
359
0
    datachunks->mSamples.AppendElement(datachunk);
360
0
  }
361
0
362
0
  if (datachunks->mSamples.IsEmpty()) {
363
0
    return SamplesPromise::CreateAndReject(
364
0
      NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
365
0
  }
366
0
367
0
  return SamplesPromise::CreateAndResolve(datachunks, __func__);
368
0
}
369
370
void
371
WAVTrackDemuxer::Reset()
372
0
{
373
0
  FastSeek(TimeUnit());
374
0
  mParser.Reset();
375
0
  mHeaderParser.Reset();
376
0
  mRIFFParser.Reset();
377
0
  mFmtParser.Reset();
378
0
}
379
380
RefPtr<WAVTrackDemuxer::SkipAccessPointPromise>
381
WAVTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
382
0
{
383
0
  return SkipAccessPointPromise::CreateAndReject(
384
0
    SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
385
0
}
386
387
int64_t
388
WAVTrackDemuxer::GetResourceOffset() const
389
0
{
390
0
  return mOffset;
391
0
}
392
393
TimeIntervals
394
WAVTrackDemuxer::GetBuffered()
395
0
{
396
0
  TimeUnit duration = Duration();
397
0
398
0
  if (duration <= TimeUnit()) {
399
0
    return TimeIntervals();
400
0
  }
401
0
402
0
  AutoPinned<MediaResource> stream(mSource.GetResource());
403
0
  return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
404
0
}
405
406
int64_t
407
WAVTrackDemuxer::StreamLength() const
408
0
{
409
0
  return mSource.GetLength();
410
0
}
411
412
TimeUnit
413
WAVTrackDemuxer::Duration() const
414
0
{
415
0
  if (!mDataLength ||!mChannels || !mSampleFormat) {
416
0
    return TimeUnit();
417
0
  }
418
0
419
0
  int64_t numSamples =
420
0
    static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat;
421
0
422
0
  int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
423
0
424
0
  if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
425
0
    numUSeconds++;
426
0
  }
427
0
428
0
  return TimeUnit::FromMicroseconds(numUSeconds);
429
0
}
430
431
TimeUnit
432
WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
433
0
{
434
0
  if (!mSamplesPerSecond || !mSamplesPerChunk) {
435
0
    return TimeUnit();
436
0
  }
437
0
  const double usPerDataChunk =
438
0
    USECS_PER_S * static_cast<double>(mSamplesPerChunk) / mSamplesPerSecond;
439
0
  return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
440
0
}
441
442
TimeUnit
443
WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
444
0
{
445
0
  if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
446
0
    return TimeUnit();
447
0
  }
448
0
449
0
  int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
450
0
451
0
  int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
452
0
453
0
  if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
454
0
    numUSeconds++;
455
0
  }
456
0
457
0
  return TimeUnit::FromMicroseconds(numUSeconds);
458
0
}
459
460
MediaByteRange
461
WAVTrackDemuxer::FindNextChunk()
462
0
{
463
0
  if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) {
464
0
    return { mOffset, mOffset + DATA_CHUNK_SIZE };
465
0
  } else {
466
0
    return { mOffset, mFirstChunkOffset + mDataLength };
467
0
  }
468
0
}
469
470
MediaByteRange
471
WAVTrackDemuxer::FindChunkHeader()
472
0
{
473
0
  return { mOffset, mOffset + CHUNK_HEAD_SIZE };
474
0
}
475
476
MediaByteRange
477
WAVTrackDemuxer::FindRIFFHeader()
478
0
{
479
0
  return { mOffset, mOffset + RIFF_CHUNK_SIZE };
480
0
}
481
482
MediaByteRange
483
WAVTrackDemuxer::FindFmtChunk()
484
0
{
485
0
  return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
486
0
}
487
488
MediaByteRange
489
WAVTrackDemuxer::FindListChunk()
490
0
{
491
0
  return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
492
0
}
493
494
MediaByteRange
495
WAVTrackDemuxer::FindInfoTag()
496
0
{
497
0
  return { mOffset, mOffset + 4 };
498
0
}
499
500
bool
501
WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange)
502
0
{
503
0
  UpdateState(aRange);
504
0
  return true;
505
0
}
506
507
already_AddRefed<MediaRawData>
508
WAVTrackDemuxer::GetNextChunk(const MediaByteRange& aRange)
509
0
{
510
0
  if (!aRange.Length()) {
511
0
    return nullptr;
512
0
  }
513
0
514
0
  RefPtr<MediaRawData> datachunk = new MediaRawData();
515
0
  datachunk->mOffset = aRange.mStart;
516
0
517
0
  UniquePtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
518
0
  if (!chunkWriter->SetSize(aRange.Length())) {
519
0
    return nullptr;
520
0
  }
521
0
522
0
  const uint32_t read =
523
0
    Read(chunkWriter->Data(), datachunk->mOffset, datachunk->Size());
524
0
525
0
  if (read != aRange.Length()) {
526
0
    return nullptr;
527
0
  }
528
0
529
0
  UpdateState(aRange);
530
0
  ++mNumParsedChunks;
531
0
  ++mChunkIndex;
532
0
533
0
  datachunk->mTime = Duration(mChunkIndex - 1);
534
0
535
0
  if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
536
0
    datachunk->mDuration = Duration(1);
537
0
  } else {
538
0
    uint32_t mBytesRemaining =
539
0
      mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
540
0
    datachunk->mDuration = DurationFromBytes(mBytesRemaining);
541
0
  }
542
0
  datachunk->mTimecode = datachunk->mTime;
543
0
  datachunk->mKeyframe = true;
544
0
545
0
  MOZ_ASSERT(!datachunk->mTime.IsNegative());
546
0
  MOZ_ASSERT(!datachunk->mDuration.IsNegative());
547
0
548
0
  return datachunk.forget();
549
0
}
550
551
already_AddRefed<MediaRawData>
552
WAVTrackDemuxer::GetFileHeader(const MediaByteRange& aRange)
553
0
{
554
0
  if (!aRange.Length()) {
555
0
    return nullptr;
556
0
  }
557
0
558
0
  RefPtr<MediaRawData> fileHeader = new MediaRawData();
559
0
  fileHeader->mOffset = aRange.mStart;
560
0
561
0
  UniquePtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
562
0
  if (!headerWriter->SetSize(aRange.Length())) {
563
0
    return nullptr;
564
0
  }
565
0
566
0
  const uint32_t read =
567
0
    Read(headerWriter->Data(), fileHeader->mOffset, fileHeader->Size());
568
0
569
0
  if (read != aRange.Length()) {
570
0
    return nullptr;
571
0
  }
572
0
573
0
  UpdateState(aRange);
574
0
575
0
  return fileHeader.forget();
576
0
}
577
578
int64_t
579
WAVTrackDemuxer::OffsetFromChunkIndex(int64_t aChunkIndex) const
580
0
{
581
0
  return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE;
582
0
}
583
584
int64_t
585
WAVTrackDemuxer::ChunkIndexFromOffset(int64_t aOffset) const
586
0
{
587
0
  int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
588
0
  return std::max<int64_t>(0, chunkIndex);
589
0
}
590
591
int64_t
592
WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
593
0
{
594
0
  if (!mSamplesPerChunk || !mSamplesPerSecond) {
595
0
    return 0;
596
0
  }
597
0
  int64_t chunkIndex =
598
0
    (aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
599
0
  return chunkIndex;
600
0
}
601
602
void
603
WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
604
0
{
605
0
  // Full chunk parsed, move offset to its end.
606
0
  mOffset = aRange.mEnd;
607
0
  mTotalChunkLen += aRange.Length();
608
0
}
609
610
int32_t
611
WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
612
0
{
613
0
  const int64_t streamLen = StreamLength();
614
0
  if (mInfo && streamLen > 0) {
615
0
    aSize = std::min<int64_t>(aSize, streamLen - aOffset);
616
0
  }
617
0
  uint32_t read = 0;
618
0
  const nsresult rv = mSource.ReadAt(aOffset,
619
0
                                     reinterpret_cast<char*>(aBuffer),
620
0
                                     static_cast<uint32_t>(aSize),
621
0
                                     &read);
622
0
  NS_ENSURE_SUCCESS(rv, 0);
623
0
  return static_cast<int32_t>(read);
624
0
}
625
626
// RIFFParser
627
628
Result<uint32_t, nsresult>
629
RIFFParser::Parse(BufferReader& aReader)
630
0
{
631
0
  for (auto res = aReader.ReadU8();
632
0
       res.isOk() && !mRiffHeader.ParseNext(res.unwrap()); res = aReader.ReadU8())
633
0
  {}
634
0
635
0
  if (mRiffHeader.IsValid()) {
636
0
    return RIFF_CHUNK_SIZE;
637
0
  }
638
0
639
0
  return 0;
640
0
}
641
642
void
643
RIFFParser::Reset()
644
0
{
645
0
  mRiffHeader.Reset();
646
0
}
647
648
const RIFFParser::RIFFHeader&
649
RIFFParser::RiffHeader() const
650
0
{
651
0
  return mRiffHeader;
652
0
}
653
654
// RIFFParser::RIFFHeader
655
656
RIFFParser::RIFFHeader::RIFFHeader()
657
0
{
658
0
  Reset();
659
0
}
660
661
void
662
RIFFParser::RIFFHeader::Reset()
663
0
{
664
0
  memset(mRaw, 0, sizeof(mRaw));
665
0
  mPos = 0;
666
0
}
667
668
bool
669
RIFFParser::RIFFHeader::ParseNext(uint8_t c)
670
0
{
671
0
  if (!Update(c)) {
672
0
    Reset();
673
0
    if (!Update(c)) {
674
0
      Reset();
675
0
    }
676
0
  }
677
0
  return IsValid();
678
0
}
679
680
bool
681
RIFFParser::RIFFHeader::IsValid(int aPos) const
682
0
{
683
0
  if (aPos > -1 && aPos < 4) {
684
0
    return RIFF[aPos] == mRaw[aPos];
685
0
  } else if (aPos > 7 && aPos < 12) {
686
0
    return WAVE[aPos - 8] == mRaw[aPos];
687
0
  } else {
688
0
    return true;
689
0
  }
690
0
}
691
692
bool
693
RIFFParser::RIFFHeader::IsValid() const
694
0
{
695
0
  return mPos >= RIFF_CHUNK_SIZE;
696
0
}
697
698
bool
699
RIFFParser::RIFFHeader::Update(uint8_t c)
700
0
{
701
0
  if (mPos < RIFF_CHUNK_SIZE) {
702
0
    mRaw[mPos] = c;
703
0
  }
704
0
  return IsValid(mPos++);
705
0
}
706
707
// HeaderParser
708
709
Result<uint32_t, nsresult>
710
HeaderParser::Parse(BufferReader& aReader)
711
0
{
712
0
  for (auto res = aReader.ReadU8();
713
0
       res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader.ReadU8())
714
0
  {}
715
0
716
0
  if (mHeader.IsValid()) {
717
0
    return CHUNK_HEAD_SIZE;
718
0
  }
719
0
720
0
  return 0;
721
0
}
722
723
void
724
HeaderParser::Reset()
725
0
{
726
0
  mHeader.Reset();
727
0
}
728
729
const HeaderParser::ChunkHeader&
730
HeaderParser::GiveHeader() const
731
0
{
732
0
  return mHeader;
733
0
}
734
735
// HeaderParser::ChunkHeader
736
737
HeaderParser::ChunkHeader::ChunkHeader()
738
0
{
739
0
  Reset();
740
0
}
741
742
void
743
HeaderParser::ChunkHeader::Reset()
744
0
{
745
0
  memset(mRaw, 0, sizeof(mRaw));
746
0
  mPos = 0;
747
0
}
748
749
bool
750
HeaderParser::ChunkHeader::ParseNext(uint8_t c)
751
0
{
752
0
  Update(c);
753
0
  return IsValid();
754
0
}
755
756
bool
757
HeaderParser::ChunkHeader::IsValid() const
758
0
{
759
0
  return mPos >= CHUNK_HEAD_SIZE;
760
0
}
761
762
uint32_t
763
HeaderParser::ChunkHeader::ChunkName() const
764
0
{
765
0
  return ((mRaw[0] << 24)
766
0
          | (mRaw[1] << 16)
767
0
          | (mRaw[2] << 8 )
768
0
          | (mRaw[3]));
769
0
}
770
771
uint32_t
772
HeaderParser::ChunkHeader::ChunkSize() const
773
0
{
774
0
  return ((mRaw[7] << 24)
775
0
          | (mRaw[6] << 16)
776
0
          | (mRaw[5] << 8 )
777
0
          | (mRaw[4]));
778
0
}
779
780
void
781
HeaderParser::ChunkHeader::Update(uint8_t c)
782
0
{
783
0
  if (mPos < CHUNK_HEAD_SIZE) {
784
0
    mRaw[mPos++] = c;
785
0
  }
786
0
}
787
788
// FormatParser
789
790
Result<uint32_t, nsresult>
791
FormatParser::Parse(BufferReader& aReader)
792
0
{
793
0
  for (auto res = aReader.ReadU8();
794
0
       res.isOk() && !mFmtChunk.ParseNext(res.unwrap()); res = aReader.ReadU8())
795
0
  {}
796
0
797
0
  if (mFmtChunk.IsValid()) {
798
0
    return FMT_CHUNK_MIN_SIZE;
799
0
  }
800
0
801
0
  return 0;
802
0
}
803
804
void
805
FormatParser::Reset()
806
0
{
807
0
  mFmtChunk.Reset();
808
0
}
809
810
const FormatParser::FormatChunk&
811
FormatParser::FmtChunk() const
812
0
{
813
0
  return mFmtChunk;
814
0
}
815
816
// FormatParser::FormatChunk
817
818
FormatParser::FormatChunk::FormatChunk()
819
0
{
820
0
  Reset();
821
0
}
822
823
void
824
FormatParser::FormatChunk::Reset()
825
0
{
826
0
  memset(mRaw, 0, sizeof(mRaw));
827
0
  mPos = 0;
828
0
}
829
830
uint16_t
831
FormatParser::FormatChunk::WaveFormat() const
832
0
{
833
0
  return (mRaw[1] << 8) | (mRaw[0]);
834
0
}
835
836
uint16_t
837
FormatParser::FormatChunk::Channels() const
838
0
{
839
0
  return (mRaw[3] << 8) | (mRaw[2]);
840
0
}
841
842
uint32_t
843
FormatParser::FormatChunk::SampleRate() const
844
0
{
845
0
  return (mRaw[7] << 24)
846
0
         | (mRaw[6] << 16)
847
0
         | (mRaw[5] << 8)
848
0
         | (mRaw[4]);
849
0
}
850
851
uint16_t
852
FormatParser::FormatChunk::FrameSize() const
853
0
{
854
0
  return (mRaw[13] << 8) | (mRaw[12]);
855
0
}
856
857
uint16_t
858
FormatParser::FormatChunk::SampleFormat() const
859
0
{
860
0
  return (mRaw[15] << 8) | (mRaw[14]);
861
0
}
862
863
bool
864
FormatParser::FormatChunk::ParseNext(uint8_t c)
865
0
{
866
0
  Update(c);
867
0
  return IsValid();
868
0
}
869
870
bool
871
FormatParser::FormatChunk::IsValid() const
872
0
{
873
0
  return (FrameSize() == SampleRate() * Channels() / 8) &&
874
0
         (mPos >= FMT_CHUNK_MIN_SIZE);
875
0
}
876
877
void
878
FormatParser::FormatChunk::Update(uint8_t c)
879
0
{
880
0
  if (mPos < FMT_CHUNK_MIN_SIZE) {
881
0
    mRaw[mPos++] = c;
882
0
  }
883
0
}
884
885
// DataParser
886
887
DataParser::DataParser()
888
0
{
889
0
}
890
891
void
892
DataParser::Reset()
893
0
{
894
0
  mChunk.Reset();
895
0
}
896
897
const DataParser::DataChunk&
898
DataParser::CurrentChunk() const
899
0
{
900
0
  return mChunk;
901
0
}
902
903
// DataParser::DataChunk
904
905
void
906
DataParser::DataChunk::Reset()
907
0
{
908
0
  mPos = 0;
909
0
}
910
911
} // namespace mozilla