summaryrefslogtreecommitdiffstats
path: root/dom/media/MP3Demuxer.cpp
diff options
context:
space:
mode:
authortrav90 <travawine@palemoon.org>2018-12-08 16:30:00 -0600
committertrav90 <travawine@palemoon.org>2018-12-08 16:30:00 -0600
commit8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8 (patch)
tree750854097c774cbe78cb29d566a5a00446989321 /dom/media/MP3Demuxer.cpp
parent6b6a28ddb0d42a93e66578d598c08020b7e45985 (diff)
downloadUXP-8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8.tar
UXP-8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8.tar.gz
UXP-8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8.tar.lz
UXP-8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8.tar.xz
UXP-8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8.zip
Move the MP3 code to it's own directory
Diffstat (limited to 'dom/media/MP3Demuxer.cpp')
-rw-r--r--dom/media/MP3Demuxer.cpp1340
1 files changed, 0 insertions, 1340 deletions
diff --git a/dom/media/MP3Demuxer.cpp b/dom/media/MP3Demuxer.cpp
deleted file mode 100644
index 5a98cabfe..000000000
--- a/dom/media/MP3Demuxer.cpp
+++ /dev/null
@@ -1,1340 +0,0 @@
-/* -*- 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 "MP3Demuxer.h"
-
-#include <inttypes.h>
-#include <algorithm>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/EndianUtils.h"
-#include "nsAutoPtr.h"
-#include "VideoUtils.h"
-#include "TimeUnits.h"
-#include "prenv.h"
-
-#ifdef PR_LOGGING
-extern mozilla::LazyLogModule gMediaDemuxerLog;
-#define MP3LOG(msg, ...) \
- MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
-#define MP3LOGV(msg, ...) \
- MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
-#else
-#define MP3LOG(msg, ...)
-#define MP3LOGV(msg, ...)
-#endif
-
-using mozilla::media::TimeUnit;
-using mozilla::media::TimeInterval;
-using mozilla::media::TimeIntervals;
-using mp4_demuxer::ByteReader;
-
-namespace mozilla {
-
-// MP3Demuxer
-
-MP3Demuxer::MP3Demuxer(MediaResource* aSource)
- : mSource(aSource)
-{}
-
-bool
-MP3Demuxer::InitInternal() {
- if (!mTrackDemuxer) {
- mTrackDemuxer = new MP3TrackDemuxer(mSource);
- }
- return mTrackDemuxer->Init();
-}
-
-RefPtr<MP3Demuxer::InitPromise>
-MP3Demuxer::Init() {
- if (!InitInternal()) {
- MP3LOG("MP3Demuxer::Init() failure: waiting for data");
-
- return InitPromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
- }
-
- MP3LOG("MP3Demuxer::Init() successful");
- return InitPromise::CreateAndResolve(NS_OK, __func__);
-}
-
-bool
-MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const {
- return aType == TrackInfo::kAudioTrack;
-}
-
-uint32_t
-MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
- return aType == TrackInfo::kAudioTrack ? 1u : 0u;
-}
-
-already_AddRefed<MediaTrackDemuxer>
-MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) {
- if (!mTrackDemuxer) {
- return nullptr;
- }
- return RefPtr<MP3TrackDemuxer>(mTrackDemuxer).forget();
-}
-
-bool
-MP3Demuxer::IsSeekable() const {
- return true;
-}
-
-void
-MP3Demuxer::NotifyDataArrived() {
- // TODO: bug 1169485.
- NS_WARNING("Unimplemented function NotifyDataArrived");
- MP3LOGV("NotifyDataArrived()");
-}
-
-void
-MP3Demuxer::NotifyDataRemoved() {
- // TODO: bug 1169485.
- NS_WARNING("Unimplemented function NotifyDataRemoved");
- MP3LOGV("NotifyDataRemoved()");
-}
-
-
-// MP3TrackDemuxer
-
-MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource)
- : mSource(aSource)
- , mOffset(0)
- , mFirstFrameOffset(0)
- , mNumParsedFrames(0)
- , mFrameIndex(0)
- , mTotalFrameLen(0)
- , mSamplesPerFrame(0)
- , mSamplesPerSecond(0)
- , mChannels(0)
-{
- Reset();
-}
-
-bool
-MP3TrackDemuxer::Init() {
- Reset();
- FastSeek(TimeUnit());
- // Read the first frame to fetch sample rate and other meta data.
- RefPtr<MediaRawData> frame(GetNextFrame(FindFirstFrame()));
-
- MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
- StreamLength(), !!frame);
-
- if (!frame) {
- return false;
- }
-
- // Rewind back to the stream begin to avoid dropping the first frame.
- FastSeek(TimeUnit());
-
- if (!mInfo) {
- mInfo = MakeUnique<AudioInfo>();
- }
-
- mInfo->mRate = mSamplesPerSecond;
- mInfo->mChannels = mChannels;
- mInfo->mBitDepth = 16;
- mInfo->mMimeType = "audio/mpeg";
- mInfo->mDuration = Duration().ToMicroseconds();
-
- MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
- mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
- mInfo->mDuration);
-
- return mSamplesPerSecond && mChannels;
-}
-
-media::TimeUnit
-MP3TrackDemuxer::SeekPosition() const {
- TimeUnit pos = Duration(mFrameIndex);
- if (Duration() > TimeUnit()) {
- pos = std::min(Duration(), pos);
- }
- return pos;
-}
-
-const FrameParser::Frame&
-MP3TrackDemuxer::LastFrame() const {
- return mParser.PrevFrame();
-}
-
-RefPtr<MediaRawData>
-MP3TrackDemuxer::DemuxSample() {
- return GetNextFrame(FindNextFrame());
-}
-
-const ID3Parser::ID3Header&
-MP3TrackDemuxer::ID3Header() const {
- return mParser.ID3Header();
-}
-
-const FrameParser::VBRHeader&
-MP3TrackDemuxer::VBRInfo() const {
- return mParser.VBRInfo();
-}
-
-UniquePtr<TrackInfo>
-MP3TrackDemuxer::GetInfo() const {
- return mInfo->Clone();
-}
-
-RefPtr<MP3TrackDemuxer::SeekPromise>
-MP3TrackDemuxer::Seek(TimeUnit aTime) {
- // Efficiently seek to the position.
- FastSeek(aTime);
- // Correct seek position by scanning the next frames.
- const TimeUnit seekTime = ScanUntil(aTime);
-
- return SeekPromise::CreateAndResolve(seekTime, __func__);
-}
-
-TimeUnit
-MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
- MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
- aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
- mFrameIndex, mOffset);
-
- const auto& vbr = mParser.VBRInfo();
- if (!aTime.ToMicroseconds()) {
- // Quick seek to the beginning of the stream.
- mFrameIndex = 0;
- } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
- // Use TOC for more precise seeking.
- const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
- Duration().ToMicroseconds();
- mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac));
- } else if (AverageFrameLength() > 0) {
- mFrameIndex = FrameIndexFromTime(aTime);
- }
-
- mOffset = OffsetFromFrameIndex(mFrameIndex);
-
- if (mOffset > mFirstFrameOffset && StreamLength() > 0) {
- mOffset = std::min(StreamLength() - 1, mOffset);
- }
-
- mParser.EndFrameSession();
-
- MP3LOG("FastSeek End TOC=%d avgFrameLen=%f mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64
- " SL=%llu NumBytes=%u",
- vbr.IsTOCPresent(), AverageFrameLength(), mNumParsedFrames, mFrameIndex,
- mFirstFrameOffset, mOffset, StreamLength(), vbr.NumBytes().valueOr(0));
-
- return Duration(mFrameIndex);
-}
-
-TimeUnit
-MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) {
- MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
- aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
- mFrameIndex, mOffset);
-
- if (!aTime.ToMicroseconds()) {
- return FastSeek(aTime);
- }
-
- if (Duration(mFrameIndex) > aTime) {
- FastSeek(aTime);
- }
-
- if (Duration(mFrameIndex + 1) > aTime) {
- return SeekPosition();
- }
-
- MediaByteRange nextRange = FindNextFrame();
- while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
- nextRange = FindNextFrame();
- MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
- AverageFrameLength(), mNumParsedFrames,
- mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
- }
-
- MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
- AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
-
- return SeekPosition();
-}
-
-RefPtr<MP3TrackDemuxer::SamplesPromise>
-MP3TrackDemuxer::GetSamples(int32_t aNumSamples) {
- MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d "
- "mSamplesPerSecond=%d mChannels=%d",
- aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
- mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
- if (!aNumSamples) {
- return SamplesPromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
- }
-
- RefPtr<SamplesHolder> frames = new SamplesHolder();
-
- while (aNumSamples--) {
- RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
- if (!frame) {
- break;
- }
-
- frames->mSamples.AppendElement(frame);
- }
-
- MP3LOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64
- " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
- " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
- "mChannels=%d",
- frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames,
- mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
- mChannels);
-
- if (frames->mSamples.IsEmpty()) {
- return SamplesPromise::CreateAndReject(
- NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
- }
- return SamplesPromise::CreateAndResolve(frames, __func__);
-}
-
-void
-MP3TrackDemuxer::Reset() {
- MP3LOG("Reset()");
-
- FastSeek(TimeUnit());
- mParser.Reset();
-}
-
-RefPtr<MP3TrackDemuxer::SkipAccessPointPromise>
-MP3TrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold) {
- // Will not be called for audio-only resources.
- return SkipAccessPointPromise::CreateAndReject(
- SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
-}
-
-int64_t
-MP3TrackDemuxer::GetResourceOffset() const {
- return mOffset;
-}
-
-TimeIntervals
-MP3TrackDemuxer::GetBuffered() {
- AutoPinned<MediaResource> stream(mSource.GetResource());
- TimeIntervals buffered;
-
- if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) {
- // Special case completely cached files. This also handles local files.
- buffered += TimeInterval(TimeUnit(), Duration());
- MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]",
- TimeUnit().ToMicroseconds(), Duration().ToMicroseconds());
- return buffered;
- }
-
- MediaByteRangeSet ranges;
- nsresult rv = stream->GetCachedRanges(ranges);
- NS_ENSURE_SUCCESS(rv, buffered);
-
- for (const auto& range: ranges) {
- if (range.IsEmpty()) {
- continue;
- }
- TimeUnit start = Duration(FrameIndexFromOffset(range.mStart));
- TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd));
- MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]",
- start.ToMicroseconds(), end.ToMicroseconds());
- buffered += TimeInterval(start, end);
- }
-
- return buffered;
-}
-
-int64_t
-MP3TrackDemuxer::StreamLength() const {
- return mSource.GetLength();
-}
-
-TimeUnit
-MP3TrackDemuxer::Duration() const {
- if (!mNumParsedFrames) {
- return TimeUnit::FromMicroseconds(-1);
- }
-
- int64_t numFrames = 0;
- const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
- if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
- // VBR headers don't include the VBR header frame.
- numFrames = numAudioFrames.value() + 1;
- } else {
- const int64_t streamLen = StreamLength();
- if (streamLen < 0) {
- // Unknown length, we can't estimate duration.
- return TimeUnit::FromMicroseconds(-1);
- }
- if (AverageFrameLength() > 0) {
- numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
- }
- }
- return Duration(numFrames);
-}
-
-TimeUnit
-MP3TrackDemuxer::Duration(int64_t aNumFrames) const {
- if (!mSamplesPerSecond) {
- return TimeUnit::FromMicroseconds(-1);
- }
-
- const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
- return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
-}
-
-MediaByteRange
-MP3TrackDemuxer::FindFirstFrame() {
- static const int MIN_SUCCESSIVE_FRAMES = 4;
-
- MediaByteRange candidateFrame = FindNextFrame();
- int numSuccFrames = candidateFrame.Length() > 0;
- MediaByteRange currentFrame = candidateFrame;
- MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
- candidateFrame.mStart, candidateFrame.Length());
-
- while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
- mParser.EndFrameSession();
- mOffset = currentFrame.mEnd;
- const MediaByteRange prevFrame = currentFrame;
-
- // FindNextFrame() here will only return frames consistent with our candidate frame.
- currentFrame = FindNextFrame();
- numSuccFrames += currentFrame.Length() > 0;
- // Multiple successive false positives, which wouldn't be caught by the consistency
- // checks alone, can be detected by wrong alignment (non-zero gap between frames).
- const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;
-
- if (!currentFrame.Length() || frameSeparation != 0) {
- MP3LOGV("FindFirst() not enough successive frames detected, "
- "rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64
- ", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(),
- frameSeparation);
-
- mParser.ResetFrameData();
- mOffset = candidateFrame.mStart + 1;
- candidateFrame = FindNextFrame();
- numSuccFrames = candidateFrame.Length() > 0;
- currentFrame = candidateFrame;
- MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
- candidateFrame.mStart, candidateFrame.Length());
- }
- }
-
- if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
- MP3LOG("FindFirst() accepting candidate frame: "
- "successiveFrames=%d", numSuccFrames);
- } else {
- MP3LOG("FindFirst() no suitable first frame found");
- }
- return candidateFrame;
-}
-
-static bool
-VerifyFrameConsistency(
- const FrameParser::Frame& aFrame1, const FrameParser::Frame& aFrame2) {
- const auto& h1 = aFrame1.Header();
- const auto& h2 = aFrame2.Header();
-
- return h1.IsValid() && h2.IsValid() &&
- h1.Layer() == h2.Layer() &&
- h1.SlotSize() == h2.SlotSize() &&
- h1.SamplesPerFrame() == h2.SamplesPerFrame() &&
- h1.Channels() == h2.Channels() &&
- h1.SampleRate() == h2.SampleRate() &&
- h1.RawVersion() == h2.RawVersion() &&
- h1.RawProtection() == h2.RawProtection();
-}
-
-MediaByteRange
-MP3TrackDemuxer::FindNextFrame() {
- static const int BUFFER_SIZE = 64;
- static const int MAX_SKIPPED_BYTES = 1024 * BUFFER_SIZE;
-
- MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
- " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
- mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
- mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
- uint8_t buffer[BUFFER_SIZE];
- int32_t read = 0;
-
- bool foundFrame = false;
- int64_t frameHeaderOffset = 0;
-
- // Check whether we've found a valid MPEG frame.
- while (!foundFrame) {
- if ((!mParser.FirstFrame().Length() &&
- mOffset - mParser.ID3Header().Size() > MAX_SKIPPED_BYTES) ||
- (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) {
- MP3LOG("FindNext() EOS or exceeded MAX_SKIPPED_BYTES without a frame");
- // This is not a valid MPEG audio stream or we've reached EOS, give up.
- break;
- }
-
- ByteReader reader(buffer, read);
- uint32_t bytesToSkip = 0;
- foundFrame = mParser.Parse(&reader, &bytesToSkip);
- frameHeaderOffset = mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE;
-
- // If we've found neither an MPEG frame header nor an ID3v2 tag,
- // the reader shouldn't have any bytes remaining.
- MOZ_ASSERT(foundFrame || bytesToSkip || !reader.Remaining());
-
- if (foundFrame && mParser.FirstFrame().Length() &&
- !VerifyFrameConsistency(mParser.FirstFrame(), mParser.CurrentFrame())) {
- // We've likely hit a false-positive, ignore it and proceed with the
- // search for the next valid frame.
- foundFrame = false;
- mOffset = frameHeaderOffset + 1;
- mParser.EndFrameSession();
- } else {
- // Advance mOffset by the amount of bytes read and if necessary,
- // skip an ID3v2 tag which stretches beyond the current buffer.
- NS_ENSURE_TRUE(mOffset + read + bytesToSkip > mOffset,
- MediaByteRange(0, 0));
- mOffset += read + bytesToSkip;
- }
- }
-
- if (!foundFrame || !mParser.CurrentFrame().Length()) {
- MP3LOG("FindNext() Exit foundFrame=%d mParser.CurrentFrame().Length()=%d ",
- foundFrame, mParser.CurrentFrame().Length());
- return { 0, 0 };
- }
-
- MP3LOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " frameHeaderOffset=%d"
- " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
- " mChannels=%d",
- mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
- mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
- return { frameHeaderOffset, frameHeaderOffset + mParser.CurrentFrame().Length() };
-}
-
-bool
-MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) {
- if (!mNumParsedFrames || !aRange.Length()) {
- // We can't skip the first frame, since it could contain VBR headers.
- RefPtr<MediaRawData> frame(GetNextFrame(aRange));
- return frame;
- }
-
- UpdateState(aRange);
-
- MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
- " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
- mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
- mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
- return true;
-}
-
-already_AddRefed<MediaRawData>
-MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
- MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})",
- aRange.mStart, aRange.Length());
- if (!aRange.Length()) {
- return nullptr;
- }
-
- RefPtr<MediaRawData> frame = new MediaRawData();
- frame->mOffset = aRange.mStart;
-
- nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
- if (!frameWriter->SetSize(aRange.Length())) {
- MP3LOG("GetNext() Exit failed to allocated media buffer");
- return nullptr;
- }
-
- const uint32_t read = Read(frameWriter->Data(), frame->mOffset, frame->Size());
-
- if (read != aRange.Length()) {
- MP3LOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size());
- return nullptr;
- }
-
- UpdateState(aRange);
-
- frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
- frame->mDuration = Duration(1).ToMicroseconds();
- frame->mTimecode = frame->mTime;
- frame->mKeyframe = true;
-
- MOZ_ASSERT(frame->mTime >= 0);
- MOZ_ASSERT(frame->mDuration > 0);
-
- if (mNumParsedFrames == 1) {
- // First frame parsed, let's read VBR info if available.
- ByteReader reader(frame->Data(), frame->Size());
- mParser.ParseVBRHeader(&reader);
- mFirstFrameOffset = frame->mOffset;
- }
-
- MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
- " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
- " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
- mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
- mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
- return frame.forget();
-}
-
-int64_t
-MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const {
- int64_t offset = 0;
- const auto& vbr = mParser.VBRInfo();
-
- if (vbr.IsComplete()) {
- offset = mFirstFrameOffset + aFrameIndex * vbr.NumBytes().value() /
- vbr.NumAudioFrames().value();
- } else if (AverageFrameLength() > 0) {
- offset = mFirstFrameOffset + aFrameIndex * AverageFrameLength();
- }
-
- MP3LOGV("OffsetFromFrameIndex(%" PRId64 ") -> %" PRId64, aFrameIndex, offset);
- return std::max<int64_t>(mFirstFrameOffset, offset);
-}
-
-int64_t
-MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const {
- int64_t frameIndex = 0;
- const auto& vbr = mParser.VBRInfo();
-
- if (vbr.IsComplete()) {
- frameIndex = static_cast<float>(aOffset - mFirstFrameOffset) /
- vbr.NumBytes().value() * vbr.NumAudioFrames().value();
- frameIndex = std::min<int64_t>(vbr.NumAudioFrames().value(), frameIndex);
- } else if (AverageFrameLength() > 0) {
- frameIndex = (aOffset - mFirstFrameOffset) / AverageFrameLength();
- }
-
- MP3LOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex);
- return std::max<int64_t>(0, frameIndex);
-}
-
-int64_t
-MP3TrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const {
- int64_t frameIndex = 0;
- if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
- frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1;
- }
-
- MP3LOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex);
- return std::max<int64_t>(0, frameIndex);
-}
-
-void
-MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange) {
- // Prevent overflow.
- if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) {
- // These variables have a linear dependency and are only used to derive the
- // average frame length.
- mTotalFrameLen /= 2;
- mNumParsedFrames /= 2;
- }
-
- // Full frame parsed, move offset to its end.
- mOffset = aRange.mEnd;
-
- mTotalFrameLen += aRange.Length();
-
- if (!mSamplesPerFrame) {
- mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame();
- mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate();
- mChannels = mParser.CurrentFrame().Header().Channels();
- }
-
- ++mNumParsedFrames;
- ++mFrameIndex;
- MOZ_ASSERT(mFrameIndex > 0);
-
- // Prepare the parser for the next frame parsing session.
- mParser.EndFrameSession();
-}
-
-int32_t
-MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) {
- MP3LOGV("MP3TrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize);
-
- const int64_t streamLen = StreamLength();
- if (mInfo && streamLen > 0) {
- // Prevent blocking reads after successful initialization.
- aSize = std::min<int64_t>(aSize, streamLen - aOffset);
- }
-
- uint32_t read = 0;
- MP3LOGV("MP3TrackDemuxer::Read -> ReadAt(%d)", aSize);
- 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);
-}
-
-double
-MP3TrackDemuxer::AverageFrameLength() const {
- if (mNumParsedFrames) {
- return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
- }
- const auto& vbr = mParser.VBRInfo();
- if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
- return static_cast<double>(vbr.NumBytes().value()) /
- (vbr.NumAudioFrames().value() + 1);
- }
- return 0.0;
-}
-
-// FrameParser
-
-namespace frame_header {
-// FrameHeader mRaw byte offsets.
-static const int SYNC1 = 0;
-static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
-static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
-static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
-} // namespace frame_header
-
-FrameParser::FrameParser()
-{
-}
-
-void
-FrameParser::Reset() {
- mID3Parser.Reset();
- mFrame.Reset();
-}
-
-void
-FrameParser::ResetFrameData() {
- mFrame.Reset();
- mFirstFrame.Reset();
- mPrevFrame.Reset();
-}
-
-void
-FrameParser::EndFrameSession() {
- if (!mID3Parser.Header().IsValid()) {
- // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
- mID3Parser.Reset();
- }
- mPrevFrame = mFrame;
- mFrame.Reset();
-}
-
-const FrameParser::Frame&
-FrameParser::CurrentFrame() const {
- return mFrame;
-}
-
-const FrameParser::Frame&
-FrameParser::PrevFrame() const {
- return mPrevFrame;
-}
-
-const FrameParser::Frame&
-FrameParser::FirstFrame() const {
- return mFirstFrame;
-}
-
-const ID3Parser::ID3Header&
-FrameParser::ID3Header() const {
- return mID3Parser.Header();
-}
-
-const FrameParser::VBRHeader&
-FrameParser::VBRInfo() const {
- return mVBRHeader;
-}
-
-bool
-FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip) {
- MOZ_ASSERT(aReader && aBytesToSkip);
- *aBytesToSkip = 0;
-
- if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) {
- // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
- // ID3v1 tags may only be at file end.
- // TODO: should we try to read ID3 tags at end of file/mid-stream, too?
- const size_t prevReaderOffset = aReader->Offset();
- const uint32_t tagSize = mID3Parser.Parse(aReader);
- if (tagSize) {
- // ID3 tag found, skip past it.
- const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
-
- if (skipSize > aReader->Remaining()) {
- // Skipping across the ID3v2 tag would take us past the end of the buffer, therefore we
- // return immediately and let the calling function handle skipping the rest of the tag.
- MP3LOGV("ID3v2 tag detected, size=%d,"
- " needing to skip %d bytes past the current buffer",
- tagSize, skipSize - aReader->Remaining());
- *aBytesToSkip = skipSize - aReader->Remaining();
- return false;
- }
- MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
- aReader->Read(skipSize);
- } else {
- // No ID3v2 tag found, rewinding reader in order to search for a MPEG frame header.
- aReader->Seek(prevReaderOffset);
- }
- }
-
- while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { }
-
- if (mFrame.Length()) {
- // MP3 frame found.
- if (!mFirstFrame.Length()) {
- mFirstFrame = mFrame;
- }
- // Indicate success.
- return true;
- }
- return false;
-}
-
-// FrameParser::Header
-
-FrameParser::FrameHeader::FrameHeader()
-{
- Reset();
-}
-
-uint8_t
-FrameParser::FrameHeader::Sync1() const {
- return mRaw[frame_header::SYNC1];
-}
-
-uint8_t
-FrameParser::FrameHeader::Sync2() const {
- return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawVersion() const {
- return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawLayer() const {
- return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawProtection() const {
- return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawBitrate() const {
- return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawSampleRate() const {
- return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2;
-}
-
-uint8_t
-FrameParser::FrameHeader::Padding() const {
- return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1;
-}
-
-uint8_t
-FrameParser::FrameHeader::Private() const {
- return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE];
-}
-
-uint8_t
-FrameParser::FrameHeader::RawChannelMode() const {
- return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6;
-}
-
-int32_t
-FrameParser::FrameHeader::Layer() const {
- static const uint8_t LAYERS[4] = { 0, 3, 2, 1 };
-
- return LAYERS[RawLayer()];
-}
-
-int32_t
-FrameParser::FrameHeader::SampleRate() const {
- // Sample rates - use [version][srate]
- static const uint16_t SAMPLE_RATE[4][4] = {
- { 11025, 12000, 8000, 0 }, // MPEG 2.5
- { 0, 0, 0, 0 }, // Reserved
- { 22050, 24000, 16000, 0 }, // MPEG 2
- { 44100, 48000, 32000, 0 } // MPEG 1
- };
-
- return SAMPLE_RATE[RawVersion()][RawSampleRate()];
-}
-
-int32_t
-FrameParser::FrameHeader::Channels() const {
- // 3 is single channel (mono), any other value is some variant of dual
- // channel.
- return RawChannelMode() == 3 ? 1 : 2;
-}
-
-int32_t
-FrameParser::FrameHeader::SamplesPerFrame() const {
- // Samples per frame - use [version][layer]
- static const uint16_t FRAME_SAMPLE[4][4] = {
- // Layer 3 2 1 Version
- { 0, 576, 1152, 384 }, // 2.5
- { 0, 0, 0, 0 }, // Reserved
- { 0, 576, 1152, 384 }, // 2
- { 0, 1152, 1152, 384 } // 1
- };
-
- return FRAME_SAMPLE[RawVersion()][RawLayer()];
-}
-
-int32_t
-FrameParser::FrameHeader::Bitrate() const {
- // Bitrates - use [version][layer][bitrate]
- static const uint16_t BITRATE[4][4][16] = {
- { // Version 2.5
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
- { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
- { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
- { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
- },
- { // Reserved
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid
- },
- { // Version 2
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
- { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
- { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
- { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
- },
- { // Version 1
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
- { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
- { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
- { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
- }
- };
-
- return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()];
-}
-
-int32_t
-FrameParser::FrameHeader::SlotSize() const {
- // Slot size (MPEG unit of measurement) - use [layer]
- static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
-
- return SLOT_SIZE[RawLayer()];
-}
-
-bool
-FrameParser::FrameHeader::ParseNext(uint8_t c) {
- if (!Update(c)) {
- Reset();
- if (!Update(c)) {
- Reset();
- }
- }
- return IsValid();
-}
-
-bool
-FrameParser::FrameHeader::IsValid(int aPos) const {
- if (aPos >= SIZE) {
- return true;
- }
- if (aPos == frame_header::SYNC1) {
- return Sync1() == 0xFF;
- }
- if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) {
- return Sync2() == 7 &&
- RawVersion() != 1 &&
- Layer() == 3;
- }
- if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) {
- return RawBitrate() != 0xF && RawBitrate() != 0 &&
- RawSampleRate() != 3;
- }
- return true;
-}
-
-bool
-FrameParser::FrameHeader::IsValid() const {
- return mPos >= SIZE;
-}
-
-void
-FrameParser::FrameHeader::Reset() {
- mPos = 0;
-}
-
-bool
-FrameParser::FrameHeader::Update(uint8_t c) {
- if (mPos < SIZE) {
- mRaw[mPos] = c;
- }
- return IsValid(mPos++);
-}
-
-// FrameParser::VBRHeader
-
-namespace vbr_header {
-static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"};
-static const uint32_t TOC_SIZE = 100;
-} // namespace vbr_header
-
-FrameParser::VBRHeader::VBRHeader()
- : mType(NONE)
-{
-}
-
-FrameParser::VBRHeader::VBRHeaderType
-FrameParser::VBRHeader::Type() const {
- return mType;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::NumAudioFrames() const {
- return mNumAudioFrames;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::NumBytes() const {
- return mNumBytes;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::Scale() const {
- return mScale;
-}
-
-bool
-FrameParser::VBRHeader::IsTOCPresent() const {
- return mTOC.size() == vbr_header::TOC_SIZE;
-}
-
-bool
-FrameParser::VBRHeader::IsValid() const {
- return mType != NONE;
-}
-
-bool
-FrameParser::VBRHeader::IsComplete() const {
- return IsValid() &&
- mNumAudioFrames.valueOr(0) > 0 &&
- mNumBytes.valueOr(0) > 0 &&
- // We don't care about the scale for any computations here.
- // mScale < 101 &&
- true;
-}
-
-int64_t
-FrameParser::VBRHeader::Offset(float aDurationFac) const {
- if (!IsTOCPresent()) {
- return -1;
- }
-
- // Constrain the duration percentage to [0, 99].
- const float durationPer = 100.0f * std::min(0.99f, std::max(0.0f, aDurationFac));
- const size_t fullPer = durationPer;
- const float rest = durationPer - fullPer;
-
- MOZ_ASSERT(fullPer < mTOC.size());
- int64_t offset = mTOC.at(fullPer);
-
- if (rest > 0.0 && fullPer + 1 < mTOC.size()) {
- offset += rest * (mTOC.at(fullPer + 1) - offset);
- }
-
- return offset;
-}
-
-bool
-FrameParser::VBRHeader::ParseXing(ByteReader* aReader) {
- static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
- static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
-
- enum Flags {
- NUM_FRAMES = 0x01,
- NUM_BYTES = 0x02,
- TOC = 0x04,
- VBR_SCALE = 0x08
- };
-
- MOZ_ASSERT(aReader);
- const size_t prevReaderOffset = aReader->Offset();
-
- // We have to search for the Xing header as its position can change.
- while (aReader->CanRead32() &&
- aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) {
- aReader->Read(1);
- }
-
- if (aReader->CanRead32()) {
- // Skip across the VBR header ID tag.
- aReader->ReadU32();
- mType = XING;
- }
- uint32_t flags = 0;
- if (aReader->CanRead32()) {
- flags = aReader->ReadU32();
- }
- if (flags & NUM_FRAMES && aReader->CanRead32()) {
- mNumAudioFrames = Some(aReader->ReadU32());
- }
- if (flags & NUM_BYTES && aReader->CanRead32()) {
- mNumBytes = Some(aReader->ReadU32());
- }
- if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
- if (!mNumBytes) {
- // We don't have the stream size to calculate offsets, skip the TOC.
- aReader->Read(vbr_header::TOC_SIZE);
- } else {
- mTOC.clear();
- mTOC.reserve(vbr_header::TOC_SIZE);
- for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
- mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value());
- }
- }
- }
- if (flags & VBR_SCALE && aReader->CanRead32()) {
- mScale = Some(aReader->ReadU32());
- }
-
- aReader->Seek(prevReaderOffset);
- return mType == XING;
-}
-
-bool
-FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader) {
- static const uint32_t TAG = BigEndian::readUint32("VBRI");
- static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
- static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14;
- static const uint32_t MIN_FRAME_SIZE = OFFSET + 26;
-
- MOZ_ASSERT(aReader);
- // ParseVBRI assumes that the ByteReader offset points to the beginning of a frame,
- // therefore as a simple check, we look for the presence of a frame sync at that position.
- MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0);
- const size_t prevReaderOffset = aReader->Offset();
-
- // VBRI have a fixed relative position, so let's check for it there.
- if (aReader->Remaining() > MIN_FRAME_SIZE) {
- aReader->Seek(prevReaderOffset + OFFSET);
- if (aReader->ReadU32() == TAG) {
- aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
- mNumAudioFrames = Some(aReader->ReadU32());
- mType = VBRI;
- aReader->Seek(prevReaderOffset);
- return true;
- }
- }
- aReader->Seek(prevReaderOffset);
- return false;
-}
-
-bool
-FrameParser::VBRHeader::Parse(ByteReader* aReader) {
- const bool rv = ParseVBRI(aReader) || ParseXing(aReader);
- if (rv) {
- MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s"
- " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%u",
- vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0),
- NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size());
- }
- return rv;
-}
-
-// FrameParser::Frame
-
-void
-FrameParser::Frame::Reset() {
- mHeader.Reset();
-}
-
-int32_t
-FrameParser::Frame::Length() const {
- if (!mHeader.IsValid() || !mHeader.SampleRate()) {
- return 0;
- }
-
- const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f;
- const int32_t frameLen = bitsPerSample * mHeader.Bitrate() /
- mHeader.SampleRate() +
- mHeader.Padding() * mHeader.SlotSize();
- return frameLen;
-}
-
-bool
-FrameParser::Frame::ParseNext(uint8_t c) {
- return mHeader.ParseNext(c);
-}
-
-const FrameParser::FrameHeader&
-FrameParser::Frame::Header() const {
- return mHeader;
-}
-
-bool
-FrameParser::ParseVBRHeader(ByteReader* aReader) {
- return mVBRHeader.Parse(aReader);
-}
-
-// ID3Parser
-
-// Constants
-namespace id3_header {
-static const int ID_LEN = 3;
-static const int VERSION_LEN = 2;
-static const int FLAGS_LEN = 1;
-static const int SIZE_LEN = 4;
-
-static const int ID_END = ID_LEN;
-static const int VERSION_END = ID_END + VERSION_LEN;
-static const int FLAGS_END = VERSION_END + FLAGS_LEN;
-static const int SIZE_END = FLAGS_END + SIZE_LEN;
-
-static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
-
-static const uint8_t MIN_MAJOR_VER = 2;
-static const uint8_t MAX_MAJOR_VER = 4;
-} // namespace id3_header
-
-uint32_t
-ID3Parser::Parse(ByteReader* aReader) {
- MOZ_ASSERT(aReader);
-
- while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { }
-
- if (mHeader.IsValid()) {
- // Header found, return total tag size.
- return ID3Header::SIZE + Header().Size() + Header().FooterSize();
- }
- return 0;
-}
-
-void
-ID3Parser::Reset() {
- mHeader.Reset();
-}
-
-const ID3Parser::ID3Header&
-ID3Parser::Header() const {
- return mHeader;
-}
-
-// ID3Parser::Header
-
-ID3Parser::ID3Header::ID3Header()
-{
- Reset();
-}
-
-void
-ID3Parser::ID3Header::Reset() {
- mSize = 0;
- mPos = 0;
-}
-
-uint8_t
-ID3Parser::ID3Header::MajorVersion() const {
- return mRaw[id3_header::ID_END];
-}
-
-uint8_t
-ID3Parser::ID3Header::MinorVersion() const {
- return mRaw[id3_header::ID_END + 1];
-}
-
-uint8_t
-ID3Parser::ID3Header::Flags() const {
- return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN];
-}
-
-uint32_t
-ID3Parser::ID3Header::Size() const {
- if (!IsValid()) {
- return 0;
- }
- return mSize;
-}
-
-uint8_t
-ID3Parser::ID3Header::FooterSize() const {
- if (Flags() & (1 << 4)) {
- return SIZE;
- }
- return 0;
-}
-
-bool
-ID3Parser::ID3Header::ParseNext(uint8_t c) {
- if (!Update(c)) {
- Reset();
- if (!Update(c)) {
- Reset();
- }
- }
- return IsValid();
-}
-
-bool
-ID3Parser::ID3Header::IsValid(int aPos) const {
- if (aPos >= SIZE) {
- return true;
- }
- const uint8_t c = mRaw[aPos];
- switch (aPos) {
- case 0: case 1: case 2:
- // Expecting "ID3".
- return id3_header::ID[aPos] == c;
- case 3:
- return MajorVersion() >= id3_header::MIN_MAJOR_VER &&
- MajorVersion() <= id3_header::MAX_MAJOR_VER;
- case 4:
- return MinorVersion() < 0xFF;
- case 5:
- // Validate flags for supported versions, see bug 949036.
- return ((0xFF >> MajorVersion()) & c) == 0;
- case 6: case 7: case 8: case 9:
- return c < 0x80;
- }
- return true;
-}
-
-bool
-ID3Parser::ID3Header::IsValid() const {
- return mPos >= SIZE;
-}
-
-bool
-ID3Parser::ID3Header::Update(uint8_t c) {
- if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN &&
- mPos < id3_header::SIZE_END) {
- mSize <<= 7;
- mSize |= c;
- }
- if (mPos < SIZE) {
- mRaw[mPos] = c;
- }
- return IsValid(mPos++);
-}
-
-} // namespace mozilla