TestDecodedStream.cpp (7184B)
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 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #include "BlankDecoderModule.h" 8 #include "DecodedStream.h" 9 #include "MediaData.h" 10 #include "MediaQueue.h" 11 #include "MediaTrackGraphImpl.h" 12 #include "MediaTrackListener.h" 13 #include "MockCubeb.h" 14 #include "gtest/gtest.h" 15 #include "mozilla/gtest/WaitFor.h" 16 #include "nsJSEnvironment.h" 17 18 using mozilla::media::TimeUnit; 19 using testing::Test; 20 21 namespace mozilla { 22 // Short-hand for DispatchToCurrentThread with a function. 23 #define DispatchFunction(f) \ 24 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f)) 25 26 enum MediaType { Audio = 1, Video = 2, AudioVideo = Audio | Video }; 27 28 template <MediaType Type> 29 CopyableTArray<RefPtr<ProcessedMediaTrack>> CreateOutputTracks( 30 MediaTrackGraphImpl* aGraph) { 31 CopyableTArray<RefPtr<ProcessedMediaTrack>> outputTracks; 32 if constexpr (Type & Audio) { 33 outputTracks.AppendElement( 34 aGraph->CreateForwardedInputTrack(MediaSegment::AUDIO)); 35 } 36 if constexpr (Type & Video) { 37 outputTracks.AppendElement( 38 aGraph->CreateForwardedInputTrack(MediaSegment::VIDEO)); 39 } 40 return outputTracks; 41 } 42 43 template <MediaType Type> 44 MediaInfo CreateMediaInfo() { 45 MediaInfo info; 46 info.mStartTime = TimeUnit::Zero(); 47 if constexpr (Type & Audio) { 48 info.EnableAudio(); 49 } 50 if constexpr (Type & Video) { 51 info.EnableVideo(); 52 } 53 return info; 54 } 55 56 class OnFallbackListener : public MediaTrackListener { 57 const RefPtr<MediaTrack> mTrack; 58 Atomic<bool> mOnFallback{true}; 59 60 public: 61 explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {} 62 63 void Reset() { mOnFallback = true; } 64 bool OnFallback() { return mOnFallback; } 65 66 void NotifyOutput(MediaTrackGraph*, TrackTime) override { 67 if (auto* ad = 68 mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) { 69 mOnFallback = ad->OnFallback(); 70 } 71 } 72 }; 73 74 template <MediaType Type> 75 class TestDecodedStream : public Test { 76 public: 77 static constexpr TrackRate kRate = 48000; 78 static constexpr uint32_t kChannels = 2; 79 const RefPtr<MockCubeb> mMockCubeb; 80 RefPtr<SmartMockCubebStream> mMockCubebStream; 81 MediaQueue<AudioData> mAudioQueue; 82 MediaQueue<VideoData> mVideoQueue; 83 RefPtr<MediaTrackGraphImpl> mGraph; 84 nsMainThreadPtrHandle<SharedDummyTrack> mDummyTrack; 85 CopyableTArray<RefPtr<ProcessedMediaTrack>> mOutputTracks; 86 Canonical<PrincipalHandle> mCanonicalOutputPrincipal; 87 RefPtr<DecodedStream> mDecodedStream; 88 89 TestDecodedStream() 90 : mMockCubeb(MakeRefPtr<MockCubeb>(MockCubeb::RunningMode::Manual)), 91 mGraph(MediaTrackGraphImpl::GetInstance( 92 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, kRate, 93 nullptr, GetMainThreadSerialEventTarget())), 94 mDummyTrack(new nsMainThreadPtrHolder<SharedDummyTrack>( 95 __func__, new SharedDummyTrack( 96 mGraph->CreateSourceTrack(MediaSegment::AUDIO)))), 97 mOutputTracks(CreateOutputTracks<Type>(mGraph)), 98 mCanonicalOutputPrincipal( 99 AbstractThread::GetCurrent(), PRINCIPAL_HANDLE_NONE, 100 "TestDecodedStream::mCanonicalOutputPrincipal"), 101 mDecodedStream(MakeRefPtr<DecodedStream>( 102 AbstractThread::GetCurrent(), mDummyTrack, mOutputTracks, 103 &mCanonicalOutputPrincipal, /* aVolume = */ 1.0, 104 /* aPlaybackRate = */ 1.0, 105 /* aPreservesPitch = */ true, mAudioQueue, mVideoQueue)) { 106 MOZ_ASSERT(NS_IsMainThread()); 107 }; 108 109 void SetUp() override { 110 MOZ_ASSERT(NS_IsMainThread()); 111 CubebUtils::ForceSetCubebContext(mMockCubeb->AsCubebContext()); 112 113 for (const auto& track : mOutputTracks) { 114 track->QueueSetAutoend(false); 115 } 116 117 // Resume the dummy track because a suspended audio track will not use an 118 // AudioCallbackDriver. 119 mDummyTrack->mTrack->Resume(); 120 121 RefPtr fallbackListener = new OnFallbackListener(mDummyTrack->mTrack); 122 mDummyTrack->mTrack->AddListener(fallbackListener); 123 124 mMockCubebStream = WaitFor(mMockCubeb->StreamInitEvent()); 125 while (mMockCubebStream->State().isNothing()) { 126 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 127 } 128 ASSERT_EQ(*mMockCubebStream->State(), CUBEB_STATE_STARTED); 129 // Wait for the AudioCallbackDriver to come into effect. 130 while (fallbackListener->OnFallback()) { 131 ASSERT_EQ(mMockCubebStream->ManualDataCallback(1), 132 MockCubebStream::KeepProcessing::Yes); 133 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 134 } 135 } 136 137 void TearDown() override { 138 MOZ_ASSERT(NS_IsMainThread()); 139 // Destroy all tracks so they're removed from the graph. 140 mDecodedStream->Shutdown(); 141 for (const auto& t : mOutputTracks) { 142 t->Destroy(); 143 } 144 mDummyTrack = nullptr; 145 // DecodedStream also has a ref to the dummy track. 146 mDecodedStream = nullptr; 147 148 // Wait for the graph to shutdown. If all tracks are indeed removed, it will 149 // not switch to another driver. 150 MockCubebStream::KeepProcessing keepProcessing{}; 151 while ((keepProcessing = mMockCubebStream->ManualDataCallback(0)) == 152 MockCubebStream::KeepProcessing::Yes) { 153 NS_ProcessPendingEvents(nullptr); 154 } 155 ASSERT_EQ(keepProcessing, MockCubebStream::KeepProcessing::No); 156 157 // Process the final track removal and run the stable state runnable. 158 NS_ProcessPendingEvents(nullptr); 159 // Process the shutdown runnable. 160 NS_ProcessPendingEvents(nullptr); 161 162 // Graph should be shut down. 163 ASSERT_TRUE(mGraph->OnGraphThreadOrNotRunning()) 164 << "Not on graph thread so graph must still be running!"; 165 ASSERT_EQ(mGraph->LifecycleStateRef(), 166 MediaTrackGraphImpl::LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) 167 << "The graph should be in its final state. Note it does not advance " 168 "the state any further on thread shutdown."; 169 CubebUtils::ForceSetCubebContext(nullptr); 170 171 // mGraph should be the last or second last reference to the graph. The last 172 // reference may be the JS-based shutdown blocker, which will eventually be 173 // destroyed by CC and GC. 174 MediaTrackGraphImpl* graph{}; 175 mGraph.forget(&graph); 176 int32_t refcnt = static_cast<int32_t>(graph->Release()); 177 EXPECT_LE(refcnt, 1); 178 179 // Attempt to release the last reference to the graph, to avoid its lifetime 180 // reaching into future tests. 181 nsJSContext::CycleCollectNow(CCReason::API); 182 nsJSContext::GarbageCollectNow(JS::GCReason::API); 183 NS_ProcessPendingEvents(nullptr); 184 } 185 186 MediaInfo CreateMediaInfo() { return mozilla::CreateMediaInfo<Type>(); } 187 }; 188 189 using TestDecodedStreamA = TestDecodedStream<Audio>; 190 using TestDecodedStreamV = TestDecodedStream<Video>; 191 using TestDecodedStreamAV = TestDecodedStream<AudioVideo>; 192 193 TEST_F(TestDecodedStreamAV, StartStop) { 194 mDecodedStream->Start(TimeUnit::Zero(), CreateMediaInfo()); 195 mDecodedStream->SetPlaying(true); 196 mDecodedStream->Stop(); 197 } 198 } // namespace mozilla