Backed out changeset ddccd40117a0 (bug 1853271) for causing bug 1854769. CLOSED TREE
[gecko.git] / dom / media / wave / WaveDemuxer.cpp
blob4ffa4d417113b413a2239047febecb73d40c9904
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/. */
7 #include "WaveDemuxer.h"
9 #include <inttypes.h>
10 #include <algorithm>
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"
19 using mozilla::media::TimeIntervals;
20 using mozilla::media::TimeUnit;
22 namespace mozilla {
24 // WAVDemuxer
26 WAVDemuxer::WAVDemuxer(MediaResource* aSource) : mSource(aSource) {
27 DDLINKCHILD("source", aSource);
30 bool WAVDemuxer::InitInternal() {
31 if (!mTrackDemuxer) {
32 mTrackDemuxer = new WAVTrackDemuxer(mSource.GetResource());
33 DDLINKCHILD("track demuxer", mTrackDemuxer.get());
35 return mTrackDemuxer->Init();
38 RefPtr<WAVDemuxer::InitPromise> WAVDemuxer::Init() {
39 if (!InitInternal()) {
40 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
41 __func__);
43 return InitPromise::CreateAndResolve(NS_OK, __func__);
46 uint32_t WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
47 return aType == TrackInfo::kAudioTrack ? 1u : 0u;
50 already_AddRefed<MediaTrackDemuxer> WAVDemuxer::GetTrackDemuxer(
51 TrackInfo::TrackType aType, uint32_t aTrackNumber) {
52 if (!mTrackDemuxer) {
53 return nullptr;
55 return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget();
58 bool WAVDemuxer::IsSeekable() const { return true; }
60 // WAVTrackDemuxer
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();
78 bool WAVTrackDemuxer::Init() {
79 Reset();
80 FastSeek(TimeUnit());
82 if (!mInfo) {
83 mInfo = MakeUnique<AudioInfo>();
84 mInfo->mCodecSpecificConfig =
85 AudioCodecSpecificVariant{WaveCodecSpecificData{}};
88 if (!RIFFParserInit()) {
89 return false;
92 bool hasValidFmt = false;
94 while (true) {
95 if (!HeaderParserInit()) {
96 return false;
99 uint32_t chunkName = mHeaderParser.GiveHeader().ChunkName();
100 uint32_t chunkSize = mHeaderParser.GiveHeader().ChunkSize();
102 if (chunkName == FRMT_CODE) {
103 hasValidFmt = FmtChunkParserInit();
104 } else if (chunkName == LIST_CODE) {
105 mHeaderParser.Reset();
106 uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + chunkSize;
107 if (endOfListChunk > UINT32_MAX) {
108 return false;
110 if (!ListChunkParserInit(chunkSize)) {
111 mOffset = endOfListChunk;
113 } else if (chunkName == DATA_CODE) {
114 mDataLength = chunkSize;
115 if (mFirstChunkOffset != mOffset) {
116 mFirstChunkOffset = mOffset;
118 break;
119 } else {
120 mOffset += chunkSize; // Skip other irrelevant chunks.
122 if (mOffset & 1) {
123 // Wave files are 2-byte aligned so we need to round up
124 mOffset += 1;
126 mHeaderParser.Reset();
129 if (!hasValidFmt) {
130 return false;
133 int64_t streamLength = StreamLength();
134 // If the chunk length and the resource length are not equal, use the
135 // resource length as the "real" data chunk length, if it's longer than the
136 // chunk size.
137 if (streamLength != -1) {
138 uint64_t streamLengthPositive = static_cast<uint64_t>(streamLength);
139 if (streamLengthPositive > mFirstChunkOffset &&
140 mDataLength > streamLengthPositive - mFirstChunkOffset) {
141 mDataLength = streamLengthPositive - mFirstChunkOffset;
145 mSamplesPerSecond = mFmtChunk.SampleRate();
146 mChannels = mFmtChunk.Channels();
147 if (!mSamplesPerSecond || !mChannels || !mFmtChunk.ValidBitsPerSamples()) {
148 return false;
150 mSamplesPerChunk =
151 DATA_CHUNK_SIZE * 8 / mChannels / mFmtChunk.ValidBitsPerSamples();
152 mSampleFormat = mFmtChunk.ValidBitsPerSamples();
154 mInfo->mRate = mSamplesPerSecond;
155 mInfo->mChannels = mChannels;
156 mInfo->mBitDepth = mFmtChunk.ValidBitsPerSamples();
157 mInfo->mProfile = AssertedCast<uint8_t>(mFmtChunk.WaveFormat() & 0x00FF);
158 mInfo->mExtendedProfile =
159 AssertedCast<uint8_t>(mFmtChunk.WaveFormat() & 0xFF00 >> 8);
160 mInfo->mMimeType = "audio/wave; codecs=";
161 mInfo->mMimeType.AppendInt(mFmtChunk.WaveFormat());
162 mInfo->mDuration = Duration();
163 mInfo->mChannelMap = mFmtChunk.ChannelMap();
165 return mInfo->mDuration.IsPositive();
168 bool WAVTrackDemuxer::RIFFParserInit() {
169 RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader());
170 if (!riffHeader) {
171 return false;
173 BufferReader RIFFReader(riffHeader->Data(), 12);
174 Unused << mRIFFParser.Parse(RIFFReader);
175 return mRIFFParser.RiffHeader().IsValid(11);
178 bool WAVTrackDemuxer::HeaderParserInit() {
179 RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader());
180 if (!header) {
181 return false;
183 BufferReader headerReader(header->Data(), 8);
184 Unused << mHeaderParser.Parse(headerReader);
185 return true;
188 bool WAVTrackDemuxer::FmtChunkParserInit() {
189 RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk());
190 if (!fmtChunk || fmtChunk->Size() < 16) {
191 return false;
193 nsTArray<uint8_t> fmtChunkData(fmtChunk->Data(), fmtChunk->Size());
194 mFmtChunk.Init(std::move(fmtChunkData));
195 return true;
198 bool WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize) {
199 uint32_t bytesRead = 0;
201 RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag());
202 if (!infoTag) {
203 return false;
206 BufferReader infoTagReader(infoTag->Data(), 4);
207 auto res = infoTagReader.ReadU32();
208 if (res.isErr() || (res.isOk() && res.unwrap() != INFO_CODE)) {
209 return false;
212 bytesRead += 4;
214 while (bytesRead < aChunkSize) {
215 if (!HeaderParserInit()) {
216 return false;
219 bytesRead += 8;
221 uint32_t id = mHeaderParser.GiveHeader().ChunkName();
222 uint32_t length = mHeaderParser.GiveHeader().ChunkSize();
224 // SubChunk Length Cannot Exceed List Chunk length.
225 if (length > aChunkSize - bytesRead) {
226 length = aChunkSize - bytesRead;
229 MediaByteRange mRange = {mOffset, mOffset + length};
230 RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
231 if (!mChunkData) {
232 return false;
235 const char* rawData = reinterpret_cast<const char*>(mChunkData->Data());
237 nsCString val(rawData, length);
238 if (length > 0 && val[length - 1] == '\0') {
239 val.SetLength(length - 1);
242 if (length % 2) {
243 mOffset += 1;
244 length += length % 2;
247 bytesRead += length;
249 if (!IsUtf8(val)) {
250 mHeaderParser.Reset();
251 continue;
254 switch (id) {
255 case 0x49415254: // IART
256 mInfo->mTags.AppendElement(MetadataTag("artist"_ns, val));
257 break;
258 case 0x49434d54: // ICMT
259 mInfo->mTags.AppendElement(MetadataTag("comments"_ns, val));
260 break;
261 case 0x49474e52: // IGNR
262 mInfo->mTags.AppendElement(MetadataTag("genre"_ns, val));
263 break;
264 case 0x494e414d: // INAM
265 mInfo->mTags.AppendElement(MetadataTag("name"_ns, val));
266 break;
269 mHeaderParser.Reset();
271 return true;
274 media::TimeUnit WAVTrackDemuxer::SeekPosition() const {
275 TimeUnit pos = Duration(mChunkIndex);
276 if (Duration() > TimeUnit()) {
277 pos = std::min(Duration(), pos);
279 return pos;
282 RefPtr<MediaRawData> WAVTrackDemuxer::DemuxSample() {
283 return GetNextChunk(FindNextChunk());
286 UniquePtr<TrackInfo> WAVTrackDemuxer::GetInfo() const { return mInfo->Clone(); }
288 RefPtr<WAVTrackDemuxer::SeekPromise> WAVTrackDemuxer::Seek(
289 const TimeUnit& aTime) {
290 FastSeek(aTime);
291 const TimeUnit seekTime = ScanUntil(aTime);
292 return SeekPromise::CreateAndResolve(seekTime, __func__);
295 TimeUnit WAVTrackDemuxer::FastSeek(const TimeUnit& aTime) {
296 if (aTime.ToMicroseconds()) {
297 mChunkIndex = ChunkIndexFromTime(aTime);
298 } else {
299 mChunkIndex = 0;
302 mOffset = OffsetFromChunkIndex(mChunkIndex);
304 if (mOffset > mFirstChunkOffset && StreamLength() > 0) {
305 mOffset = std::min(static_cast<uint64_t>(StreamLength() - 1), mOffset);
308 return Duration(mChunkIndex);
311 TimeUnit WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime) {
312 if (!aTime.ToMicroseconds()) {
313 return FastSeek(aTime);
316 if (Duration(mChunkIndex) > aTime) {
317 FastSeek(aTime);
320 return SeekPosition();
323 RefPtr<WAVTrackDemuxer::SamplesPromise> WAVTrackDemuxer::GetSamples(
324 int32_t aNumSamples) {
325 MOZ_ASSERT(aNumSamples);
327 RefPtr<SamplesHolder> datachunks = new SamplesHolder();
329 while (aNumSamples--) {
330 RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk());
331 if (!datachunk) {
332 break;
334 if (!datachunk->HasValidTime()) {
335 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
336 __func__);
338 datachunks->AppendSample(datachunk);
341 if (datachunks->GetSamples().IsEmpty()) {
342 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
343 __func__);
346 return SamplesPromise::CreateAndResolve(datachunks, __func__);
349 void WAVTrackDemuxer::Reset() {
350 FastSeek(TimeUnit());
351 mParser.Reset();
352 mHeaderParser.Reset();
353 mRIFFParser.Reset();
356 RefPtr<WAVTrackDemuxer::SkipAccessPointPromise>
357 WAVTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
358 return SkipAccessPointPromise::CreateAndReject(
359 SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
362 int64_t WAVTrackDemuxer::GetResourceOffset() const {
363 return AssertedCast<int64_t>(mOffset);
366 TimeIntervals WAVTrackDemuxer::GetBuffered() {
367 TimeUnit duration = Duration();
369 if (duration <= TimeUnit()) {
370 return TimeIntervals();
373 AutoPinned<MediaResource> stream(mSource.GetResource());
374 return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
377 int64_t WAVTrackDemuxer::StreamLength() const { return mSource.GetLength(); }
379 TimeUnit WAVTrackDemuxer::Duration() const {
380 if (!mDataLength || !mChannels || !mSampleFormat) {
381 return TimeUnit();
384 int64_t numSamples =
385 static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat;
387 int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
389 if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
390 numUSeconds++;
393 return TimeUnit::FromMicroseconds(numUSeconds);
396 TimeUnit WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const {
397 if (!mSamplesPerSecond || !mSamplesPerChunk) {
398 return TimeUnit();
400 const int64_t frames = mSamplesPerChunk * aNumDataChunks;
401 return TimeUnit(frames, mSamplesPerSecond);
404 TimeUnit WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const {
405 if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
406 return TimeUnit();
409 uint64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
411 return TimeUnit(numSamples, mSamplesPerSecond);
414 MediaByteRange WAVTrackDemuxer::FindNextChunk() {
415 if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) {
416 return {mOffset, mOffset + DATA_CHUNK_SIZE};
418 return {mOffset, mFirstChunkOffset + mDataLength};
421 MediaByteRange WAVTrackDemuxer::FindChunkHeader() {
422 return {mOffset, mOffset + CHUNK_HEAD_SIZE};
425 MediaByteRange WAVTrackDemuxer::FindRIFFHeader() {
426 return {mOffset, mOffset + RIFF_CHUNK_SIZE};
429 MediaByteRange WAVTrackDemuxer::FindFmtChunk() {
430 return {mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize()};
433 MediaByteRange WAVTrackDemuxer::FindListChunk() {
434 return {mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize()};
437 MediaByteRange WAVTrackDemuxer::FindInfoTag() { return {mOffset, mOffset + 4}; }
439 bool WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange) {
440 UpdateState(aRange);
441 return true;
444 already_AddRefed<MediaRawData> WAVTrackDemuxer::GetNextChunk(
445 const MediaByteRange& aRange) {
446 if (!aRange.Length()) {
447 return nullptr;
450 RefPtr<MediaRawData> datachunk = new MediaRawData();
451 datachunk->mOffset = aRange.mStart;
453 UniquePtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
454 if (!chunkWriter->SetSize(static_cast<uint32_t>(aRange.Length()))) {
455 return nullptr;
458 const uint32_t read = Read(chunkWriter->Data(), datachunk->mOffset,
459 AssertedCast<int64_t>(datachunk->Size()));
461 if (read != aRange.Length()) {
462 return nullptr;
465 UpdateState(aRange);
466 ++mNumParsedChunks;
467 ++mChunkIndex;
469 datachunk->mTime = Duration(mChunkIndex - 1);
471 if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
472 datachunk->mDuration = Duration(1);
473 } else {
474 uint32_t mBytesRemaining = mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
475 datachunk->mDuration = DurationFromBytes(mBytesRemaining);
477 datachunk->mTimecode = datachunk->mTime;
478 datachunk->mKeyframe = true;
480 MOZ_ASSERT(!datachunk->mTime.IsNegative());
481 MOZ_ASSERT(!datachunk->mDuration.IsNegative());
483 return datachunk.forget();
486 already_AddRefed<MediaRawData> WAVTrackDemuxer::GetFileHeader(
487 const MediaByteRange& aRange) {
488 if (!aRange.Length()) {
489 return nullptr;
492 RefPtr<MediaRawData> fileHeader = new MediaRawData();
493 fileHeader->mOffset = aRange.mStart;
495 UniquePtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
496 if (!headerWriter->SetSize(static_cast<uint32_t>(aRange.Length()))) {
497 return nullptr;
500 const uint32_t read = Read(headerWriter->Data(), fileHeader->mOffset,
501 AssertedCast<int64_t>(fileHeader->Size()));
503 if (read != aRange.Length()) {
504 return nullptr;
507 UpdateState(aRange);
509 return fileHeader.forget();
512 uint64_t WAVTrackDemuxer::OffsetFromChunkIndex(uint32_t aChunkIndex) const {
513 return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE;
516 uint64_t WAVTrackDemuxer::ChunkIndexFromTime(
517 const media::TimeUnit& aTime) const {
518 if (!mSamplesPerChunk || !mSamplesPerSecond) {
519 return 0;
521 double chunkDurationS =
522 mSamplesPerChunk / static_cast<double>(mSamplesPerSecond);
523 int64_t chunkIndex = std::floor(aTime.ToSeconds() / chunkDurationS);
524 return chunkIndex;
527 void WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange) {
528 // Full chunk parsed, move offset to its end.
529 mOffset = static_cast<uint32_t>(aRange.mEnd);
530 mTotalChunkLen += static_cast<uint64_t>(aRange.Length());
533 int64_t WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset,
534 int64_t aSize) {
535 const int64_t streamLen = StreamLength();
536 if (mInfo && streamLen > 0) {
537 int64_t max = streamLen > aOffset ? streamLen - aOffset : 0;
538 aSize = std::min(aSize, max);
540 uint32_t read = 0;
541 const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
542 static_cast<uint32_t>(aSize), &read);
543 NS_ENSURE_SUCCESS(rv, 0);
544 return read;
547 // RIFFParser
549 Result<uint32_t, nsresult> RIFFParser::Parse(BufferReader& aReader) {
550 for (auto res = aReader.ReadU8();
551 res.isOk() && !mRiffHeader.ParseNext(res.unwrap());
552 res = aReader.ReadU8()) {
555 if (mRiffHeader.IsValid()) {
556 return RIFF_CHUNK_SIZE;
559 return 0;
562 void RIFFParser::Reset() { mRiffHeader.Reset(); }
564 const RIFFParser::RIFFHeader& RIFFParser::RiffHeader() const {
565 return mRiffHeader;
568 // RIFFParser::RIFFHeader
570 RIFFParser::RIFFHeader::RIFFHeader() { Reset(); }
572 void RIFFParser::RIFFHeader::Reset() {
573 memset(mRaw, 0, sizeof(mRaw));
574 mPos = 0;
577 bool RIFFParser::RIFFHeader::ParseNext(uint8_t c) {
578 if (!Update(c)) {
579 Reset();
580 if (!Update(c)) {
581 Reset();
584 return IsValid();
587 bool RIFFParser::RIFFHeader::IsValid(int aPos) const {
588 if (aPos > -1 && aPos < 4) {
589 return RIFF[aPos] == mRaw[aPos];
591 if (aPos > 7 && aPos < 12) {
592 return WAVE[aPos - 8] == mRaw[aPos];
594 return true;
597 bool RIFFParser::RIFFHeader::IsValid() const { return mPos >= RIFF_CHUNK_SIZE; }
599 bool RIFFParser::RIFFHeader::Update(uint8_t c) {
600 if (mPos < RIFF_CHUNK_SIZE) {
601 mRaw[mPos] = c;
603 return IsValid(mPos++);
606 // HeaderParser
608 Result<uint32_t, nsresult> HeaderParser::Parse(BufferReader& aReader) {
609 for (auto res = aReader.ReadU8();
610 res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader.ReadU8()) {
613 if (mHeader.IsValid()) {
614 return CHUNK_HEAD_SIZE;
617 return 0;
620 void HeaderParser::Reset() { mHeader.Reset(); }
622 const HeaderParser::ChunkHeader& HeaderParser::GiveHeader() const {
623 return mHeader;
626 // HeaderParser::ChunkHeader
628 HeaderParser::ChunkHeader::ChunkHeader() { Reset(); }
630 void HeaderParser::ChunkHeader::Reset() {
631 memset(mRaw, 0, sizeof(mRaw));
632 mPos = 0;
635 bool HeaderParser::ChunkHeader::ParseNext(uint8_t c) {
636 Update(c);
637 return IsValid();
640 bool HeaderParser::ChunkHeader::IsValid() const {
641 return mPos >= CHUNK_HEAD_SIZE;
644 uint32_t HeaderParser::ChunkHeader::ChunkName() const {
645 return static_cast<uint32_t>(
646 ((mRaw[0] << 24) | (mRaw[1] << 16) | (mRaw[2] << 8) | (mRaw[3])));
649 uint32_t HeaderParser::ChunkHeader::ChunkSize() const {
650 return static_cast<uint32_t>(
651 ((mRaw[7] << 24) | (mRaw[6] << 16) | (mRaw[5] << 8) | (mRaw[4])));
654 void HeaderParser::ChunkHeader::Update(uint8_t c) {
655 if (mPos < CHUNK_HEAD_SIZE) {
656 mRaw[mPos++] = c;
660 // FormatChunk
662 void FormatChunk::Init(nsTArray<uint8_t>&& aData) { mRaw = std::move(aData); }
664 uint16_t FormatChunk::WaveFormat() const { return (mRaw[1] << 8) | (mRaw[0]); }
666 uint16_t FormatChunk::Channels() const { return (mRaw[3] << 8) | (mRaw[2]); }
668 uint32_t FormatChunk::SampleRate() const {
669 return static_cast<uint32_t>((mRaw[7] << 24) | (mRaw[6] << 16) |
670 (mRaw[5] << 8) | (mRaw[4]));
673 uint16_t FormatChunk::AverageBytesPerSec() const {
674 return static_cast<uint16_t>((mRaw[11] << 24) | (mRaw[10] << 16) |
675 (mRaw[9] << 8) | (mRaw[8]));
678 uint16_t FormatChunk::BlockAlign() const {
679 return static_cast<uint16_t>(mRaw[13] << 8) | (mRaw[12]);
682 uint16_t FormatChunk::ValidBitsPerSamples() const {
683 return (mRaw[15] << 8) | (mRaw[14]);
686 uint16_t FormatChunk::ExtraFormatInfoSize() const {
687 uint16_t value = static_cast<uint16_t>(mRaw[17] << 8) | (mRaw[16]);
688 if (WaveFormat() != 0xFFFE && value != 0) {
689 NS_WARNING(
690 "Found non-zero extra format info length and the wave format"
691 " isn't WAVEFORMATEXTENSIBLE.");
692 return 0;
694 if (WaveFormat() == 0xFFFE && value < 22) {
695 NS_WARNING(
696 "Wave format is WAVEFORMATEXTENSIBLE and extra data size isn't at"
697 " least 22 bytes");
698 return 0;
700 return value;
703 AudioConfig::ChannelLayout::ChannelMap FormatChunk::ChannelMap() const {
704 // Regular mapping if file doesn't have channel mapping info. Alternatively,
705 // if the chunk size doesn't have the field for the size of the extension
706 // data, return a regular mapping.
707 if (WaveFormat() != 0xFFFE || mRaw.Length() < 18) {
708 return AudioConfig::ChannelLayout(Channels()).Map();
710 // The length of this chunk is at least 18, check if it's long enough to
711 // hold the WAVE_FORMAT_EXTENSIBLE struct, that is 22 bytes. If not, fall
712 // back to a common mapping. The channel mapping is four bytes, starting at
713 // offset 18.
714 if (ExtraFormatInfoSize() < 22 || mRaw.Length() < 22) {
715 return AudioConfig::ChannelLayout(Channels()).Map();
717 // ChannelLayout::ChannelMap is by design bit-per-bit compatible with
718 // WAVEFORMATEXTENSIBLE's dwChannelMask attribute, we can just cast here.
719 auto channelMap = static_cast<AudioConfig::ChannelLayout::ChannelMap>(
720 mRaw[21] | mRaw[20] | mRaw[19] | mRaw[18]);
721 return channelMap;
724 // DataParser
726 DataParser::DataParser() = default;
728 void DataParser::Reset() { mChunk.Reset(); }
730 const DataParser::DataChunk& DataParser::CurrentChunk() const { return mChunk; }
732 // DataParser::DataChunk
734 void DataParser::DataChunk::Reset() { mPos = 0; }
736 } // namespace mozilla