tor-browser

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

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