LCOV - code coverage report
Current view: top level - dom/media/webaudio - AudioBuffer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 193 1.0 %
Date: 2017-07-14 16:53:18 Functions: 0 32 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "AudioBuffer.h"
       8             : #include "mozilla/dom/AudioBufferBinding.h"
       9             : #include "jsfriendapi.h"
      10             : #include "mozilla/ErrorResult.h"
      11             : #include "AudioSegment.h"
      12             : #include "AudioChannelFormat.h"
      13             : #include "mozilla/PodOperations.h"
      14             : #include "mozilla/CheckedInt.h"
      15             : #include "mozilla/MemoryReporting.h"
      16             : #include "AudioNodeEngine.h"
      17             : 
      18             : namespace mozilla {
      19             : namespace dom {
      20             : 
      21             : NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
      22             : 
      23           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
      24           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSChannels)
      25           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
      26           0 :   tmp->ClearJSChannels();
      27           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      28             : 
      29           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
      30           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      31             : 
      32           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
      33           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
      34           0 :   for (uint32_t i = 0; i < tmp->mJSChannels.Length(); ++i) {
      35           0 :     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSChannels[i])
      36             :   }
      37           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
      38             : 
      39           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
      40           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
      41             : 
      42             : /**
      43             :  * AudioBuffers can be shared between AudioContexts, so we need a separate
      44             :  * mechanism to track their memory usage. This thread-safe class keeps track of
      45             :  * all the AudioBuffers, and gets called back by the memory reporting system
      46             :  * when a memory report is needed, reporting how much memory is used by the
      47             :  * buffers backing AudioBuffer objects. */
      48             : class AudioBufferMemoryTracker : public nsIMemoryReporter
      49             : {
      50             :   NS_DECL_THREADSAFE_ISUPPORTS
      51             :   NS_DECL_NSIMEMORYREPORTER
      52             : 
      53             : private:
      54             :   AudioBufferMemoryTracker();
      55             :   virtual ~AudioBufferMemoryTracker();
      56             : 
      57             : public:
      58             :   /* Those methods can be called on any thread. */
      59             :   static void RegisterAudioBuffer(const AudioBuffer* aAudioBuffer);
      60             :   static void UnregisterAudioBuffer(const AudioBuffer* aAudioBuffer);
      61             : private:
      62             :   static AudioBufferMemoryTracker* GetInstance();
      63             :   /* Those methods must be called with the lock held. */
      64             :   void RegisterAudioBufferInternal(const AudioBuffer* aAudioBuffer);
      65             :   /* Returns the number of buffers still present in the hash table. */
      66             :   uint32_t UnregisterAudioBufferInternal(const AudioBuffer* aAudioBuffer);
      67             :   void Init();
      68             : 
      69             :   /* This protects all members of this class. */
      70             :   static StaticMutex sMutex;
      71             :   static StaticRefPtr<AudioBufferMemoryTracker> sSingleton;
      72             :   nsTHashtable<nsPtrHashKey<const AudioBuffer>> mBuffers;
      73             : };
      74             : 
      75           3 : StaticRefPtr<AudioBufferMemoryTracker> AudioBufferMemoryTracker::sSingleton;
      76           3 : StaticMutex AudioBufferMemoryTracker::sMutex;
      77             : 
      78           0 : NS_IMPL_ISUPPORTS(AudioBufferMemoryTracker, nsIMemoryReporter);
      79             : 
      80           0 : AudioBufferMemoryTracker* AudioBufferMemoryTracker::GetInstance()
      81             : {
      82           0 :   sMutex.AssertCurrentThreadOwns();
      83           0 :   if (!sSingleton) {
      84           0 :     sSingleton = new AudioBufferMemoryTracker();
      85           0 :     sSingleton->Init();
      86             :   }
      87           0 :   return sSingleton;
      88             : }
      89             : 
      90           0 : AudioBufferMemoryTracker::AudioBufferMemoryTracker()
      91             : {
      92           0 : }
      93             : 
      94             : void
      95           0 : AudioBufferMemoryTracker::Init()
      96             : {
      97           0 :   RegisterWeakMemoryReporter(this);
      98           0 : }
      99             : 
     100           0 : AudioBufferMemoryTracker::~AudioBufferMemoryTracker()
     101             : {
     102           0 :   UnregisterWeakMemoryReporter(this);
     103           0 : }
     104             : 
     105             : void
     106           0 : AudioBufferMemoryTracker::RegisterAudioBuffer(const AudioBuffer* aAudioBuffer)
     107             : {
     108           0 :   StaticMutexAutoLock lock(sMutex);
     109           0 :   AudioBufferMemoryTracker* tracker = AudioBufferMemoryTracker::GetInstance();
     110           0 :   tracker->RegisterAudioBufferInternal(aAudioBuffer);
     111           0 : }
     112             : 
     113             : void
     114           0 : AudioBufferMemoryTracker::UnregisterAudioBuffer(const AudioBuffer* aAudioBuffer)
     115             : {
     116           0 :   StaticMutexAutoLock lock(sMutex);
     117           0 :   AudioBufferMemoryTracker* tracker = AudioBufferMemoryTracker::GetInstance();
     118             :   uint32_t count;
     119           0 :   count = tracker->UnregisterAudioBufferInternal(aAudioBuffer);
     120           0 :   if (count == 0) {
     121           0 :     sSingleton = nullptr;
     122             :   }
     123           0 : }
     124             : 
     125             : void
     126           0 : AudioBufferMemoryTracker::RegisterAudioBufferInternal(const AudioBuffer* aAudioBuffer)
     127             : {
     128           0 :   sMutex.AssertCurrentThreadOwns();
     129           0 :   mBuffers.PutEntry(aAudioBuffer);
     130           0 : }
     131             : 
     132             : uint32_t
     133           0 : AudioBufferMemoryTracker::UnregisterAudioBufferInternal(const AudioBuffer* aAudioBuffer)
     134             : {
     135           0 :   sMutex.AssertCurrentThreadOwns();
     136           0 :   mBuffers.RemoveEntry(aAudioBuffer);
     137           0 :   return mBuffers.Count();
     138             : }
     139             : 
     140           0 : MOZ_DEFINE_MALLOC_SIZE_OF(AudioBufferMemoryTrackerMallocSizeOf)
     141             : 
     142             : NS_IMETHODIMP
     143           0 : AudioBufferMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
     144             :                                          nsISupports* aData, bool)
     145             : {
     146           0 :   size_t amount = 0;
     147             : 
     148           0 :   for (auto iter = mBuffers.Iter(); !iter.Done(); iter.Next()) {
     149           0 :     amount += iter.Get()->GetKey()->SizeOfIncludingThis(AudioBufferMemoryTrackerMallocSizeOf);
     150             :   }
     151             : 
     152           0 :   MOZ_COLLECT_REPORT(
     153             :     "explicit/webaudio/audiobuffer", KIND_HEAP, UNITS_BYTES, amount,
     154           0 :     "Memory used by AudioBuffer objects (Web Audio).");
     155             : 
     156           0 :   return NS_OK;
     157             : }
     158             : 
     159           0 : AudioBuffer::AudioBuffer(nsPIDOMWindowInner* aWindow,
     160             :                          uint32_t aNumberOfChannels,
     161             :                          uint32_t aLength,
     162             :                          float aSampleRate,
     163             :                          already_AddRefed<ThreadSharedFloatArrayBufferList>
     164           0 :                            aInitialContents)
     165           0 :   : mOwnerWindow(do_GetWeakReference(aWindow)),
     166             :     mSharedChannels(aInitialContents),
     167             :     mLength(aLength),
     168           0 :     mSampleRate(aSampleRate)
     169             : {
     170           0 :   MOZ_ASSERT(!mSharedChannels ||
     171             :              mSharedChannels->GetChannels() == aNumberOfChannels);
     172           0 :   mJSChannels.SetLength(aNumberOfChannels);
     173           0 :   mozilla::HoldJSObjects(this);
     174           0 :   AudioBufferMemoryTracker::RegisterAudioBuffer(this);
     175           0 : }
     176             : 
     177           0 : AudioBuffer::~AudioBuffer()
     178             : {
     179           0 :   AudioBufferMemoryTracker::UnregisterAudioBuffer(this);
     180           0 :   ClearJSChannels();
     181           0 :   mozilla::DropJSObjects(this);
     182           0 : }
     183             : 
     184             : /* static */ already_AddRefed<AudioBuffer>
     185           0 : AudioBuffer::Constructor(const GlobalObject& aGlobal,
     186             :                          const AudioBufferOptions& aOptions,
     187             :                          ErrorResult& aRv)
     188             : {
     189           0 :   if (!aOptions.mNumberOfChannels) {
     190           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     191           0 :     return nullptr;
     192             :   }
     193             : 
     194             :   nsCOMPtr<nsPIDOMWindowInner> window =
     195           0 :     do_QueryInterface(aGlobal.GetAsSupports());
     196             : 
     197           0 :   return Create(window, aOptions.mNumberOfChannels, aOptions.mLength,
     198           0 :                 aOptions.mSampleRate, aRv);
     199             : }
     200             : 
     201             : void
     202           0 : AudioBuffer::ClearJSChannels()
     203             : {
     204           0 :   mJSChannels.Clear();
     205           0 : }
     206             : 
     207             : /* static */ already_AddRefed<AudioBuffer>
     208           0 : AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
     209             :                     uint32_t aLength, float aSampleRate,
     210             :                     already_AddRefed<ThreadSharedFloatArrayBufferList>
     211             :                       aInitialContents,
     212             :                     ErrorResult& aRv)
     213             : {
     214             :   // Note that a buffer with zero channels is permitted here for the sake of
     215             :   // AudioProcessingEvent, where channel counts must match parameters passed
     216             :   // to createScriptProcessor(), one of which may be zero.
     217           0 :   if (aSampleRate < WebAudioUtils::MinSampleRate ||
     218           0 :       aSampleRate > WebAudioUtils::MaxSampleRate ||
     219           0 :       aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
     220           0 :       !aLength || aLength > INT32_MAX) {
     221           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     222           0 :     return nullptr;
     223             :   }
     224             : 
     225             :   RefPtr<AudioBuffer> buffer =
     226             :     new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate,
     227           0 :                     Move(aInitialContents));
     228             : 
     229           0 :   return buffer.forget();
     230             : }
     231             : 
     232             : JSObject*
     233           0 : AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     234             : {
     235           0 :   return AudioBufferBinding::Wrap(aCx, this, aGivenProto);
     236             : }
     237             : 
     238             : bool
     239           0 : AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
     240             : {
     241           0 :   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     242           0 :     if (mJSChannels[i]) {
     243             :       // Already have data in JS array.
     244           0 :       continue;
     245             :     }
     246             : 
     247             :     // The following code first zeroes the array and then copies our data
     248             :     // into it. We could avoid this with additional JS APIs to construct
     249             :     // an array (or ArrayBuffer) containing initial data.
     250             :     JS::Rooted<JSObject*> array(aJSContext,
     251           0 :                                 JS_NewFloat32Array(aJSContext, mLength));
     252           0 :     if (!array) {
     253           0 :       return false;
     254             :     }
     255           0 :     if (mSharedChannels) {
     256             :       // "4. Attach ArrayBuffers containing copies of the data to the
     257             :       // AudioBuffer, to be returned by the next call to getChannelData."
     258           0 :       const float* data = mSharedChannels->GetData(i);
     259           0 :       JS::AutoCheckCannotGC nogc;
     260             :       bool isShared;
     261           0 :       mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc), data, mLength);
     262           0 :       MOZ_ASSERT(!isShared); // Was created as unshared above
     263             :     }
     264           0 :     mJSChannels[i] = array;
     265             :   }
     266             : 
     267           0 :   mSharedChannels = nullptr;
     268             : 
     269           0 :   return true;
     270             : }
     271             : 
     272             : void
     273           0 : AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
     274             :                              uint32_t aStartInChannel, ErrorResult& aRv)
     275             : {
     276           0 :   aDestination.ComputeLengthAndData();
     277             : 
     278           0 :   uint32_t length = aDestination.Length();
     279           0 :   CheckedInt<uint32_t> end = aStartInChannel;
     280           0 :   end += length;
     281           0 :   if (aChannelNumber >= NumberOfChannels() ||
     282           0 :       !end.isValid() || end.value() > mLength) {
     283           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     284           0 :     return;
     285             :   }
     286             : 
     287           0 :   JS::AutoCheckCannotGC nogc;
     288           0 :   JSObject* channelArray = mJSChannels[aChannelNumber];
     289           0 :   const float* sourceData = nullptr;
     290           0 :   if (channelArray) {
     291           0 :     if (JS_GetTypedArrayLength(channelArray) != mLength) {
     292             :       // The array's buffer was detached.
     293           0 :       aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     294           0 :       return;
     295             :     }
     296             : 
     297           0 :     bool isShared = false;
     298           0 :     sourceData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
     299             :     // The sourceData arrays should all have originated in
     300             :     // RestoreJSChannelData, where they are created unshared.
     301           0 :     MOZ_ASSERT(!isShared);
     302           0 :   } else if (mSharedChannels) {
     303           0 :     sourceData = mSharedChannels->GetData(aChannelNumber);
     304             :   }
     305             : 
     306           0 :   if (sourceData) {
     307           0 :     PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
     308             :   } else {
     309           0 :     PodZero(aDestination.Data(), length);
     310             :   }
     311             : }
     312             : 
     313             : void
     314           0 : AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
     315             :                            uint32_t aChannelNumber, uint32_t aStartInChannel,
     316             :                            ErrorResult& aRv)
     317             : {
     318           0 :   aSource.ComputeLengthAndData();
     319             : 
     320           0 :   uint32_t length = aSource.Length();
     321           0 :   CheckedInt<uint32_t> end = aStartInChannel;
     322           0 :   end += length;
     323           0 :   if (aChannelNumber >= NumberOfChannels() ||
     324           0 :       !end.isValid() || end.value() > mLength) {
     325           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     326           0 :     return;
     327             :   }
     328             : 
     329           0 :   if (!RestoreJSChannelData(aJSContext)) {
     330           0 :     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     331           0 :     return;
     332             :   }
     333             : 
     334           0 :   JS::AutoCheckCannotGC nogc;
     335           0 :   JSObject* channelArray = mJSChannels[aChannelNumber];
     336           0 :   if (JS_GetTypedArrayLength(channelArray) != mLength) {
     337             :     // The array's buffer was detached.
     338           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     339           0 :     return;
     340             :   }
     341             : 
     342           0 :   bool isShared = false;
     343           0 :   float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
     344             :   // The channelData arrays should all have originated in
     345             :   // RestoreJSChannelData, where they are created unshared.
     346           0 :   MOZ_ASSERT(!isShared);
     347           0 :   PodMove(channelData + aStartInChannel, aSource.Data(), length);
     348             : }
     349             : 
     350             : void
     351           0 : AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
     352             :                             JS::MutableHandle<JSObject*> aRetval,
     353             :                             ErrorResult& aRv)
     354             : {
     355           0 :   if (aChannel >= NumberOfChannels()) {
     356           0 :     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     357           0 :     return;
     358             :   }
     359             : 
     360           0 :   if (!RestoreJSChannelData(aJSContext)) {
     361           0 :     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     362           0 :     return;
     363             :   }
     364             : 
     365           0 :   aRetval.set(mJSChannels[aChannel]);
     366             : }
     367             : 
     368             : already_AddRefed<ThreadSharedFloatArrayBufferList>
     369           0 : AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
     370             : {
     371             :   // "1. If any of the AudioBuffer's ArrayBuffer have been detached, abort
     372             :   // these steps, and return a zero-length channel data buffers to the
     373             :   // invoker."
     374           0 :   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     375           0 :     JSObject* channelArray = mJSChannels[i];
     376           0 :     if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) {
     377             :       // Either empty buffer or one of the arrays' buffers was detached.
     378           0 :       return nullptr;
     379             :     }
     380             :   }
     381             : 
     382             :   // "2. Detach all ArrayBuffers for arrays previously returned by
     383             :   // getChannelData on this AudioBuffer."
     384             :   // "3. Retain the underlying data buffers from those ArrayBuffers and return
     385             :   // references to them to the invoker."
     386             :   RefPtr<ThreadSharedFloatArrayBufferList> result =
     387           0 :     new ThreadSharedFloatArrayBufferList(mJSChannels.Length());
     388           0 :   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     389           0 :     JS::Rooted<JSObject*> arrayBufferView(aJSContext, mJSChannels[i]);
     390             :     bool isSharedMemory;
     391             :     JS::Rooted<JSObject*> arrayBuffer(aJSContext,
     392           0 :                                       JS_GetArrayBufferViewBuffer(aJSContext,
     393             :                                                                   arrayBufferView,
     394           0 :                                                                   &isSharedMemory));
     395             :     // The channel data arrays should all have originated in
     396             :     // RestoreJSChannelData, where they are created unshared.
     397           0 :     MOZ_ASSERT(!isSharedMemory);
     398             :     auto stolenData =
     399           0 :       arrayBuffer ? static_cast<float*>(
     400           0 :                       JS_StealArrayBufferContents(aJSContext, arrayBuffer))
     401           0 :                   : nullptr;
     402           0 :     if (stolenData) {
     403           0 :       result->SetData(i, stolenData, js_free, stolenData);
     404             :     } else {
     405           0 :       NS_ASSERTION(i == 0, "some channels lost when contents not acquired");
     406           0 :       return nullptr;
     407             :     }
     408             :   }
     409             : 
     410           0 :   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     411           0 :     mJSChannels[i] = nullptr;
     412             :   }
     413             : 
     414           0 :   return result.forget();
     415             : }
     416             : 
     417             : ThreadSharedFloatArrayBufferList*
     418           0 : AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
     419             : {
     420           0 :   if (!mSharedChannels) {
     421           0 :     mSharedChannels = StealJSArrayDataIntoSharedChannels(aJSContext);
     422             :   }
     423             : 
     424           0 :   return mSharedChannels;
     425             : }
     426             : 
     427             : size_t
     428           0 : AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     429             : {
     430           0 :   size_t amount = aMallocSizeOf(this);
     431           0 :   amount += mJSChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
     432           0 :   if (mSharedChannels) {
     433           0 :     amount += mSharedChannels->SizeOfIncludingThis(aMallocSizeOf);
     434             :   }
     435           0 :   return amount;
     436             : }
     437             : 
     438             : } // namespace dom
     439             : } // namespace mozilla

Generated by: LCOV version 1.13