tor-browser

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

TestMediaFormatReader.cpp (9054B)


      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 "ImageContainer.h"
      8 #include "MediaFormatReader.h"
      9 #include "MockDecoderModule.h"
     10 #include "MockMediaDataDemuxer.h"
     11 #include "MockMediaDecoderOwner.h"
     12 #include "PDMFactory.h"
     13 #include "ReaderProxy.h"
     14 #include "TimeUnits.h"
     15 #include "VideoFrameContainer.h"
     16 #include "gtest/gtest.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/gtest/MozAssertions.h"
     19 #include "mozilla/gtest/WaitFor.h"
     20 #include "nsQueryObject.h"
     21 
     22 using namespace mozilla;
     23 using namespace mozilla::layers;
     24 
     25 using DecodePromise = MediaDataDecoder::DecodePromise;
     26 using SamplesHolder = MediaTrackDemuxer::SamplesHolder;
     27 using SamplesPromise = MediaTrackDemuxer::SamplesPromise;
     28 using SeekPromise = MediaTrackDemuxer::SeekPromise;
     29 using TrackType = TrackInfo::TrackType;
     30 using media::TimeIntervals;
     31 using media::TimeUnit;
     32 using testing::InSequence;
     33 using testing::MockFunction;
     34 using testing::Return;
     35 using testing::StrEq;
     36 
     37 TEST(TestMediaFormatReader, WaitingForDemuxAfterInternalSeek)
     38 {
     39  RefPtr<MediaFormatReader> reader;
     40  // Thread scheduling provides ordering for thread initializations before
     41  // their first read.
     42  RefPtr<TaskQueue> demuxerThread;
     43  RefPtr<TaskQueue> decoderThread;
     44 
     45  // Wait enough for the MediaFormatReader to process at least
     46  // aCount demuxer or decoder operations, if pending.
     47  auto WaitForReaderOperations = [&](int aCount) {
     48    // AwaitIdle() ensures that no tasks are pending and any task for another
     49    // thread is already in the other thread's queue, only if dispatch across
     50    // threads is not via tail dispatch.  Tail dispatch is not used because
     51    // the demuxer and decoder threads do not support tail dispatch, even
     52    // though the MediaFormatReader task queue supports tail dispatch.
     53    // https://searchfox.org/mozilla-central/rev/126697140e711e04a9d95edae537541c3bde89cc/xpcom/threads/AbstractThread.cpp#285-289
     54    MOZ_ASSERT(!demuxerThread->SupportsTailDispatch());
     55    MOZ_ASSERT(!decoderThread->SupportsTailDispatch());
     56    // Check that the reader thread has dispatched the first request to
     57    // the demuxer or decoder thread.
     58    reader->OwnerThread()->AwaitIdle();
     59    for (int i = 0; i < aCount; ++i) {
     60      demuxerThread->AwaitIdle();
     61      decoderThread->AwaitIdle();
     62      reader->OwnerThread()->AwaitIdle();
     63    }
     64  };
     65 
     66  RefPtr dataDemuxer = new MockMediaDataDemuxer();
     67  RefPtr trackDemuxer =
     68      // VideoInfo::IsValid() needs dimensions.
     69      new MockMediaTrackDemuxer("video/x-test; width=640; height=360");
     70 
     71  ON_CALL(*dataDemuxer, GetNumberTracks(TrackType::kVideoTrack))
     72      .WillByDefault(Return(1));
     73 
     74  ON_CALL(*dataDemuxer, GetTrackDemuxer)
     75      .WillByDefault([&](TrackType aType, uint32_t aTrackNumber) {
     76        EXPECT_EQ(aTrackNumber, 0u);
     77        EXPECT_EQ(aType, TrackType::kVideoTrack);
     78        if (!demuxerThread) {
     79          demuxerThread = do_QueryObject(AbstractThread::GetCurrent());
     80        }
     81        return do_AddRef(trackDemuxer);
     82      });
     83 
     84  RefPtr pdm = new MockDecoderModule();
     85  PDMFactory::AutoForcePDM autoForcePDM(pdm);
     86  RefPtr<MockVideoDataDecoder> decoder;
     87  MozPromiseHolder<DecodePromise> drainPromise;
     88  EXPECT_CALL(*pdm, CreateVideoDecoder)
     89      .WillOnce([&](const CreateDecoderParams& aParams) {
     90        decoder = new MockVideoDataDecoder(aParams);
     91        InSequence s;
     92        // The first drain requires two calls: one to fetch the frames...
     93        EXPECT_CALL(*decoder, Drain).WillOnce([&] {
     94          MOZ_ASSERT(!decoderThread);
     95          decoderThread = do_QueryObject(AbstractThread::GetCurrent());
     96          return decoder->DummyMediaDataDecoder::Drain();
     97        });
     98        // ... and a second to confirm that no more frames are remaining.
     99        EXPECT_CALL(*decoder, Drain).Times(1);
    100        // Delay responding to the second drain request until testing is done.
    101        EXPECT_CALL(*decoder, Drain).WillOnce([&] {
    102          return drainPromise.Ensure(__func__);
    103        });
    104        decoder->SetLatencyFrameCount(8);
    105        return do_AddRef(decoder);
    106      });
    107 
    108  MockFunction<void(const char* name)> checkpoint;
    109  {
    110    InSequence s;
    111 
    112    EXPECT_CALL(*trackDemuxer, MockGetSamples).Times(2).WillRepeatedly([]() {
    113      static int count = 0;
    114      RefPtr sample = new MediaRawData;
    115      sample->mTime = TimeUnit(count, 30);
    116      ++count;
    117      RefPtr<SamplesHolder> samples = new SamplesHolder;
    118      samples->AppendSample(std::move(sample));
    119      return SamplesPromise::CreateAndResolve(samples, __func__);
    120    });
    121    EXPECT_CALL(*trackDemuxer, MockGetSamples).WillOnce([]() {
    122      return SamplesPromise::CreateAndReject(
    123          NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
    124    });
    125    EXPECT_CALL(*trackDemuxer, Seek).WillOnce([&](const TimeUnit& aTime) {
    126      // Reset mWaitingForDataStartTime so that OnDemuxFailed() calls
    127      // RequestDrain().
    128      EXPECT_NS_SUCCEEDED(reader->OwnerThread()->Dispatch(
    129          NewRunnableMethod("NotifyDataArrived", reader.get(),
    130                            &MediaFormatReader::NotifyDataArrived)));
    131      return SeekPromise::CreateAndResolve(TimeUnit::Zero(), __func__);
    132    });
    133    EXPECT_CALL(*trackDemuxer, MockGetSamples).WillOnce([]() {
    134      RefPtr sample = new MediaRawData;
    135      // Time is zero after the seek.
    136      sample->mTime = TimeUnit(0, 30);
    137      RefPtr<SamplesHolder> samples = new SamplesHolder;
    138      samples->AppendSample(std::move(sample));
    139      return SamplesPromise::CreateAndResolve(samples, __func__);
    140    });
    141    EXPECT_CALL(*trackDemuxer, MockGetSamples).WillOnce([]() {
    142      return SamplesPromise::CreateAndReject(
    143          NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
    144    });
    145    EXPECT_CALL(checkpoint, Call(StrEq("Internal seek waiting for data")));
    146 
    147    EXPECT_CALL(*trackDemuxer, MockGetSamples).WillRepeatedly([]() {
    148      return SamplesPromise::CreateAndReject(
    149          NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
    150    });
    151  }
    152 
    153  auto owner = std::make_unique<MockMediaDecoderOwner>();
    154  RefPtr container = new VideoFrameContainer(
    155      owner.get(),
    156      MakeAndAddRef<ImageContainer>(ImageUsageType::VideoFrameContainer,
    157 #ifdef MOZ_WIDGET_ANDROID
    158                                    // Work around bug 1922144
    159                                    ImageContainer::SYNCHRONOUS
    160 #else
    161                                    ImageContainer::ASYNCHRONOUS
    162 #endif
    163                                    ));
    164  MediaFormatReaderInit init;
    165  init.mVideoFrameContainer = container;
    166  reader = new MediaFormatReader(init, dataDemuxer);
    167  RefPtr proxy = new ReaderProxy(AbstractThread::MainThread(), reader);
    168  EXPECT_NS_SUCCEEDED(reader->Init());
    169 
    170  // ReadMetadata() to init demuxer.
    171  (void)WaitForResolve(proxy->ReadMetadata());
    172  // Two samples are provided by the demuxer, but the third demux request is
    173  // rejected.  The first drain provides two decoded samples.
    174  for (int i = 0; i < 2; ++i) {
    175    (void)WaitForResolve(proxy->RequestVideoData(TimeUnit(), false));
    176  }
    177  // A third sample is not available.
    178  MediaResult result =
    179      WaitForReject(proxy->RequestVideoData(TimeUnit(), false));
    180  EXPECT_EQ(result.Code(), NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA);
    181  // The first drain is complete.  Wait for the internal seek to begin
    182  // re-priming the decoder, for NotifyDataArrived to be processed by the
    183  // demuxer, for a successful demux, for a decode, and for a failed demux.
    184  // Demux failure triggers a drain.  This drain is not beneficial or
    185  // necessary because no samples are available for the current playback
    186  // position, but MediaFormatReader repeats the drain process because of the
    187  // NotifyDataArrived triggered by the mock Seek().
    188  WaitForReaderOperations(5);
    189 
    190  checkpoint.Call("Internal seek waiting for data");
    191  MOZ_ASSERT(!drainPromise.IsEmpty());
    192  // Request more data to check that this does not clear the status of the
    193  // in-progress drain, as in step 5 of
    194  // https://bugzilla.mozilla.org/show_bug.cgi?id=1941164#c6
    195  // At the time of writing, without bug 1941164, MediaFormatReader does not
    196  // reject this promise until the drain completes.  However, the promise
    197  // could sensibly be rejected earlier because the failed demux has indicated
    198  // that video data is not available for the current playback position.
    199  (void)proxy->RequestVideoData(TimeUnit(), false);
    200  // Trigger another Update() to check that another drain does not start.
    201  EXPECT_NS_SUCCEEDED(reader->OwnerThread()->Dispatch(
    202      NewRunnableMethod("NotifyDataArrived", reader.get(),
    203                        &MediaFormatReader::NotifyDataArrived)));
    204  // Wait for NotifyDataArrived to be processed by the demuxer and for another
    205  // demux request to complete.
    206  WaitForReaderOperations(2);
    207  // Clean up.
    208  WaitForResolve(proxy->Shutdown());
    209  drainPromise.Reject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
    210 }