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