tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestAudioDecoderInputTrack.cpp (17419B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <utility>
      8 
      9 #include "AudioDecoderInputTrack.h"
     10 #include "GraphDriver.h"
     11 #include "MediaInfo.h"
     12 #include "MediaTrackGraphImpl.h"
     13 #include "VideoUtils.h"
     14 #include "gmock/gmock.h"
     15 #include "gtest/gtest.h"
     16 #include "mozilla/gtest/WaitFor.h"
     17 #include "nsThreadUtils.h"
     18 
     19 using namespace mozilla;
     20 using namespace mozilla::media;
     21 using testing::AssertionResult;
     22 using testing::NiceMock;
     23 using testing::Return;
     24 using ControlMessageInterface = MediaTrack::ControlMessageInterface;
     25 
     26 constexpr uint32_t kNoFlags = 0;
     27 constexpr TrackRate kRate = 44100;
     28 constexpr uint32_t kChannels = 2;
     29 
     30 class MockTestGraph : public MediaTrackGraphImpl {
     31 public:
     32  explicit MockTestGraph(TrackRate aRate)
     33      : MediaTrackGraphImpl(0, aRate, nullptr, NS_GetCurrentThread()) {
     34    ON_CALL(*this, OnGraphThread).WillByDefault(Return(true));
     35  }
     36 
     37  void Init(uint32_t aChannels) {
     38    MediaTrackGraphImpl::Init(OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, aChannels);
     39    // We have to call `Destroy()` manually in order to break the reference.
     40    // The reason we don't assign a null driver is because we would add a track
     41    // to the graph, then it would trigger graph's `EnsureNextIteration()` that
     42    // requires a non-null driver.
     43    SetCurrentDriver(new NiceMock<MockDriver>());
     44  }
     45 
     46  MOCK_CONST_METHOD0(OnGraphThread, bool());
     47  MOCK_METHOD1(AppendMessage, void(UniquePtr<ControlMessageInterface>));
     48 
     49 protected:
     50  ~MockTestGraph() = default;
     51 
     52  class MockDriver : public GraphDriver {
     53    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockDriver, override);
     54 
     55    MockDriver() : GraphDriver(nullptr, nullptr, 0) {
     56      ON_CALL(*this, OnThread).WillByDefault(Return(true));
     57      ON_CALL(*this, ThreadRunning).WillByDefault(Return(true));
     58    }
     59 
     60    MOCK_METHOD0(Start, void());
     61    MOCK_METHOD0(Shutdown, void());
     62    MOCK_METHOD0(IterationDuration, TimeDuration());
     63    MOCK_METHOD0(EnsureNextIteration, void());
     64    MOCK_CONST_METHOD0(OnThread, bool());
     65    MOCK_CONST_METHOD0(ThreadRunning, bool());
     66 
     67   protected:
     68    ~MockDriver() = default;
     69  };
     70 };
     71 
     72 AudioData* CreateAudioDataFromInfo(uint32_t aFrames, const AudioInfo& aInfo) {
     73  AlignedAudioBuffer samples(aFrames * aInfo.mChannels);
     74  return new AudioData(0, TimeUnit::Zero(), std::move(samples), aInfo.mChannels,
     75                       aInfo.mRate);
     76 }
     77 
     78 AudioDecoderInputTrack* CreateTrack(MediaTrackGraph* aGraph,
     79                                    nsISerialEventTarget* aThread,
     80                                    const AudioInfo& aInfo,
     81                                    float aPlaybackRate = 1.0,
     82                                    float aVolume = 1.0,
     83                                    bool aPreservesPitch = true) {
     84  return AudioDecoderInputTrack::Create(aGraph, aThread, aInfo, aPlaybackRate,
     85                                        aVolume, aPreservesPitch);
     86 }
     87 
     88 class TestAudioDecoderInputTrack : public testing::Test {
     89 protected:
     90  void SetUp() override {
     91    mGraph = MakeRefPtr<NiceMock<MockTestGraph>>(kRate);
     92    mGraph->Init(kChannels);
     93 
     94    mInfo.mRate = kRate;
     95    mInfo.mChannels = kChannels;
     96    mTrack = CreateTrack(mGraph, NS_GetCurrentThread(), mInfo);
     97    EXPECT_FALSE(mTrack->Ended());
     98  }
     99 
    100  void TearDown() override {
    101    // This simulates the normal usage where the `Close()` is always be called
    102    // before the `Destroy()`.
    103    mTrack->Close();
    104    mTrack->Destroy();
    105    // Remove the reference of the track from the mock graph, and then release
    106    // the self-reference of mock graph.
    107    mGraph->RemoveTrackGraphThread(mTrack);
    108    mGraph->Destroy();
    109  }
    110 
    111  AudioData* CreateAudioData(uint32_t aFrames) {
    112    return CreateAudioDataFromInfo(aFrames, mInfo);
    113  }
    114 
    115  AudioSegment* GetTrackSegment() { return mTrack->GetData<AudioSegment>(); }
    116 
    117  AssertionResult ExpectSegmentNonSilence(const char* aStartExpr,
    118                                          const char* aEndExpr,
    119                                          TrackTime aStart, TrackTime aEnd) {
    120    AudioSegment checkedRange;
    121    checkedRange.AppendSlice(*mTrack->GetData(), aStart, aEnd);
    122    if (!checkedRange.IsNull()) {
    123      return testing::AssertionSuccess();
    124    }
    125    return testing::AssertionFailure()
    126           << "segment [" << aStart << ":" << aEnd << "] should be non-silence";
    127  }
    128 
    129  AssertionResult ExpectSegmentSilence(const char* aStartExpr,
    130                                       const char* aEndExpr, TrackTime aStart,
    131                                       TrackTime aEnd) {
    132    AudioSegment checkedRange;
    133    checkedRange.AppendSlice(*mTrack->GetData(), aStart, aEnd);
    134    if (checkedRange.IsNull()) {
    135      return testing::AssertionSuccess();
    136    }
    137    return testing::AssertionFailure()
    138           << "segment [" << aStart << ":" << aEnd << "] should be silence";
    139  }
    140 
    141  RefPtr<MockTestGraph> mGraph;
    142  RefPtr<AudioDecoderInputTrack> mTrack;
    143  AudioInfo mInfo;
    144 };
    145 
    146 TEST_F(TestAudioDecoderInputTrack, BasicAppendData) {
    147  // Start from [0:10] and each time we move the time by 10ms.
    148  // Expected: outputDuration=10, outputFrames=0, outputSilence=10
    149  TrackTime start = 0;
    150  TrackTime end = 10;
    151  mTrack->ProcessInput(start, end, kNoFlags);
    152  EXPECT_EQ(mTrack->GetEnd(), end);
    153  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    154 
    155  // Expected: outputDuration=20, outputFrames=5, outputSilence=15
    156  RefPtr<AudioData> audio1 = CreateAudioData(5);
    157  mTrack->AppendData(audio1, nullptr);
    158  start = end;
    159  end += 10;
    160  mTrack->ProcessInput(start, end, kNoFlags);
    161  EXPECT_EQ(mTrack->GetEnd(), end);
    162  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, start + audio1->Frames());
    163  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start + audio1->Frames(), end);
    164 
    165  // Expected: outputDuration=30, outputFrames=15, outputSilence=15
    166  RefPtr<AudioData> audio2 = CreateAudioData(10);
    167  mTrack->AppendData(audio2, nullptr);
    168  start = end;
    169  end += 10;
    170  mTrack->ProcessInput(start, end, kNoFlags);
    171  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    172  EXPECT_EQ(mTrack->GetEnd(), end);
    173 
    174  // Expected : sent all data, track should be ended in the next iteration and
    175  // fill slience in this iteration.
    176  mTrack->NotifyEndOfStream();
    177  start = end;
    178  end += 10;
    179  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    180  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    181  EXPECT_EQ(mTrack->GetEnd(), end);
    182  EXPECT_FALSE(mTrack->Ended());
    183 
    184  // Expected : track ended
    185  start = end;
    186  end += 10;
    187  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    188  EXPECT_EQ(mTrack->WrittenFrames(), audio1->Frames() + audio2->Frames());
    189 }
    190 
    191 TEST_F(TestAudioDecoderInputTrack, ClearFuture) {
    192  // Start from [0:10] and each time we move the time by 10ms.
    193  // Expected: appended=30, expected duration=10
    194  RefPtr<AudioData> audio1 = CreateAudioData(30);
    195  mTrack->AppendData(audio1, nullptr);
    196  TrackTime start = 0;
    197  TrackTime end = 10;
    198  mTrack->ProcessInput(start, end, kNoFlags);
    199  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    200 
    201  // In next iteration [10:20], we would consume the remaining data that was
    202  // appended in the previous iteration.
    203  start = end;
    204  end += 10;
    205  mTrack->ProcessInput(start, end, kNoFlags);
    206  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    207 
    208  // Clear future data which is the remaining 10 frames so the track would
    209  // only output silence.
    210  mTrack->ClearFutureData();
    211  start = end;
    212  end += 10;
    213  mTrack->ProcessInput(start, end, kNoFlags);
    214  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    215 
    216  // Test appending data again, to see if we can append data correctly after
    217  // calling `ClearFutureData()`.
    218  RefPtr<AudioData> audio2 = CreateAudioData(10);
    219  mTrack->AppendData(audio2, nullptr);
    220  start = end;
    221  end += 10;
    222  mTrack->ProcessInput(start, end, kNoFlags);
    223  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    224 
    225  // Run another iteration that should only contains silence because the data
    226  // we appended only enough for one iteration.
    227  start = end;
    228  end += 10;
    229  mTrack->ProcessInput(start, end, kNoFlags);
    230  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    231 
    232  // Clear future data would also remove the EOS.
    233  mTrack->NotifyEndOfStream();
    234  mTrack->ClearFutureData();
    235  start = end;
    236  end += 10;
    237  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    238  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    239  EXPECT_FALSE(mTrack->Ended());
    240 
    241  // As EOS has been removed, in next iteration the track would still be
    242  // running.
    243  start = end;
    244  end += 10;
    245  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    246  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    247  EXPECT_FALSE(mTrack->Ended());
    248  EXPECT_EQ(mTrack->WrittenFrames(),
    249            (audio1->Frames() - 10 /* got clear */) + audio2->Frames());
    250 }
    251 
    252 TEST_F(TestAudioDecoderInputTrack, InputRateChange) {
    253  // Start from [0:10] and each time we move the time by 10ms.
    254  // Expected: appended=10, expected duration=10
    255  RefPtr<AudioData> audio1 = CreateAudioData(10);
    256  mTrack->AppendData(audio1, nullptr);
    257  TrackTime start = 0;
    258  TrackTime end = 10;
    259  mTrack->ProcessInput(start, end, kNoFlags);
    260  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    261 
    262  // Change input sample rate to the half, input data should be resampled and
    263  // its duration would become longer.
    264  // Expected: appended=10 + 5,
    265  //           expected duration=10 + 5*2 (resampled)
    266  mInfo.mRate = kRate / 2;
    267  RefPtr<AudioData> audioHalfSampleRate = CreateAudioData(5);
    268  mTrack->AppendData(audioHalfSampleRate, nullptr);
    269  start = end;
    270  end += 10;
    271  mTrack->ProcessInput(start, end, kNoFlags);
    272  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    273 
    274  // Change input sample rate to the double, input data should be resampled and
    275  // its duration would become shorter.
    276  // Expected: appended=10 + 10 + 10,
    277  //           expected duration=10 + 10 + 10/2(resampled) + 5(silence)
    278  mInfo.mRate = kRate * 2;
    279  RefPtr<AudioData> audioDoubleSampleRate = CreateAudioData(10);
    280  TrackTime expectedDuration = audioDoubleSampleRate->Frames() / 2;
    281  mTrack->AppendData(audioDoubleSampleRate, nullptr);
    282  start = end;
    283  end += 10;
    284  mTrack->ProcessInput(start, end, kNoFlags);
    285  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, start + expectedDuration);
    286  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start + expectedDuration, end);
    287  EXPECT_EQ(mTrack->WrittenFrames(), audio1->Frames() +
    288                                         audioHalfSampleRate->Frames() * 2 +
    289                                         audioDoubleSampleRate->Frames() / 2);
    290 }
    291 
    292 TEST_F(TestAudioDecoderInputTrack, ChannelChange) {
    293  // Start from [0:10] and each time we move the time by 10ms.
    294  // Track was initialized in stero.
    295  EXPECT_EQ(mTrack->NumberOfChannels(), uint32_t(2));
    296 
    297  // But first audio data is mono, so the `NumberOfChannels()` changes to
    298  // reflect the maximum channel in the audio segment.
    299  mInfo.mChannels = 1;
    300  RefPtr<AudioData> audioMono = CreateAudioData(10);
    301  mTrack->AppendData(audioMono, nullptr);
    302  TrackTime start = 0;
    303  TrackTime end = 10;
    304  mTrack->ProcessInput(start, end, kNoFlags);
    305  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    306  EXPECT_EQ(mTrack->NumberOfChannels(), audioMono->mChannels);
    307 
    308  // Then append audio data with 5 channels.
    309  mInfo.mChannels = 5;
    310  RefPtr<AudioData> audioWithFiveChannels = CreateAudioData(10);
    311  mTrack->AppendData(audioWithFiveChannels, nullptr);
    312  start = end;
    313  end += 10;
    314  mTrack->ProcessInput(start, end, kNoFlags);
    315  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    316  EXPECT_EQ(mTrack->NumberOfChannels(), audioWithFiveChannels->mChannels);
    317  EXPECT_EQ(mTrack->WrittenFrames(),
    318            audioMono->Frames() + audioWithFiveChannels->Frames());
    319 }
    320 
    321 TEST_F(TestAudioDecoderInputTrack, VolumeChange) {
    322  // In order to run the volume change directly without using a real graph.
    323  // one for setting the track's volume, another for the track destruction.
    324  EXPECT_CALL(*mGraph, AppendMessage)
    325      .Times(2)
    326      .WillOnce(
    327          [](UniquePtr<ControlMessageInterface> aMessage) { aMessage->Run(); })
    328      .WillOnce([](UniquePtr<ControlMessageInterface> aMessage) {});
    329 
    330  // The default volume is 1.0.
    331  float expectedVolume = 1.0;
    332  RefPtr<AudioData> audio = CreateAudioData(20);
    333  TrackTime start = 0;
    334  TrackTime end = 10;
    335  mTrack->AppendData(audio, nullptr);
    336  mTrack->ProcessInput(start, end, kNoFlags);
    337  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    338  EXPECT_TRUE(GetTrackSegment()->GetLastChunk()->mVolume == expectedVolume);
    339 
    340  // After setting volume on the track, the data in the output chunk should be
    341  // changed as well.
    342  expectedVolume = 0.1;
    343  mTrack->SetVolume(expectedVolume);
    344  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    345      "TEST_F(TestAudioDecoderInputTrack, VolumeChange)"_ns,
    346      [&] { return mTrack->Volume() == expectedVolume; });
    347  start = end;
    348  end += 10;
    349  mTrack->ProcessInput(start, end, kNoFlags);
    350  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    351  EXPECT_TRUE(GetTrackSegment()->GetLastChunk()->mVolume == expectedVolume);
    352 }
    353 
    354 TEST_F(TestAudioDecoderInputTrack, BatchedData) {
    355  uint32_t appendedFrames = 0;
    356  RefPtr<AudioData> audio = CreateAudioData(10);
    357  for (size_t idx = 0; idx < 50; idx++) {
    358    mTrack->AppendData(audio, nullptr);
    359    appendedFrames += audio->Frames();
    360  }
    361 
    362  // First we need to call `ProcessInput` at least once to drain the track's
    363  // SPSC queue, otherwise we're not able to push the batched data later.
    364  TrackTime start = 0;
    365  TrackTime end = 10;
    366  uint32_t expectedFrames = end - start;
    367  mTrack->ProcessInput(start, end, kNoFlags);
    368  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    369 
    370  // The batched data would be pushed to the graph thread in around 10ms after
    371  // the track first time started to batch data, which we can't control here.
    372  // Therefore, we need to wait until the batched data gets cleared.
    373  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    374      "TEST_F(TestAudioDecoderInputTrack, BatchedData)"_ns,
    375      [&] { return !mTrack->HasBatchedData(); });
    376 
    377  // Check that we received all the remainging data previously appended.
    378  start = end;
    379  end = start + (appendedFrames - expectedFrames);
    380  mTrack->ProcessInput(start, end, kNoFlags);
    381  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, end);
    382 
    383  // Check that we received no more data than previously appended.
    384  start = end;
    385  end += 10;
    386  mTrack->ProcessInput(start, end, kNoFlags);
    387  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start, end);
    388  EXPECT_EQ(mTrack->WrittenFrames(), appendedFrames);
    389 }
    390 
    391 TEST_F(TestAudioDecoderInputTrack, OutputAndEndEvent) {
    392  // Append an audio and EOS, the output event should notify the amount of
    393  // frames that is equal to the amount of audio we appended.
    394  RefPtr<AudioData> audio = CreateAudioData(10);
    395  auto outputPromise = TakeN(mTrack->OnOutput(), 1);
    396  mTrack->AppendData(audio, nullptr);
    397  mTrack->NotifyEndOfStream();
    398  TrackTime start = 0;
    399  TrackTime end = 10;
    400  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    401  auto output = WaitFor(outputPromise).unwrap()[0];
    402  EXPECT_EQ(std::get<int64_t>(output), audio->Frames());
    403 
    404  // Track should end in this iteration, so the end event should be notified.
    405  auto endPromise = TakeN(mTrack->OnEnd(), 1);
    406  start = end;
    407  end += 10;
    408  mTrack->ProcessInput(start, end, ProcessedMediaTrack::ALLOW_END);
    409  (void)WaitFor(endPromise);
    410 }
    411 
    412 TEST_F(TestAudioDecoderInputTrack, PlaybackRateChange) {
    413  // In order to run the playback change directly without using a real graph.
    414  // one for setting the track's playback, another for the track destruction.
    415  EXPECT_CALL(*mGraph, AppendMessage)
    416      .Times(2)
    417      .WillOnce(
    418          [](UniquePtr<ControlMessageInterface> aMessage) { aMessage->Run(); })
    419      .WillOnce([](UniquePtr<ControlMessageInterface> aMessage) {});
    420 
    421  // Changing the playback rate.
    422  float expectedPlaybackRate = 2.0;
    423  mTrack->SetPlaybackRate(expectedPlaybackRate);
    424  SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
    425      "TEST_F(TestAudioDecoderInputTrack, PlaybackRateChange)"_ns,
    426      [&] { return mTrack->PlaybackRate() == expectedPlaybackRate; });
    427 
    428  // Time stretcher in the track would usually need certain amount of data
    429  // before it outputs the time-stretched result. As we're in testing, we would
    430  // only append data once, so signal an EOS after appending data, in order to
    431  // ask the track to flush all samples from the time strecther.
    432  RefPtr<AudioData> audio = CreateAudioData(100);
    433  mTrack->AppendData(audio, nullptr);
    434  mTrack->NotifyEndOfStream();
    435 
    436  // Playback rate is 2x, so we should only get 1/2x sample frames, another 1/2
    437  // should be silence. Output should not be rate-aware.
    438  auto outputPromise = TakeN(mTrack->OnOutput(), 1);
    439  TrackTime start = 0;
    440  TrackTime end = audio->Frames();
    441  mTrack->ProcessInput(start, end, kNoFlags);
    442  auto output = WaitFor(outputPromise).unwrap()[0];
    443  EXPECT_EQ(std::get<int64_t>(output), audio->Frames());
    444  EXPECT_PRED_FORMAT2(ExpectSegmentNonSilence, start, audio->Frames() / 2);
    445  EXPECT_PRED_FORMAT2(ExpectSegmentSilence, start + audio->Frames() / 2, end);
    446 }