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