ContainerParser.cpp 23 KB

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