--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -1,42 +1,28 @@
/* -*- 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 <limits>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/SizePrintfMacros.h"
-#include "nsAutoPtr.h"
#include "VideoUtils.h"
-#include "TimeUnits.h"
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__))
-using mozilla::media::TimeUnit;
-using mozilla::media::TimeInterval;
-using mozilla::media::TimeIntervals;
-using mp4_demuxer::ByteReader;
namespace mozilla {
namespace mp3 {
-// MP3Demuxer
-
MP3Demuxer::MP3Demuxer(MediaResource* aSource) : mSource(aSource) { }
bool
MP3Demuxer::InitInternal()
{
if (!mTrackDemuxer) {
mTrackDemuxer = new MP3TrackDemuxer(mSource);
}
@@ -82,697 +68,25 @@ bool
MP3Demuxer::IsSeekable() const
{
return true;
}
void
MP3Demuxer::NotifyDataArrived()
{
- // TODO: bug 1169485.
- NS_WARNING("Unimplemented function NotifyDataArrived");
- MP3LOGV("NotifyDataArrived()");
+ if (!mTrackDemuxer) {
+ return;
+ }
+ mTrackDemuxer->NotifyDataArrived();
}
void
MP3Demuxer::NotifyDataRemoved()
{
- // TODO: bug 1169485.
- NS_WARNING("Unimplemented function NotifyDataRemoved");
- MP3LOGV("NotifyDataRemoved()");
-}
-
-
-// MP3TrackDemuxer
-
-MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource)
- : mSource(aSource)
- , mFrameLock(false)
- , 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();
-
- MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
- mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
- mInfo->mDuration.ToMicroseconds());
-
- 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(const 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=%" PRId64 " mOffset=%" PRIu64
- " SL=%" PRId64 " 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) {
- // We've seeked past the target time, rewind back a little to correct it.
- const int64_t rewind = aTime.ToMicroseconds() / 100;
- FastSeek(aTime - TimeUnit::FromMicroseconds(rewind));
- }
-
- 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()=%" PRIuSIZE " 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(const 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);
+ if (!mTrackDemuxer) {
+ return;
}
-
- const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
- return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
-}
-
-MediaByteRange
-MP3TrackDemuxer::FindFirstFrame()
-{
- // We attempt to find multiple successive frames to avoid locking onto a false
- // positive if we're fed a stream that has been cut mid-frame.
- // For compatibility reasons we have to use the same frame count as Chrome, since
- // some web sites actually use a file that short to test our playback capabilities.
- static const int MIN_SUCCESSIVE_FRAMES = 3;
- mFrameLock = false;
-
- 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);
- mFrameLock = true;
- } 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 uint32_t MAX_SKIPPABLE_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;
- int64_t startOffset = mOffset;
- const bool searchingForID3 = !mParser.ID3Header().Size();
-
- // Check whether we've found a valid MPEG frame.
- while (!foundFrame) {
- // How many bytes we can go without finding a valid MPEG frame
- // (effectively rounded up to the next full buffer size multiple, as we
- // only check this before reading the next set of data into the buffer).
-
- // This default value of 0 will be used during testing whether we're being
- // fed a valid stream, which shouldn't have any gaps between frames.
- uint32_t maxSkippableBytes = 0;
-
- if (!mParser.FirstFrame().Length()) {
- // We're looking for the first valid frame. A well-formed file should
- // have its first frame header right at the start (skipping an ID3 tag
- // if necessary), but in order to support files that might have been
- // improperly cut, we search the first few kB for a frame header.
- maxSkippableBytes = MAX_SKIPPABLE_BYTES;
- // Since we're counting the skipped bytes from the offset we started
- // this parsing session with, we need to discount the ID3 tag size only
- // if we were looking for one during the current frame parsing session.
- if (searchingForID3) {
- maxSkippableBytes += mParser.ID3Header().TotalTagSize();
- }
- } else if (mFrameLock) {
- // We've found a valid MPEG stream, so don't impose any limits
- // to allow skipping corrupted data until we hit EOS.
- maxSkippableBytes = std::numeric_limits<uint32_t>::max();
- }
-
- if ((mOffset - startOffset > maxSkippableBytes)
- || (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) {
- MP3LOG("FindNext() EOS or exceeded maxSkippeableBytes 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=%" PRId64
- " 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()=%" PRIuSIZE, read, frame->Size());
- return nullptr;
- }
-
- UpdateState(aRange);
-
- frame->mTime = Duration(mFrameIndex - 1);
- frame->mDuration = Duration(1);
- frame->mTimecode = frame->mTime;
- frame->mKeyframe = true;
-
- MOZ_ASSERT(!frame->mTime.IsNegative());
- MOZ_ASSERT(frame->mDuration.IsPositive());
-
- 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;
+ mTrackDemuxer->NotifyDataRemoved();
}
} // namespace mp3
} // namespace mozilla