Bug 1341454 - MP4Demuxer::Init() pre-caches everything from MP4Metadata - r=jya
authorGerald Squelart <gsquelart@mozilla.com>
Thu, 23 Feb 2017 14:56:51 +1100
changeset 344534 aa4f4a00727d2879c9a949382211c52caf0d5f79
parent 344533 a8775af9fb3432e4a6191851a5977cefa45e422f
child 344535 1ef637e0cf2048d5e99e6a01a03c59f186f0e6cf
push id31413
push usercbook@mozilla.com
push dateFri, 24 Feb 2017 10:18:46 +0000
treeherdermozilla-central@c7935d540027 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1341454
milestone54.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 1341454 - MP4Demuxer::Init() pre-caches everything from MP4Metadata - r=jya MP4Demuxer::Init() used to just create a minimal MP4Metadata structure, and report success/failure from that alone. But other later-called functions (e.g.: GetNumberTracks, GetTrackDemuxer, etc.) could still fail with no useful error reporting, when MP4Metadata tried to gather more of the needed information. Also, MP4Demuxer needed to keep this MP4Metadata around forever, even though it could contain an arbitrary amount of extra data that is not needed. With this patch, MP4Demuxer::Init() fetches all the data that could ever be needed, and then discards the MP4Metadata. This ensures that no late-initialization errors could creep in, and also helps reporting errors early and with better diagnostic information. This bug focuses on Init(), a later bug will give MP4Metadata the ability to report its own even-more-detailed errors. MozReview-Commit-ID: 1NjzOeKa1JI
dom/media/fmp4/MP4Demuxer.cpp
dom/media/fmp4/MP4Demuxer.h
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -111,127 +111,166 @@ AccumulateSPSTelemetry(const MediaByteBu
   }
 
   return true;
 }
 
 MP4Demuxer::MP4Demuxer(MediaResource* aResource)
   : mResource(aResource)
   , mStream(new mp4_demuxer::ResourceStream(aResource))
-  , mInitData(new MediaByteBuffer)
 {
 }
 
 RefPtr<MP4Demuxer::InitPromise>
 MP4Demuxer::Init()
 {
   AutoPinned<mp4_demuxer::ResourceStream> stream(mStream);
 
   // Check that we have enough data to read the metadata.
   if (!mp4_demuxer::MP4Metadata::HasCompleteMetadata(stream)) {
-    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                                        __func__);
+    return InitPromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                  RESULT_DETAIL("Incomplete MP4 metadata")),
+      __func__);
   }
 
-  mInitData = mp4_demuxer::MP4Metadata::Metadata(stream);
-  if (!mInitData) {
-    // OOM
-    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                                        __func__);
+  RefPtr<MediaByteBuffer> initData = mp4_demuxer::MP4Metadata::Metadata(stream);
+  if (!initData) {
+    return InitPromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                  RESULT_DETAIL("Invalid MP4 metadata or OOM")),
+      __func__);
   }
 
   RefPtr<mp4_demuxer::BufferStream> bufferstream =
-    new mp4_demuxer::BufferStream(mInitData);
+    new mp4_demuxer::BufferStream(initData);
+
+  mp4_demuxer::MP4Metadata metadata{bufferstream};
 
-  mMetadata = MakeUnique<mp4_demuxer::MP4Metadata>(bufferstream);
+  auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
+  auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack);
+  if (audioTrackCount == 0 && videoTrackCount == 0) {
+    return InitPromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                  RESULT_DETAIL("No MP4 audio or video tracks")),
+      __func__);
+  }
 
-  if (!mMetadata->GetNumberTracks(mozilla::TrackInfo::kAudioTrack)
-      && !mMetadata->GetNumberTracks(mozilla::TrackInfo::kVideoTrack)) {
-    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                                        __func__);
+  if (audioTrackCount != 0) {
+    mAudioDemuxers.SetLength(audioTrackCount);
+    for (size_t i = 0; i < audioTrackCount; i++) {
+      UniquePtr<TrackInfo> info =
+        metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
+      if (info) {
+        FallibleTArray<mp4_demuxer::Index::Indice> indices;
+        if (metadata.ReadTrackIndex(indices, info->mTrackId)) {
+          mAudioDemuxers[i] = new MP4TrackDemuxer(this, Move(info), indices);
+        }
+      }
+    }
   }
 
+  if (videoTrackCount != 0) {
+    mVideoDemuxers.SetLength(videoTrackCount);
+    for (size_t i = 0; i < videoTrackCount; i++) {
+      UniquePtr<TrackInfo> info =
+        metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
+      if (info) {
+        FallibleTArray<mp4_demuxer::Index::Indice> indices;
+        if (metadata.ReadTrackIndex(indices, info->mTrackId)) {
+          mVideoDemuxers[i] = new MP4TrackDemuxer(this, Move(info), indices);
+        }
+      }
+    }
+  }
+
+  const mp4_demuxer::CryptoFile& cryptoFile = metadata.Crypto();
+  if (cryptoFile.valid) {
+    const nsTArray<mp4_demuxer::PsshInfo>& psshs = cryptoFile.pssh;
+    for (uint32_t i = 0; i < psshs.Length(); i++) {
+      mCryptoInitData.AppendElements(psshs[i].data);
+    }
+  }
+
+  mIsSeekable = metadata.CanSeek();
+
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
 bool
 MP4Demuxer::HasTrackType(TrackInfo::TrackType aType) const
 {
-  return !!GetNumberTracks(aType);
+  return GetNumberTracks(aType) != 0;
 }
 
 uint32_t
 MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
-  return mMetadata->GetNumberTracks(aType);
+  switch (aType) {
+    case TrackInfo::kAudioTrack: return uint32_t(mAudioDemuxers.Length());
+    case TrackInfo::kVideoTrack: return uint32_t(mVideoDemuxers.Length());
+    default: return 0;
+  }
 }
 
 already_AddRefed<MediaTrackDemuxer>
 MP4Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
 {
-  if (mMetadata->GetNumberTracks(aType) <= aTrackNumber) {
-    return nullptr;
-  }
-  UniquePtr<TrackInfo> info = mMetadata->GetTrackInfo(aType, aTrackNumber);
-  if (!info) {
-    return nullptr;
+  switch (aType) {
+    case TrackInfo::kAudioTrack:
+      if (aTrackNumber >= uint32_t(mAudioDemuxers.Length())) {
+        return nullptr;
+      }
+      return RefPtr<MediaTrackDemuxer>(mAudioDemuxers[aTrackNumber]).forget();
+    case TrackInfo::kVideoTrack:
+      if (aTrackNumber >= uint32_t(mVideoDemuxers.Length())) {
+        return nullptr;
+      }
+      return RefPtr<MediaTrackDemuxer>(mVideoDemuxers[aTrackNumber]).forget();
+    default:
+      return nullptr;
   }
-  FallibleTArray<mp4_demuxer::Index::Indice> indices;
-  if (!mMetadata->ReadTrackIndex(indices, info->mTrackId)) {
-    return nullptr;
-  }
-  RefPtr<MP4TrackDemuxer> e = new MP4TrackDemuxer(this, Move(info), indices);
-  mDemuxers.AppendElement(e);
-
-  return e.forget();
 }
 
 bool
 MP4Demuxer::IsSeekable() const
 {
-  return mMetadata->CanSeek();
+  return mIsSeekable;
 }
 
 void
 MP4Demuxer::NotifyDataArrived()
 {
-  for (uint32_t i = 0; i < mDemuxers.Length(); i++) {
-    mDemuxers[i]->NotifyDataArrived();
+  for (auto& dmx : mAudioDemuxers) {
+    dmx->NotifyDataArrived();
+  }
+  for (auto& dmx : mVideoDemuxers) {
+    dmx->NotifyDataArrived();
   }
 }
 
 void
 MP4Demuxer::NotifyDataRemoved()
 {
-  for (uint32_t i = 0; i < mDemuxers.Length(); i++) {
-    mDemuxers[i]->NotifyDataArrived();
+  for (auto& dmx : mAudioDemuxers) {
+    dmx->NotifyDataArrived();
+  }
+  for (auto& dmx : mVideoDemuxers) {
+    dmx->NotifyDataArrived();
   }
 }
 
 UniquePtr<EncryptionInfo>
 MP4Demuxer::GetCrypto()
 {
-  const mp4_demuxer::CryptoFile& cryptoFile = mMetadata->Crypto();
-  if (!cryptoFile.valid) {
-    return nullptr;
+  UniquePtr<EncryptionInfo> crypto;
+  if (!mCryptoInitData.IsEmpty()) {
+    crypto.reset(new EncryptionInfo{});
+    crypto->AddInitData(NS_LITERAL_STRING("cenc"), mCryptoInitData);
   }
-
-  const nsTArray<mp4_demuxer::PsshInfo>& psshs = cryptoFile.pssh;
-  nsTArray<uint8_t> initData;
-  for (uint32_t i = 0; i < psshs.Length(); i++) {
-    initData.AppendElements(psshs[i].data);
-  }
-
-  if (initData.IsEmpty()) {
-    return nullptr;
-  }
-
-  auto crypto = MakeUnique<EncryptionInfo>();
-  crypto->AddInitData(NS_LITERAL_STRING("cenc"), Move(initData));
-
   return crypto;
 }
 
 MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent,
                                  UniquePtr<TrackInfo>&& aInfo,
                                  const nsTArray<mp4_demuxer::Index::Indice>& indices)
   : mParent(aParent)
   , mStream(new mp4_demuxer::ResourceStream(mParent->mResource))
--- a/dom/media/fmp4/MP4Demuxer.h
+++ b/dom/media/fmp4/MP4Demuxer.h
@@ -43,16 +43,17 @@ public:
   void NotifyDataArrived() override;
 
   void NotifyDataRemoved() override;
 
 private:
   friend class MP4TrackDemuxer;
   RefPtr<MediaResource> mResource;
   RefPtr<mp4_demuxer::ResourceStream> mStream;
-  RefPtr<MediaByteBuffer> mInitData;
-  UniquePtr<mp4_demuxer::MP4Metadata> mMetadata;
-  nsTArray<RefPtr<MP4TrackDemuxer>> mDemuxers;
+  AutoTArray<RefPtr<MP4TrackDemuxer>, 1> mAudioDemuxers;
+  AutoTArray<RefPtr<MP4TrackDemuxer>, 1> mVideoDemuxers;
+  nsTArray<uint8_t> mCryptoInitData;
+  bool mIsSeekable;
 };
 
 } // namespace mozilla
 
 #endif