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