Bug 1196398 - [mp4] Do not allocate memory spanning across ftyp and moov atom. r=kentuckyfriedtakahe, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 20 Aug 2015 16:50:08 +1000
changeset 288865 f4b2c0381b884b23f69b6502112c77cf6c9b57cb
parent 288864 5894913e42598cd8578958710b3e965409931c72
child 288866 49ba692877f98f33100955babdbe60138e864154
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe, sledru
bugs1196398
milestone42.0a2
Bug 1196398 - [mp4] Do not allocate memory spanning across ftyp and moov atom. r=kentuckyfriedtakahe, a=sledru A typical non-fragmented mp4 would have the ftyp atom located at the beginning of the mp4 and the moov at the end. We would to calculate the location of the metadata by spanning the byte range of the two atoms. As such, we would typically allocate an amount of memory equivalent to the size of the mp4. Instead we now reconstruct the metadata to only have the ftyp and moov atoms contiguously.
dom/media/fmp4/MP4Demuxer.cpp
media/libstagefright/binding/MP4Metadata.cpp
media/libstagefright/binding/MoofParser.cpp
media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -74,32 +74,26 @@ MP4Demuxer::MP4Demuxer(MediaResource* aR
 }
 
 nsRefPtr<MP4Demuxer::InitPromise>
 MP4Demuxer::Init()
 {
   AutoPinned<mp4_demuxer::ResourceStream> stream(mStream);
 
   // Check that we have enough data to read the metadata.
-  MediaByteRange br = mp4_demuxer::MP4Metadata::MetadataRange(stream);
-  if (br.IsNull()) {
+  if (!mp4_demuxer::MP4Metadata::HasCompleteMetadata(stream)) {
     return InitPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__);
   }
 
-  if (!mInitData->SetLength(br.Length(), fallible)) {
+  mInitData = mp4_demuxer::MP4Metadata::Metadata(stream);
+  if (!mInitData) {
     // OOM
     return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
-  size_t size;
-  mStream->ReadAt(br.mStart, mInitData->Elements(), br.Length(), &size);
-  if (size != size_t(br.Length())) {
-    return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
-  }
-
   nsRefPtr<mp4_demuxer::BufferStream> bufferstream =
     new mp4_demuxer::BufferStream(mInitData);
 
   mMetadata = MakeUnique<mp4_demuxer::MP4Metadata>(bufferstream);
 
   if (!mMetadata->GetNumberTracks(mozilla::TrackInfo::kAudioTrack) &&
       !mMetadata->GetNumberTracks(mozilla::TrackInfo::kVideoTrack)) {
     return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -282,22 +282,19 @@ MP4Metadata::HasCompleteMetadata(Stream*
 {
   // The MoofParser requires a monitor, but we don't need one here.
   mozilla::Monitor monitor("MP4Metadata::HasCompleteMetadata");
   mozilla::MonitorAutoLock mon(monitor);
   auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false, &monitor);
   return parser->HasMetadata();
 }
 
-/*static*/ mozilla::MediaByteRange
-MP4Metadata::MetadataRange(Stream* aSource)
+/*static*/ already_AddRefed<mozilla::MediaByteBuffer>
+MP4Metadata::Metadata(Stream* aSource)
 {
   // The MoofParser requires a monitor, but we don't need one here.
   mozilla::Monitor monitor("MP4Metadata::HasCompleteMetadata");
   mozilla::MonitorAutoLock mon(monitor);
   auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false, &monitor);
-  if (parser->HasMetadata()) {
-    return parser->mInitRange;
-  }
-  return mozilla::MediaByteRange();
+  return parser->Metadata();
 }
 
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -143,43 +143,77 @@ MoofParser::BlockingReadNextMoof()
       byteRanges.Clear();
       byteRanges.AppendElement(MediaByteRange(mOffset, box.Range().mEnd));
       return RebuildFragmentedIndex(context);
     }
   }
   return false;
 }
 
-bool
-MoofParser::HasMetadata()
+void
+MoofParser::ScanForMetadata(mozilla::MediaByteRange& aFtyp,
+                            mozilla::MediaByteRange& aMoov)
 {
   int64_t length = std::numeric_limits<int64_t>::max();
   mSource->Length(&length);
   nsTArray<MediaByteRange> byteRanges;
   byteRanges.AppendElement(MediaByteRange(0, length));
   nsRefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
 
-  MediaByteRange ftyp;
-  MediaByteRange moov;
   BoxContext context(stream, byteRanges);
   for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
     if (box.IsType("ftyp")) {
-      ftyp = box.Range();
+      aFtyp = box.Range();
       continue;
     }
     if (box.IsType("moov")) {
-      moov = box.Range();
+      aMoov = box.Range();
       break;
     }
   }
+  mInitRange = aFtyp.Extents(aMoov);
+}
+
+bool
+MoofParser::HasMetadata()
+{
+  MediaByteRange ftyp;
+  MediaByteRange moov;
+  ScanForMetadata(ftyp, moov);
+  return !!ftyp.Length() && !!moov.Length();
+}
+
+already_AddRefed<mozilla::MediaByteBuffer>
+MoofParser::Metadata()
+{
+  MediaByteRange ftyp;
+  MediaByteRange moov;
+  ScanForMetadata(ftyp, moov);
   if (!ftyp.Length() || !moov.Length()) {
-    return false;
+    return nullptr;
   }
-  mInitRange = ftyp.Extents(moov);
-  return true;
+  nsRefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
+  if (!metadata->SetLength(ftyp.Length() + moov.Length(), fallible)) {
+    // OOM
+    return nullptr;
+  }
+
+  nsRefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
+  size_t read;
+  bool rv =
+    stream->ReadAt(ftyp.mStart, metadata->Elements(), ftyp.Length(), &read);
+  if (!rv || read != ftyp.Length()) {
+    return nullptr;
+  }
+  rv =
+    stream->ReadAt(moov.mStart, metadata->Elements() + ftyp.Length(), moov.Length(), &read);
+  if (!rv || read != moov.Length()) {
+    return nullptr;
+  }
+  return metadata.forget();
 }
 
 Interval<Microseconds>
 MoofParser::GetCompositionRange(const nsTArray<MediaByteRange>& aByteRanges)
 {
   Interval<Microseconds> compositionRange;
   BoxContext context(mSource, aByteRanges);
   for (size_t i = 0; i < mMoofs.Length(); i++) {
--- a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
@@ -25,17 +25,17 @@ struct StageFrightPrivate;
 
 class MP4Metadata
 {
 public:
   explicit MP4Metadata(Stream* aSource);
   ~MP4Metadata();
 
   static bool HasCompleteMetadata(Stream* aSource);
-  static mozilla::MediaByteRange MetadataRange(Stream* aSource);
+  static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource);
   uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
   mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                                       size_t aTrackNumber) const;
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const
   {
     return mCrypto;
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -223,31 +223,34 @@ public:
   void ParseMinf(Box& aBox);
   void ParseStbl(Box& aBox);
   void ParseStsd(Box& aBox);
   void ParseEncrypted(Box& aBox);
   void ParseSinf(Box& aBox);
 
   bool BlockingReadNextMoof();
   bool HasMetadata();
+  already_AddRefed<mozilla::MediaByteBuffer> Metadata();
   MediaByteRange FirstCompleteMediaSegment();
   MediaByteRange FirstCompleteMediaHeader();
 
   mozilla::MediaByteRange mInitRange;
   nsRefPtr<Stream> mSource;
   uint64_t mOffset;
   nsTArray<uint64_t> mMoofOffsets;
   Mvhd mMvhd;
   Mdhd mMdhd;
   Trex mTrex;
   Tfdt mTfdt;
   Edts mEdts;
   Sinf mSinf;
   Monitor* mMonitor;
   nsTArray<Moof>& Moofs() { mMonitor->AssertCurrentThreadOwns(); return mMoofs; }
 private:
+  void ScanForMetadata(mozilla::MediaByteRange& aFtyp,
+                       mozilla::MediaByteRange& aMoov);
   nsTArray<Moof> mMoofs;
   nsTArray<MediaByteRange> mMediaRanges;
   bool mIsAudio;
 };
 }
 
 #endif