tor-browser

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

MediaEngineFake.cpp (21260B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "MediaEngineFake.h"
      6 
      7 #include "AudioSegment.h"
      8 #include "FakeVideoSource.h"
      9 #include "ImageContainer.h"
     10 #include "MediaEnginePrefs.h"
     11 #include "MediaEngineSource.h"
     12 #include "MediaTrackConstraints.h"
     13 #include "MediaTrackGraph.h"
     14 #include "MediaTrackListener.h"
     15 #include "SineWaveGenerator.h"
     16 #include "Tracing.h"
     17 #include "VideoSegment.h"
     18 #include "mozilla/MediaManager.h"
     19 #include "mozilla/SyncRunnable.h"
     20 #include "mozilla/UniquePtr.h"
     21 #include "nsCOMPtr.h"
     22 #include "nsContentUtils.h"
     23 
     24 #ifdef MOZ_WIDGET_ANDROID
     25 #  include "nsISupportsUtils.h"
     26 #endif
     27 
     28 namespace mozilla {
     29 
     30 using namespace mozilla::gfx;
     31 using dom::GetEnumString;
     32 using dom::MediaSourceEnum;
     33 using dom::MediaTrackCapabilities;
     34 using dom::MediaTrackConstraints;
     35 using dom::MediaTrackSettings;
     36 using dom::VideoFacingModeEnum;
     37 using dom::VideoResizeModeEnum;
     38 
     39 #ifdef DEBUG
     40 static constexpr int VIDEO_WIDTH_DEFAULT =
     41    MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH / 2;
     42 #else
     43 static constexpr int VIDEO_WIDTH_DEFAULT =
     44    MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH;
     45 #endif
     46 static constexpr int VIDEO_WIDTH_MAX = 4096;
     47 #ifdef DEBUG
     48 static constexpr int VIDEO_HEIGHT_DEFAULT =
     49    MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT / 2;
     50 #else
     51 static constexpr int VIDEO_HEIGHT_DEFAULT =
     52    MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT;
     53 #endif
     54 static constexpr int VIDEO_HEIGHT_MAX = 2160;
     55 
     56 static nsString FakeVideoName() {
     57  // For the purpose of testing we allow to change the name of the fake device
     58  // by pref.
     59  nsAutoString cameraNameFromPref;
     60  nsresult rv;
     61  auto getPref = [&]() {
     62    rv = Preferences::GetString("media.getusermedia.fake-camera-name",
     63                                cameraNameFromPref);
     64  };
     65  if (NS_IsMainThread()) {
     66    getPref();
     67  } else {
     68    // Here it is preferred a "hard" block, instead of "soft" block provided
     69    // by sync dispatch, which allows the waiting thread to spin its event
     70    // loop. The latter would allow multiple enumeration requests being
     71    // processed out-of-order.
     72    RefPtr runnable = NS_NewRunnableFunction(__func__, getPref);
     73    SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable);
     74  }
     75 
     76  if (NS_SUCCEEDED(rv)) {
     77    return std::move(cameraNameFromPref);
     78  }
     79  return u"Default Video Device"_ns;
     80 }
     81 
     82 /**
     83 * Fake video source.
     84 */
     85 class MediaEngineFakeVideoSource : public MediaEngineSource {
     86 public:
     87  MediaEngineFakeVideoSource();
     88 
     89  static already_AddRefed<MediaEngineFakeVideoSource> CreateFrom(
     90      const MediaEngineFakeVideoSource* aSource);
     91 
     92  static nsString GetGroupId();
     93 
     94  nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
     95                    const MediaEnginePrefs& aPrefs, uint64_t aWindowID,
     96                    const char** aOutBadConstraint) override;
     97  void SetTrack(const RefPtr<MediaTrack>& aTrack,
     98                const PrincipalHandle& aPrincipal) override;
     99  nsresult Start() override;
    100  nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints,
    101                       const MediaEnginePrefs& aPrefs,
    102                       const char** aOutBadConstraint) override;
    103  nsresult Stop() override;
    104  nsresult Deallocate() override;
    105 
    106  uint32_t GetBestFitnessDistance(
    107      const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
    108      const MediaEnginePrefs& aPrefs) const override;
    109  void GetSettings(dom::MediaTrackSettings& aOutSettings) const override;
    110 
    111  void GetCapabilities(
    112      dom::MediaTrackCapabilities& aOutCapabilities) const override;
    113 
    114  bool IsFake() const override { return true; }
    115 
    116 protected:
    117  ~MediaEngineFakeVideoSource() {
    118    mGeneratedImageListener.DisconnectIfExists();
    119  }
    120 
    121  void OnGeneratedImage(RefPtr<layers::Image> aImage, TimeStamp aTime);
    122 
    123  // Owning thread only.
    124  RefPtr<FakeVideoSource> mCapturer;
    125  MediaEventListener mGeneratedImageListener;
    126 
    127  // Current state of this source.
    128  MediaEngineSourceState mState = kReleased;
    129  RefPtr<SourceMediaTrack> mTrack;
    130  PrincipalHandle mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
    131 
    132  MediaEnginePrefs mOpts;
    133 
    134  // Main thread only.
    135  const RefPtr<media::Refcountable<dom::MediaTrackSettings>> mSettings;
    136 };
    137 
    138 MediaEngineFakeVideoSource::MediaEngineFakeVideoSource()
    139    : mSettings(MakeAndAddRef<media::Refcountable<MediaTrackSettings>>()) {
    140  mSettings->mWidth.Construct(int32_t(VIDEO_WIDTH_DEFAULT));
    141  mSettings->mHeight.Construct(int32_t(VIDEO_HEIGHT_DEFAULT));
    142  mSettings->mFrameRate.Construct(double(MediaEnginePrefs::DEFAULT_VIDEO_FPS));
    143  mSettings->mFacingMode.Construct(NS_ConvertASCIItoUTF16(
    144      dom::GetEnumString(VideoFacingModeEnum::Environment)));
    145  mSettings->mResizeMode.Construct(NS_ConvertASCIItoUTF16(
    146      dom::GetEnumString(dom::VideoResizeModeEnum::None)));
    147 }
    148 
    149 /*static*/ already_AddRefed<MediaEngineFakeVideoSource>
    150 MediaEngineFakeVideoSource::CreateFrom(
    151    const MediaEngineFakeVideoSource* aSource) {
    152  auto src = MakeRefPtr<MediaEngineFakeVideoSource>();
    153  *static_cast<MediaTrackSettings*>(src->mSettings) = *aSource->mSettings;
    154  src->mOpts = aSource->mOpts;
    155  return src.forget();
    156 }
    157 
    158 nsString MediaEngineFakeVideoSource::GetGroupId() {
    159  return u"Fake Video Group"_ns;
    160 }
    161 
    162 uint32_t MediaEngineFakeVideoSource::GetBestFitnessDistance(
    163    const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
    164    const MediaEnginePrefs& aPrefs) const {
    165  AssertIsOnOwningThread();
    166 
    167  uint64_t distance = 0;
    168 
    169 #ifdef MOZ_WEBRTC
    170  // distance is read from first entry only
    171  if (aConstraintSets.Length() >= 1) {
    172    const auto* cs = aConstraintSets.ElementAt(0);
    173    const auto resizeMode = MediaConstraintsHelper::GetResizeMode(*cs, aPrefs);
    174    using H = MediaConstraintsHelper;
    175    Maybe<nsString> facingMode = Nothing();
    176    distance += H::FitnessDistance(facingMode, cs->mFacingMode);
    177 
    178    if (resizeMode.valueOr(dom::VideoResizeModeEnum::None) ==
    179        dom::VideoResizeModeEnum::None) {
    180      distance +=
    181          H::FitnessDistance(VIDEO_WIDTH_DEFAULT, cs->mWidth) +
    182          H::FitnessDistance(VIDEO_HEIGHT_DEFAULT, cs->mHeight) +
    183          H::FitnessDistance(AssertedCast<double>(aPrefs.mFPS), cs->mFrameRate);
    184    } else {
    185      distance += H::FeasibilityDistance(VIDEO_WIDTH_DEFAULT, cs->mWidth) +
    186                  H::FeasibilityDistance(VIDEO_HEIGHT_DEFAULT, cs->mHeight) +
    187                  H::FeasibilityDistance(AssertedCast<double>(aPrefs.mFPS),
    188                                         cs->mFrameRate);
    189    }
    190  }
    191 #endif
    192 
    193  return SaturatingCast<uint32_t>(distance);
    194 }
    195 
    196 void MediaEngineFakeVideoSource::GetSettings(
    197    MediaTrackSettings& aOutSettings) const {
    198  MOZ_ASSERT(NS_IsMainThread());
    199  aOutSettings = *mSettings;
    200 }
    201 
    202 void MediaEngineFakeVideoSource::GetCapabilities(
    203    MediaTrackCapabilities& aOutCapabilities) const {
    204  MOZ_ASSERT(NS_IsMainThread());
    205 
    206  NS_ConvertASCIItoUTF16 facingString(
    207      GetEnumString(VideoFacingModeEnum::Environment));
    208  nsTArray<nsString> facing;
    209  facing.AppendElement(facingString);
    210  aOutCapabilities.mFacingMode.Construct(std::move(facing));
    211 
    212  if (mOpts.mResizeModeEnabled) {
    213    nsTArray<nsString> resizeModes{
    214        NS_ConvertASCIItoUTF16(GetEnumString(dom::VideoResizeModeEnum::None)),
    215        NS_ConvertASCIItoUTF16(
    216            GetEnumString(dom::VideoResizeModeEnum::Crop_and_scale))};
    217    aOutCapabilities.mResizeMode.Construct(std::move(resizeModes));
    218  }
    219 
    220  dom::ULongRange widthRange;
    221  widthRange.mMax.Construct(VIDEO_WIDTH_MAX);
    222  widthRange.mMin.Construct(1);
    223  aOutCapabilities.mWidth.Construct(widthRange);
    224 
    225  dom::ULongRange heightRange;
    226  heightRange.mMax.Construct(VIDEO_HEIGHT_MAX);
    227  heightRange.mMin.Construct(1);
    228  aOutCapabilities.mHeight.Construct(heightRange);
    229 
    230  dom::DoubleRange frameRateRange;
    231  frameRateRange.mMax.Construct(double(MediaEnginePrefs::DEFAULT_VIDEO_FPS));
    232  frameRateRange.mMin.Construct(0);
    233  aOutCapabilities.mFrameRate.Construct(frameRateRange);
    234 }
    235 
    236 nsresult MediaEngineFakeVideoSource::Allocate(
    237    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    238    uint64_t aWindowID, const char** aOutBadConstraint) {
    239  AssertIsOnOwningThread();
    240 
    241  MOZ_ASSERT(mState == kReleased);
    242 
    243  FlattenedConstraints c(aConstraints);
    244 
    245  // emulator debug is very, very slow; reduce load on it with smaller/slower
    246  // fake video
    247  mOpts = aPrefs;
    248  mOpts.mWidth = aPrefs.mWidth ? aPrefs.mWidth : VIDEO_WIDTH_DEFAULT;
    249  mOpts.mHeight = aPrefs.mHeight ? aPrefs.mHeight : VIDEO_HEIGHT_DEFAULT;
    250 
    251  const auto resizeMode = MediaConstraintsHelper::GetResizeMode(c, mOpts);
    252  const auto resizeModeString = resizeMode.map(
    253      [](auto aRM) { return NS_ConvertASCIItoUTF16(dom::GetEnumString(aRM)); });
    254  if (resizeMode.valueOr(VideoResizeModeEnum::None) ==
    255      VideoResizeModeEnum::Crop_and_scale) {
    256    mOpts.mWidth = c.mWidth.Get(mOpts.mWidth);
    257    mOpts.mHeight = c.mHeight.Get(mOpts.mHeight);
    258    mOpts.mFPS = std::min(mOpts.mFPS, SaturatingCast<int32_t>(c.mFrameRate.Get(
    259                                          AssertedCast<double>(mOpts.mFPS))));
    260  }
    261 
    262  mOpts.mWidth = std::clamp(mOpts.mWidth, 1, VIDEO_WIDTH_MAX);
    263  mOpts.mHeight = std::clamp(mOpts.mHeight, 1, VIDEO_HEIGHT_MAX);
    264  mOpts.mFPS = std::clamp(mOpts.mFPS, 0, MediaEnginePrefs::DEFAULT_VIDEO_FPS);
    265 
    266  nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
    267  mCapturer = MakeRefPtr<FakeVideoSource>(target);
    268  mGeneratedImageListener = mCapturer->GeneratedImageEvent().Connect(
    269      target, this, &MediaEngineFakeVideoSource::OnGeneratedImage);
    270 
    271  NS_DispatchToMainThread(NS_NewRunnableFunction(
    272      __func__,
    273      [settings = mSettings, frameRate = mOpts.mFPS, width = mOpts.mWidth,
    274       height = mOpts.mHeight, resizeModeString]() {
    275        settings->mFrameRate.Value() = frameRate;
    276        settings->mWidth.Value() = width;
    277        settings->mHeight.Value() = height;
    278        settings->mResizeMode.Reset();
    279        resizeModeString.apply(
    280            [&](const auto& aStr) { settings->mResizeMode.Construct(aStr); });
    281      }));
    282 
    283  mState = kAllocated;
    284  return NS_OK;
    285 }
    286 
    287 nsresult MediaEngineFakeVideoSource::Deallocate() {
    288  AssertIsOnOwningThread();
    289 
    290  MOZ_ASSERT(mState == kStopped || mState == kAllocated);
    291 
    292  mGeneratedImageListener.Disconnect();
    293  mCapturer = nullptr;
    294  if (mTrack) {
    295    mTrack->End();
    296    mTrack = nullptr;
    297    mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
    298  }
    299  mState = kReleased;
    300 
    301  return NS_OK;
    302 }
    303 
    304 void MediaEngineFakeVideoSource::SetTrack(const RefPtr<MediaTrack>& aTrack,
    305                                          const PrincipalHandle& aPrincipal) {
    306  AssertIsOnOwningThread();
    307 
    308  MOZ_ASSERT(mState == kAllocated);
    309  MOZ_ASSERT(!mTrack);
    310  MOZ_ASSERT(aTrack->AsSourceTrack());
    311 
    312  mTrack = aTrack->AsSourceTrack();
    313  mPrincipalHandle = aPrincipal;
    314 }
    315 
    316 nsresult MediaEngineFakeVideoSource::Start() {
    317  AssertIsOnOwningThread();
    318 
    319  MOZ_ASSERT(mState == kAllocated || mState == kStopped);
    320  MOZ_ASSERT(mTrack, "SetTrack() must happen before Start()");
    321 
    322  int32_t rv = mCapturer->StartCapture(
    323      mOpts.mWidth, mOpts.mHeight, TimeDuration::FromSeconds(1.0 / mOpts.mFPS));
    324  if (NS_WARN_IF(rv != 0)) {
    325    return NS_ERROR_FAILURE;
    326  }
    327 
    328  mState = kStarted;
    329  return NS_OK;
    330 }
    331 
    332 nsresult MediaEngineFakeVideoSource::Stop() {
    333  AssertIsOnOwningThread();
    334 
    335  if (mState == kStopped || mState == kAllocated) {
    336    return NS_OK;
    337  }
    338 
    339  MOZ_ASSERT(mState == kStarted);
    340  MOZ_ASSERT(mTrack);
    341 
    342  int32_t rv = mCapturer->StopCapture();
    343  if (NS_WARN_IF(rv != 0)) {
    344    return NS_ERROR_FAILURE;
    345  }
    346 
    347  mState = kStopped;
    348 
    349  return NS_OK;
    350 }
    351 
    352 nsresult MediaEngineFakeVideoSource::Reconfigure(
    353    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    354    const char** aOutBadConstraint) {
    355  return NS_OK;
    356 }
    357 
    358 void MediaEngineFakeVideoSource::OnGeneratedImage(RefPtr<layers::Image> aImage,
    359                                                  TimeStamp aTime) {
    360  VideoSegment segment;
    361  segment.AppendFrame(aImage.forget(),
    362                      gfx::IntSize(mOpts.mWidth, mOpts.mHeight),
    363                      mPrincipalHandle, /*aForceBlack=*/false, aTime);
    364  mTrack->AppendData(&segment);
    365 }
    366 
    367 // This class is created on the media thread, as part of Start(), then entirely
    368 // self-sustained until destruction, just forwarding calls to Pull().
    369 class AudioSourcePullListener : public MediaTrackListener {
    370 public:
    371  AudioSourcePullListener(RefPtr<SourceMediaTrack> aTrack,
    372                          const PrincipalHandle& aPrincipalHandle,
    373                          uint32_t aFrequency)
    374      : mTrack(std::move(aTrack)),
    375        mPrincipalHandle(aPrincipalHandle),
    376        mSineGenerator(MakeUnique<SineWaveGenerator<int16_t>>(
    377            mTrack->mSampleRate, aFrequency)) {
    378    MOZ_COUNT_CTOR(AudioSourcePullListener);
    379  }
    380 
    381  MOZ_COUNTED_DTOR(AudioSourcePullListener)
    382 
    383  void NotifyPull(MediaTrackGraph* aGraph, TrackTime aEndOfAppendedData,
    384                  TrackTime aDesiredTime) override;
    385 
    386  const RefPtr<SourceMediaTrack> mTrack;
    387  const PrincipalHandle mPrincipalHandle;
    388  const UniquePtr<SineWaveGenerator<int16_t>> mSineGenerator;
    389 };
    390 
    391 /**
    392 * Fake audio source.
    393 */
    394 class MediaEngineFakeAudioSource : public MediaEngineSource {
    395 public:
    396  MediaEngineFakeAudioSource() = default;
    397 
    398  static nsString GetUUID();
    399  static nsString GetGroupId();
    400 
    401  nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
    402                    const MediaEnginePrefs& aPrefs, uint64_t aWindowID,
    403                    const char** aOutBadConstraint) override;
    404  void SetTrack(const RefPtr<MediaTrack>& aTrack,
    405                const PrincipalHandle& aPrincipal) override;
    406  nsresult Start() override;
    407  nsresult Reconfigure(const dom::MediaTrackConstraints& aConstraints,
    408                       const MediaEnginePrefs& aPrefs,
    409                       const char** aOutBadConstraint) override;
    410  nsresult Stop() override;
    411  nsresult Deallocate() override;
    412 
    413  bool IsFake() const override { return true; }
    414 
    415  void GetSettings(dom::MediaTrackSettings& aOutSettings) const override;
    416 
    417  void GetCapabilities(
    418      dom::MediaTrackCapabilities& aOutCapabilities) const override;
    419 
    420 protected:
    421  ~MediaEngineFakeAudioSource() = default;
    422 
    423  // Current state of this source.
    424  MediaEngineSourceState mState = kReleased;
    425  RefPtr<SourceMediaTrack> mTrack;
    426  PrincipalHandle mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
    427  uint32_t mFrequency = 1000;
    428  RefPtr<AudioSourcePullListener> mPullListener;
    429 };
    430 
    431 nsString MediaEngineFakeAudioSource::GetUUID() {
    432  return u"B7CBD7C1-53EF-42F9-8353-73F61C70C092"_ns;
    433 }
    434 
    435 nsString MediaEngineFakeAudioSource::GetGroupId() {
    436  return u"Fake Audio Group"_ns;
    437 }
    438 
    439 void MediaEngineFakeAudioSource::GetSettings(
    440    MediaTrackSettings& aOutSettings) const {
    441  MOZ_ASSERT(NS_IsMainThread());
    442  aOutSettings.mAutoGainControl.Construct(false);
    443  aOutSettings.mEchoCancellation.Construct(false);
    444  aOutSettings.mNoiseSuppression.Construct(false);
    445  aOutSettings.mChannelCount.Construct(1);
    446 }
    447 
    448 void MediaEngineFakeAudioSource::GetCapabilities(
    449    MediaTrackCapabilities& aOutCapabilities) const {
    450  MOZ_ASSERT(NS_IsMainThread());
    451  nsTArray<bool> echoCancellation;
    452  echoCancellation.AppendElement(false);
    453  aOutCapabilities.mEchoCancellation.Construct(std::move(echoCancellation));
    454 
    455  nsTArray<bool> autoGainControl;
    456  autoGainControl.AppendElement(false);
    457  aOutCapabilities.mAutoGainControl.Construct(std::move(autoGainControl));
    458 
    459  nsTArray<bool> noiseSuppression;
    460  noiseSuppression.AppendElement(false);
    461  aOutCapabilities.mNoiseSuppression.Construct(std::move(noiseSuppression));
    462 
    463  dom::ULongRange channelCountRange;
    464  channelCountRange.mMax.Construct(1);
    465  channelCountRange.mMin.Construct(1);
    466  aOutCapabilities.mChannelCount.Construct(channelCountRange);
    467 }
    468 
    469 nsresult MediaEngineFakeAudioSource::Allocate(
    470    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    471    uint64_t aWindowID, const char** aOutBadConstraint) {
    472  AssertIsOnOwningThread();
    473 
    474  MOZ_ASSERT(mState == kReleased);
    475 
    476  mFrequency = aPrefs.mFreq ? aPrefs.mFreq : 1000;
    477 
    478  mState = kAllocated;
    479  return NS_OK;
    480 }
    481 
    482 nsresult MediaEngineFakeAudioSource::Deallocate() {
    483  AssertIsOnOwningThread();
    484 
    485  MOZ_ASSERT(mState == kStopped || mState == kAllocated);
    486 
    487  if (mTrack) {
    488    mTrack->End();
    489    mTrack = nullptr;
    490    mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
    491  }
    492  mState = kReleased;
    493  return NS_OK;
    494 }
    495 
    496 void MediaEngineFakeAudioSource::SetTrack(const RefPtr<MediaTrack>& aTrack,
    497                                          const PrincipalHandle& aPrincipal) {
    498  AssertIsOnOwningThread();
    499 
    500  MOZ_ASSERT(mState == kAllocated);
    501  MOZ_ASSERT(!mTrack);
    502  MOZ_ASSERT(aTrack->AsSourceTrack());
    503 
    504  mTrack = aTrack->AsSourceTrack();
    505  mPrincipalHandle = aPrincipal;
    506 }
    507 
    508 nsresult MediaEngineFakeAudioSource::Start() {
    509  AssertIsOnOwningThread();
    510 
    511  if (mState == kStarted) {
    512    return NS_OK;
    513  }
    514 
    515  MOZ_ASSERT(mState == kAllocated || mState == kStopped);
    516  MOZ_ASSERT(mTrack, "SetTrack() must happen before Start()");
    517 
    518  if (!mPullListener) {
    519    mPullListener = MakeAndAddRef<AudioSourcePullListener>(
    520        mTrack, mPrincipalHandle, mFrequency);
    521  }
    522 
    523  mState = kStarted;
    524 
    525  NS_DispatchToMainThread(NS_NewRunnableFunction(
    526      __func__, [track = mTrack, listener = mPullListener]() {
    527        if (track->IsDestroyed()) {
    528          return;
    529        }
    530        track->AddListener(listener);
    531        track->SetPullingEnabled(true);
    532      }));
    533 
    534  return NS_OK;
    535 }
    536 
    537 nsresult MediaEngineFakeAudioSource::Stop() {
    538  AssertIsOnOwningThread();
    539 
    540  if (mState == kStopped || mState == kAllocated) {
    541    return NS_OK;
    542  }
    543  MOZ_ASSERT(mState == kStarted);
    544  mState = kStopped;
    545 
    546  NS_DispatchToMainThread(NS_NewRunnableFunction(
    547      __func__, [track = mTrack, listener = std::move(mPullListener)]() {
    548        if (track->IsDestroyed()) {
    549          return;
    550        }
    551        track->RemoveListener(listener);
    552        track->SetPullingEnabled(false);
    553      }));
    554  return NS_OK;
    555 }
    556 
    557 nsresult MediaEngineFakeAudioSource::Reconfigure(
    558    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    559    const char** aOutBadConstraint) {
    560  return NS_OK;
    561 }
    562 
    563 void AudioSourcePullListener::NotifyPull(MediaTrackGraph* aGraph,
    564                                         TrackTime aEndOfAppendedData,
    565                                         TrackTime aDesiredTime) {
    566  TRACE_COMMENT("SourceMediaTrack::NotifyPull", "SourceMediaTrack %p",
    567                mTrack.get());
    568  AudioSegment segment;
    569  TrackTicks delta = aDesiredTime - aEndOfAppendedData;
    570  CheckedInt<size_t> bufferSize(sizeof(int16_t));
    571  bufferSize *= delta;
    572  RefPtr<SharedBuffer> buffer = SharedBuffer::Create(bufferSize);
    573  int16_t* dest = static_cast<int16_t*>(buffer->Data());
    574  mSineGenerator->generate(dest, delta);
    575  AutoTArray<const int16_t*, 1> channels;
    576  channels.AppendElement(dest);
    577  segment.AppendFrames(buffer.forget(), channels, delta, mPrincipalHandle);
    578  mTrack->AppendData(&segment);
    579 }
    580 
    581 MediaEngineFake::MediaEngineFake() = default;
    582 MediaEngineFake::~MediaEngineFake() = default;
    583 
    584 void MediaEngineFake::EnumerateDevices(
    585    MediaSourceEnum aMediaSource, MediaSinkEnum aMediaSink,
    586    nsTArray<RefPtr<MediaDevice>>* aDevices) {
    587  AssertIsOnOwningThread();
    588  using IsScary = MediaDevice::IsScary;
    589  using OsPromptable = MediaDevice::OsPromptable;
    590 
    591  if (aMediaSink == MediaSinkEnum::Speaker) {
    592    NS_WARNING("No default implementation for MediaSinkEnum::Speaker");
    593  }
    594 
    595  switch (aMediaSource) {
    596    case MediaSourceEnum::Camera: {
    597      nsString name = FakeVideoName();
    598      aDevices->EmplaceBack(
    599          new MediaDevice(this, aMediaSource, name, /*aRawId=*/name,
    600                          MediaEngineFakeVideoSource::GetGroupId(), IsScary::No,
    601                          OsPromptable::No));
    602      return;
    603    }
    604    case MediaSourceEnum::Microphone:
    605      aDevices->EmplaceBack(
    606          new MediaDevice(this, aMediaSource, u"Default Audio Device"_ns,
    607                          MediaEngineFakeAudioSource::GetUUID(),
    608                          MediaEngineFakeAudioSource::GetGroupId(), IsScary::No,
    609                          OsPromptable::No));
    610      return;
    611    default:
    612      MOZ_ASSERT_UNREACHABLE("Unsupported source type");
    613      return;
    614  }
    615 }
    616 
    617 RefPtr<MediaEngineSource> MediaEngineFake::CreateSource(
    618    const MediaDevice* aMediaDevice) {
    619  MOZ_ASSERT(aMediaDevice->mEngine == this);
    620  switch (aMediaDevice->mMediaSource) {
    621    case MediaSourceEnum::Camera:
    622      return new MediaEngineFakeVideoSource();
    623    case MediaSourceEnum::Microphone:
    624      return new MediaEngineFakeAudioSource();
    625    default:
    626      MOZ_ASSERT_UNREACHABLE("Unsupported source type");
    627      return nullptr;
    628  }
    629 }
    630 
    631 RefPtr<MediaEngineSource> MediaEngineFake::CreateSourceFrom(
    632    const MediaEngineSource* aSource, const MediaDevice* aMediaDevice) {
    633  MOZ_ASSERT(aMediaDevice->mEngine == this);
    634  switch (aMediaDevice->mMediaSource) {
    635    case MediaSourceEnum::Camera:
    636      return MediaEngineFakeVideoSource::CreateFrom(
    637          static_cast<const MediaEngineFakeVideoSource*>(aSource));
    638    case MediaSourceEnum::Microphone:
    639      // No main thread members that need to be deep cloned.
    640      return new MediaEngineFakeAudioSource();
    641    default:
    642      MOZ_ASSERT_UNREACHABLE("Unsupported source type");
    643      return nullptr;
    644  }
    645 }
    646 
    647 }  // namespace mozilla