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