mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 00:55:37 +00:00
e672cebce4
Fly by fix unrelated to bug 1262753
.
MozReview-Commit-ID: 3IHtrWS280f
--HG--
extra : rebase_source : a4aec3f7a348de0a92ec122ef5676eb424824f27
900 lines
18 KiB
C++
900 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "WaveDemuxer.h"
|
|
|
|
#include <inttypes.h>
|
|
#include <algorithm>
|
|
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Endian.h"
|
|
#include "VideoUtils.h"
|
|
#include "TimeUnits.h"
|
|
#include "prenv.h"
|
|
|
|
using mozilla::media::TimeUnit;
|
|
using mozilla::media::TimeIntervals;
|
|
using mp4_demuxer::ByteReader;
|
|
|
|
namespace mozilla {
|
|
|
|
// WAVDemuxer
|
|
|
|
WAVDemuxer::WAVDemuxer(MediaResource* aSource)
|
|
: mSource(aSource)
|
|
{
|
|
}
|
|
|
|
bool
|
|
WAVDemuxer::InitInternal()
|
|
{
|
|
if (!mTrackDemuxer) {
|
|
mTrackDemuxer = new WAVTrackDemuxer(mSource);
|
|
}
|
|
return mTrackDemuxer->Init();
|
|
}
|
|
|
|
RefPtr<WAVDemuxer::InitPromise>
|
|
WAVDemuxer::Init()
|
|
{
|
|
if (!InitInternal()) {
|
|
return InitPromise::CreateAndReject(
|
|
DemuxerFailureReason::DEMUXER_ERROR, __func__);
|
|
}
|
|
return InitPromise::CreateAndResolve(NS_OK, __func__);
|
|
}
|
|
|
|
bool
|
|
WAVDemuxer::HasTrackType(TrackInfo::TrackType aType) const
|
|
{
|
|
return aType == TrackInfo::kAudioTrack;
|
|
}
|
|
|
|
uint32_t
|
|
WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
|
|
{
|
|
return aType == TrackInfo::kAudioTrack ? 1u : 0u;
|
|
}
|
|
|
|
already_AddRefed<MediaTrackDemuxer>
|
|
WAVDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
|
|
{
|
|
if (!mTrackDemuxer) {
|
|
return nullptr;
|
|
}
|
|
return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget();
|
|
}
|
|
|
|
bool
|
|
WAVDemuxer::IsSeekable() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// WAVTrackDemuxer
|
|
|
|
WAVTrackDemuxer::WAVTrackDemuxer(MediaResourceIndex aSource)
|
|
: mSource(aSource)
|
|
, mOffset(0)
|
|
, mFirstChunkOffset(0)
|
|
, mNumParsedChunks(0)
|
|
, mChunkIndex(0)
|
|
, mTotalChunkLen(0)
|
|
, mSamplesPerChunk(0)
|
|
, mSamplesPerSecond(0)
|
|
, mChannels(0)
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::Init()
|
|
{
|
|
Reset();
|
|
FastSeek(TimeUnit());
|
|
|
|
if (!mInfo) {
|
|
mInfo = MakeUnique<AudioInfo>();
|
|
}
|
|
|
|
if (!RIFFParserInit()) {
|
|
return false;
|
|
}
|
|
|
|
while (true) {
|
|
if (!HeaderParserInit()) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t aChunkName = mHeaderParser.GiveHeader().ChunkName();
|
|
uint32_t aChunkSize = mHeaderParser.GiveHeader().ChunkSize();
|
|
|
|
aChunkSize += aChunkSize % 2;
|
|
|
|
if (aChunkName == FRMT_CODE) {
|
|
if (!FmtChunkParserInit()) {
|
|
return false;
|
|
}
|
|
} else if (aChunkName == LIST_CODE) {
|
|
mHeaderParser.Reset();
|
|
uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + aChunkSize;
|
|
if (endOfListChunk > UINT32_MAX) {
|
|
return false;
|
|
}
|
|
if (!ListChunkParserInit(aChunkSize)) {
|
|
mOffset = endOfListChunk;
|
|
}
|
|
} else if (aChunkName == DATA_CODE) {
|
|
mDataLength = aChunkSize;
|
|
if (mFirstChunkOffset != mOffset) {
|
|
mFirstChunkOffset = mOffset;
|
|
}
|
|
break;
|
|
} else {
|
|
mOffset += aChunkSize; // Skip other irrelevant chunks.
|
|
}
|
|
mHeaderParser.Reset();
|
|
}
|
|
|
|
if (mDataLength > StreamLength() - mFirstChunkOffset) {
|
|
mDataLength = StreamLength() - mFirstChunkOffset;
|
|
}
|
|
|
|
mSamplesPerSecond = mFmtParser.FmtChunk().SampleRate();
|
|
mChannels = mFmtParser.FmtChunk().Channels();
|
|
mSampleFormat = mFmtParser.FmtChunk().SampleFormat();
|
|
if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
|
|
return false;
|
|
}
|
|
mSamplesPerChunk = DATA_CHUNK_SIZE * 8 / mChannels / mSampleFormat;
|
|
|
|
mInfo->mRate = mSamplesPerSecond;
|
|
mInfo->mChannels = mChannels;
|
|
mInfo->mBitDepth = mSampleFormat;
|
|
mInfo->mProfile = mFmtParser.FmtChunk().WaveFormat() & 0x00FF;
|
|
mInfo->mExtendedProfile = (mFmtParser.FmtChunk().WaveFormat() & 0xFF00) >> 8;
|
|
mInfo->mMimeType = "audio/wave; codecs=";
|
|
mInfo->mMimeType.AppendInt(mFmtParser.FmtChunk().WaveFormat());
|
|
mInfo->mDuration = Duration().ToMicroseconds();
|
|
|
|
return !!(mInfo->mDuration);
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::RIFFParserInit()
|
|
{
|
|
RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader());
|
|
if (!riffHeader) {
|
|
return false;
|
|
}
|
|
ByteReader RIFFReader = ByteReader(riffHeader->Data(), 12);
|
|
mRIFFParser.Parse(RIFFReader);
|
|
return mRIFFParser.RiffHeader().IsValid(11);
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::HeaderParserInit()
|
|
{
|
|
RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader());
|
|
if (!header) {
|
|
return false;
|
|
}
|
|
ByteReader HeaderReader = ByteReader(header->Data(), 8);
|
|
mHeaderParser.Parse(HeaderReader);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::FmtChunkParserInit()
|
|
{
|
|
RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk());
|
|
if (!fmtChunk) {
|
|
return false;
|
|
}
|
|
ByteReader fmtReader = ByteReader(fmtChunk->Data(),
|
|
mHeaderParser.GiveHeader().ChunkSize());
|
|
mFmtParser.Parse(fmtReader);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize)
|
|
{
|
|
uint32_t bytesRead = 0;
|
|
|
|
RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag());
|
|
if (!infoTag) {
|
|
return false;
|
|
}
|
|
ByteReader infoTagReader = ByteReader(infoTag->Data(), 4);
|
|
if (!infoTagReader.CanRead32() || infoTagReader.ReadU32() != INFO_CODE) {
|
|
return false;
|
|
}
|
|
|
|
bytesRead += 4;
|
|
|
|
while (bytesRead < aChunkSize) {
|
|
if (!HeaderParserInit()) {
|
|
return false;
|
|
}
|
|
|
|
bytesRead += 8;
|
|
|
|
uint32_t id = mHeaderParser.GiveHeader().ChunkName();
|
|
uint32_t length = mHeaderParser.GiveHeader().ChunkSize();
|
|
|
|
// SubChunk Length Cannot Exceed List Chunk length.
|
|
if (length > aChunkSize - bytesRead) {
|
|
length = aChunkSize - bytesRead;
|
|
}
|
|
|
|
MediaByteRange mRange = { mOffset, mOffset + length };
|
|
RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
|
|
if (!mChunkData) {
|
|
return false;
|
|
}
|
|
|
|
const char* rawData = reinterpret_cast<const char*>(mChunkData->Data());
|
|
|
|
nsCString val(rawData, length);
|
|
if (length > 0 && val[length - 1] == '\0') {
|
|
val.SetLength(length - 1);
|
|
}
|
|
|
|
if (length % 2) {
|
|
mOffset += 1;
|
|
length += length % 2;
|
|
}
|
|
|
|
bytesRead += length;
|
|
|
|
if (!IsUTF8(val)) {
|
|
mHeaderParser.Reset();
|
|
continue;
|
|
}
|
|
|
|
switch (id) {
|
|
case 0x49415254: // IART
|
|
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("artist"), val));
|
|
break;
|
|
case 0x49434d54: // ICMT
|
|
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("comments"), val));
|
|
break;
|
|
case 0x49474e52: // IGNR
|
|
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("genre"), val));
|
|
break;
|
|
case 0x494e414d: // INAM
|
|
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("name"), val));
|
|
break;
|
|
}
|
|
|
|
mHeaderParser.Reset();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
media::TimeUnit
|
|
WAVTrackDemuxer::SeekPosition() const
|
|
{
|
|
TimeUnit pos = Duration(mChunkIndex);
|
|
if (Duration() > TimeUnit()) {
|
|
pos = std::min(Duration(), pos);
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
RefPtr<MediaRawData>
|
|
WAVTrackDemuxer::DemuxSample()
|
|
{
|
|
return GetNextChunk(FindNextChunk());
|
|
}
|
|
|
|
UniquePtr<TrackInfo>
|
|
WAVTrackDemuxer::GetInfo() const
|
|
{
|
|
return mInfo->Clone();
|
|
}
|
|
|
|
RefPtr<WAVTrackDemuxer::SeekPromise>
|
|
WAVTrackDemuxer::Seek(TimeUnit aTime)
|
|
{
|
|
FastSeek(aTime);
|
|
const TimeUnit seekTime = ScanUntil(aTime);
|
|
return SeekPromise::CreateAndResolve(seekTime, __func__);
|
|
}
|
|
|
|
TimeUnit
|
|
WAVTrackDemuxer::FastSeek(const TimeUnit& aTime)
|
|
{
|
|
if (aTime.ToMicroseconds()) {
|
|
mChunkIndex = ChunkIndexFromTime(aTime);
|
|
} else {
|
|
mChunkIndex = 0;
|
|
}
|
|
|
|
mOffset = OffsetFromChunkIndex(mChunkIndex);
|
|
|
|
if (mOffset > mFirstChunkOffset && StreamLength() > 0) {
|
|
mOffset = std::min(StreamLength() - 1, mOffset);
|
|
}
|
|
|
|
return Duration(mChunkIndex);
|
|
}
|
|
|
|
TimeUnit
|
|
WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime)
|
|
{
|
|
if (!aTime.ToMicroseconds()) {
|
|
return FastSeek(aTime);
|
|
}
|
|
|
|
if (Duration(mChunkIndex) > aTime) {
|
|
FastSeek(aTime);
|
|
}
|
|
|
|
return SeekPosition();
|
|
}
|
|
|
|
RefPtr<WAVTrackDemuxer::SamplesPromise>
|
|
WAVTrackDemuxer::GetSamples(int32_t aNumSamples)
|
|
{
|
|
if (!aNumSamples) {
|
|
return SamplesPromise::CreateAndReject(
|
|
DemuxerFailureReason::DEMUXER_ERROR, __func__);
|
|
}
|
|
|
|
RefPtr<SamplesHolder> datachunks = new SamplesHolder();
|
|
|
|
while (aNumSamples--) {
|
|
RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk());
|
|
if (!datachunk) {
|
|
break;
|
|
}
|
|
datachunks->mSamples.AppendElement(datachunk);
|
|
}
|
|
|
|
if (datachunks->mSamples.IsEmpty()) {
|
|
return SamplesPromise::CreateAndReject(
|
|
DemuxerFailureReason::END_OF_STREAM, __func__);
|
|
}
|
|
|
|
return SamplesPromise::CreateAndResolve(datachunks, __func__);
|
|
}
|
|
|
|
void
|
|
WAVTrackDemuxer::Reset()
|
|
{
|
|
FastSeek(TimeUnit());
|
|
mParser.Reset();
|
|
mHeaderParser.Reset();
|
|
mRIFFParser.Reset();
|
|
mFmtParser.Reset();
|
|
}
|
|
|
|
RefPtr<WAVTrackDemuxer::SkipAccessPointPromise>
|
|
WAVTrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold)
|
|
{
|
|
return SkipAccessPointPromise::CreateAndReject(
|
|
SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__);
|
|
}
|
|
|
|
int64_t
|
|
WAVTrackDemuxer::GetResourceOffset() const
|
|
{
|
|
return mOffset;
|
|
}
|
|
|
|
TimeIntervals
|
|
WAVTrackDemuxer::GetBuffered()
|
|
{
|
|
TimeUnit duration = Duration();
|
|
|
|
if (duration <= TimeUnit()) {
|
|
return TimeIntervals();
|
|
}
|
|
|
|
AutoPinned<MediaResource> stream(mSource.GetResource());
|
|
return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
|
|
}
|
|
|
|
int64_t
|
|
WAVTrackDemuxer::StreamLength() const
|
|
{
|
|
return mSource.GetLength();
|
|
}
|
|
|
|
TimeUnit
|
|
WAVTrackDemuxer::Duration() const
|
|
{
|
|
if (!mDataLength ||!mChannels || !mSampleFormat) {
|
|
return TimeUnit();
|
|
}
|
|
|
|
int64_t numSamples =
|
|
static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat;
|
|
|
|
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
|
|
|
|
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
|
|
numUSeconds++;
|
|
}
|
|
|
|
return TimeUnit::FromMicroseconds(numUSeconds);
|
|
}
|
|
|
|
TimeUnit
|
|
WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
|
|
{
|
|
if (!mSamplesPerSecond || !mSamplesPerChunk) {
|
|
return TimeUnit();
|
|
}
|
|
const double usPerDataChunk = USECS_PER_S *
|
|
static_cast<double>(mSamplesPerChunk) /
|
|
mSamplesPerSecond;
|
|
return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
|
|
}
|
|
|
|
TimeUnit
|
|
WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
|
|
{
|
|
if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
|
|
return TimeUnit();
|
|
}
|
|
|
|
int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
|
|
|
|
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
|
|
|
|
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
|
|
numUSeconds++;
|
|
}
|
|
|
|
return TimeUnit::FromMicroseconds(numUSeconds);
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindNextChunk()
|
|
{
|
|
if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) {
|
|
return { mOffset, mOffset + DATA_CHUNK_SIZE };
|
|
} else {
|
|
return { mOffset, mFirstChunkOffset + mDataLength };
|
|
}
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindChunkHeader()
|
|
{
|
|
return { mOffset, mOffset + CHUNK_HEAD_SIZE };
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindRIFFHeader()
|
|
{
|
|
return { mOffset, mOffset + RIFF_CHUNK_SIZE };
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindFmtChunk()
|
|
{
|
|
return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindListChunk()
|
|
{
|
|
return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
|
|
}
|
|
|
|
MediaByteRange
|
|
WAVTrackDemuxer::FindInfoTag()
|
|
{
|
|
return { mOffset, mOffset + 4 };
|
|
}
|
|
|
|
bool
|
|
WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange)
|
|
{
|
|
UpdateState(aRange);
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<MediaRawData>
|
|
WAVTrackDemuxer::GetNextChunk(const MediaByteRange& aRange)
|
|
{
|
|
if (!aRange.Length()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<MediaRawData> datachunk = new MediaRawData();
|
|
datachunk->mOffset = aRange.mStart;
|
|
|
|
nsAutoPtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
|
|
if (!chunkWriter->SetSize(aRange.Length())) {
|
|
return nullptr;
|
|
}
|
|
|
|
const uint32_t read = Read(chunkWriter->Data(),
|
|
datachunk->mOffset,
|
|
datachunk->Size());
|
|
|
|
if (read != aRange.Length()) {
|
|
return nullptr;
|
|
}
|
|
|
|
UpdateState(aRange);
|
|
++mNumParsedChunks;
|
|
++mChunkIndex;
|
|
|
|
datachunk->mTime = Duration(mChunkIndex - 1).ToMicroseconds();
|
|
|
|
if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
|
|
datachunk->mDuration = Duration(1).ToMicroseconds();
|
|
} else {
|
|
uint32_t mBytesRemaining =
|
|
mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
|
|
datachunk->mDuration = DurationFromBytes(mBytesRemaining).ToMicroseconds();
|
|
}
|
|
datachunk->mTimecode = datachunk->mTime;
|
|
datachunk->mKeyframe = true;
|
|
|
|
MOZ_ASSERT(datachunk->mTime >= 0);
|
|
MOZ_ASSERT(datachunk->mDuration >= 0);
|
|
|
|
return datachunk.forget();
|
|
}
|
|
|
|
already_AddRefed<MediaRawData>
|
|
WAVTrackDemuxer::GetFileHeader(const MediaByteRange& aRange)
|
|
{
|
|
if (!aRange.Length()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<MediaRawData> fileHeader = new MediaRawData();
|
|
fileHeader->mOffset = aRange.mStart;
|
|
|
|
nsAutoPtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
|
|
if (!headerWriter->SetSize(aRange.Length())) {
|
|
return nullptr;
|
|
}
|
|
|
|
const uint32_t read = Read(headerWriter->Data(),
|
|
fileHeader->mOffset,
|
|
fileHeader->Size());
|
|
|
|
if (read != aRange.Length()) {
|
|
return nullptr;
|
|
}
|
|
|
|
UpdateState(aRange);
|
|
|
|
return fileHeader.forget();
|
|
}
|
|
|
|
int64_t
|
|
WAVTrackDemuxer::OffsetFromChunkIndex(int64_t aChunkIndex) const
|
|
{
|
|
return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE;
|
|
}
|
|
|
|
int64_t
|
|
WAVTrackDemuxer::ChunkIndexFromOffset(int64_t aOffset) const
|
|
{
|
|
int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
|
|
return std::max<int64_t>(0, chunkIndex);
|
|
}
|
|
|
|
int64_t
|
|
WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
|
|
{
|
|
if (!mSamplesPerChunk || !mSamplesPerSecond) {
|
|
return 0;
|
|
}
|
|
int64_t chunkIndex =
|
|
(aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
|
|
return chunkIndex;
|
|
}
|
|
|
|
void
|
|
WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
|
|
{
|
|
// Full chunk parsed, move offset to its end.
|
|
mOffset = aRange.mEnd;
|
|
mTotalChunkLen += aRange.Length();
|
|
}
|
|
|
|
int32_t
|
|
WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
|
|
{
|
|
const int64_t streamLen = StreamLength();
|
|
if (mInfo && streamLen > 0) {
|
|
aSize = std::min<int64_t>(aSize, streamLen - aOffset);
|
|
}
|
|
uint32_t read = 0;
|
|
const nsresult rv = mSource.ReadAt(aOffset,
|
|
reinterpret_cast<char*>(aBuffer),
|
|
static_cast<uint32_t>(aSize),
|
|
&read);
|
|
NS_ENSURE_SUCCESS(rv, 0);
|
|
return static_cast<int32_t>(read);
|
|
}
|
|
|
|
// RIFFParser
|
|
|
|
uint32_t
|
|
RIFFParser::Parse(ByteReader& aReader)
|
|
{
|
|
while (aReader.CanRead8() && !mRiffHeader.ParseNext(aReader.ReadU8())) { }
|
|
|
|
if (mRiffHeader.IsValid()) {
|
|
return RIFF_CHUNK_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
RIFFParser::Reset()
|
|
{
|
|
mRiffHeader.Reset();
|
|
}
|
|
|
|
const RIFFParser::RIFFHeader&
|
|
RIFFParser::RiffHeader() const
|
|
{
|
|
return mRiffHeader;
|
|
}
|
|
|
|
// RIFFParser::RIFFHeader
|
|
|
|
RIFFParser::RIFFHeader::RIFFHeader()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void
|
|
RIFFParser::RIFFHeader::Reset()
|
|
{
|
|
memset(mRaw, 0, sizeof(mRaw));
|
|
mPos = 0;
|
|
}
|
|
|
|
bool
|
|
RIFFParser::RIFFHeader::ParseNext(uint8_t c)
|
|
{
|
|
if (!Update(c)) {
|
|
Reset();
|
|
if (!Update(c)) {
|
|
Reset();
|
|
}
|
|
}
|
|
return IsValid();
|
|
}
|
|
|
|
bool
|
|
RIFFParser::RIFFHeader::IsValid(int aPos) const
|
|
{
|
|
if (aPos > -1 && aPos < 4) {
|
|
return RIFF[aPos] == mRaw[aPos];
|
|
} else if (aPos > 7 && aPos < 12) {
|
|
return WAVE[aPos - 8] == mRaw[aPos];
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
RIFFParser::RIFFHeader::IsValid() const
|
|
{
|
|
return mPos >= RIFF_CHUNK_SIZE;
|
|
}
|
|
|
|
bool
|
|
RIFFParser::RIFFHeader::Update(uint8_t c)
|
|
{
|
|
if (mPos < RIFF_CHUNK_SIZE) {
|
|
mRaw[mPos] = c;
|
|
}
|
|
return IsValid(mPos++);
|
|
}
|
|
|
|
// HeaderParser
|
|
|
|
uint32_t
|
|
HeaderParser::Parse(ByteReader& aReader)
|
|
{
|
|
while (aReader.CanRead8() && !mHeader.ParseNext(aReader.ReadU8())) { }
|
|
|
|
if (mHeader.IsValid()) {
|
|
return CHUNK_HEAD_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
HeaderParser::Reset()
|
|
{
|
|
mHeader.Reset();
|
|
}
|
|
|
|
const HeaderParser::ChunkHeader&
|
|
HeaderParser::GiveHeader() const
|
|
{
|
|
return mHeader;
|
|
}
|
|
|
|
// HeaderParser::ChunkHeader
|
|
|
|
HeaderParser::ChunkHeader::ChunkHeader()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void
|
|
HeaderParser::ChunkHeader::Reset()
|
|
{
|
|
memset(mRaw, 0, sizeof(mRaw));
|
|
mPos = 0;
|
|
}
|
|
|
|
bool
|
|
HeaderParser::ChunkHeader::ParseNext(uint8_t c)
|
|
{
|
|
Update(c);
|
|
return IsValid();
|
|
}
|
|
|
|
bool
|
|
HeaderParser::ChunkHeader::IsValid() const
|
|
{
|
|
return mPos >= CHUNK_HEAD_SIZE;
|
|
}
|
|
|
|
uint32_t
|
|
HeaderParser::ChunkHeader::ChunkName() const
|
|
{
|
|
return ((mRaw[0] << 24) | (mRaw[1] << 16) |
|
|
(mRaw[2] << 8 ) | (mRaw[3]));
|
|
}
|
|
|
|
uint32_t
|
|
HeaderParser::ChunkHeader::ChunkSize() const
|
|
{
|
|
return ((mRaw[7] << 24) | (mRaw[6] << 16) |
|
|
(mRaw[5] << 8 ) | (mRaw[4]));
|
|
}
|
|
|
|
void
|
|
HeaderParser::ChunkHeader::Update(uint8_t c)
|
|
{
|
|
if (mPos < CHUNK_HEAD_SIZE) {
|
|
mRaw[mPos++] = c;
|
|
}
|
|
}
|
|
|
|
// FormatParser
|
|
|
|
uint32_t
|
|
FormatParser::Parse(ByteReader& aReader)
|
|
{
|
|
while (aReader.CanRead8() && !mFmtChunk.ParseNext(aReader.ReadU8())) { }
|
|
|
|
if (mFmtChunk.IsValid()) {
|
|
return FMT_CHUNK_MIN_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
FormatParser::Reset()
|
|
{
|
|
mFmtChunk.Reset();
|
|
}
|
|
|
|
const FormatParser::FormatChunk&
|
|
FormatParser::FmtChunk() const
|
|
{
|
|
return mFmtChunk;
|
|
}
|
|
|
|
// FormatParser::FormatChunk
|
|
|
|
FormatParser::FormatChunk::FormatChunk()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void
|
|
FormatParser::FormatChunk::Reset()
|
|
{
|
|
memset(mRaw, 0, sizeof(mRaw));
|
|
mPos = 0;
|
|
}
|
|
|
|
uint16_t
|
|
FormatParser::FormatChunk::WaveFormat() const
|
|
{
|
|
return (mRaw[1] << 8) | (mRaw[0]);
|
|
}
|
|
|
|
uint16_t
|
|
FormatParser::FormatChunk::Channels() const
|
|
{
|
|
return (mRaw[3] << 8) | (mRaw[2]);
|
|
}
|
|
|
|
uint32_t
|
|
FormatParser::FormatChunk::SampleRate() const
|
|
{
|
|
return (mRaw[7] << 24) | (mRaw[6] << 16) |
|
|
(mRaw[5] << 8 ) | (mRaw[4]);
|
|
}
|
|
|
|
uint16_t
|
|
FormatParser::FormatChunk::FrameSize() const
|
|
{
|
|
return (mRaw[13] << 8) | (mRaw[12]);
|
|
}
|
|
|
|
uint16_t
|
|
FormatParser::FormatChunk::SampleFormat() const
|
|
{
|
|
return (mRaw[15] << 8) | (mRaw[14]);
|
|
}
|
|
|
|
bool
|
|
FormatParser::FormatChunk::ParseNext(uint8_t c)
|
|
{
|
|
Update(c);
|
|
return IsValid();
|
|
}
|
|
|
|
bool
|
|
FormatParser::FormatChunk::IsValid() const
|
|
{
|
|
return (FrameSize() == SampleRate() * Channels() / 8) &&
|
|
(mPos >= FMT_CHUNK_MIN_SIZE);
|
|
}
|
|
|
|
void
|
|
FormatParser::FormatChunk::Update(uint8_t c)
|
|
{
|
|
if (mPos < FMT_CHUNK_MIN_SIZE) {
|
|
mRaw[mPos++] = c;
|
|
}
|
|
}
|
|
|
|
// DataParser
|
|
|
|
DataParser::DataParser()
|
|
{
|
|
}
|
|
|
|
void
|
|
DataParser::Reset()
|
|
{
|
|
mChunk.Reset();
|
|
}
|
|
|
|
const DataParser::DataChunk&
|
|
DataParser::CurrentChunk() const
|
|
{
|
|
return mChunk;
|
|
}
|
|
|
|
// DataParser::DataChunk
|
|
|
|
void
|
|
DataParser::DataChunk::Reset()
|
|
{
|
|
mPos = 0;
|
|
}
|
|
|
|
} // namespace mozilla
|