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 }