LCOV - code coverage report
Current view: top level - dom/media/mp3 - MP3Demuxer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 334 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 39 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "MP3Demuxer.h"
       8             : 
       9             : #include <algorithm>
      10             : #include <inttypes.h>
      11             : #include <limits>
      12             : 
      13             : #include "mozilla/Assertions.h"
      14             : #include "mozilla/SizePrintfMacros.h"
      15             : #include "nsAutoPtr.h"
      16             : #include "TimeUnits.h"
      17             : #include "VideoUtils.h"
      18             : 
      19             : extern mozilla::LazyLogModule gMediaDemuxerLog;
      20             : #define MP3LOG(msg, ...) \
      21             :   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
      22             : #define MP3LOGV(msg, ...) \
      23             :   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
      24             : 
      25             : using mozilla::media::TimeUnit;
      26             : using mozilla::media::TimeInterval;
      27             : using mozilla::media::TimeIntervals;
      28             : using mp4_demuxer::ByteReader;
      29             : 
      30             : namespace mozilla {
      31             : 
      32             : // MP3Demuxer
      33             : 
      34           0 : MP3Demuxer::MP3Demuxer(MediaResource* aSource) : mSource(aSource) { }
      35             : 
      36             : bool
      37           0 : MP3Demuxer::InitInternal()
      38             : {
      39           0 :   if (!mTrackDemuxer) {
      40           0 :     mTrackDemuxer = new MP3TrackDemuxer(mSource);
      41             :   }
      42           0 :   return mTrackDemuxer->Init();
      43             : }
      44             : 
      45             : RefPtr<MP3Demuxer::InitPromise>
      46           0 : MP3Demuxer::Init()
      47             : {
      48           0 :   if (!InitInternal()) {
      49           0 :     MP3LOG("MP3Demuxer::Init() failure: waiting for data");
      50             : 
      51             :     return InitPromise::CreateAndReject(
      52           0 :       NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
      53             :   }
      54             : 
      55           0 :   MP3LOG("MP3Demuxer::Init() successful");
      56           0 :   return InitPromise::CreateAndResolve(NS_OK, __func__);
      57             : }
      58             : 
      59             : bool
      60           0 : MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const
      61             : {
      62           0 :   return aType == TrackInfo::kAudioTrack;
      63             : }
      64             : 
      65             : uint32_t
      66           0 : MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
      67             : {
      68           0 :   return aType == TrackInfo::kAudioTrack ? 1u : 0u;
      69             : }
      70             : 
      71             : already_AddRefed<MediaTrackDemuxer>
      72           0 : MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
      73             : {
      74           0 :   if (!mTrackDemuxer) {
      75           0 :     return nullptr;
      76             :   }
      77           0 :   return RefPtr<MP3TrackDemuxer>(mTrackDemuxer).forget();
      78             : }
      79             : 
      80             : bool
      81           0 : MP3Demuxer::IsSeekable() const
      82             : {
      83           0 :   return true;
      84             : }
      85             : 
      86             : void
      87           0 : MP3Demuxer::NotifyDataArrived()
      88             : {
      89             :   // TODO: bug 1169485.
      90           0 :   NS_WARNING("Unimplemented function NotifyDataArrived");
      91           0 :   MP3LOGV("NotifyDataArrived()");
      92           0 : }
      93             : 
      94             : void
      95           0 : MP3Demuxer::NotifyDataRemoved()
      96             : {
      97             :   // TODO: bug 1169485.
      98           0 :   NS_WARNING("Unimplemented function NotifyDataRemoved");
      99           0 :   MP3LOGV("NotifyDataRemoved()");
     100           0 : }
     101             : 
     102             : 
     103             : // MP3TrackDemuxer
     104             : 
     105           0 : MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource)
     106             :   : mSource(aSource)
     107             :   , mFrameLock(false)
     108             :   , mOffset(0)
     109             :   , mFirstFrameOffset(0)
     110             :   , mNumParsedFrames(0)
     111             :   , mFrameIndex(0)
     112             :   , mTotalFrameLen(0)
     113             :   , mSamplesPerFrame(0)
     114             :   , mSamplesPerSecond(0)
     115           0 :   , mChannels(0)
     116             : {
     117           0 :   Reset();
     118           0 : }
     119             : 
     120             : bool
     121           0 : MP3TrackDemuxer::Init()
     122             : {
     123           0 :   Reset();
     124           0 :   FastSeek(TimeUnit());
     125             :   // Read the first frame to fetch sample rate and other meta data.
     126           0 :   RefPtr<MediaRawData> frame(GetNextFrame(FindFirstFrame()));
     127             : 
     128           0 :   MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
     129             :          StreamLength(), !!frame);
     130             : 
     131           0 :   if (!frame) {
     132           0 :     return false;
     133             :   }
     134             : 
     135             :   // Rewind back to the stream begin to avoid dropping the first frame.
     136           0 :   FastSeek(TimeUnit());
     137             : 
     138           0 :   if (!mInfo) {
     139           0 :     mInfo = MakeUnique<AudioInfo>();
     140             :   }
     141             : 
     142           0 :   mInfo->mRate = mSamplesPerSecond;
     143           0 :   mInfo->mChannels = mChannels;
     144           0 :   mInfo->mBitDepth = 16;
     145           0 :   mInfo->mMimeType = "audio/mpeg";
     146           0 :   mInfo->mDuration = Duration();
     147             : 
     148           0 :   MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
     149             :          mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
     150             :          mInfo->mDuration.ToMicroseconds());
     151             : 
     152           0 :   return mSamplesPerSecond && mChannels;
     153             : }
     154             : 
     155             : media::TimeUnit
     156           0 : MP3TrackDemuxer::SeekPosition() const
     157             : {
     158           0 :   TimeUnit pos = Duration(mFrameIndex);
     159           0 :   if (Duration() > TimeUnit()) {
     160           0 :     pos = std::min(Duration(), pos);
     161             :   }
     162           0 :   return pos;
     163             : }
     164             : 
     165             : const FrameParser::Frame&
     166           0 : MP3TrackDemuxer::LastFrame() const
     167             : {
     168           0 :   return mParser.PrevFrame();
     169             : }
     170             : 
     171             : RefPtr<MediaRawData>
     172           0 : MP3TrackDemuxer::DemuxSample()
     173             : {
     174           0 :   return GetNextFrame(FindNextFrame());
     175             : }
     176             : 
     177             : const ID3Parser::ID3Header&
     178           0 : MP3TrackDemuxer::ID3Header() const
     179             : {
     180           0 :   return mParser.ID3Header();
     181             : }
     182             : 
     183             : const FrameParser::VBRHeader&
     184           0 : MP3TrackDemuxer::VBRInfo() const
     185             : {
     186           0 :   return mParser.VBRInfo();
     187             : }
     188             : 
     189             : UniquePtr<TrackInfo>
     190           0 : MP3TrackDemuxer::GetInfo() const
     191             : {
     192           0 :   return mInfo->Clone();
     193             : }
     194             : 
     195             : RefPtr<MP3TrackDemuxer::SeekPromise>
     196           0 : MP3TrackDemuxer::Seek(const TimeUnit& aTime)
     197             : {
     198             :   // Efficiently seek to the position.
     199           0 :   FastSeek(aTime);
     200             :   // Correct seek position by scanning the next frames.
     201           0 :   const TimeUnit seekTime = ScanUntil(aTime);
     202             : 
     203           0 :   return SeekPromise::CreateAndResolve(seekTime, __func__);
     204             : }
     205             : 
     206             : TimeUnit
     207           0 : MP3TrackDemuxer::FastSeek(const TimeUnit& aTime)
     208             : {
     209           0 :   MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
     210             :          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
     211             :          aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
     212             :          mFrameIndex, mOffset);
     213             : 
     214           0 :   const auto& vbr = mParser.VBRInfo();
     215           0 :   if (!aTime.ToMicroseconds()) {
     216             :     // Quick seek to the beginning of the stream.
     217           0 :     mFrameIndex = 0;
     218           0 :   } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
     219             :     // Use TOC for more precise seeking.
     220           0 :     const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
     221           0 :                                                   Duration().ToMicroseconds();
     222           0 :     mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac));
     223           0 :   } else if (AverageFrameLength() > 0) {
     224           0 :     mFrameIndex = FrameIndexFromTime(aTime);
     225             :   }
     226             : 
     227           0 :   mOffset = OffsetFromFrameIndex(mFrameIndex);
     228             : 
     229           0 :   if (mOffset > mFirstFrameOffset && StreamLength() > 0) {
     230           0 :     mOffset = std::min(StreamLength() - 1, mOffset);
     231             :   }
     232             : 
     233           0 :   mParser.EndFrameSession();
     234             : 
     235           0 :   MP3LOG("FastSeek End TOC=%d avgFrameLen=%f mNumParsedFrames=%" PRIu64
     236             :          " mFrameIndex=%" PRId64 " mFirstFrameOffset=%" PRId64 " mOffset=%" PRIu64
     237             :          " SL=%" PRId64 " NumBytes=%u",
     238             :          vbr.IsTOCPresent(), AverageFrameLength(), mNumParsedFrames, mFrameIndex,
     239             :          mFirstFrameOffset, mOffset, StreamLength(), vbr.NumBytes().valueOr(0));
     240             : 
     241           0 :   return Duration(mFrameIndex);
     242             : }
     243             : 
     244             : TimeUnit
     245           0 : MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime)
     246             : {
     247           0 :   MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
     248             :          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
     249             :          aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
     250             :          mFrameIndex, mOffset);
     251             : 
     252           0 :   if (!aTime.ToMicroseconds()) {
     253           0 :     return FastSeek(aTime);
     254             :   }
     255             : 
     256           0 :   if (Duration(mFrameIndex) > aTime) {
     257             :     // We've seeked past the target time, rewind back a little to correct it.
     258           0 :     const int64_t rewind = aTime.ToMicroseconds() / 100;
     259           0 :     FastSeek(aTime - TimeUnit::FromMicroseconds(rewind));
     260             :   }
     261             : 
     262           0 :   if (Duration(mFrameIndex + 1) > aTime) {
     263           0 :     return SeekPosition();
     264             :   }
     265             : 
     266           0 :   MediaByteRange nextRange = FindNextFrame();
     267           0 :   while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
     268           0 :     nextRange = FindNextFrame();
     269           0 :     MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
     270             :             " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
     271             :             AverageFrameLength(), mNumParsedFrames,
     272             :             mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
     273             :   }
     274             : 
     275           0 :   MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
     276             :          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
     277             :          AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
     278             : 
     279           0 :   return SeekPosition();
     280             : }
     281             : 
     282             : RefPtr<MP3TrackDemuxer::SamplesPromise>
     283           0 : MP3TrackDemuxer::GetSamples(int32_t aNumSamples)
     284             : {
     285           0 :   MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
     286             :           " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
     287             :           " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
     288             :           aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
     289             :           mSamplesPerFrame, mSamplesPerSecond, mChannels);
     290             : 
     291           0 :   if (!aNumSamples) {
     292             :     return SamplesPromise::CreateAndReject(
     293           0 :         NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
     294             :   }
     295             : 
     296           0 :   RefPtr<SamplesHolder> frames = new SamplesHolder();
     297             : 
     298           0 :   while (aNumSamples--) {
     299           0 :     RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
     300           0 :     if (!frame) {
     301           0 :       break;
     302             :     }
     303             : 
     304           0 :     frames->mSamples.AppendElement(frame);
     305             :   }
     306             : 
     307           0 :   MP3LOGV("GetSamples() End mSamples.Size()=%" PRIuSIZE " aNumSamples=%d mOffset=%" PRIu64
     308             :           " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
     309             :           " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
     310             :           "mChannels=%d",
     311             :           frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames,
     312             :           mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
     313             :           mChannels);
     314             : 
     315           0 :   if (frames->mSamples.IsEmpty()) {
     316             :     return SamplesPromise::CreateAndReject(
     317           0 :         NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
     318             :   }
     319           0 :   return SamplesPromise::CreateAndResolve(frames, __func__);
     320             : }
     321             : 
     322             : void
     323           0 : MP3TrackDemuxer::Reset()
     324             : {
     325           0 :   MP3LOG("Reset()");
     326             : 
     327           0 :   FastSeek(TimeUnit());
     328           0 :   mParser.Reset();
     329           0 : }
     330             : 
     331             : RefPtr<MP3TrackDemuxer::SkipAccessPointPromise>
     332           0 : MP3TrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
     333             : {
     334             :   // Will not be called for audio-only resources.
     335             :   return SkipAccessPointPromise::CreateAndReject(
     336           0 :     SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
     337             : }
     338             : 
     339             : int64_t
     340           0 : MP3TrackDemuxer::GetResourceOffset() const
     341             : {
     342           0 :   return mOffset;
     343             : }
     344             : 
     345             : TimeIntervals
     346           0 : MP3TrackDemuxer::GetBuffered()
     347             : {
     348           0 :   AutoPinned<MediaResource> stream(mSource.GetResource());
     349           0 :   TimeIntervals buffered;
     350             : 
     351           0 :   if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) {
     352             :     // Special case completely cached files. This also handles local files.
     353           0 :     buffered += TimeInterval(TimeUnit(), Duration());
     354           0 :     MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]",
     355             :             TimeUnit().ToMicroseconds(), Duration().ToMicroseconds());
     356           0 :     return buffered;
     357             :   }
     358             : 
     359           0 :   MediaByteRangeSet ranges;
     360           0 :   nsresult rv = stream->GetCachedRanges(ranges);
     361           0 :   NS_ENSURE_SUCCESS(rv, buffered);
     362             : 
     363           0 :   for (const auto& range: ranges) {
     364           0 :     if (range.IsEmpty()) {
     365           0 :       continue;
     366             :     }
     367           0 :     TimeUnit start = Duration(FrameIndexFromOffset(range.mStart));
     368           0 :     TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd));
     369           0 :     MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]",
     370             :             start.ToMicroseconds(), end.ToMicroseconds());
     371           0 :     buffered += TimeInterval(start, end);
     372             :   }
     373             : 
     374           0 :   return buffered;
     375             : }
     376             : 
     377             : int64_t
     378           0 : MP3TrackDemuxer::StreamLength() const
     379             : {
     380           0 :   return mSource.GetLength();
     381             : }
     382             : 
     383             : TimeUnit
     384           0 : MP3TrackDemuxer::Duration() const
     385             : {
     386           0 :   if (!mNumParsedFrames) {
     387           0 :     return TimeUnit::FromMicroseconds(-1);
     388             :   }
     389             : 
     390           0 :   int64_t numFrames = 0;
     391           0 :   const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
     392           0 :   if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
     393             :     // VBR headers don't include the VBR header frame.
     394           0 :     numFrames = numAudioFrames.value() + 1;
     395             :   } else {
     396           0 :     const int64_t streamLen = StreamLength();
     397           0 :     if (streamLen < 0) {
     398             :       // Unknown length, we can't estimate duration.
     399           0 :       return TimeUnit::FromMicroseconds(-1);
     400             :     }
     401           0 :     if (AverageFrameLength() > 0) {
     402           0 :       numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
     403             :     }
     404             :   }
     405           0 :   return Duration(numFrames);
     406             : }
     407             : 
     408             : TimeUnit
     409           0 : MP3TrackDemuxer::Duration(int64_t aNumFrames) const
     410             : {
     411           0 :   if (!mSamplesPerSecond) {
     412           0 :     return TimeUnit::FromMicroseconds(-1);
     413             :   }
     414             : 
     415           0 :   const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
     416           0 :   return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
     417             : }
     418             : 
     419             : MediaByteRange
     420           0 : MP3TrackDemuxer::FindFirstFrame()
     421             : {
     422             :   // We attempt to find multiple successive frames to avoid locking onto a false
     423             :   // positive if we're fed a stream that has been cut mid-frame.
     424             :   // For compatibility reasons we have to use the same frame count as Chrome, since
     425             :   // some web sites actually use a file that short to test our playback capabilities.
     426             :   static const int MIN_SUCCESSIVE_FRAMES = 3;
     427           0 :   mFrameLock = false;
     428             : 
     429           0 :   MediaByteRange candidateFrame = FindNextFrame();
     430           0 :   int numSuccFrames = candidateFrame.Length() > 0;
     431           0 :   MediaByteRange currentFrame = candidateFrame;
     432           0 :   MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64
     433             :           " Length()=%" PRIu64,
     434             :           candidateFrame.mStart, candidateFrame.Length());
     435             : 
     436           0 :   while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
     437           0 :     mParser.EndFrameSession();
     438           0 :     mOffset = currentFrame.mEnd;
     439           0 :     const MediaByteRange prevFrame = currentFrame;
     440             : 
     441             :     // FindNextFrame() here will only return frames consistent with our candidate frame.
     442           0 :     currentFrame = FindNextFrame();
     443           0 :     numSuccFrames += currentFrame.Length() > 0;
     444             :     // Multiple successive false positives, which wouldn't be caught by the consistency
     445             :     // checks alone, can be detected by wrong alignment (non-zero gap between frames).
     446           0 :     const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;
     447             : 
     448           0 :     if (!currentFrame.Length() || frameSeparation != 0) {
     449           0 :       MP3LOGV("FindFirst() not enough successive frames detected, "
     450             :               "rejecting candidate frame: successiveFrames=%d, last "
     451             :               "Length()=%" PRIu64 ", last frameSeparation=%" PRId64,
     452             :               numSuccFrames, currentFrame.Length(), frameSeparation);
     453             : 
     454           0 :       mParser.ResetFrameData();
     455           0 :       mOffset = candidateFrame.mStart + 1;
     456           0 :       candidateFrame = FindNextFrame();
     457           0 :       numSuccFrames = candidateFrame.Length() > 0;
     458           0 :       currentFrame = candidateFrame;
     459           0 :       MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64
     460             :               " Length()=%" PRIu64,
     461             :               candidateFrame.mStart, candidateFrame.Length());
     462             :     }
     463             :   }
     464             : 
     465           0 :   if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
     466           0 :     MP3LOG("FindFirst() accepting candidate frame: "
     467             :            "successiveFrames=%d", numSuccFrames);
     468           0 :     mFrameLock = true;
     469             :   } else {
     470           0 :     MP3LOG("FindFirst() no suitable first frame found");
     471             :   }
     472           0 :   return candidateFrame;
     473             : }
     474             : 
     475             : static bool
     476           0 : VerifyFrameConsistency(const FrameParser::Frame& aFrame1,
     477             :                        const FrameParser::Frame& aFrame2)
     478             : {
     479           0 :   const auto& h1 = aFrame1.Header();
     480           0 :   const auto& h2 = aFrame2.Header();
     481             : 
     482           0 :   return h1.IsValid()
     483           0 :          && h2.IsValid()
     484           0 :          && h1.Layer() == h2.Layer()
     485           0 :          && h1.SlotSize() == h2.SlotSize()
     486           0 :          && h1.SamplesPerFrame() == h2.SamplesPerFrame()
     487           0 :          && h1.Channels() == h2.Channels()
     488           0 :          && h1.SampleRate() == h2.SampleRate()
     489           0 :          && h1.RawVersion() == h2.RawVersion()
     490           0 :          && h1.RawProtection() == h2.RawProtection();
     491             : }
     492             : 
     493             : MediaByteRange
     494           0 : MP3TrackDemuxer::FindNextFrame()
     495             : {
     496             :   static const int BUFFER_SIZE = 64;
     497             :   static const uint32_t MAX_SKIPPABLE_BYTES = 1024 * BUFFER_SIZE;
     498             : 
     499           0 :   MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
     500             :           " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
     501             :           " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
     502             :           mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
     503             :           mSamplesPerFrame, mSamplesPerSecond, mChannels);
     504             : 
     505             :   uint8_t buffer[BUFFER_SIZE];
     506           0 :   int32_t read = 0;
     507             : 
     508           0 :   bool foundFrame = false;
     509           0 :   int64_t frameHeaderOffset = 0;
     510           0 :   int64_t startOffset = mOffset;
     511           0 :   const bool searchingForID3 = !mParser.ID3Header().Size();
     512             : 
     513             :   // Check whether we've found a valid MPEG frame.
     514           0 :   while (!foundFrame) {
     515             :     // How many bytes we can go without finding a valid MPEG frame
     516             :     // (effectively rounded up to the next full buffer size multiple, as we
     517             :     // only check this before reading the next set of data into the buffer).
     518             : 
     519             :     // This default value of 0 will be used during testing whether we're being
     520             :     // fed a valid stream, which shouldn't have any gaps between frames.
     521           0 :     uint32_t maxSkippableBytes = 0;
     522             : 
     523           0 :     if (!mParser.FirstFrame().Length()) {
     524             :       // We're looking for the first valid frame. A well-formed file should
     525             :       // have its first frame header right at the start (skipping an ID3 tag
     526             :       // if necessary), but in order to support files that might have been
     527             :       // improperly cut, we search the first few kB for a frame header.
     528           0 :       maxSkippableBytes = MAX_SKIPPABLE_BYTES;
     529             :       // Since we're counting the skipped bytes from the offset we started
     530             :       // this parsing session with, we need to discount the ID3 tag size only
     531             :       // if we were looking for one during the current frame parsing session.
     532           0 :       if (searchingForID3) {
     533           0 :         maxSkippableBytes += mParser.ID3Header().TotalTagSize();
     534             :       }
     535           0 :     } else if (mFrameLock) {
     536             :       // We've found a valid MPEG stream, so don't impose any limits
     537             :       // to allow skipping corrupted data until we hit EOS.
     538           0 :       maxSkippableBytes = std::numeric_limits<uint32_t>::max();
     539             :     }
     540             : 
     541           0 :     if ((mOffset - startOffset > maxSkippableBytes)
     542           0 :         || (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) {
     543           0 :       MP3LOG("FindNext() EOS or exceeded maxSkippeableBytes without a frame");
     544             :       // This is not a valid MPEG audio stream or we've reached EOS, give up.
     545           0 :       break;
     546             :     }
     547             : 
     548           0 :     ByteReader reader(buffer, read);
     549           0 :     uint32_t bytesToSkip = 0;
     550           0 :     foundFrame = mParser.Parse(&reader, &bytesToSkip);
     551           0 :     frameHeaderOffset =
     552           0 :       mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE;
     553             : 
     554             :     // If we've found neither an MPEG frame header nor an ID3v2 tag,
     555             :     // the reader shouldn't have any bytes remaining.
     556           0 :     MOZ_ASSERT(foundFrame || bytesToSkip || !reader.Remaining());
     557             : 
     558           0 :     if (foundFrame && mParser.FirstFrame().Length()
     559           0 :         && !VerifyFrameConsistency(mParser.FirstFrame(),
     560             :                                    mParser.CurrentFrame())) {
     561             :       // We've likely hit a false-positive, ignore it and proceed with the
     562             :       // search for the next valid frame.
     563           0 :       foundFrame = false;
     564           0 :       mOffset = frameHeaderOffset + 1;
     565           0 :       mParser.EndFrameSession();
     566             :     } else {
     567             :       // Advance mOffset by the amount of bytes read and if necessary,
     568             :       // skip an ID3v2 tag which stretches beyond the current buffer.
     569           0 :       NS_ENSURE_TRUE(mOffset + read + bytesToSkip > mOffset,
     570             :                      MediaByteRange(0, 0));
     571           0 :       mOffset += read + bytesToSkip;
     572             :     }
     573             :   }
     574             : 
     575           0 :   if (!foundFrame || !mParser.CurrentFrame().Length()) {
     576           0 :     MP3LOG("FindNext() Exit foundFrame=%d mParser.CurrentFrame().Length()=%d ",
     577             :            foundFrame, mParser.CurrentFrame().Length());
     578           0 :     return { 0, 0 };
     579             :   }
     580             : 
     581           0 :   MP3LOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
     582             :           " mFrameIndex=%" PRId64 " frameHeaderOffset=%" PRId64
     583             :           " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
     584             :           " mChannels=%d",
     585             :           mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
     586             :           mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
     587             : 
     588           0 :   return { frameHeaderOffset, frameHeaderOffset + mParser.CurrentFrame().Length() };
     589             : }
     590             : 
     591             : bool
     592           0 : MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange)
     593             : {
     594           0 :   if (!mNumParsedFrames || !aRange.Length()) {
     595             :     // We can't skip the first frame, since it could contain VBR headers.
     596           0 :     RefPtr<MediaRawData> frame(GetNextFrame(aRange));
     597           0 :     return frame;
     598             :   }
     599             : 
     600           0 :   UpdateState(aRange);
     601             : 
     602           0 :   MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
     603             :           " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
     604             :           " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
     605             :           mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
     606             :           mSamplesPerFrame, mSamplesPerSecond, mChannels);
     607             : 
     608           0 :   return true;
     609             : }
     610             : 
     611             : already_AddRefed<MediaRawData>
     612           0 : MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange)
     613             : {
     614           0 :   MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})",
     615             :          aRange.mStart, aRange.Length());
     616           0 :   if (!aRange.Length()) {
     617           0 :     return nullptr;
     618             :   }
     619             : 
     620           0 :   RefPtr<MediaRawData> frame = new MediaRawData();
     621           0 :   frame->mOffset = aRange.mStart;
     622             : 
     623           0 :   nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
     624           0 :   if (!frameWriter->SetSize(aRange.Length())) {
     625           0 :     MP3LOG("GetNext() Exit failed to allocated media buffer");
     626           0 :     return nullptr;
     627             :   }
     628             : 
     629             :   const uint32_t read =
     630           0 :     Read(frameWriter->Data(), frame->mOffset, frame->Size());
     631             : 
     632           0 :   if (read != aRange.Length()) {
     633           0 :     MP3LOG("GetNext() Exit read=%u frame->Size()=%" PRIuSIZE, read, frame->Size());
     634           0 :     return nullptr;
     635             :   }
     636             : 
     637           0 :   UpdateState(aRange);
     638             : 
     639           0 :   frame->mTime = Duration(mFrameIndex - 1);
     640           0 :   frame->mDuration = Duration(1);
     641           0 :   frame->mTimecode = frame->mTime;
     642           0 :   frame->mKeyframe = true;
     643             : 
     644           0 :   MOZ_ASSERT(!frame->mTime.IsNegative());
     645           0 :   MOZ_ASSERT(frame->mDuration.IsPositive());
     646             : 
     647           0 :   if (mNumParsedFrames == 1) {
     648             :     // First frame parsed, let's read VBR info if available.
     649           0 :     ByteReader reader(frame->Data(), frame->Size());
     650           0 :     mParser.ParseVBRHeader(&reader);
     651           0 :     mFirstFrameOffset = frame->mOffset;
     652             :   }
     653             : 
     654           0 :   MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
     655             :           " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
     656             :           " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
     657             :           mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
     658             :           mSamplesPerFrame, mSamplesPerSecond, mChannels);
     659             : 
     660           0 :   return frame.forget();
     661             : }
     662             : 
     663             : int64_t
     664           0 : MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const
     665             : {
     666           0 :   int64_t offset = 0;
     667           0 :   const auto& vbr = mParser.VBRInfo();
     668             : 
     669           0 :   if (vbr.IsComplete()) {
     670           0 :     offset = mFirstFrameOffset
     671           0 :              + aFrameIndex * vbr.NumBytes().value()
     672           0 :                / vbr.NumAudioFrames().value();
     673           0 :   } else if (AverageFrameLength() > 0) {
     674           0 :     offset = mFirstFrameOffset + aFrameIndex * AverageFrameLength();
     675             :   }
     676             : 
     677           0 :   MP3LOGV("OffsetFromFrameIndex(%" PRId64 ") -> %" PRId64, aFrameIndex, offset);
     678           0 :   return std::max<int64_t>(mFirstFrameOffset, offset);
     679             : }
     680             : 
     681             : int64_t
     682           0 : MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const
     683             : {
     684           0 :   int64_t frameIndex = 0;
     685           0 :   const auto& vbr = mParser.VBRInfo();
     686             : 
     687           0 :   if (vbr.IsComplete()) {
     688           0 :     frameIndex = static_cast<float>(aOffset - mFirstFrameOffset)
     689           0 :                  / vbr.NumBytes().value()
     690           0 :                  * vbr.NumAudioFrames().value();
     691           0 :     frameIndex = std::min<int64_t>(vbr.NumAudioFrames().value(), frameIndex);
     692           0 :   } else if (AverageFrameLength() > 0) {
     693           0 :     frameIndex = (aOffset - mFirstFrameOffset) / AverageFrameLength();
     694             :   }
     695             : 
     696           0 :   MP3LOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex);
     697           0 :   return std::max<int64_t>(0, frameIndex);
     698             : }
     699             : 
     700             : int64_t
     701           0 : MP3TrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const
     702             : {
     703           0 :   int64_t frameIndex = 0;
     704           0 :   if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
     705           0 :     frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1;
     706             :   }
     707             : 
     708           0 :   MP3LOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(),
     709             :           frameIndex);
     710           0 :   return std::max<int64_t>(0, frameIndex);
     711             : }
     712             : 
     713             : void
     714           0 : MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange)
     715             : {
     716             :   // Prevent overflow.
     717           0 :   if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) {
     718             :     // These variables have a linear dependency and are only used to derive the
     719             :     // average frame length.
     720           0 :     mTotalFrameLen /= 2;
     721           0 :     mNumParsedFrames /= 2;
     722             :   }
     723             : 
     724             :   // Full frame parsed, move offset to its end.
     725           0 :   mOffset = aRange.mEnd;
     726             : 
     727           0 :   mTotalFrameLen += aRange.Length();
     728             : 
     729           0 :   if (!mSamplesPerFrame) {
     730           0 :     mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame();
     731           0 :     mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate();
     732           0 :     mChannels = mParser.CurrentFrame().Header().Channels();
     733             :   }
     734             : 
     735           0 :   ++mNumParsedFrames;
     736           0 :   ++mFrameIndex;
     737           0 :   MOZ_ASSERT(mFrameIndex > 0);
     738             : 
     739             :   // Prepare the parser for the next frame parsing session.
     740           0 :   mParser.EndFrameSession();
     741           0 : }
     742             : 
     743             : int32_t
     744           0 : MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
     745             : {
     746           0 :   MP3LOGV("MP3TrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize);
     747             : 
     748           0 :   const int64_t streamLen = StreamLength();
     749           0 :   if (mInfo && streamLen > 0) {
     750             :     // Prevent blocking reads after successful initialization.
     751           0 :     aSize = std::min<int64_t>(aSize, streamLen - aOffset);
     752             :   }
     753             : 
     754           0 :   uint32_t read = 0;
     755           0 :   MP3LOGV("MP3TrackDemuxer::Read        -> ReadAt(%d)", aSize);
     756           0 :   const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
     757           0 :                                      static_cast<uint32_t>(aSize), &read);
     758           0 :   NS_ENSURE_SUCCESS(rv, 0);
     759           0 :   return static_cast<int32_t>(read);
     760             : }
     761             : 
     762             : double
     763           0 : MP3TrackDemuxer::AverageFrameLength() const
     764             : {
     765           0 :   if (mNumParsedFrames) {
     766           0 :     return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
     767             :   }
     768           0 :   const auto& vbr = mParser.VBRInfo();
     769           0 :   if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
     770           0 :     return static_cast<double>(vbr.NumBytes().value())
     771           0 :            / (vbr.NumAudioFrames().value() + 1);
     772             :   }
     773           0 :   return 0.0;
     774             : }
     775             : 
     776             : } // namespace mozilla

Generated by: LCOV version 1.13