tor-browser

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

FuzzMedia.cpp (13697B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "ADTSDemuxer.h"
      7 #include "BufferMediaResource.h"
      8 #include "FlacDemuxer.h"
      9 #include "FuzzingInterface.h"
     10 #include "MP3Demuxer.h"
     11 #include "MP4Demuxer.h"
     12 #include "MediaDataDecoderProxy.h"
     13 #include "OggDemuxer.h"
     14 #include "PDMFactory.h"
     15 #include "PlatformDecoderModule.h"
     16 #include "QueueObject.h"
     17 #include "WaveDemuxer.h"
     18 #include "WebMDemuxer.h"
     19 #include "mozilla/AbstractThread.h"
     20 #include "mozilla/SpinEventLoopUntil.h"
     21 #include "mozilla/TaskQueue.h"
     22 #include "mozilla/gfx/gfxVars.h"
     23 #include "systemservices/MediaUtils.h"
     24 
     25 using namespace mozilla;
     26 
     27 class Benchmark;
     28 
     29 class BenchmarkPlayback : public QueueObject {
     30  friend class Benchmark;
     31  BenchmarkPlayback(Benchmark* aGlobalState, MediaDataDemuxer* aDemuxer);
     32  void DemuxSamples();
     33  void DemuxNextSample();
     34  void GlobalShutdown();
     35  void InitDecoder(UniquePtr<TrackInfo>&& aInfo);
     36 
     37  void Output(MediaDataDecoder::DecodedData&& aResults);
     38  void Error(const MediaResult& aError);
     39  void InputExhausted();
     40 
     41  // Shutdown trackdemuxer and demuxer if any and shutdown the task queues.
     42  void FinalizeShutdown();
     43 
     44  Atomic<Benchmark*> mGlobalState;
     45 
     46  RefPtr<TaskQueue> mDecoderTaskQueue;
     47  RefPtr<MediaDataDecoder> mDecoder;
     48 
     49  // Object only accessed on Thread()
     50  RefPtr<MediaDataDemuxer> mDemuxer;
     51  RefPtr<MediaTrackDemuxer> mTrackDemuxer;
     52  nsTArray<RefPtr<MediaRawData>> mSamples;
     53  UniquePtr<TrackInfo> mInfo;
     54  size_t mSampleIndex;
     55  Maybe<TimeStamp> mDecodeStartTime;
     56  uint32_t mFrameCount;
     57  bool mFinished;
     58  bool mDrained;
     59 };
     60 
     61 // Init() must have been called at least once prior on the
     62 // main thread.
     63 class Benchmark : public QueueObject {
     64 public:
     65  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Benchmark)
     66 
     67  struct Parameters {
     68    Parameters()
     69        : mFramesToMeasure(UINT32_MAX),
     70          mStartupFrame(1),
     71          mTimeout(TimeDuration::Forever()) {}
     72 
     73    Parameters(uint32_t aFramesToMeasure, uint32_t aStartupFrame,
     74               uint32_t aStopAtFrame, const TimeDuration& aTimeout)
     75        : mFramesToMeasure(aFramesToMeasure),
     76          mStartupFrame(aStartupFrame),
     77          mStopAtFrame(Some(aStopAtFrame)),
     78          mTimeout(aTimeout) {}
     79 
     80    const uint32_t mFramesToMeasure;
     81    const uint32_t mStartupFrame;
     82    const Maybe<uint32_t> mStopAtFrame;
     83    const TimeDuration mTimeout;
     84  };
     85 
     86  typedef MozPromise<uint32_t, MediaResult, /* IsExclusive = */ true>
     87      BenchmarkPromise;
     88 
     89  explicit Benchmark(MediaDataDemuxer* aDemuxer,
     90                     const Parameters& aParameters = Parameters());
     91  RefPtr<BenchmarkPromise> Run();
     92 
     93  // Must be called on the main thread.
     94  static void Init();
     95 
     96 private:
     97  friend class BenchmarkPlayback;
     98  virtual ~Benchmark();
     99  void ReturnResult(uint32_t aDecodeFps);
    100  void ReturnError(const MediaResult& aError);
    101  void Dispose();
    102  const Parameters mParameters;
    103  RefPtr<Benchmark> mKeepAliveUntilComplete;
    104  BenchmarkPlayback mPlaybackState;
    105  MozPromiseHolder<BenchmarkPromise> mPromise;
    106 };
    107 
    108 class FuzzRunner {
    109 public:
    110  explicit FuzzRunner(Benchmark* aBenchmark) : mBenchmark(aBenchmark) {}
    111 
    112  void Run() {
    113    // Assert we're on the main thread, otherwise `done` must be synchronized.
    114    MOZ_ASSERT(NS_IsMainThread());
    115    bool done = false;
    116 
    117    Benchmark::Init();
    118    mBenchmark->Run()->Then(
    119        // Non DocGroup-version of AbstractThread::MainThread() is fine for
    120        // testing.
    121        AbstractThread::MainThread(), __func__,
    122        [&](uint32_t aDecodeFps) { done = true; }, [&]() { done = true; });
    123 
    124    // Wait until benchmark completes.
    125    SpinEventLoopUntil("FuzzRunner::Run"_ns, [&]() { return done; });
    126  }
    127 
    128 private:
    129  RefPtr<Benchmark> mBenchmark;
    130 };
    131 
    132 Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
    133    : QueueObject(
    134          TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    135                            "Benchmark::QueueObject")),
    136      mParameters(aParameters),
    137      mPlaybackState(this, aDemuxer) {
    138  MOZ_COUNT_CTOR(Benchmark);
    139 }
    140 
    141 Benchmark::~Benchmark() { MOZ_COUNT_DTOR(Benchmark); }
    142 
    143 RefPtr<Benchmark::BenchmarkPromise> Benchmark::Run() {
    144  RefPtr<Benchmark> self = this;
    145  mKeepAliveUntilComplete = this;
    146  return InvokeAsync(Thread(), __func__, [self] {
    147    RefPtr<BenchmarkPromise> p = self->mPromise.Ensure(__func__);
    148    self->mPlaybackState.Dispatch(NS_NewRunnableFunction(
    149        "Benchmark::Run", [self]() { self->mPlaybackState.DemuxSamples(); }));
    150    return p;
    151  });
    152 }
    153 
    154 void Benchmark::ReturnResult(uint32_t aDecodeFps) {
    155  MOZ_ASSERT(OnThread());
    156 
    157  mPromise.ResolveIfExists(aDecodeFps, __func__);
    158 }
    159 
    160 void Benchmark::ReturnError(const MediaResult& aError) {
    161  MOZ_ASSERT(OnThread());
    162 
    163  mPromise.RejectIfExists(aError, __func__);
    164 }
    165 
    166 void Benchmark::Dispose() {
    167  MOZ_ASSERT(OnThread());
    168 
    169  mKeepAliveUntilComplete = nullptr;
    170 }
    171 
    172 void Benchmark::Init() {
    173  MOZ_ASSERT(NS_IsMainThread());
    174  mozilla::gfx::gfxVars::Initialize();
    175 }
    176 
    177 BenchmarkPlayback::BenchmarkPlayback(Benchmark* aGlobalState,
    178                                     MediaDataDemuxer* aDemuxer)
    179    : QueueObject(
    180          TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    181                            "BenchmarkPlayback::QueueObject")),
    182      mGlobalState(aGlobalState),
    183      mDecoderTaskQueue(TaskQueue::Create(
    184          GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
    185          "BenchmarkPlayback::mDecoderTaskQueue")),
    186      mDemuxer(aDemuxer),
    187      mSampleIndex(0),
    188      mFrameCount(0),
    189      mFinished(false),
    190      mDrained(false) {}
    191 
    192 void BenchmarkPlayback::DemuxSamples() {
    193  MOZ_ASSERT(OnThread());
    194 
    195  RefPtr<Benchmark> ref(mGlobalState);
    196  mDemuxer->Init()->Then(
    197      Thread(), __func__,
    198      [this, ref](nsresult aResult) {
    199        MOZ_ASSERT(OnThread());
    200        if (mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack)) {
    201          mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
    202        } else if (mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack)) {
    203          mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
    204        }
    205        if (!mTrackDemuxer) {
    206          Error(MediaResult(NS_ERROR_FAILURE, "Can't create track demuxer"));
    207          return;
    208        }
    209        DemuxNextSample();
    210      },
    211      [this, ref](const MediaResult& aError) { Error(aError); });
    212 }
    213 
    214 void BenchmarkPlayback::DemuxNextSample() {
    215  MOZ_ASSERT(OnThread());
    216 
    217  RefPtr<Benchmark> ref(mGlobalState);
    218  RefPtr<MediaTrackDemuxer::SamplesPromise> promise =
    219      mTrackDemuxer->GetSamples();
    220  promise->Then(
    221      Thread(), __func__,
    222      [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
    223        mSamples.AppendElements(aHolder->GetMovableSamples());
    224        if (ref->mParameters.mStopAtFrame &&
    225            mSamples.Length() == ref->mParameters.mStopAtFrame.ref()) {
    226          InitDecoder(mTrackDemuxer->GetInfo());
    227        } else {
    228          Dispatch(
    229              NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
    230                                     [this, ref]() { DemuxNextSample(); }));
    231        }
    232      },
    233      [this, ref](const MediaResult& aError) {
    234        switch (aError.Code()) {
    235          case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
    236            InitDecoder(mTrackDemuxer->GetInfo());
    237            break;
    238          default:
    239            Error(aError);
    240            break;
    241        }
    242      });
    243 }
    244 
    245 void BenchmarkPlayback::InitDecoder(UniquePtr<TrackInfo>&& aInfo) {
    246  MOZ_ASSERT(OnThread());
    247 
    248  if (!aInfo) {
    249    Error(MediaResult(NS_ERROR_FAILURE, "Invalid TrackInfo"));
    250    return;
    251  }
    252 
    253  RefPtr<PDMFactory> platform = new PDMFactory();
    254  mInfo = std::move(aInfo);
    255  RefPtr<Benchmark> ref(mGlobalState);
    256  platform->CreateDecoder(CreateDecoderParams{*mInfo})
    257      ->Then(
    258          Thread(), __func__,
    259          [this, ref](RefPtr<MediaDataDecoder>&& aDecoder) {
    260            mDecoder = new MediaDataDecoderProxy(
    261                aDecoder.forget(), do_AddRef(mDecoderTaskQueue.get()));
    262            mDecoder->Init()->Then(
    263                Thread(), __func__,
    264                [this, ref](TrackInfo::TrackType aTrackType) {
    265                  InputExhausted();
    266                },
    267                [this, ref](const MediaResult& aError) { Error(aError); });
    268          },
    269          [this, ref](const MediaResult& aError) { Error(aError); });
    270 }
    271 
    272 void BenchmarkPlayback::FinalizeShutdown() {
    273  MOZ_ASSERT(OnThread());
    274 
    275  MOZ_ASSERT(mFinished, "GlobalShutdown must have been run");
    276  MOZ_ASSERT(!mDecoder, "mDecoder must have been shutdown already");
    277  MOZ_ASSERT(!mDemuxer, "mDemuxer must have been shutdown already");
    278  MOZ_DIAGNOSTIC_ASSERT(mDecoderTaskQueue->IsEmpty());
    279  mDecoderTaskQueue = nullptr;
    280 
    281  RefPtr<Benchmark> ref(mGlobalState);
    282  ref->Thread()->Dispatch(NS_NewRunnableFunction(
    283      "BenchmarkPlayback::FinalizeShutdown", [ref]() { ref->Dispose(); }));
    284 }
    285 
    286 void BenchmarkPlayback::GlobalShutdown() {
    287  MOZ_ASSERT(OnThread());
    288 
    289  MOZ_ASSERT(!mFinished, "We've already shutdown");
    290 
    291  mFinished = true;
    292 
    293  if (mTrackDemuxer) {
    294    mTrackDemuxer->Reset();
    295    mTrackDemuxer->BreakCycles();
    296    mTrackDemuxer = nullptr;
    297  }
    298  mDemuxer = nullptr;
    299 
    300  if (mDecoder) {
    301    RefPtr<Benchmark> ref(mGlobalState);
    302    mDecoder->Flush()->Then(
    303        Thread(), __func__,
    304        [ref, this]() {
    305          mDecoder->Shutdown()->Then(
    306              Thread(), __func__, [ref, this]() { FinalizeShutdown(); },
    307              []() { MOZ_CRASH("not reached"); });
    308          mDecoder = nullptr;
    309          mInfo = nullptr;
    310        },
    311        []() { MOZ_CRASH("not reached"); });
    312  } else {
    313    FinalizeShutdown();
    314  }
    315 }
    316 
    317 void BenchmarkPlayback::Output(MediaDataDecoder::DecodedData&& aResults) {
    318  MOZ_ASSERT(OnThread());
    319  MOZ_ASSERT(!mFinished);
    320 
    321  RefPtr<Benchmark> ref(mGlobalState);
    322  mFrameCount += aResults.Length();
    323  if (!mDecodeStartTime && mFrameCount >= ref->mParameters.mStartupFrame) {
    324    mDecodeStartTime = Some(TimeStamp::Now());
    325  }
    326  TimeStamp now = TimeStamp::Now();
    327  uint32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
    328  TimeDuration elapsedTime = now - mDecodeStartTime.refOr(now);
    329  if (((frames == ref->mParameters.mFramesToMeasure) &&
    330       mFrameCount > ref->mParameters.mStartupFrame && frames > 0) ||
    331      elapsedTime >= ref->mParameters.mTimeout || mDrained) {
    332    uint32_t decodeFps = frames / elapsedTime.ToSeconds();
    333    GlobalShutdown();
    334    ref->Dispatch(NS_NewRunnableFunction(
    335        "BenchmarkPlayback::Output",
    336        [ref, decodeFps]() { ref->ReturnResult(decodeFps); }));
    337  }
    338 }
    339 
    340 void BenchmarkPlayback::Error(const MediaResult& aError) {
    341  MOZ_ASSERT(OnThread());
    342 
    343  RefPtr<Benchmark> ref(mGlobalState);
    344  GlobalShutdown();
    345  ref->Dispatch(
    346      NS_NewRunnableFunction("BenchmarkPlayback::Error",
    347                             [ref, aError]() { ref->ReturnError(aError); }));
    348 }
    349 
    350 void BenchmarkPlayback::InputExhausted() {
    351  MOZ_ASSERT(OnThread());
    352  MOZ_ASSERT(!mFinished);
    353 
    354  if (mSampleIndex >= mSamples.Length()) {
    355    Error(MediaResult(NS_ERROR_FAILURE, "Nothing left to decode"));
    356    return;
    357  }
    358 
    359  RefPtr<MediaRawData> sample = mSamples[mSampleIndex];
    360  RefPtr<Benchmark> ref(mGlobalState);
    361  RefPtr<MediaDataDecoder::DecodePromise> p = mDecoder->Decode(sample);
    362 
    363  mSampleIndex++;
    364  if (mSampleIndex == mSamples.Length() && !ref->mParameters.mStopAtFrame) {
    365    // Complete current frame decode then drain if still necessary.
    366    p->Then(
    367        Thread(), __func__,
    368        [ref, this](MediaDataDecoder::DecodedData&& aResults) {
    369          Output(std::move(aResults));
    370          if (!mFinished) {
    371            mDecoder->Drain()->Then(
    372                Thread(), __func__,
    373                [ref, this](MediaDataDecoder::DecodedData&& aResults) {
    374                  mDrained = true;
    375                  Output(std::move(aResults));
    376                  MOZ_ASSERT(mFinished, "We must be done now");
    377                },
    378                [ref, this](const MediaResult& aError) { Error(aError); });
    379          }
    380        },
    381        [ref, this](const MediaResult& aError) { Error(aError); });
    382  } else {
    383    if (mSampleIndex == mSamples.Length() && ref->mParameters.mStopAtFrame) {
    384      mSampleIndex = 0;
    385    }
    386    // Continue decoding
    387    p->Then(
    388        Thread(), __func__,
    389        [ref, this](MediaDataDecoder::DecodedData&& aResults) {
    390          Output(std::move(aResults));
    391          if (!mFinished) {
    392            InputExhausted();
    393          }
    394        },
    395        [ref, this](const MediaResult& aError) { Error(aError); });
    396  }
    397 }
    398 
    399 #define MOZ_MEDIA_FUZZER(_name)                                         \
    400  static int FuzzingRunMedia##_name(const uint8_t* data, size_t size) { \
    401    if (!size) {                                                        \
    402      return 0;                                                         \
    403    }                                                                   \
    404    RefPtr<BufferMediaResource> resource =                              \
    405        new BufferMediaResource(data, size);                            \
    406    FuzzRunner runner(new Benchmark(new _name##Demuxer(resource)));     \
    407    runner.Run();                                                       \
    408    return 0;                                                           \
    409  }                                                                     \
    410  MOZ_FUZZING_INTERFACE_RAW(nullptr, FuzzingRunMedia##_name, Media##_name);
    411 
    412 MOZ_MEDIA_FUZZER(ADTS);
    413 MOZ_MEDIA_FUZZER(Flac);
    414 MOZ_MEDIA_FUZZER(MP3);
    415 MOZ_MEDIA_FUZZER(MP4);
    416 MOZ_MEDIA_FUZZER(Ogg);
    417 MOZ_MEDIA_FUZZER(WAV);
    418 MOZ_MEDIA_FUZZER(WebM);