Bug 1692903 - Add gtest. r?padenot draft
authorAndreas Pehrson <apehrson@mozilla.com>
Wed, 17 Feb 2021 12:58:15 +0000
changeset 3545282 51cef2d1cfeebe8415625922c4405fcefb54d368
parent 3542602 00b18dc4bfac9e9d226627f9ccbf2a2f4e3e6a9d
child 3545283 82dba868ed57830e37db29989b10aab1fed8848b
push id656171
push userreviewbot
push dateWed, 17 Feb 2021 12:59:13 +0000
treeherdertry@82dba868ed57 [default view] [failures only]
reviewerspadenot
bugs1692903
milestone87.0a1
Bug 1692903 - Add gtest. r?padenot Differential Revision: https://phabricator.services.mozilla.com/D105301 Differential Diff: PHID-DIFF-il44i5tocepvtkq6tl54
dom/media/MediaTrackGraphImpl.h
dom/media/gtest/TestAudioInputProcessing.cpp
dom/media/gtest/moz.build
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
dom/media/webrtc/MediaEngineWebRTCAudio.h
--- a/dom/media/MediaTrackGraphImpl.h
+++ b/dom/media/MediaTrackGraphImpl.h
@@ -975,19 +975,20 @@ class MediaTrackGraphImpl : public Media
    */
   bool mTrackOrderDirty;
   const RefPtr<AbstractThread> mAbstractMainThread;
 
   // used to limit graph shutdown time
   // Only accessed on the main thread.
   nsCOMPtr<nsITimer> mShutdownTimer;
 
- private:
+ protected:
   virtual ~MediaTrackGraphImpl();
 
+ private:
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * This class uses manual memory management, and all pointers to it are raw
    * pointers. However, in order for it to implement nsIMemoryReporter, it needs
    * to implement nsISupports and so be ref-counted. So it maintains a single
    * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime,
    * and Destroy() nulls this self-reference in order to trigger self-deletion.
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestAudioInputProcessing.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "AudioGenerator.h"
+#include "MediaEngineWebRTCAudio.h"
+#include "MediaTrackGraphImpl.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+using namespace mozilla;
+using testing::NiceMock;
+using testing::Return;
+
+class MockGraph : public MediaTrackGraphImpl {
+ public:
+  MockGraph(TrackRate aRate, uint32_t aChannels)
+      : MediaTrackGraphImpl(OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, aRate,
+                            aChannels, nullptr, AbstractThread::MainThread()) {
+    ON_CALL(*this, OnGraphThread).WillByDefault(Return(true));
+    // Remove this graph's driver since it holds a ref. If no AppendMessage
+    // takes place, the driver never starts. This will also make sure no-one
+    // tries to use it. We are still kept alive by the self-ref. Destroy() must
+    // be called to break that cycle.
+    SetCurrentDriver(nullptr);
+  }
+
+  MOCK_CONST_METHOD0(OnGraphThread, bool());
+
+ protected:
+  ~MockGraph() = default;
+};
+
+TEST(TestAudioInputProcessing, UnaccountedPacketizerBuffering)
+{
+  const TrackRate rate = 48000;
+  const uint32_t channels = 2;
+  auto graph = MakeRefPtr<NiceMock<MockGraph>>(48000, 2);
+  auto aip = MakeRefPtr<AudioInputProcessing>(channels, PRINCIPAL_HANDLE_NONE);
+  AudioGenerator<AudioDataValue> generator(channels, rate);
+
+  // The packetizer takes 480 frames. To trigger this we need to populate the
+  // packetizer without filling it completely the first iteration, then trigger
+  // the unbounded-buffering-assertion on the second iteration.
+
+  const size_t nrFrames = 440;
+  const size_t bufferSize = nrFrames * channels;
+  GraphTime processedTime;
+  GraphTime nextTime;
+  nsTArray<AudioDataValue> buffer(bufferSize);
+  buffer.AppendElements(bufferSize);
+  AudioSegment segment;
+  bool ended;
+
+  aip->Start();
+
+  {
+    // First iteration.
+    // 440 does not fill the packetizer but accounts for pre-silence buffering.
+    // Iterations have processed 72 frames more than provided by callbacks:
+    //     512 - 440 = 72
+    // Thus the total amount of pre-silence buffering added is:
+    //     480 + 128 - 72 = 536
+    // The iteration pulls in 512 frames of silence, leaving 24 frames buffered.
+    processedTime = 0;
+    nextTime = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(nrFrames);
+    generator.GenerateInterleaved(buffer.Elements(), nrFrames);
+    aip->NotifyInputData(graph, buffer.Elements(), nrFrames, rate, channels,
+                         nextTime - nrFrames);
+    aip->Pull(graph, processedTime, nextTime, segment.GetDuration(), &segment,
+              true, &ended);
+    EXPECT_EQ(aip->NumBufferedFrames(graph), 24U);
+  }
+
+  {
+    // Second iteration.
+    // 880 fills a packet of 480 frames. 400 left in the packetizer.
+    // Last iteration left 24 frames buffered, making this iteration have 504
+    // frames in the buffer while pulling 384 frames.
+    // That leaves 120 frames buffered, which must be no more than the total
+    // intended buffering of 480 + 128 = 608 frames.
+    processedTime = nextTime;
+    nextTime = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(2 * nrFrames);
+    generator.GenerateInterleaved(buffer.Elements(), nrFrames);
+    aip->NotifyInputData(graph, buffer.Elements(), nrFrames, rate, channels,
+                         nextTime - (2 * nrFrames));
+    aip->Pull(graph, processedTime, nextTime, segment.GetDuration(), &segment,
+              true, &ended);
+    EXPECT_EQ(aip->NumBufferedFrames(graph), 120U);
+  }
+
+  graph->Destroy();
+}
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -16,16 +16,17 @@ LOCAL_INCLUDES += [
 
 UNIFIED_SOURCES += [
     "MockCubeb.cpp",
     "MockMediaResource.cpp",
     "TestAudioBuffers.cpp",
     "TestAudioCallbackDriver.cpp",
     "TestAudioCompactor.cpp",
     "TestAudioDriftCorrection.cpp",
+    "TestAudioInputProcessing.cpp",
     "TestAudioMixer.cpp",
     "TestAudioPacketizer.cpp",
     "TestAudioRingBuffer.cpp",
     "TestAudioSegment.cpp",
     "TestAudioTrackEncoder.cpp",
     "TestAudioTrackGraph.cpp",
     "TestBenchmarkStorage.cpp",
     "TestBitWriter.cpp",
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -1216,16 +1216,22 @@ void AudioInputProcessing::DeviceChanged
   ResetProcessingIfNeeded(noise_suppression);
 }
 
 void AudioInputProcessing::End() {
   mEnded = true;
   mSegment.Clear();
 }
 
+TrackTime AudioInputProcessing::NumBufferedFrames(
+    MediaTrackGraphImpl* aGraph) const {
+  MOZ_ASSERT(aGraph->OnGraphThread());
+  return mSegment.GetDuration();
+}
+
 void AudioInputTrack::Destroy() {
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<CubebUtils::AudioDeviceID> id = Nothing();
   CloseAudioInput(id);
 
   MediaTrack::Destroy();
 }
 
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.h
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.h
@@ -192,16 +192,18 @@ class AudioInputProcessing : public Audi
                          webrtc::EchoControlMobile::RoutingMode aRoutingMode);
   void UpdateAGCSettings(bool aEnable, webrtc::GainControl::Mode aMode);
   void UpdateHPFSettings(bool aEnable);
   void UpdateNSSettings(bool aEnable, webrtc::NoiseSuppression::Level aLevel);
   void UpdateAPMExtraOptions(bool aExtendedFilter, bool aDelayAgnostic);
 
   void End();
 
+  TrackTime NumBufferedFrames(MediaTrackGraphImpl* aGraph) const;
+
  private:
   ~AudioInputProcessing() = default;
   // This implements the processing algoritm to apply to the input (e.g. a
   // microphone). If all algorithms are disabled, this class in not used. This
   // class only accepts audio chunks of 10ms. It has two inputs and one output:
   // it is fed the speaker data and the microphone data. It outputs processed
   // input data.
   const UniquePtr<webrtc::AudioProcessing> mAudioProcessing;