Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/media/gtest/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 26 kB image not shown  

Quelle  MockCubeb.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "MockCubeb.h"

#include "gtest/gtest.h"

namespace mozilla {

using KeepProcessing = MockCubebStream::KeepProcessing;

void PrintDevice(cubeb_device_info aInfo) {
  printf(
      "id: %zu\n"
      "device_id: %s\n"
      "friendly_name: %s\n"
      "group_id: %s\n"
      "vendor_name: %s\n"
      "type: %d\n"
      "state: %d\n"
      "preferred: %d\n"
      "format: %d\n"
      "default_format: %d\n"
      "max_channels: %d\n"
      "default_rate: %d\n"
      "max_rate: %d\n"
      "min_rate: %d\n"
      "latency_lo: %d\n"
      "latency_hi: %d\n",
      reinterpret_cast<uintptr_t>(aInfo.devid), aInfo.device_id,
      aInfo.friendly_name, aInfo.group_id, aInfo.vendor_name, aInfo.type,
      aInfo.state, aInfo.preferred, aInfo.format, aInfo.default_format,
      aInfo.max_channels, aInfo.default_rate, aInfo.max_rate, aInfo.min_rate,
      aInfo.latency_lo, aInfo.latency_hi);
}

void PrintDevice(AudioDeviceInfo* aInfo) {
  cubeb_devid id;
  nsString name;
  nsString groupid;
  nsString vendor;
  uint16_t type;
  uint16_t state;
  uint16_t preferred;
  uint16_t supportedFormat;
  uint16_t defaultFormat;
  uint32_t maxChannels;
  uint32_t defaultRate;
  uint32_t maxRate;
  uint32_t minRate;
  uint32_t maxLatency;
  uint32_t minLatency;

  id = aInfo->DeviceID();
  aInfo->GetName(name);
  aInfo->GetGroupId(groupid);
  aInfo->GetVendor(vendor);
  aInfo->GetType(&type);
  aInfo->GetState(&state);
  aInfo->GetPreferred(&preferred);
  aInfo->GetSupportedFormat(&supportedFormat);
  aInfo->GetDefaultFormat(&defaultFormat);
  aInfo->GetMaxChannels(&maxChannels);
  aInfo->GetDefaultRate(&defaultRate);
  aInfo->GetMaxRate(&maxRate);
  aInfo->GetMinRate(&minRate);
  aInfo->GetMinLatency(&minLatency);
  aInfo->GetMaxLatency(&maxLatency);

  printf(
      "device id: %zu\n"
      "friendly_name: %s\n"
      "group_id: %s\n"
      "vendor_name: %s\n"
      "type: %d\n"
      "state: %d\n"
      "preferred: %d\n"
      "format: %d\n"
      "default_format: %d\n"
      "max_channels: %d\n"
      "default_rate: %d\n"
      "max_rate: %d\n"
      "min_rate: %d\n"
      "latency_lo: %d\n"
      "latency_hi: %d\n",
      reinterpret_cast<uintptr_t>(id), NS_LossyConvertUTF16toASCII(name).get(),
      NS_LossyConvertUTF16toASCII(groupid).get(),
      NS_LossyConvertUTF16toASCII(vendor).get(), type, state, preferred,
      supportedFormat, defaultFormat, maxChannels, defaultRate, maxRate,
      minRate, minLatency, maxLatency);
}

cubeb_device_info DeviceTemplate(cubeb_devid aId, cubeb_device_type aType,
                                 const char* name) {
  // A fake input device
  cubeb_device_info device;
  device.devid = aId;
  device.device_id = "nice name";
  device.friendly_name = name;
  device.group_id = "the physical device";
  device.vendor_name = "mozilla";
  device.type = aType;
  device.state = CUBEB_DEVICE_STATE_ENABLED;
  device.preferred = CUBEB_DEVICE_PREF_NONE;
  device.format = CUBEB_DEVICE_FMT_F32NE;
  device.default_format = CUBEB_DEVICE_FMT_F32NE;
  device.max_channels = 2;
  device.default_rate = 44100;
  device.max_rate = 44100;
  device.min_rate = 16000;
  device.latency_lo = 256;
  device.latency_hi = 1024;

  return device;
}

cubeb_device_info DeviceTemplate(cubeb_devid aId, cubeb_device_type aType) {
  return DeviceTemplate(aId, aType, "nice name");
}

void AddDevices(MockCubeb* mock, uint32_t device_count,
                cubeb_device_type deviceType) {
  mock->ClearDevices(deviceType);
  // Add a few input devices (almost all the same but it does not really
  // matter as long as they have distinct IDs and only one is the default
  // devices)
  for (uintptr_t i = 0; i < device_count; i++) {
    cubeb_device_info device =
        DeviceTemplate(reinterpret_cast<void*>(i + 1), deviceType);
    // Make it so that the last device is the default input device.
    if (i == device_count - 1) {
      device.preferred = CUBEB_DEVICE_PREF_ALL;
    }
    mock->AddDevice(device);
  }
}

void cubeb_mock_destroy(cubeb* context) {
  MockCubeb::AsMock(context)->Destroy();
}

MockCubebStream::MockCubebStream(
    cubeb* aContext, char const* aStreamName, cubeb_devid aInputDevice,
    cubeb_stream_params* aInputStreamParams, cubeb_devid aOutputDevice,
    cubeb_stream_params* aOutputStreamParams, cubeb_data_callback aDataCallback,
    cubeb_state_callback aStateCallback, void* aUserPtr,
    SmartMockCubebStream* aSelf, RunningMode aRunningMode, bool aFrozenStart)
    : context(aContext),
      mUserPtr(aUserPtr),
      mRunningMode(aRunningMode),
      mHasInput(aInputStreamParams),
      mHasOutput(aOutputStreamParams),
      mSelf(aSelf),
      mFrozenStartMonitor("MockCubebStream::mFrozenStartMonitor"),
      mFrozenStart(aFrozenStart),
      mDataCallback(aDataCallback),
      mStateCallback(aStateCallback),
      mName(aStreamName),
      mInputDeviceID(aInputDevice),
      mOutputDeviceID(aOutputDevice),
      mAudioGenerator(aInputStreamParams ? aInputStreamParams->channels
                                         : MAX_INPUT_CHANNELS,
                      aInputStreamParams ? aInputStreamParams->rate
                                         : aOutputStreamParams->rate,
                      100 /* aFrequency */),
      mAudioVerifier(aInputStreamParams ? aInputStreamParams->rate
                                        : aOutputStreamParams->rate,
                     100 /* aFrequency */) {
  MOZ_RELEASE_ASSERT(mAudioGenerator.ChannelCount() <= MAX_INPUT_CHANNELS,
                     "mInputBuffer has no enough space to hold generated data");
  if (mFrozenStart) {
    MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  }
  if (aInputStreamParams) {
    mInputParams = *aInputStreamParams;
  }
  if (aOutputStreamParams) {
    mOutputParams = *aOutputStreamParams;
    MOZ_RELEASE_ASSERT(SampleRate() == mOutputParams.rate);
  }
}

MockCubebStream::~MockCubebStream() = default;

int MockCubebStream::Start() {
  {
    MutexAutoLock l(mMutex);
    NotifyState(CUBEB_STATE_STARTED);
  }
  {
    MonitorAutoLock lock(mFrozenStartMonitor);
    if (mFrozenStart) {
      // We need to grab mFrozenStartMonitor before returning to avoid races in
      // the calling code -- it controls when to mFrozenStartMonitor.Notify().
      // TempData helps facilitate this by holding what's needed to block the
      // calling thread until the background thread has grabbed the lock.
      struct TempData {
        NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TempData)
        static_assert(HasThreadSafeRefCnt::value,
                      "Silence a -Wunused-local-typedef warning");
        Monitor mMonitor{"MockCubebStream::Start::TempData::mMonitor"};
        bool mFinished = false;

       private:
        ~TempData() = default;
      };
      auto temp = MakeRefPtr<TempData>();
      MonitorAutoLock lock(temp->mMonitor);
      NS_DispatchBackgroundTask(NS_NewRunnableFunction(
          "MockCubebStream::WaitForThawBeforeStart",
          [temp, this, self = RefPtr<SmartMockCubebStream>(mSelf)]() mutable {
            {
              // Unblock MockCubebStream::Start now that we have locked the
              // frozen start monitor.
              MonitorAutoLock tempLock(temp->mMonitor);
              temp->mFinished = true;
              temp->mMonitor.Notify();
              temp = nullptr;
            }
            {
              MonitorAutoLock lock(mFrozenStartMonitor);
              while (mFrozenStart) {
                mFrozenStartMonitor.Wait();
              }
            }
            if (MutexAutoLock l(mMutex);
                !mState || *mState != CUBEB_STATE_STARTED) {
              return;
            }
            MockCubeb::AsMock(context)->StartStream(mSelf);
          }));
      while (!temp->mFinished) {
        temp->mMonitor.Wait();
      }
      return CUBEB_OK;
    }
  }
  MockCubeb::AsMock(context)->StartStream(this);
  return CUBEB_OK;
}

int MockCubebStream::Stop() {
  MockCubeb::AsMock(context)->StopStream(this);
  MutexAutoLock l(mMutex);
  mOutputVerificationEvent.Notify(std::make_tuple(
      mAudioVerifier.PreSilenceSamples(), mAudioVerifier.EstimatedFreq(),
      mAudioVerifier.CountDiscontinuities()));
  NotifyState(CUBEB_STATE_STOPPED);
  return CUBEB_OK;
}

uint64_t MockCubebStream::Position() {
  MutexAutoLock l(mMutex);
  return mPosition;
}

void MockCubebStream::Destroy() {
  // Stop() even if cubeb_stream_stop() has already been called, as with
  // audioipc.  https://bugzilla.mozilla.org/show_bug.cgi?id=1801190#c1
  // This provides an extra STOPPED state callback as with audioipc.
  // It also ensures that this stream is removed from MockCubeb::mLiveStreams.
  Stop();
  {
    MutexAutoLock l(mMutex);
    mDestroyed = true;
  }
  MockCubeb::AsMock(context)->StreamDestroy(this);
}

int MockCubebStream::SetName(char const* aName) {
  MutexAutoLock l(mMutex);
  mName = aName;
  mNameSetEvent.Notify(mName);
  return CUBEB_OK;
}

int MockCubebStream::RegisterDeviceChangedCallback(
    cubeb_device_changed_callback aDeviceChangedCallback) {
  MutexAutoLock l(mMutex);
  if (mDeviceChangedCallback && aDeviceChangedCallback) {
    return CUBEB_ERROR_INVALID_PARAMETER;
  }
  mDeviceChangedCallback = aDeviceChangedCallback;
  return CUBEB_OK;
}

int MockCubebStream::SetInputProcessingParams(
    cubeb_input_processing_params aParams) {
  MockCubeb* mock = MockCubeb::AsMock(context);
  auto res = mock->SupportedInputProcessingParams();
  if (res.isErr()) {
    return CUBEB_ERROR_NOT_SUPPORTED;
  }
  cubeb_input_processing_params supported = res.unwrap();
  if ((supported & aParams) != aParams) {
    return CUBEB_ERROR_INVALID_PARAMETER;
  }
  return mock->InputProcessingApplyRv();
}

cubeb_stream* MockCubebStream::AsCubebStream() {
  MutexAutoLock l(mMutex);
  return AsCubebStreamLocked();
}

cubeb_stream* MockCubebStream::AsCubebStreamLocked() {
  MOZ_RELEASE_ASSERT(!mDestroyed);
  mMutex.AssertCurrentThreadOwns();
  return reinterpret_cast<cubeb_stream*>(this);
}

MockCubebStream* MockCubebStream::AsMock(cubeb_stream* aStream) {
  auto* mockStream = reinterpret_cast<MockCubebStream*>(aStream);
  MutexAutoLock l(mockStream->mMutex);
  return AsMockLocked(aStream);
}

MockCubebStream* MockCubebStream::AsMockLocked(cubeb_stream* aStream) {
  auto* mockStream = reinterpret_cast<MockCubebStream*>(aStream);
  mockStream->mMutex.AssertCurrentThreadOwns();
  MOZ_RELEASE_ASSERT(!mockStream->mDestroyed);
  return mockStream;
}

cubeb_devid MockCubebStream::GetInputDeviceID() const { return mInputDeviceID; }

cubeb_devid MockCubebStream::GetOutputDeviceID() const {
  return mOutputDeviceID;
}

uint32_t MockCubebStream::InputChannels() const {
  MutexAutoLock l(mMutex);
  return InputChannelsLocked();
}

uint32_t MockCubebStream::InputChannelsLocked() const {
  mMutex.AssertCurrentThreadOwns();
  return mAudioGenerator.ChannelCount();
}

uint32_t MockCubebStream::OutputChannels() const {
  MutexAutoLock l(mMutex);
  return OutputChannelsLocked();
}

uint32_t MockCubebStream::OutputChannelsLocked() const {
  mMutex.AssertCurrentThreadOwns();
  return mOutputParams.channels;
}

uint32_t MockCubebStream::SampleRate() const {
  MutexAutoLock l(mMutex);
  return SampleRateLocked();
}

uint32_t MockCubebStream::SampleRateLocked() const {
  mMutex.AssertCurrentThreadOwns();
  return mAudioGenerator.mSampleRate;
}

uint32_t MockCubebStream::InputFrequency() const {
  MutexAutoLock l(mMutex);
  return InputFrequencyLocked();
}

uint32_t MockCubebStream::InputFrequencyLocked() const {
  mMutex.AssertCurrentThreadOwns();
  return mAudioGenerator.mFrequency;
}

Maybe<cubeb_state> MockCubebStream::State() const {
  MutexAutoLock l(mMutex);
  return mState;
}

nsTArray<AudioDataValue>&& MockCubebStream::TakeRecordedOutput() {
  MutexAutoLock l(mMutex);
  return std::move(mRecordedOutput);
}

nsTArray<AudioDataValue>&& MockCubebStream::TakeRecordedInput() {
  MutexAutoLock l(mMutex);
  return std::move(mRecordedInput);
}

void MockCubebStream::SetDriftFactor(float aDriftFactor) {
  MOZ_RELEASE_ASSERT(mRunningMode == MockCubeb::RunningMode::Automatic);
  MutexAutoLock l(mMutex);
  mDriftFactor = aDriftFactor;
}

void MockCubebStream::ForceError() {
  MutexAutoLock l(mMutex);
  mForceErrorState = true;
}

void MockCubebStream::ForceDeviceChanged() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  MutexAutoLock l(mMutex);
  mForceDeviceChanged = true;
};

void MockCubebStream::NotifyDeviceChangedNow() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Manual);
  NotifyDeviceChanged();
}

void MockCubebStream::Thaw() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  MonitorAutoLock l(mFrozenStartMonitor);
  mFrozenStart = false;
  mFrozenStartMonitor.Notify();
}

void MockCubebStream::SetOutputRecordingEnabled(bool aEnabled) {
  MutexAutoLock l(mMutex);
  mOutputRecordingEnabled = aEnabled;
}

void MockCubebStream::SetInputRecordingEnabled(bool aEnabled) {
  MutexAutoLock l(mMutex);
  mInputRecordingEnabled = aEnabled;
}

MediaEventSource<nsCString>& MockCubebStream::NameSetEvent() {
  return mNameSetEvent;
}

MediaEventSource<cubeb_state>& MockCubebStream::StateEvent() {
  return mStateEvent;
}

MediaEventSource<uint32_t>& MockCubebStream::FramesProcessedEvent() {
  return mFramesProcessedEvent;
}

MediaEventSource<uint32_t>& MockCubebStream::FramesVerifiedEvent() {
  return mFramesVerifiedEvent;
}

MediaEventSource<std::tuple<uint64_t, float, uint32_t>>&
MockCubebStream::OutputVerificationEvent() {
  return mOutputVerificationEvent;
}

MediaEventSource<void>& MockCubebStream::ErrorForcedEvent() {
  return mErrorForcedEvent;
}

MediaEventSource<void>& MockCubebStream::DeviceChangeForcedEvent() {
  return mDeviceChangedForcedEvent;
}

KeepProcessing MockCubebStream::ManualDataCallback(long aNrFrames) {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Manual);
  MOZ_RELEASE_ASSERT(aNrFrames <= kMaxNrFrames);
  MutexAutoLock l(mMutex);
  return Process(aNrFrames);
}

KeepProcessing MockCubebStream::Process(long aNrFrames) {
  mMutex.AssertCurrentThreadOwns();
  if (!mState || *mState != CUBEB_STATE_STARTED) {
    return KeepProcessing::InvalidState;
  }
  if (mInputParams.rate) {
    mAudioGenerator.GenerateInterleaved(mInputBuffer, aNrFrames);
  }
  cubeb_stream* stream = AsCubebStreamLocked();
  const long outframes =
      mDataCallback(stream, mUserPtr, mHasInput ? mInputBuffer : nullptr,
                    mHasOutput ? mOutputBuffer : nullptr, aNrFrames);

  if (outframes > 0) {
    if (mInputRecordingEnabled && mHasInput) {
      mRecordedInput.AppendElements(mInputBuffer,
                                    outframes * InputChannelsLocked());
    }
    if (mOutputRecordingEnabled && mHasOutput) {
      mRecordedOutput.AppendElements(mOutputBuffer,

                                     outframes * OutputChannelsLocked());
    }
    mAudioVerifier.AppendDataInterleaved(mOutputBuffer, outframes,
                                         MAX_OUTPUT_CHANNELS);
    mPosition += outframes;

    mFramesProcessedEvent.Notify(outframes);
    if (mAudioVerifier.PreSilenceEnded()) {
      mFramesVerifiedEvent.Notify(outframes);
    }
  }

  if (outframes < aNrFrames) {
    NotifyState(CUBEB_STATE_DRAINED);
    return KeepProcessing::No;
  }
  if (mForceErrorState) {
    mForceErrorState = false;
    NotifyState(CUBEB_STATE_ERROR);
    mErrorForcedEvent.Notify();
    return KeepProcessing::No;
  }
  if (mForceDeviceChanged) {
    mForceDeviceChanged = false;
    // The device-changed callback is not necessary to be run in the
    // audio-callback thread. It's up to the platform APIs. We don't have any
    // control over them. Fire the device-changed callback in another thread to
    // simulate this.
    NS_DispatchBackgroundTask(NS_NewRunnableFunction(
        __func__, [this, self = RefPtr(mSelf)] { NotifyDeviceChanged(); }));
  }
  return KeepProcessing::Yes;
}

KeepProcessing MockCubebStream::Process10Ms() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  MutexAutoLock l(mMutex);
  uint32_t rate = SampleRateLocked();
  const long nrFrames =
      static_cast<long>(static_cast<float>(rate * 10) * mDriftFactor) /
      PR_MSEC_PER_SEC;
  return Process(nrFrames);
}

void MockCubebStream::NotifyState(cubeb_state aState) {
  mMutex.AssertCurrentThreadOwns();
  mState = Some(aState);
  mStateCallback(AsCubebStreamLocked(), mUserPtr, aState);
  mStateEvent.Notify(aState);
}

void MockCubebStream::NotifyDeviceChanged() {
  MutexAutoLock l(mMutex);
  mDeviceChangedCallback(this->mUserPtr);
  mDeviceChangedForcedEvent.Notify();
}

MockCubeb::MockCubeb() : MockCubeb(MockCubeb::RunningMode::Automatic) {}

MockCubeb::MockCubeb(RunningMode aRunningMode)
    : ops(&mock_ops), mRunningMode(aRunningMode) {}

MockCubeb::~MockCubeb() { MOZ_RELEASE_ASSERT(!mFakeAudioThread); };

void MockCubeb::Destroy() {
  MOZ_RELEASE_ASSERT(mHasCubebContext);
  {
    auto streams = mLiveStreams.Lock();
    MOZ_RELEASE_ASSERT(streams->IsEmpty());
  }
  mDestroyed = true;
  Release();
}

cubeb* MockCubeb::AsCubebContext() {
  MOZ_RELEASE_ASSERT(!mDestroyed);
  if (mHasCubebContext.compareExchange(falsetrue)) {
    AddRef();
  }
  return reinterpret_cast<cubeb*>(this);
}

MockCubeb* MockCubeb::AsMock(cubeb* aContext) {
  auto* mockCubeb = reinterpret_cast<MockCubeb*>(aContext);
  MOZ_RELEASE_ASSERT(!mockCubeb->mDestroyed);
  return mockCubeb;
}

int MockCubeb::EnumerateDevices(cubeb_device_type aType,
                                cubeb_device_collection* aCollection) {
#ifdef ANDROID
  EXPECT_TRUE(false) << "This is not to be called on Android.";
#endif
  size_t count = 0;
  if (aType & CUBEB_DEVICE_TYPE_INPUT) {
    count += mInputDevices.Length();
  }
  if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
    count += mOutputDevices.Length();
  }
  aCollection->device = new cubeb_device_info[count];
  aCollection->count = count;

  uint32_t collection_index = 0;
  if (aType & CUBEB_DEVICE_TYPE_INPUT) {
    for (auto& device : mInputDevices) {
      aCollection->device[collection_index] = device;
      collection_index++;
    }
  }
  if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
    for (auto& device : mOutputDevices) {
      aCollection->device[collection_index] = device;
      collection_index++;
    }
  }

  return CUBEB_OK;
}

int MockCubeb::DestroyDeviceCollection(cubeb_device_collection* aCollection) {
  delete[] aCollection->device;
  aCollection->count = 0;
  return CUBEB_OK;
}

int MockCubeb::RegisterDeviceCollectionChangeCallback(
    cubeb_device_type aDevType,
    cubeb_device_collection_changed_callback aCallback, void* aUserPtr) {
  if (!mSupportsDeviceCollectionChangedCallback) {
    return CUBEB_ERROR;
  }

  if (aDevType & CUBEB_DEVICE_TYPE_INPUT) {
    mInputDeviceCollectionChangeCallback = aCallback;
    mInputDeviceCollectionChangeUserPtr = aUserPtr;
  }
  if (aDevType & CUBEB_DEVICE_TYPE_OUTPUT) {
    mOutputDeviceCollectionChangeCallback = aCallback;
    mOutputDeviceCollectionChangeUserPtr = aUserPtr;
  }

  return CUBEB_OK;
}

Result<cubeb_input_processing_params, int>
MockCubeb::SupportedInputProcessingParams() const {
  const auto& [params, rv] = mSupportedInputProcessingParams;
  if (rv != CUBEB_OK) {
    return Err(rv);
  }
  return params;
}

void MockCubeb::SetSupportedInputProcessingParams(
    cubeb_input_processing_params aParams, int aRv) {
  mSupportedInputProcessingParams = std::make_pair(aParams, aRv);
}

void MockCubeb::SetInputProcessingApplyRv(int aRv) {
  mInputProcessingParamsApplyRv = aRv;
}

int MockCubeb::InputProcessingApplyRv() const {
  return mInputProcessingParamsApplyRv;
}

void MockCubeb::AddDevice(cubeb_device_info aDevice) {
  if (aDevice.type == CUBEB_DEVICE_TYPE_INPUT) {
    mInputDevices.AppendElement(aDevice);
  } else if (aDevice.type == CUBEB_DEVICE_TYPE_OUTPUT) {
    mOutputDevices.AppendElement(aDevice);
  } else {
    MOZ_CRASH("bad device type when adding a device in mock cubeb backend");
  }

  bool isInput = aDevice.type & CUBEB_DEVICE_TYPE_INPUT;
  if (isInput && mInputDeviceCollectionChangeCallback) {
    mInputDeviceCollectionChangeCallback(AsCubebContext(),
                                         mInputDeviceCollectionChangeUserPtr);
  }
  if (!isInput && mOutputDeviceCollectionChangeCallback) {
    mOutputDeviceCollectionChangeCallback(AsCubebContext(),
                                          mOutputDeviceCollectionChangeUserPtr);
  }
}

bool MockCubeb::RemoveDevice(cubeb_devid aId) {
  bool foundInput = false;
  bool foundOutput = false;
  mInputDevices.RemoveElementsBy(
      [aId, &foundInput](cubeb_device_info& aDeviceInfo) {
        bool foundThisTime = aDeviceInfo.devid == aId;
        foundInput |= foundThisTime;
        return foundThisTime;
      });
  mOutputDevices.RemoveElementsBy(
      [aId, &foundOutput](cubeb_device_info& aDeviceInfo) {
        bool foundThisTime = aDeviceInfo.devid == aId;
        foundOutput |= foundThisTime;
        return foundThisTime;
      });

  if (foundInput && mInputDeviceCollectionChangeCallback) {
    mInputDeviceCollectionChangeCallback(AsCubebContext(),
                                         mInputDeviceCollectionChangeUserPtr);
  }
  if (foundOutput && mOutputDeviceCollectionChangeCallback) {
    mOutputDeviceCollectionChangeCallback(AsCubebContext(),
                                          mOutputDeviceCollectionChangeUserPtr);
  }
  // If the device removed was a default device, set another device as the
  // default, if there are still devices available.
  bool foundDefault = false;
  for (uint32_t i = 0; i < mInputDevices.Length(); i++) {
    foundDefault |= mInputDevices[i].preferred != CUBEB_DEVICE_PREF_NONE;
  }

  if (!foundDefault) {
    if (!mInputDevices.IsEmpty()) {
      mInputDevices[mInputDevices.Length() - 1].preferred =
          CUBEB_DEVICE_PREF_ALL;
    }
  }

  foundDefault = false;
  for (uint32_t i = 0; i < mOutputDevices.Length(); i++) {
    foundDefault |= mOutputDevices[i].preferred != CUBEB_DEVICE_PREF_NONE;
  }

  if (!foundDefault) {
    if (!mOutputDevices.IsEmpty()) {
      mOutputDevices[mOutputDevices.Length() - 1].preferred =
          CUBEB_DEVICE_PREF_ALL;
    }
  }

  return foundInput | foundOutput;
}

void MockCubeb::ClearDevices(cubeb_device_type aType) {
  mInputDevices.Clear();
  mOutputDevices.Clear();
}

void MockCubeb::SetSupportDeviceChangeCallback(bool aSupports) {
  mSupportsDeviceCollectionChangedCallback = aSupports;
}

void MockCubeb::ForceStreamInitError() { mStreamInitErrorState = true; }

void MockCubeb::SetStreamStartFreezeEnabled(bool aEnabled) {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  mStreamStartFreezeEnabled = aEnabled;
}

auto MockCubeb::ForceAudioThread() -> RefPtr<ForcedAudioThreadPromise> {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  RefPtr<ForcedAudioThreadPromise> p =
      mForcedAudioThreadPromise.Ensure(__func__);
  mForcedAudioThread = true;
  StartStream(nullptr);
  return p;
}

void MockCubeb::UnforceAudioThread() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  mForcedAudioThread = false;
}

int MockCubeb::StreamInit(cubeb* aContext, cubeb_stream** aStream,
                          char const* aStreamName, cubeb_devid aInputDevice,
                          cubeb_stream_params* aInputStreamParams,
                          cubeb_devid aOutputDevice,
                          cubeb_stream_params* aOutputStreamParams,
                          cubeb_data_callback aDataCallback,
                          cubeb_state_callback aStateCallback, void* aUserPtr) {
  if (mStreamInitErrorState.compareExchange(truefalse)) {
    mStreamInitEvent.Notify(nullptr);
    return CUBEB_ERROR_DEVICE_UNAVAILABLE;
  }

  auto mockStream = MakeRefPtr<SmartMockCubebStream>(
      aContext, aStreamName, aInputDevice, aInputStreamParams, aOutputDevice,
      aOutputStreamParams, aDataCallback, aStateCallback, aUserPtr,
      mRunningMode, mStreamStartFreezeEnabled);
  *aStream = mockStream->AsCubebStream();
  mStreamInitEvent.Notify(mockStream);
  // AddRef the stream to keep it alive. StreamDestroy releases it.
  Unused << mockStream.forget().take();
  return CUBEB_OK;
}

void MockCubeb::StreamDestroy(MockCubebStream* aStream) {
  RefPtr<SmartMockCubebStream> mockStream = dont_AddRef(aStream->mSelf);
  mStreamDestroyEvent.Notify(mockStream);
}

void MockCubeb::GoFaster() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  mFastMode = true;
}

void MockCubeb::DontGoFaster() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  mFastMode = false;
}

MediaEventSource<RefPtr<SmartMockCubebStream>>& MockCubeb::StreamInitEvent() {
  return mStreamInitEvent;
}

MediaEventSource<RefPtr<SmartMockCubebStream>>&
MockCubeb::StreamDestroyEvent() {
  return mStreamDestroyEvent;
}

void MockCubeb::StartStream(MockCubebStream* aStream) {
  if (aStream) {
    aStream->mMutex.AssertNotCurrentThreadOwns();
  }
  auto streams = mLiveStreams.Lock();
  if (aStream) {
    MOZ_RELEASE_ASSERT(!streams->Contains(aStream->mSelf));
    streams->AppendElement(aStream->mSelf);
  } else {
    MOZ_RELEASE_ASSERT(mForcedAudioThread);
    // Forcing an audio thread must happen before starting streams
    MOZ_RELEASE_ASSERT(streams->IsEmpty());
  }
  if (!mFakeAudioThread && mRunningMode == RunningMode::Automatic) {
    AddRef();  // released when the thread exits
    mFakeAudioThread = WrapUnique(new std::thread(ThreadFunction_s, this));
  }
}

void MockCubeb::StopStream(MockCubebStream* aStream) {
  aStream->mMutex.AssertNotCurrentThreadOwns();
  {
    auto streams = mLiveStreams.Lock();
    if (!streams->Contains(aStream->mSelf)) {
      return;
    }
    streams->RemoveElement(aStream->mSelf);
  }
}

void MockCubeb::ThreadFunction() {
  MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
  if (mForcedAudioThread) {
    mForcedAudioThreadPromise.Resolve(MakeRefPtr<AudioThreadAutoUnforcer>(this),
                                      __func__);
  }
  while (true) {
    {
      auto streams = mLiveStreams.Lock();
      for (auto& stream : *streams) {
        auto keepProcessing = stream->Process10Ms();
        if (keepProcessing == KeepProcessing::No) {
          stream = nullptr;
        }
      }
      streams->RemoveElementsBy([](const auto& stream) { return !stream; });
      MOZ_RELEASE_ASSERT(mFakeAudioThread);
      if (streams->IsEmpty() && !mForcedAudioThread) {
        // This leaks the std::thread if Gecko's main thread has already been
        // shut down.
        NS_DispatchToMainThread(NS_NewRunnableFunction(
            __func__, [audioThread = std::move(mFakeAudioThread)] {
              audioThread->join();
            }));
        break;
      }
    }
    std::this_thread::sleep_for(
        std::chrono::microseconds(mFastMode ? 0 : 10 * PR_USEC_PER_MSEC));
  }
  Release();
}

}  // namespace mozilla

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.