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

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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 "ContainerParser.h"
       8             : 
       9             : #include "WebMBufferedParser.h"
      10             : #include "mozilla/EndianUtils.h"
      11             : #include "mozilla/IntegerPrintfMacros.h"
      12             : #include "mozilla/ErrorResult.h"
      13             : #include "mozilla/SizePrintfMacros.h"
      14             : #include "mp4_demuxer/MoofParser.h"
      15             : #include "mozilla/Logging.h"
      16             : #include "mozilla/Maybe.h"
      17             : #include "MediaData.h"
      18             : #ifdef MOZ_FMP4
      19             : #include "MP4Stream.h"
      20             : #include "mp4_demuxer/AtomType.h"
      21             : #include "mp4_demuxer/ByteReader.h"
      22             : #endif
      23             : #include "nsAutoPtr.h"
      24             : #include "SourceBufferResource.h"
      25             : #include <algorithm>
      26             : 
      27             : extern mozilla::LogModule* GetMediaSourceSamplesLog();
      28             : 
      29             : #define STRINGIFY(x) #x
      30             : #define TOSTRING(x) STRINGIFY(x)
      31             : #define MSE_DEBUG(name, arg, ...) MOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
      32             : #define MSE_DEBUGV(name, arg, ...) MOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Verbose, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
      33             : 
      34             : namespace mozilla {
      35             : 
      36           0 : ContainerParser::ContainerParser(const MediaContainerType& aType)
      37             :   : mHasInitData(false)
      38             :   , mTotalParsed(0)
      39             :   , mGlobalOffset(0)
      40           0 :   , mType(aType)
      41             : {
      42           0 : }
      43             : 
      44             : ContainerParser::~ContainerParser() = default;
      45             : 
      46             : MediaResult
      47           0 : ContainerParser::IsInitSegmentPresent(MediaByteBuffer* aData)
      48             : {
      49           0 :   MSE_DEBUG(ContainerParser, "aLength=%" PRIuSIZE " [%x%x%x%x]",
      50             :             aData->Length(),
      51             :             aData->Length() > 0 ? (*aData)[0] : 0,
      52             :             aData->Length() > 1 ? (*aData)[1] : 0,
      53             :             aData->Length() > 2 ? (*aData)[2] : 0,
      54             :             aData->Length() > 3 ? (*aData)[3] : 0);
      55           0 :   return NS_ERROR_NOT_AVAILABLE;
      56             : }
      57             : 
      58             : MediaResult
      59           0 : ContainerParser::IsMediaSegmentPresent(MediaByteBuffer* aData)
      60             : {
      61           0 :   MSE_DEBUG(ContainerParser, "aLength=%" PRIuSIZE " [%x%x%x%x]",
      62             :             aData->Length(),
      63             :             aData->Length() > 0 ? (*aData)[0] : 0,
      64             :             aData->Length() > 1 ? (*aData)[1] : 0,
      65             :             aData->Length() > 2 ? (*aData)[2] : 0,
      66             :             aData->Length() > 3 ? (*aData)[3] : 0);
      67           0 :   return NS_ERROR_NOT_AVAILABLE;
      68             : }
      69             : 
      70             : MediaResult
      71           0 : ContainerParser::ParseStartAndEndTimestamps(MediaByteBuffer* aData,
      72             :                                             int64_t& aStart, int64_t& aEnd)
      73             : {
      74           0 :   return NS_ERROR_NOT_AVAILABLE;
      75             : }
      76             : 
      77             : bool
      78           0 : ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
      79             : {
      80           0 :   return llabs(aLhs - aRhs) <= GetRoundingError();
      81             : }
      82             : 
      83             : int64_t
      84           0 : ContainerParser::GetRoundingError()
      85             : {
      86           0 :   NS_WARNING("Using default ContainerParser::GetRoundingError implementation");
      87           0 :   return 0;
      88             : }
      89             : 
      90             : bool
      91           0 : ContainerParser::HasCompleteInitData()
      92             : {
      93           0 :   return mHasInitData && !!mInitData->Length();
      94             : }
      95             : 
      96             : MediaByteBuffer*
      97           0 : ContainerParser::InitData()
      98             : {
      99           0 :   return mInitData;
     100             : }
     101             : 
     102             : MediaByteRange
     103           0 : ContainerParser::InitSegmentRange()
     104             : {
     105           0 :   return mCompleteInitSegmentRange;
     106             : }
     107             : 
     108             : MediaByteRange
     109           0 : ContainerParser::MediaHeaderRange()
     110             : {
     111           0 :   return mCompleteMediaHeaderRange;
     112             : }
     113             : 
     114             : MediaByteRange
     115           0 : ContainerParser::MediaSegmentRange()
     116             : {
     117           0 :   return mCompleteMediaSegmentRange;
     118             : }
     119             : 
     120           0 : class WebMContainerParser : public ContainerParser
     121             : {
     122             : public:
     123           0 :   explicit WebMContainerParser(const MediaContainerType& aType)
     124           0 :     : ContainerParser(aType)
     125             :     , mParser(0)
     126           0 :     , mOffset(0)
     127             :   {
     128           0 :   }
     129             : 
     130             :   static const unsigned NS_PER_USEC = 1000;
     131             :   static const unsigned USEC_PER_SEC = 1000000;
     132             : 
     133           0 :   MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override
     134             :   {
     135           0 :     ContainerParser::IsInitSegmentPresent(aData);
     136             :     // XXX: This is overly primitive, needs to collect data as it's appended
     137             :     // to the SB and handle, rather than assuming everything is present in a
     138             :     // single aData segment.
     139             :     // 0x1a45dfa3 // EBML
     140             :     // ...
     141             :     // DocType == "webm"
     142             :     // ...
     143             :     // 0x18538067 // Segment (must be "unknown" size or contain a value large
     144             :                   // enough to include the Segment Information and Tracks
     145             :                   // elements that follow)
     146             :     // 0x1549a966 // -> Segment Info
     147             :     // 0x1654ae6b // -> One or more Tracks
     148             : 
     149             :     // 0x1a45dfa3 // EBML
     150           0 :     if (aData->Length() < 4) {
     151           0 :       return NS_ERROR_NOT_AVAILABLE;
     152             :     }
     153           0 :     if ((*aData)[0] == 0x1a && (*aData)[1] == 0x45 && (*aData)[2] == 0xdf &&
     154           0 :         (*aData)[3] == 0xa3) {
     155           0 :       return NS_OK;
     156             :     }
     157           0 :     return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content"));
     158             :   }
     159             : 
     160           0 :   MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override
     161             :   {
     162           0 :     ContainerParser::IsMediaSegmentPresent(aData);
     163             :     // XXX: This is overly primitive, needs to collect data as it's appended
     164             :     // to the SB and handle, rather than assuming everything is present in a
     165             :     // single aData segment.
     166             :     // 0x1a45dfa3 // EBML
     167             :     // ...
     168             :     // DocType == "webm"
     169             :     // ...
     170             :     // 0x18538067 // Segment (must be "unknown" size)
     171             :     // 0x1549a966 // -> Segment Info
     172             :     // 0x1654ae6b // -> One or more Tracks
     173             : 
     174             :     // 0x1f43b675 // Cluster
     175           0 :     if (aData->Length() < 4) {
     176           0 :       return NS_ERROR_NOT_AVAILABLE;
     177             :     }
     178           0 :     if ((*aData)[0] == 0x1f && (*aData)[1] == 0x43 && (*aData)[2] == 0xb6 &&
     179           0 :         (*aData)[3] == 0x75) {
     180           0 :       return NS_OK;
     181             :     }
     182             :     // 0x1c53bb6b // Cues
     183           0 :     if ((*aData)[0] == 0x1c && (*aData)[1] == 0x53 && (*aData)[2] == 0xbb &&
     184           0 :         (*aData)[3] == 0x6b) {
     185           0 :       return NS_OK;
     186             :     }
     187           0 :     return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content"));
     188             :   }
     189             : 
     190           0 :   MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
     191             :                                          int64_t& aStart,
     192             :                                          int64_t& aEnd) override
     193             :   {
     194           0 :     bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
     195             : 
     196           0 :     if (mLastMapping &&
     197           0 :         (initSegment || NS_SUCCEEDED(IsMediaSegmentPresent(aData)))) {
     198             :       // The last data contained a complete cluster but we can only detect it
     199             :       // now that a new one is starting.
     200             :       // We use mOffset as end position to ensure that any blocks not reported
     201             :       // by WebMBufferParser are properly skipped.
     202             :       mCompleteMediaSegmentRange =
     203           0 :         MediaByteRange(mLastMapping.ref().mSyncOffset, mOffset) + mGlobalOffset;
     204           0 :       mLastMapping.reset();
     205           0 :       MSE_DEBUG(WebMContainerParser,
     206             :                 "New cluster found at start, ending previous one");
     207           0 :       return NS_ERROR_NOT_AVAILABLE;
     208             :     }
     209             : 
     210           0 :     if (initSegment) {
     211           0 :       mOffset = 0;
     212           0 :       mParser = WebMBufferedParser(0);
     213           0 :       mOverlappedMapping.Clear();
     214           0 :       mInitData = new MediaByteBuffer();
     215           0 :       mResource = new SourceBufferResource();
     216           0 :       mCompleteInitSegmentRange = MediaByteRange();
     217           0 :       mCompleteMediaHeaderRange = MediaByteRange();
     218           0 :       mCompleteMediaSegmentRange = MediaByteRange();
     219           0 :       mGlobalOffset = mTotalParsed;
     220             :     }
     221             : 
     222             :     // XXX if it only adds new mappings, overlapped but not available
     223             :     // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
     224           0 :     nsTArray<WebMTimeDataOffset> mapping;
     225           0 :     mapping.AppendElements(mOverlappedMapping);
     226           0 :     mOverlappedMapping.Clear();
     227           0 :     ReentrantMonitor dummy("dummy");
     228           0 :     mParser.Append(aData->Elements(), aData->Length(), mapping, dummy);
     229           0 :     if (mResource) {
     230           0 :       mResource->AppendData(aData);
     231             :     }
     232             : 
     233             :     // XXX This is a bit of a hack.  Assume if there are no timecodes
     234             :     // present and it's an init segment that it's _just_ an init segment.
     235             :     // We should be more precise.
     236           0 :     if (initSegment || !HasCompleteInitData()) {
     237           0 :       if (mParser.mInitEndOffset > 0) {
     238           0 :         MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength());
     239           0 :         if (!mInitData->SetLength(mParser.mInitEndOffset, fallible)) {
     240             :           // Super unlikely OOM
     241           0 :           return NS_ERROR_OUT_OF_MEMORY;
     242             :         }
     243             :         mCompleteInitSegmentRange =
     244           0 :           MediaByteRange(0, mParser.mInitEndOffset) + mGlobalOffset;
     245           0 :         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
     246           0 :         mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset);
     247           0 :         MSE_DEBUG(WebMContainerParser, "Stashed init of %" PRId64 " bytes.",
     248             :                   mParser.mInitEndOffset);
     249           0 :         mResource = nullptr;
     250             :       } else {
     251           0 :         MSE_DEBUG(WebMContainerParser, "Incomplete init found.");
     252             :       }
     253           0 :       mHasInitData = true;
     254             :     }
     255           0 :     mOffset += aData->Length();
     256           0 :     mTotalParsed += aData->Length();
     257             : 
     258           0 :     if (mapping.IsEmpty()) {
     259           0 :       return NS_ERROR_NOT_AVAILABLE;
     260             :     }
     261             : 
     262             :     // Calculate media range for first media segment.
     263             : 
     264             :     // Check if we have a cluster finishing in the current data.
     265           0 :     uint32_t endIdx = mapping.Length() - 1;
     266           0 :     bool foundNewCluster = false;
     267           0 :     while (mapping[0].mSyncOffset != mapping[endIdx].mSyncOffset) {
     268           0 :       endIdx -= 1;
     269           0 :       foundNewCluster = true;
     270             :     }
     271             : 
     272           0 :     int32_t completeIdx = endIdx;
     273           0 :     while (completeIdx >= 0 && mOffset < mapping[completeIdx].mEndOffset) {
     274           0 :       MSE_DEBUG(WebMContainerParser, "block is incomplete, missing: %" PRId64,
     275             :                 mapping[completeIdx].mEndOffset - mOffset);
     276           0 :       completeIdx -= 1;
     277             :     }
     278             : 
     279             :     // Save parsed blocks for which we do not have all data yet.
     280           0 :     mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1,
     281           0 :                                       mapping.Length() - completeIdx - 1);
     282             : 
     283           0 :     if (completeIdx < 0) {
     284           0 :       mLastMapping.reset();
     285           0 :       return NS_ERROR_NOT_AVAILABLE;
     286             :     }
     287             : 
     288           0 :     if (mCompleteMediaHeaderRange.IsEmpty()) {
     289             :       mCompleteMediaHeaderRange =
     290           0 :         MediaByteRange(mapping[0].mSyncOffset, mapping[0].mEndOffset) +
     291           0 :         mGlobalOffset;
     292             :     }
     293             : 
     294           0 :     if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
     295             :       // We now have all information required to delimit a complete cluster.
     296           0 :       int64_t endOffset = mapping[endIdx+1].mSyncOffset;
     297           0 :       if (mapping[endIdx+1].mInitOffset > mapping[endIdx].mInitOffset) {
     298             :         // We have a new init segment before this cluster.
     299           0 :         endOffset = mapping[endIdx+1].mInitOffset;
     300             :       }
     301             :       mCompleteMediaSegmentRange =
     302           0 :         MediaByteRange(mapping[endIdx].mSyncOffset, endOffset) + mGlobalOffset;
     303           0 :     } else if (mapping[endIdx].mClusterEndOffset >= 0 &&
     304           0 :                mOffset >= mapping[endIdx].mClusterEndOffset) {
     305             :       mCompleteMediaSegmentRange =
     306           0 :         MediaByteRange(
     307           0 :           mapping[endIdx].mSyncOffset,
     308           0 :           mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset))
     309           0 :         + mGlobalOffset;
     310             :     }
     311             : 
     312           0 :     Maybe<WebMTimeDataOffset> previousMapping;
     313           0 :     if (completeIdx) {
     314           0 :       previousMapping = Some(mapping[completeIdx - 1]);
     315             :     } else {
     316           0 :       previousMapping = mLastMapping;
     317             :     }
     318             : 
     319           0 :     mLastMapping = Some(mapping[completeIdx]);
     320             : 
     321           0 :     if (!previousMapping && completeIdx + 1u >= mapping.Length()) {
     322             :       // We have no previous nor next block available,
     323             :       // so we can't estimate this block's duration.
     324           0 :       return NS_ERROR_NOT_AVAILABLE;
     325             :     }
     326             : 
     327             :     uint64_t frameDuration =
     328           0 :       (completeIdx + 1u < mapping.Length())
     329           0 :       ? mapping[completeIdx + 1].mTimecode - mapping[completeIdx].mTimecode
     330           0 :       : mapping[completeIdx].mTimecode - previousMapping.ref().mTimecode;
     331           0 :     aStart = mapping[0].mTimecode / NS_PER_USEC;
     332           0 :     aEnd = (mapping[completeIdx].mTimecode + frameDuration) / NS_PER_USEC;
     333             : 
     334           0 :     MSE_DEBUG(WebMContainerParser,
     335             :               "[%" PRId64 ", %" PRId64 "] [fso=%" PRId64 ", leo=%" PRId64
     336             :               ", l=%" PRIuSIZE " processedIdx=%u fs=%" PRId64 "]",
     337             :               aStart,
     338             :               aEnd,
     339             :               mapping[0].mSyncOffset,
     340             :               mapping[completeIdx].mEndOffset,
     341             :               mapping.Length(),
     342             :               completeIdx,
     343             :               mCompleteMediaSegmentRange.mEnd);
     344             : 
     345           0 :     return NS_OK;
     346             :   }
     347             : 
     348           0 :   int64_t GetRoundingError() override
     349             :   {
     350           0 :     int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
     351           0 :     return error * 2;
     352             :   }
     353             : 
     354             : private:
     355             :   WebMBufferedParser mParser;
     356             :   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
     357             :   int64_t mOffset;
     358             :   Maybe<WebMTimeDataOffset> mLastMapping;
     359             : };
     360             : 
     361             : #ifdef MOZ_FMP4
     362           0 : class MP4ContainerParser : public ContainerParser
     363             : {
     364             : public:
     365           0 :   explicit MP4ContainerParser(const MediaContainerType& aType)
     366           0 :     : ContainerParser(aType)
     367             :   {
     368           0 :   }
     369             : 
     370           0 :   MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override
     371             :   {
     372           0 :     ContainerParser::IsInitSegmentPresent(aData);
     373             :     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     374             :     // file is the 'ftyp' atom followed by a file type. We just check for a
     375             :     // vaguely valid 'ftyp' atom.
     376           0 :     if (aData->Length() < 8) {
     377           0 :       return NS_ERROR_NOT_AVAILABLE;
     378             :     }
     379           0 :     AtomParser parser(mType, aData);
     380           0 :     if (!parser.IsValid()) {
     381             :       return MediaResult(
     382             :         NS_ERROR_FAILURE,
     383           0 :         RESULT_DETAIL("Invalid Top-Level Box:%s", parser.LastInvalidBox()));
     384             :     }
     385           0 :     return parser.StartWithInitSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
     386             :   }
     387             : 
     388           0 :   MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override
     389             :   {
     390           0 :     if (aData->Length() < 8) {
     391           0 :       return NS_ERROR_NOT_AVAILABLE;
     392             :     }
     393           0 :     AtomParser parser(mType, aData);
     394           0 :     if (!parser.IsValid()) {
     395             :       return MediaResult(
     396             :         NS_ERROR_FAILURE,
     397           0 :         RESULT_DETAIL("Invalid Box:%s", parser.LastInvalidBox()));
     398             :     }
     399           0 :     return parser.StartWithMediaSegment() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
     400             :   }
     401             : 
     402             : private:
     403           0 :   class AtomParser {
     404             :   public:
     405           0 :     AtomParser(const MediaContainerType& aType, const MediaByteBuffer* aData)
     406           0 :     {
     407           0 :       const MediaContainerType mType(aType); // for logging macro.
     408           0 :       mp4_demuxer::ByteReader reader(aData);
     409           0 :       mp4_demuxer::AtomType initAtom("moov");
     410           0 :       mp4_demuxer::AtomType mediaAtom("moof");
     411             : 
     412             :       // Valid top-level boxes defined in ISO/IEC 14496-12 (Table 1)
     413             :       static const mp4_demuxer::AtomType validBoxes[] = {
     414             :         "ftyp", "moov", // init segment
     415             :         "pdin", "free", "sidx", // optional prior moov box
     416             :         "styp", "moof", "mdat", // media segment
     417             :         "mfra", "skip", "meta", "meco", "ssix", "prft" // others.
     418             :         "pssh", // optional with encrypted EME, though ignored.
     419             :         "emsg", // ISO23009-1:2014 Section 5.10.3.3
     420             :         "bloc", "uuid" // boxes accepted by chrome.
     421           0 :       };
     422             : 
     423           0 :       while (reader.Remaining() >= 8) {
     424           0 :         uint64_t size = reader.ReadU32();
     425           0 :         const uint8_t* typec = reader.Peek(4);
     426           0 :         mp4_demuxer::AtomType type(reader.ReadU32());
     427           0 :         MSE_DEBUGV(AtomParser ,"Checking atom:'%c%c%c%c' @ %u",
     428             :                    typec[0], typec[1], typec[2], typec[3],
     429             :                    (uint32_t)reader.Offset() - 8);
     430           0 :         if (std::find(std::begin(validBoxes), std::end(validBoxes), type)
     431           0 :             == std::end(validBoxes)) {
     432             :           // No valid box found, no point continuing.
     433           0 :           mLastInvalidBox[0] = typec[0];
     434           0 :           mLastInvalidBox[1] = typec[1];
     435           0 :           mLastInvalidBox[2] = typec[2];
     436           0 :           mLastInvalidBox[3] = typec[3];
     437           0 :           mLastInvalidBox[4] = '\0';
     438           0 :           mValid = false;
     439           0 :           break;
     440             :         }
     441           0 :         if (mInitOffset.isNothing() &&
     442           0 :             mp4_demuxer::AtomType(type) == initAtom) {
     443           0 :           mInitOffset = Some(reader.Offset());
     444             :         }
     445           0 :         if (mMediaOffset.isNothing() &&
     446           0 :             mp4_demuxer::AtomType(type) == mediaAtom) {
     447           0 :           mMediaOffset = Some(reader.Offset());
     448             :         }
     449           0 :         if (mInitOffset.isSome() && mMediaOffset.isSome()) {
     450             :           // We have everything we need.
     451           0 :           break;
     452             :         }
     453           0 :         if (size == 1) {
     454             :           // 64 bits size.
     455           0 :           if (!reader.CanReadType<uint64_t>()) {
     456           0 :             break;
     457             :           }
     458           0 :           size = reader.ReadU64();
     459           0 :         } else if (size == 0) {
     460             :           // Atom extends to the end of the buffer, it can't have what we're
     461             :           // looking for.
     462           0 :           break;
     463             :         }
     464           0 :         if (reader.Remaining() < size - 8) {
     465             :           // Incomplete atom.
     466           0 :           break;
     467             :         }
     468           0 :         reader.Read(size - 8);
     469             :       }
     470           0 :     }
     471             : 
     472           0 :     bool StartWithInitSegment() const
     473             :     {
     474           0 :       return mInitOffset.isSome() &&
     475           0 :         (mMediaOffset.isNothing() || mInitOffset.ref() < mMediaOffset.ref());
     476             :     }
     477           0 :     bool StartWithMediaSegment() const
     478             :     {
     479           0 :       return mMediaOffset.isSome() &&
     480           0 :         (mInitOffset.isNothing() || mMediaOffset.ref() < mInitOffset.ref());
     481             :     }
     482           0 :     bool IsValid() const { return mValid; }
     483           0 :     const char* LastInvalidBox() const { return mLastInvalidBox; }
     484             :   private:
     485             :     Maybe<size_t> mInitOffset;
     486             :     Maybe<size_t> mMediaOffset;
     487             :     bool mValid = true;
     488             :     char mLastInvalidBox[5];
     489             :   };
     490             : 
     491             : public:
     492           0 :   MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
     493             :                                          int64_t& aStart,
     494             :                                          int64_t& aEnd) override
     495             :   {
     496           0 :     bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
     497           0 :     if (initSegment) {
     498           0 :       mResource = new SourceBufferResource();
     499           0 :       mStream = new MP4Stream(mResource);
     500             :       // We use a timestampOffset of 0 for ContainerParser, and require
     501             :       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
     502             :       // manually. This allows the ContainerParser to be shared across different
     503             :       // timestampOffsets.
     504           0 :       mParser = new mp4_demuxer::MoofParser(mStream, 0, /* aIsAudio = */ false);
     505           0 :       mInitData = new MediaByteBuffer();
     506           0 :       mCompleteInitSegmentRange = MediaByteRange();
     507           0 :       mCompleteMediaHeaderRange = MediaByteRange();
     508           0 :       mCompleteMediaSegmentRange = MediaByteRange();
     509           0 :       mGlobalOffset = mTotalParsed;
     510           0 :     } else if (!mStream || !mParser) {
     511           0 :       mTotalParsed += aData->Length();
     512           0 :       return NS_ERROR_NOT_AVAILABLE;
     513             :     }
     514             : 
     515           0 :     mResource->AppendData(aData);
     516           0 :     MediaByteRangeSet byteRanges;
     517             :     byteRanges +=
     518           0 :       MediaByteRange(int64_t(mParser->mOffset), mResource->GetLength());
     519           0 :     mParser->RebuildFragmentedIndex(byteRanges);
     520             : 
     521           0 :     if (initSegment || !HasCompleteInitData()) {
     522           0 :       MediaByteRange& range = mParser->mInitRange;
     523           0 :       if (range.Length()) {
     524           0 :         mCompleteInitSegmentRange = range + mGlobalOffset;
     525           0 :         if (!mInitData->SetLength(range.Length(), fallible)) {
     526             :           // Super unlikely OOM
     527           0 :           return NS_ERROR_OUT_OF_MEMORY;
     528             :         }
     529           0 :         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
     530           0 :         mResource->ReadFromCache(buffer, range.mStart, range.Length());
     531           0 :         MSE_DEBUG(MP4ContainerParser ,"Stashed init of %" PRIu64 " bytes.",
     532             :                   range.Length());
     533             :       } else {
     534           0 :         MSE_DEBUG(MP4ContainerParser, "Incomplete init found.");
     535             :       }
     536           0 :       mHasInitData = true;
     537             :     }
     538           0 :     mTotalParsed += aData->Length();
     539             : 
     540             :     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
     541           0 :       mParser->GetCompositionRange(byteRanges);
     542             : 
     543             :     mCompleteMediaHeaderRange =
     544           0 :       mParser->FirstCompleteMediaHeader() + mGlobalOffset;
     545             :     mCompleteMediaSegmentRange =
     546           0 :       mParser->FirstCompleteMediaSegment() + mGlobalOffset;
     547             : 
     548           0 :     ErrorResult rv;
     549           0 :     if (HasCompleteInitData()) {
     550           0 :       mResource->EvictData(mParser->mOffset, mParser->mOffset, rv);
     551             :     }
     552           0 :     if (NS_WARN_IF(rv.Failed())) {
     553           0 :       rv.SuppressException();
     554           0 :       return NS_ERROR_OUT_OF_MEMORY;
     555             :     }
     556             : 
     557           0 :     if (compositionRange.IsNull()) {
     558           0 :       return NS_ERROR_NOT_AVAILABLE;
     559             :     }
     560           0 :     aStart = compositionRange.start;
     561           0 :     aEnd = compositionRange.end;
     562           0 :     MSE_DEBUG(MP4ContainerParser, "[%" PRId64 ", %" PRId64 "]",
     563             :               aStart, aEnd);
     564           0 :     return NS_OK;
     565             :   }
     566             : 
     567             :   // Gaps of up to 35ms (marginally longer than a single frame at 30fps) are
     568             :   // considered to be sequential frames.
     569           0 :   int64_t GetRoundingError() override
     570             :   {
     571           0 :     return 35000;
     572             :   }
     573             : 
     574             : private:
     575             :   RefPtr<MP4Stream> mStream;
     576             :   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
     577             : };
     578             : #endif // MOZ_FMP4
     579             : 
     580             : #ifdef MOZ_FMP4
     581           0 : class ADTSContainerParser : public ContainerParser
     582             : {
     583             : public:
     584           0 :   explicit ADTSContainerParser(const MediaContainerType& aType)
     585           0 :     : ContainerParser(aType)
     586             :   {
     587           0 :   }
     588             : 
     589             :   typedef struct
     590             :   {
     591             :     size_t header_length; // Length of just the initialization data.
     592             :     size_t frame_length;  // Includes header_length;
     593             :     uint8_t aac_frames;   // Number of AAC frames in the ADTS frame.
     594             :     bool have_crc;
     595             :   } Header;
     596             : 
     597             :   /// Helper to parse the ADTS header, returning data we care about.
     598             :   /// Returns true if the header is parsed successfully.
     599             :   /// Returns false if the header is invalid or incomplete,
     600             :   /// without modifying the passed-in Header object.
     601           0 :   bool Parse(MediaByteBuffer* aData, Header& header)
     602             :   {
     603           0 :     MOZ_ASSERT(aData);
     604             : 
     605             :     // ADTS initialization segments are just the packet header.
     606           0 :     if (aData->Length() < 7) {
     607           0 :       MSE_DEBUG(ADTSContainerParser, "buffer too short for header.");
     608           0 :       return false;
     609             :     }
     610             :     // Check 0xfffx sync word plus layer 0.
     611           0 :     if (((*aData)[0] != 0xff) || (((*aData)[1] & 0xf6) != 0xf0)) {
     612           0 :       MSE_DEBUG(ADTSContainerParser, "no syncword.");
     613           0 :       return false;
     614             :     }
     615           0 :     bool have_crc = !((*aData)[1] & 0x01);
     616           0 :     if (have_crc && aData->Length() < 9) {
     617           0 :       MSE_DEBUG(ADTSContainerParser, "buffer too short for header with crc.");
     618           0 :       return false;
     619             :     }
     620           0 :     uint8_t frequency_index = ((*aData)[2] & 0x3c) >> 2;
     621           0 :     MOZ_ASSERT(frequency_index < 16);
     622           0 :     if (frequency_index == 15) {
     623           0 :       MSE_DEBUG(ADTSContainerParser, "explicit frequency disallowed.");
     624           0 :       return false;
     625             :     }
     626           0 :     size_t header_length = have_crc ? 9 : 7;
     627           0 :     size_t data_length = (((*aData)[3] & 0x03) << 11) |
     628           0 :                          (((*aData)[4] & 0xff) << 3) |
     629           0 :                          (((*aData)[5] & 0xe0) >> 5);
     630           0 :     uint8_t frames = ((*aData)[6] & 0x03) + 1;
     631           0 :     MOZ_ASSERT(frames > 0);
     632           0 :     MOZ_ASSERT(frames < 4);
     633             : 
     634             :     // Return successfully parsed data.
     635           0 :     header.header_length = header_length;
     636           0 :     header.frame_length = header_length + data_length;
     637           0 :     header.aac_frames = frames;
     638           0 :     header.have_crc = have_crc;
     639           0 :     return true;
     640             :   }
     641             : 
     642           0 :   MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override
     643             :   {
     644             :     // Call superclass for logging.
     645           0 :     ContainerParser::IsInitSegmentPresent(aData);
     646             : 
     647             :     Header header;
     648           0 :     if (!Parse(aData, header)) {
     649           0 :       return NS_ERROR_NOT_AVAILABLE;
     650             :     }
     651             : 
     652           0 :     MSE_DEBUGV(ADTSContainerParser, "%llu byte frame %d aac frames%s",
     653             :         (unsigned long long)header.frame_length, (int)header.aac_frames,
     654             :         header.have_crc ? " crc" : "");
     655             : 
     656           0 :     return NS_OK;
     657             :   }
     658             : 
     659           0 :   MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override
     660             :   {
     661             :     // Call superclass for logging.
     662           0 :     ContainerParser::IsMediaSegmentPresent(aData);
     663             : 
     664             :     // Make sure we have a header so we know how long the frame is.
     665             :     // NB this assumes the media segment buffer starts with an
     666             :     // initialization segment. Since every frame has an ADTS header
     667             :     // this is a normal place to divide packets, but we can re-parse
     668             :     // mInitData if we need to handle separate media segments.
     669             :     Header header;
     670           0 :     if (!Parse(aData, header)) {
     671           0 :       return NS_ERROR_NOT_AVAILABLE;
     672             :     }
     673             :     // We're supposed to return true as long as aData contains the
     674             :     // start of a media segment, whether or not it's complete. So
     675             :     // return true if we have any data beyond the header.
     676           0 :     if (aData->Length() <= header.header_length) {
     677           0 :       return NS_ERROR_NOT_AVAILABLE;
     678             :     }
     679             : 
     680             :     // We should have at least a partial frame.
     681           0 :     return NS_OK;
     682             :   }
     683             : 
     684           0 :   MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
     685             :                                          int64_t& aStart,
     686             :                                          int64_t& aEnd) override
     687             :   {
     688             :     // ADTS header.
     689             :     Header header;
     690           0 :     if (!Parse(aData, header)) {
     691           0 :       return NS_ERROR_NOT_AVAILABLE;
     692             :     }
     693           0 :     mHasInitData = true;
     694             :     mCompleteInitSegmentRange =
     695           0 :       MediaByteRange(0, int64_t(header.header_length));
     696             : 
     697             :     // Cache raw header in case the caller wants a copy.
     698           0 :     mInitData = new MediaByteBuffer(header.header_length);
     699           0 :     mInitData->AppendElements(aData->Elements(), header.header_length);
     700             : 
     701             :     // Check that we have enough data for the frame body.
     702           0 :     if (aData->Length() < header.frame_length) {
     703           0 :       MSE_DEBUGV(ADTSContainerParser, "Not enough data for %llu byte frame"
     704             :           " in %llu byte buffer.",
     705             :           (unsigned long long)header.frame_length,
     706             :           (unsigned long long)(aData->Length()));
     707           0 :       return NS_ERROR_NOT_AVAILABLE;
     708             :     }
     709           0 :     mCompleteMediaSegmentRange = MediaByteRange(header.header_length,
     710           0 :                                                 header.frame_length);
     711             :     // The ADTS MediaSource Byte Stream Format document doesn't
     712             :     // define media header. Just treat it the same as the whole
     713             :     // media segment.
     714           0 :     mCompleteMediaHeaderRange = mCompleteMediaSegmentRange;
     715             : 
     716           0 :     MSE_DEBUG(ADTSContainerParser, "[%" PRId64 ", %" PRId64 "]",
     717             :               aStart, aEnd);
     718             :     // We don't update timestamps, regardless.
     719           0 :     return NS_ERROR_NOT_AVAILABLE;
     720             :   }
     721             : 
     722             :   // Audio shouldn't have gaps.
     723             :   // Especially when we generate the timestamps ourselves.
     724           0 :   int64_t GetRoundingError() override
     725             :   {
     726           0 :     return 0;
     727             :   }
     728             : };
     729             : #endif // MOZ_FMP4
     730             : 
     731             : /*static*/ ContainerParser*
     732           0 : ContainerParser::CreateForMIMEType(const MediaContainerType& aType)
     733             : {
     734           0 :   if (aType.Type() == MEDIAMIMETYPE("video/webm") ||
     735           0 :       aType.Type() == MEDIAMIMETYPE("audio/webm")) {
     736           0 :     return new WebMContainerParser(aType);
     737             :   }
     738             : 
     739             : #ifdef MOZ_FMP4
     740           0 :   if (aType.Type() == MEDIAMIMETYPE("video/mp4") ||
     741           0 :       aType.Type() == MEDIAMIMETYPE("audio/mp4")) {
     742           0 :     return new MP4ContainerParser(aType);
     743             :   }
     744           0 :   if (aType.Type() == MEDIAMIMETYPE("audio/aac")) {
     745           0 :     return new ADTSContainerParser(aType);
     746             :   }
     747             : #endif
     748             : 
     749           0 :   return new ContainerParser(aType);
     750             : }
     751             : 
     752             : #undef MSE_DEBUG
     753             : #undef MSE_DEBUGV
     754             : 
     755             : } // namespace mozilla

Generated by: LCOV version 1.13