Bug 958378 - Adding memory reporters to measure memory for MediaRecorder framework r=roc, rlin
authorRishab Arora <ra.rishab@gmail.com>
Tue, 01 Jul 2014 10:55:29 +0530
changeset 206193 0c15fcd00145a3f74cf8c1ed151baa1778a93243
parent 206192 23400439c1da8dab2478d4f11bac93c2b796235e
child 206194 ef9834778bc060cee50bb0678dc4d63a1d66f959
push id27516
push userryanvm@gmail.com
push dateFri, 19 Sep 2014 17:54:48 +0000
treeherdermozilla-central@b00bdb144e06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, rlin
bugs958378
milestone35.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 958378 - Adding memory reporters to measure memory for MediaRecorder framework r=roc, rlin
content/media/MediaRecorder.cpp
content/media/MediaRecorder.h
content/media/encoder/MediaEncoder.cpp
content/media/encoder/MediaEncoder.h
content/media/encoder/TrackEncoder.cpp
content/media/encoder/TrackEncoder.h
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaRecorder.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
 #include "EncodedBufferCache.h"
 #include "MediaEncoder.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "mozilla/dom/RecordErrorEvent.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 #include "nsError.h"
 #include "nsIDocument.h"
@@ -30,16 +31,83 @@ PRLogModuleInfo* gMediaRecorderLog;
 #else
 #define LOG(type, msg)
 #endif
 
 namespace mozilla {
 
 namespace dom {
 
+/**
++ * MediaRecorderReporter measures memory being used by the Media Recorder.
++ *
++ * It is a singleton reporter and the single class object lives as long as at
++ * least one Recorder is registered. In MediaRecorder, the reporter is unregistered
++ * when it is destroyed.
++ */
+class MediaRecorderReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  MediaRecorderReporter() {};
+  static MediaRecorderReporter* UniqueInstance();
+  void InitMemoryReporter();
+
+  static void AddMediaRecorder(MediaRecorder *aRecorder)
+  {
+    GetRecorders().AppendElement(aRecorder);
+  }
+
+  static void RemoveMediaRecorder(MediaRecorder *aRecorder)
+  {
+    RecordersArray& recorders = GetRecorders();
+    recorders.RemoveElement(aRecorder);
+    if (recorders.IsEmpty()) {
+      sUniqueInstance = nullptr;
+    }
+  }
+
+  NS_METHOD
+  CollectReports(nsIHandleReportCallback* aHandleReport,
+                 nsISupports* aData, bool aAnonymize)
+  {
+    int64_t amount = 0;
+    RecordersArray& recorders = GetRecorders();
+    for (size_t i = 0; i < recorders.Length(); ++i) {
+      amount += recorders[i]->SizeOfExcludingThis(MallocSizeOf);
+    }
+
+  #define MEMREPORT(_path, _amount, _desc)                                    \
+    do {                                                                      \
+      nsresult rv;                                                            \
+      rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
+                                   KIND_HEAP, UNITS_BYTES, _amount,           \
+                                   NS_LITERAL_CSTRING(_desc), aData);         \
+      NS_ENSURE_SUCCESS(rv, rv);                                              \
+    } while (0)
+
+    MEMREPORT("explicit/media/recorder", amount,
+              "Memory used by media recorder.");
+
+    return NS_OK;
+  }
+
+private:
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+  virtual ~MediaRecorderReporter();
+  static StaticRefPtr<MediaRecorderReporter> sUniqueInstance;
+  typedef nsTArray<MediaRecorder*> RecordersArray;
+  static RecordersArray& GetRecorders()
+  {
+    return UniqueInstance()->mRecorders;
+  }
+  RecordersArray mRecorders;
+};
+NS_IMPL_ISUPPORTS(MediaRecorderReporter, nsIMemoryReporter);
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder, DOMEventTargetHelper,
                                    mDOMStream, mAudioNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MediaRecorder, DOMEventTargetHelper)
@@ -327,16 +395,24 @@ public:
   bool IsEncoderError()
   {
     if (mEncoder && mEncoder->HasError()) {
       return true;
     }
     return false;
   }
 
+  size_t
+  SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = mEncoder->SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+
 private:
   // Only DestroyRunnable is allowed to delete Session object.
   virtual ~Session()
   {
     LOG(PR_LOG_DEBUG, ("Session.~Session (%p)", this));
     CleanupStreams();
   }
   // Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
@@ -676,28 +752,29 @@ MediaRecorder::Start(const Optional<int3
   if (aTimeSlice.WasPassed()) {
     if (aTimeSlice.Value() < 0) {
       aResult.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
 
     timeSlice = aTimeSlice.Value();
   }
-
+  MediaRecorderReporter::AddMediaRecorder(this);
   mState = RecordingState::Recording;
   // Start a session.
   mSessions.AppendElement();
   mSessions.LastElement() = new Session(this, timeSlice);
   mSessions.LastElement()->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
   LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
+  MediaRecorderReporter::RemoveMediaRecorder(this);
   if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   mState = RecordingState::Inactive;
   MOZ_ASSERT(mSessions.Length() > 0);
   mSessions.LastElement()->Stop();
 }
@@ -983,10 +1060,41 @@ MediaRecorder::GetSourcePrincipal()
   if (mDOMStream != nullptr) {
     return mDOMStream->GetPrincipal();
   }
   MOZ_ASSERT(mAudioNode != nullptr);
   nsIDocument* doc = mAudioNode->GetOwner()->GetExtantDoc();
   return doc ? doc->NodePrincipal() : nullptr;
 }
 
+size_t
+MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 42;
+  for (size_t i = 0; i < mSessions.Length(); ++i) {
+    amount += mSessions[i]->SizeOfExcludingThis(aMallocSizeOf);
+  }
+  return amount;
+}
+
+StaticRefPtr<MediaRecorderReporter> MediaRecorderReporter::sUniqueInstance;
+
+MediaRecorderReporter* MediaRecorderReporter::UniqueInstance()
+{
+  if (!sUniqueInstance) {
+    sUniqueInstance = new MediaRecorderReporter();
+    sUniqueInstance->InitMemoryReporter();
+  }
+  return sUniqueInstance;
+ }
+
+void MediaRecorderReporter::InitMemoryReporter()
+{
+  RegisterWeakMemoryReporter(this);
+}
+
+MediaRecorderReporter::~MediaRecorderReporter()
+{
+  UnregisterWeakMemoryReporter(this);
+}
+
 }
 }
--- a/content/media/MediaRecorder.h
+++ b/content/media/MediaRecorder.h
@@ -4,16 +4,17 @@
  * 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 MediaRecorder_h
 #define MediaRecorder_h
 
 #include "mozilla/dom/MediaRecorderBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/MemoryReporting.h"
 #include "nsIDocumentActivity.h"
 
 // Max size for allowing queue encoded data in memory
 #define MAX_ALLOW_MEMORY_BUFFER 1024000
 namespace mozilla {
 
 class AudioNodeStream;
 class DOMMediaStream;
@@ -85,16 +86,21 @@ public:
   // Construct a recorder with a Web Audio destination node as its source.
   static already_AddRefed<MediaRecorder>
   Constructor(const GlobalObject& aGlobal,
               AudioNode& aSrcAudioNode,
               uint32_t aSrcOutput,
               const MediaRecorderOptions& aInitDict,
               ErrorResult& aRv);
 
+  /*
+   * Measure the size of the buffer, and memory occupied by mAudioEncoder
+   * and mVideoEncoder
+   */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   // EventHandler
   IMPL_EVENT_HANDLER(dataavailable)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(start)
   IMPL_EVENT_HANDLER(stop)
   IMPL_EVENT_HANDLER(warning)
 
   NS_DECL_NSIDOCUMENTACTIVITY
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -3,16 +3,17 @@
  * 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/. */
 #include "MediaEncoder.h"
 #include "MediaDecoder.h"
 #include "nsIPrincipal.h"
 #include "nsMimeTypes.h"
 #include "prlog.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
 
 #include "OggWriter.h"
 #ifdef MOZ_OPUS
 #include "OpusTrackEncoder.h"
 
 #endif
 
 #ifdef MOZ_VORBIS
@@ -199,16 +200,19 @@ MediaEncoder::GetEncodedData(nsTArray<ns
       rv = CopyMetadataToMuxer(mVideoEncoder.get());
       if (NS_FAILED(rv)) {
         LOG(PR_LOG_ERROR, ("Error! Fail to Set Video Metadata"));
         break;
       }
 
       rv = mWriter->GetContainerData(aOutputBufs,
                                      ContainerWriter::GET_HEADER);
+      if (aOutputBufs != nullptr) {
+        mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
+      }
       if (NS_FAILED(rv)) {
        LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
        mState = ENCODE_ERROR;
        break;
       }
       LOG(PR_LOG_DEBUG, ("Finish ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
       mState = ENCODE_TRACK;
       break;
@@ -231,30 +235,34 @@ MediaEncoder::GetEncodedData(nsTArray<ns
       }
       LOG(PR_LOG_DEBUG, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
       // In audio only or video only case, let unavailable track's flag to be true.
       bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
       bool isVideoCompleted = (mVideoEncoder && mVideoEncoder->IsEncodingComplete()) || !mVideoEncoder;
       rv = mWriter->GetContainerData(aOutputBufs,
                                      isAudioCompleted && isVideoCompleted ?
                                      ContainerWriter::FLUSH_NEEDED : 0);
+      if (aOutputBufs != nullptr) {
+        mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
+      }
       if (NS_SUCCEEDED(rv)) {
         // Successfully get the copy of final container data from writer.
         reloop = false;
       }
       mState = (mWriter->IsWritingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
       LOG(PR_LOG_DEBUG, ("END ENCODE_TRACK TimeStamp = %f "
           "mState = %d aComplete %d vComplete %d",
           GetEncodeTimeStamp(), mState, isAudioCompleted, isVideoCompleted));
       break;
     }
 
     case ENCODE_DONE:
     case ENCODE_ERROR:
       LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
+      mSizeOfBuffer = 0;
       mShutdown = true;
       reloop = false;
       break;
     default:
       MOZ_CRASH("Invalid encode state");
     }
   }
 }
@@ -318,9 +326,26 @@ MediaEncoder::IsWebMEncoderEnabled()
 #ifdef MOZ_OMX_ENCODER
 bool
 MediaEncoder::IsOMXEncoderEnabled()
 {
   return Preferences::GetBool("media.encoder.omx.enabled");
 }
 #endif
 
+/*
+ * SizeOfExcludingThis measures memory being used by the Media Encoder.
+ * Currently it measures the size of the Encoder buffer and memory occupied
+ * by mAudioEncoder and mVideoEncoder.
+ */
+size_t
+MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+  if (mState == ENCODE_TRACK) {
+    amount = mSizeOfBuffer +
+             (mAudioEncoder != nullptr ? mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0) +
+             (mVideoEncoder != nullptr ? mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0);
+  }
+  return amount;
 }
+
+}
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -5,16 +5,18 @@
 
 #ifndef MediaEncoder_h_
 #define MediaEncoder_h_
 
 #include "mozilla/DebugOnly.h"
 #include "TrackEncoder.h"
 #include "ContainerWriter.h"
 #include "MediaStreamGraph.h"
+#include "nsIMemoryReporter.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 /**
  * MediaEncoder is the framework of encoding module, it controls and manages
  * procedures between ContainerWriter and TrackEncoder. ContainerWriter packs
  * the encoded track data with a specific container (e.g. ogg, mp4).
  * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and
@@ -61,16 +63,17 @@ public :
                AudioTrackEncoder* aAudioEncoder,
                VideoTrackEncoder* aVideoEncoder,
                const nsAString& aMIMEType)
     : mWriter(aWriter)
     , mAudioEncoder(aAudioEncoder)
     , mVideoEncoder(aVideoEncoder)
     , mStartTime(TimeStamp::Now())
     , mMIMEType(aMIMEType)
+    , mSizeOfBuffer(0)
     , mState(MediaEncoder::ENCODE_METADDATA)
     , mShutdown(false)
   {}
 
   ~MediaEncoder() {};
 
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
@@ -135,26 +138,34 @@ public :
 #ifdef MOZ_WEBM_ENCODER
   static bool IsWebMEncoderEnabled();
 #endif
 
 #ifdef MOZ_OMX_ENCODER
   static bool IsOMXEncoderEnabled();
 #endif
 
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+  /*
+   * Measure the size of the buffer, and memory occupied by mAudioEncoder
+   * and mVideoEncoder
+   */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
   // Get encoded data from trackEncoder and write to muxer
   nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
   // Get metadata from trackEncoder and copy to muxer
   nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder);
   nsAutoPtr<ContainerWriter> mWriter;
   nsAutoPtr<AudioTrackEncoder> mAudioEncoder;
   nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
   TimeStamp mStartTime;
   nsString mMIMEType;
+  int64_t mSizeOfBuffer;
   int mState;
   bool mShutdown;
   // Get duration from create encoder, for logging purpose
   double GetEncodeTimeStamp()
   {
     TimeDuration decodeTime;
     decodeTime = TimeStamp::Now() - mStartTime;
     return decodeTime.ToMilliseconds();
--- a/content/media/encoder/TrackEncoder.cpp
+++ b/content/media/encoder/TrackEncoder.cpp
@@ -168,16 +168,22 @@ AudioTrackEncoder::DeInterleaveTrackData
 {
   for (int32_t i = 0; i < aChannels; ++i) {
     for(int32_t j = 0; j < aDuration; ++j) {
       aOutput[i * aDuration + j] = aInput[i + j * aChannels];
     }
   }
 }
 
+size_t
+AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                             TrackID aID,
                                             TrackRate aTrackRate,
                                             TrackTicks aTrackOffset,
                                             uint32_t aTrackEvents,
                                             const MediaSegment& aQueuedMedia)
 {
@@ -258,9 +264,15 @@ VideoTrackEncoder::NotifyEndOfStream()
          DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, DEFAULT_TRACK_RATE);
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   mEndOfStream = true;
   mReentrantMonitor.NotifyAll();
 }
 
+size_t
+VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
 }
+
+}
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -163,16 +163,20 @@ public:
                                   AudioDataValue* aOutput);
 
   /**
    * De-interleaves the aInput data and stores the result into aOutput.
    * No up-mix or down-mix operations inside.
    */
   static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration,
                                     int32_t aChannels, AudioDataValue* aOutput);
+  /**
+  * Measure size of mRawSegment
+  */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   /**
    * Number of samples per channel in a pcm buffer. This is also the value of
    * frame size required by audio encoder, and mReentrantMonitor will be
    * notified when at least this much data has been added to mRawSegment.
    */
   virtual int GetPacketDuration() { return 0; }
@@ -236,16 +240,20 @@ public:
    * Notified by the same callbcak of MediaEncoder when it has received a track
    * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
+  /**
+  * Measure size of mRawSegment
+  */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   /**
    * Initialized the video encoder. In order to collect the value of width and
    * height of source frames, this initialization is delayed until we have
    * received the first valid video frame from MediaStreamGraph;
    * mReentrantMonitor will be notified after it has successfully initialized,
    * and this method is called on the MediaStramGraph thread.