Bug 961618 - Reduce memory usage by swapping audio/video array pointer. r=cpearce
authorAlfredo Yang <ayang@mozilla.com>
Fri, 31 Jan 2014 21:51:27 -0500
changeset 182447 81d4d1ff63335cd03fb99d0256548b4822f4ff23
parent 182446 0dca58ecbad24eda37b6dd822dac62d6f2dc4325
child 182448 340f43a32a7aaa80a89d9818b0e9500e42d0e533
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs961618
milestone29.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 961618 - Reduce memory usage by swapping audio/video array pointer. r=cpearce
content/media/encoder/fmp4_muxer/ISOControl.cpp
content/media/encoder/fmp4_muxer/ISOControl.h
content/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
--- a/content/media/encoder/fmp4_muxer/ISOControl.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOControl.cpp
@@ -10,18 +10,16 @@
 #include "EncodedFrameContainer.h"
 
 namespace mozilla {
 
 // For MP4 creation_time and modification_time offset from January 1, 1904 to
 // January 1, 1970.
 #define iso_time_offset 2082844800
 
-const static uint32_t MUXING_BUFFER_SIZE = 512*1024;
-
 FragmentBuffer::FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration,
                                TrackMetadataBase* aMetadata)
   : mTrackType(aTrackType)
   , mFragDuration(aFragDuration)
   , mMediaStartTime(0)
   , mFragmentNumber(0)
   , mEOS(false)
 {
@@ -135,17 +133,18 @@ FragmentBuffer::GetFirstFragmentSampleSi
 ISOControl::ISOControl()
   : mAudioFragmentBuffer(nullptr)
   , mVideoFragmentBuffer(nullptr)
   , mFragNum(0)
   , mOutputSize(0)
   , mBitCount(0)
   , mBit(0)
 {
-  mOutBuffer.SetCapacity(MUXING_BUFFER_SIZE);
+  // Create a data array for first mp4 Box, ftyp.
+  mOutBuffers.SetLength(1);
   MOZ_COUNT_CTOR(ISOControl);
 }
 
 ISOControl::~ISOControl()
 {
   MOZ_COUNT_DTOR(ISOControl);
 }
 
@@ -245,32 +244,58 @@ ISOControl::GetFragment(uint32_t aType)
   } else if (aType == Video_Track){
     return mVideoFragmentBuffer;
   }
   MOZ_ASSERT(0);
   return nullptr;
 }
 
 nsresult
-ISOControl::GetBuf(nsTArray<uint8_t>& aOutBuf)
+ISOControl::GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs)
 {
-  mOutputSize += mOutBuffer.Length();
-  aOutBuf.SwapElements(mOutBuffer);
+  uint32_t len = mOutBuffers.Length();
+  for (uint32_t i = 0; i < len; i++) {
+    mOutBuffers[i].SwapElements(*aOutputBufs->AppendElement());
+    mOutputSize += mOutBuffers[i].Length();
+  }
   return FlushBuf();
 }
 
 nsresult
 ISOControl::FlushBuf()
 {
-  mOutBuffer.SetCapacity(MUXING_BUFFER_SIZE);
+  mOutBuffers.SetLength(1);
   mLastWrittenBoxPos = 0;
   return NS_OK;
 }
 
 uint32_t
+ISOControl::WriteAVData(nsTArray<uint8_t>& aArray)
+{
+  MOZ_ASSERT(!mBitCount);
+
+  uint32_t len = aArray.Length();
+  if (!len) {
+    return 0;
+  }
+
+  // The last element already has data, allocated a new element for pointer
+  // swapping.
+  if (mOutBuffers.LastElement().Length()) {
+    mOutBuffers.AppendElement();
+  }
+  // Swap the video/audio data pointer.
+  mOutBuffers.LastElement().SwapElements(aArray);
+  // Following data could be boxes, so appending a new uint8_t array here.
+  mOutBuffers.AppendElement();
+
+  return len;
+}
+
+uint32_t
 ISOControl::WriteBits(uint64_t aBits, size_t aNumBits)
 {
   uint8_t output_byte = 0;
 
   MOZ_ASSERT(aNumBits <= 64);
   // TODO: rewritten following with bitset?
   for (size_t i = aNumBits; i > 0; i--) {
     mBit |= (((aBits >> (i - 1)) & 1) << (8 - ++mBitCount));
@@ -282,29 +307,40 @@ ISOControl::WriteBits(uint64_t aBits, si
     }
   }
   return output_byte;
 }
 
 uint32_t
 ISOControl::Write(uint8_t* aBuf, uint32_t aSize)
 {
-  mOutBuffer.AppendElements(aBuf, aSize);
+  mOutBuffers.LastElement().AppendElements(aBuf, aSize);
   return aSize;
 }
 
 uint32_t
 ISOControl::Write(uint8_t aData)
 {
   MOZ_ASSERT(!mBitCount);
   Write((uint8_t*)&aData, sizeof(uint8_t));
   return sizeof(uint8_t);
 }
 
 uint32_t
+ISOControl::GetBufPos()
+{
+  uint32_t len = mOutBuffers.Length();
+  uint32_t pos = 0;
+  for (uint32_t i = 0; i < len; i++) {
+    pos += mOutBuffers.ElementAt(i).Length();
+  }
+  return pos;
+}
+
+uint32_t
 ISOControl::WriteFourCC(const char* aType)
 {
   // Bit operation should be aligned to byte before writing any byte data.
   MOZ_ASSERT(!mBitCount);
 
   uint32_t size = strlen(aType);
   if (size == 4) {
     return Write((uint8_t*)aType, size);
--- a/content/media/encoder/fmp4_muxer/ISOControl.h
+++ b/content/media/encoder/fmp4_muxer/ISOControl.h
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ISOCOMPOSITOR_H_
 #define ISOCOMPOSITOR_H_
 
 #include "mozilla/Endian.h"
-#include "TrackMetadataBase.h"
 #include "nsTArray.h"
 #include "ISOTrackMetadata.h"
 #include "EncodedFrameContainer.h"
 
 namespace mozilla {
 
 class Box;
 class ISOControl;
@@ -125,16 +124,19 @@ friend class Box;
 public:
   ISOControl();
   ~ISOControl();
 
   nsresult GenerateFtyp();
   nsresult GenerateMoov();
   nsresult GenerateMoof(uint32_t aTrackType);
 
+  // Swap elementary stream pointer to output buffers.
+  uint32_t WriteAVData(nsTArray<uint8_t>& aArray);
+
   uint32_t Write(uint8_t* aBuf, uint32_t aSize);
 
   uint32_t Write(uint8_t aData);
 
   template <typename T>
   uint32_t Write(T aData) {
     MOZ_ASSERT(!mBitCount);
 
@@ -155,18 +157,18 @@ public:
   }
 
   uint32_t WriteFourCC(const char* aType);
 
   // Bit writing. Note: it needs to be byte-boundary before using
   // others non-bit writing function.
   uint32_t WriteBits(uint64_t aBits, size_t aNumBits);
 
-  // This is called by GetContainerData and swap the buffer to aOutBuf.
-  nsresult GetBuf(nsTArray<uint8_t>& aOutBuf);
+  // This is called by GetContainerData and swap all the buffers to aOutputBuffers.
+  nsresult GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs);
 
   // Presentation time in seconds since midnight, Jan. 1, 1904, in UTC time.
   uint32_t GetTime();
 
   // current fragment number
   uint32_t GetCurFragmentNumber() { return mFragNum; }
 
   nsresult SetFragment(FragmentBuffer* aFragment);
@@ -179,37 +181,47 @@ public:
   // Track ID is the Metadata index in mMetaArray.
   uint32_t GetTrackID(uint32_t aTrackType);
   uint32_t GetNextTrackID();
 
   bool HasAudioTrack();
   bool HasVideoTrack();
 
 private:
-  uint32_t GetBufPos() { return mOutBuffer.Length(); }
+  uint32_t GetBufPos();
   nsresult FlushBuf();
 
   // Audio and video fragments are owned by ISOMediaWriter.
   // They don't need to worry about pointer going stale.
   FragmentBuffer* mAudioFragmentBuffer;
   FragmentBuffer* mVideoFragmentBuffer;
 
   // Generated fragment number
   uint32_t mFragNum;
 
   // The (index + 1) will be the track ID.
   nsTArray<nsRefPtr<TrackMetadataBase>> mMetaArray;
 
-  // TODO:
-  // ContainerWriter accepts a array of uint8_t array so it is possible to
-  // create a serial of small box header + swap the raw data pointer from
-  // WriteEncodedTrack to another array without any memory copy.
-  nsTArray<uint8_t> mOutBuffer;
+  // Array of output buffers.
+  // To save memory usage, audio/video sample will be swapped into a new element
+  // of this array.
+  //
+  // For example,
+  //   mOutBuffers[0] --> boxes (allocated by muxer)
+  //   mOutBuffers[1] --> video raw data (allocated by encoder)
+  //   mOutBuffers[2] --> video raw data (allocated by encoder)
+  //   mOutBuffers[3] --> video raw data (allocated by encoder)
+  //   mOutBuffers[4] --> boxes (allocated by muxer)
+  //   mOutBuffers[5] --> audio raw data (allocated by encoder)
+  //   ...etc.
+  //
+  nsTArray<nsTArray<uint8_t>> mOutBuffers;
 
-  // Last written position of current box, it is for box checking purpose.
+  // Last written position of current box, it is for box checking purpose and
+  // calculating the sample offset in moof.
   uint32_t mLastWrittenBoxPos;
 
   // Accumulate size of output fragments.
   uint64_t mOutputSize;
 
   // Bit writing operation. Note: the mBitCount should be 0 before any
   // byte-boundary writing method be called (Write(uint32_t), Write(uint16_t)...etc);
   // otherwise, there will be assertion on these functions.
--- a/content/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
@@ -35,17 +35,20 @@ Box::BoxSizeChecker::BoxSizeChecker(ISOC
 }
 
 Box::BoxSizeChecker::~BoxSizeChecker()
 {
   uint32_t cur_size = mControl->GetBufPos();
   if ((cur_size - ori_size) != box_size) {
     MOZ_ASSERT(false);
   }
-  mControl->mLastWrittenBoxPos = mControl->mOutBuffer.Length();
+
+  // Keeps the last box size position, it is for counting the sample offset when
+  // generating moof.
+  mControl->mLastWrittenBoxPos += box_size;
   MOZ_COUNT_DTOR(BoxSizeChecker);
 }
 
 nsresult
 MediaDataBox::Generate(uint32_t* aBoxSize)
 {
   mFirstSampleOffset = size;
   mAllSampleSize = 0;
@@ -80,18 +83,19 @@ MediaDataBox::Write()
       nsTArray<nsRefPtr<EncodedFrame>> frames;
 
       // Here is the last time we get fragment frames, flush it!
       rv = frag->GetFirstFragment(frames, true);
       NS_ENSURE_SUCCESS(rv, rv);
 
       uint32_t len = frames.Length();
       for (uint32_t i = 0; i < len; i++) {
-        mControl->Write((uint8_t*)frames.ElementAt(i)->GetFrameData().Elements(),
-            frames.ElementAt(i)->GetFrameData().Length());
+        nsTArray<uint8_t> frame_buffer;
+        frames.ElementAt(i)->SwapOutFrameData(frame_buffer);
+        mControl->WriteAVData(frame_buffer);
       }
     }
   }
 
   return NS_OK;
 }
 
 MediaDataBox::MediaDataBox(uint32_t aTrackType, ISOControl* aControl)
--- a/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -112,20 +112,22 @@ ISOMediaWriter::WriteEncodedTrack(const 
 
     frag->AddFrame(frame);
   }
 
   // Encoder should send CSD (codec specific data) frame before sending the
   // audio/video frames. When CSD data is ready, it is sufficient to generate a
   // moov data. If encoder doesn't send CSD yet, muxer needs to wait before
   // generating anything.
-  if (mType & Audio_Track && !mAudioFragmentBuffer->HasCSD()) {
+  if (mType & Audio_Track && (!mAudioFragmentBuffer ||
+                              !mAudioFragmentBuffer->HasCSD())) {
     return NS_OK;
   }
-  if (mType & Video_Track && !mVideoFragmentBuffer->HasCSD()) {
+  if (mType & Video_Track && (!mVideoFragmentBuffer ||
+                              !mVideoFragmentBuffer->HasCSD())) {
     return NS_OK;
   }
 
   // Only one FrameType in EncodedFrameContainer so it doesn't need to be
   // inside the for-loop.
   if (frag && (aFlags & END_OF_STREAM)) {
     frag->SetEndOfStream();
   }
@@ -188,18 +190,17 @@ nsresult
 ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
                                  uint32_t aFlags)
 {
   if (mBlobReady) {
     if (mState == MUXING_DONE) {
       mIsWritingComplete = true;
     }
     mBlobReady = false;
-    aOutputBufs->AppendElement();
-    return mControl->GetBuf(aOutputBufs->LastElement());
+    return mControl->GetBufs(aOutputBufs);
   }
   return NS_OK;
 }
 
 nsresult
 ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
 {
   if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ) {