Bug 1175058: P3. Properly search for the required MP4 Atoms rather than make assumptions. r=kentuckyfriedtakahe
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 17 Jun 2015 11:21:24 +1000
changeset 280499 d7aad45010b6adac72489dd2338606a90d76b87b
parent 280498 993db9bb30995667ac86c299ceb7047ec35b9454
child 280500 a64d14d0bf260a2e29bd23ad2f29b657c2dcfa11
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe
bugs1175058
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1175058: P3. Properly search for the required MP4 Atoms rather than make assumptions. r=kentuckyfriedtakahe
dom/media/mediasource/ContainerParser.cpp
dom/media/mediasource/TrackBuffersManager.cpp
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -9,29 +9,32 @@
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
 #include "mozilla/ErrorResult.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mozilla/Logging.h"
 #include "MediaData.h"
 #ifdef MOZ_FMP4
 #include "MP4Stream.h"
+#include "mp4_demuxer/AtomType.h"
+#include "mp4_demuxer/ByteReader.h"
 #endif
 #include "SourceBufferResource.h"
 
 extern PRLogModuleInfo* GetMediaSourceLog();
 
 /* Polyfill __func__ on MSVC to pass to the log. */
 #ifdef _MSC_VER
 #define __func__ __FUNCTION__
 #endif
 
 #define STRINGIFY(x) #x
 #define TOSTRING(x) STRINGIFY(x)
 #define MSE_DEBUG(name, arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
+#define MSE_DEBUGV(name, arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 ContainerParser::ContainerParser(const nsACString& aType)
   : mHasInitData(false)
   , mType(aType)
 {
 }
@@ -253,54 +256,63 @@ private:
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
     , mMonitor("MP4ContainerParser Index Monitor")
   {}
 
+  bool HasAtom(const mp4_demuxer::AtomType& aAtom, const MediaByteBuffer* aData) {
+    mp4_demuxer::ByteReader reader(aData);
+
+    while (reader.Remaining() >= 8) {
+      uint64_t size = reader.ReadU32();
+      const uint8_t* typec = reader.Peek(4);
+      uint32_t type = reader.ReadU32();
+      MSE_DEBUGV(MP4ContainerParser ,"Checking atom:'%c%c%c%c'",
+                typec[0], typec[1], typec[2], typec[3]);
+      if (mp4_demuxer::AtomType(type) == aAtom) {
+        reader.DiscardRemaining();
+        return true;
+      }
+      if (size == 1) {
+        // 64 bits size.
+        if (!reader.CanReadType<uint64_t>()) {
+          break;
+        }
+        size = reader.ReadU64();
+      } else if (size == 0) {
+        // Atom extends to the end of the buffer, it can't have what we're
+        // looking for.
+        break;
+      }
+      if (reader.Remaining() < size - 8) {
+        // Incomplete atom.
+        break;
+      }
+      reader.Read(size - 8);
+    }
+    reader.DiscardRemaining();
+    return false;
+  }
+
   bool IsInitSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
-
-    if (aData->Length() < 8) {
-      return false;
-    }
-
-    uint32_t chunk_size = BigEndian::readUint32(aData->Elements());
-    if (chunk_size < 8) {
-      return false;
-    }
-
-    return (*aData)[4] == 'f' && (*aData)[5] == 't' && (*aData)[6] == 'y' &&
-           (*aData)[7] == 'p';
+    return HasAtom(mp4_demuxer::AtomType("ftyp"), aData);
   }
 
   bool IsMediaSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsMediaSegmentPresent(aData);
-    if (aData->Length() < 8) {
-      return false;
-    }
-
-    uint32_t chunk_size = BigEndian::readUint32(aData->Elements());
-    if (chunk_size < 8) {
-      return false;
-    }
-
-    return ((*aData)[4] == 'm' && (*aData)[5] == 'o' && (*aData)[6] == 'o' &&
-            (*aData)[7] == 'f') ||
-           ((*aData)[4] == 's' && (*aData)[5] == 't' && (*aData)[6] == 'y' &&
-            (*aData)[7] == 'p') ||
-           ((*aData)[4] == 's' && (*aData)[5] == 'i' && (*aData)[6] == 'd' &&
-            (*aData)[7] == 'x');
+    return HasAtom(mp4_demuxer::AtomType("moof"), aData);
   }
 
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
                                    // but mParser requires us to hold a monitor.
     bool initSegment = IsInitSegmentPresent(aData);
@@ -391,10 +403,11 @@ ContainerParser::CreateForMIMEType(const
   if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
     return new MP4ContainerParser(aType);
   }
 #endif
   return new ContainerParser(aType);
 }
 
 #undef MSE_DEBUG
+#undef MSE_DEBUGV
 
 } // namespace mozilla
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -626,17 +626,19 @@ TrackBuffersManager::SegmentParserLoop()
     }
     // 2. If the input buffer contains bytes that violate the SourceBuffer
     // byte stream format specification, then run the append error algorithm with
     // the decode error parameter set to true and abort this algorithm.
     // TODO
 
     // 3. Remove any bytes that the byte stream format specifications say must be
     // ignored from the start of the input buffer.
-    // TODO
+    // We do not remove bytes from our input buffer. Instead we enforce that
+    // our ContainerParser is able to skip over all data that is supposed to be
+    // ignored.
 
     // 4. If the append state equals WAITING_FOR_SEGMENT, then run the following
     // steps:
     if (mAppendState == AppendState::WAITING_FOR_SEGMENT) {
       if (mParser->IsInitSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_INIT_SEGMENT);
         if (mFirstInitializationSegmentReceived) {
           // This is a new initialization segment. Obsolete the old one.
@@ -645,19 +647,20 @@ TrackBuffersManager::SegmentParserLoop()
         }
         continue;
       }
       if (mParser->IsMediaSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
         continue;
       }
       // We have neither an init segment nor a media segment, this is invalid
-      // data.
-      MSE_DEBUG("Found invalid data");
-      RejectAppend(NS_ERROR_FAILURE, __func__);
+      // data. We can ignore it.
+      MSE_DEBUG("Found invalid data, ignoring.");
+      mInputBuffer = nullptr;
+      NeedMoreData();
       return;
     }
 
     int64_t start, end;
     mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
     mProcessedInput += mInputBuffer->Length();
 
     // 5. If the append state equals PARSING_INIT_SEGMENT, then run the
@@ -762,17 +765,21 @@ void
 TrackBuffersManager::InitializationSegmentReceived()
 {
   MOZ_ASSERT(mParser->HasCompleteInitData());
   mInitData = mParser->InitData();
   mCurrentInputBuffer = new SourceBufferResource(mType);
   mCurrentInputBuffer->AppendData(mInitData);
   uint32_t length =
     mParser->InitSegmentRange().mEnd - (mProcessedInput - mInputBuffer->Length());
-  mInputBuffer->RemoveElementsAt(0, length);
+  if (mInputBuffer->Length() == length) {
+    mInputBuffer = nullptr;
+  } else {
+    mInputBuffer->RemoveElementsAt(0, length);
+  }
   CreateDemuxerforMIMEType();
   if (!mInputDemuxer) {
     MOZ_ASSERT(false, "TODO type not supported");
     return;
   }
   mDemuxerInitRequest.Begin(mInputDemuxer->Init()
                       ->Then(GetTaskQueue(), __func__,
                              this,