tor-browser

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

CubebUtils.cpp (36375B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CubebUtils.h"
      8 
      9 #include "audio_thread_priority.h"
     10 #include "mozilla/AbstractThread.h"
     11 #include "mozilla/Components.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/Preferences.h"
     14 #include "mozilla/SharedThreadPool.h"
     15 #include "mozilla/Sprintf.h"
     16 #include "mozilla/StaticMutex.h"
     17 #include "mozilla/StaticPtr.h"
     18 #include "mozilla/UnderrunHandler.h"
     19 #include "mozilla/dom/ContentChild.h"
     20 #include "mozilla/glean/DomMediaMetrics.h"
     21 #include "mozilla/ipc/FileDescriptor.h"
     22 #if defined(MOZ_SANDBOX)
     23 #  include "mozilla/SandboxSettings.h"
     24 #endif
     25 #include <stdint.h>
     26 
     27 #include <algorithm>
     28 
     29 #include "nsContentUtils.h"
     30 #include "nsDebug.h"
     31 #include "nsIStringBundle.h"
     32 #include "nsString.h"
     33 #include "nsThreadUtils.h"
     34 #include "prdtoa.h"
     35 #ifdef MOZ_WIDGET_ANDROID
     36 #  include "mozilla/java/GeckoAppShellWrappers.h"
     37 #endif
     38 #ifdef XP_WIN
     39 #  include "mozilla/mscom/EnsureMTA.h"
     40 #endif
     41 #include <cmath>
     42 #include <thread>
     43 
     44 #include "CallbackThreadRegistry.h"
     45 #include "CubebDeviceEnumerator.h"
     46 #include "audioipc2_client_ffi_generated.h"
     47 #include "audioipc2_server_ffi_generated.h"
     48 #include "mozilla/StaticPrefs_media.h"
     49 
     50 #if defined(ENABLE_TESTS) || defined(FUZZING)
     51 #  define ENABLE_MOCK_CUBEB 1
     52 #  include "MockCubeb.h"
     53 #endif
     54 
     55 #define AUDIOIPC_STACK_SIZE_DEFAULT (64 * 4096)
     56 
     57 #define PREF_VOLUME_SCALE "media.volume_scale"
     58 #define PREF_CUBEB_BACKEND "media.cubeb.backend"
     59 #define PREF_CUBEB_OUTPUT_DEVICE "media.cubeb.output_device"
     60 #define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
     61 #define PREF_CUBEB_LATENCY_MTG "media.cubeb_latency_mtg_frames"
     62 // Allows to get something non-default for the preferred sample-rate, to allow
     63 // troubleshooting in the field and testing.
     64 #define PREF_CUBEB_FORCE_SAMPLE_RATE "media.cubeb.force_sample_rate"
     65 #define PREF_CUBEB_LOGGING_LEVEL "logging.cubeb"
     66 // Hidden pref used by tests to force failure to obtain cubeb context
     67 #define PREF_CUBEB_FORCE_NULL_CONTEXT "media.cubeb.force_null_context"
     68 #define PREF_CUBEB_FORCE_MOCK_CONTEXT "media.cubeb.force_mock_context"
     69 #define PREF_CUBEB_OUTPUT_VOICE_ROUTING "media.cubeb.output_voice_routing"
     70 #define PREF_CUBEB_SANDBOX "media.cubeb.sandbox"
     71 #define PREF_AUDIOIPC_STACK_SIZE "media.audioipc.stack_size"
     72 #define PREF_AUDIOIPC_SHM_AREA_SIZE "media.audioipc.shm_area_size"
     73 
     74 #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN)
     75 #  define MOZ_CUBEB_REMOTING
     76 #endif
     77 
     78 namespace mozilla {
     79 
     80 namespace {
     81 
     82 using glean::media_audio::BackendLabel;
     83 
     84 LazyLogModule gCubebLog("cubeb");
     85 
     86 void CubebLogCallback(const char* aFmt, ...) {
     87  char buffer[1024];
     88 
     89  va_list arglist;
     90  va_start(arglist, aFmt);
     91  VsprintfLiteral(buffer, aFmt, arglist);
     92  MOZ_LOG(gCubebLog, LogLevel::Error, ("%s", buffer));
     93  va_end(arglist);
     94 }
     95 
     96 // This mutex protects the variables below.
     97 StaticMutex sMutex;
     98 enum class CubebState {
     99  Uninitialized = 0,
    100  Initialized,
    101  Shutdown
    102 } sCubebState = CubebState::Uninitialized;
    103 StaticRefPtr<CubebUtils::CubebHandle> sCubebHandle;
    104 double sVolumeScale = 1.0;
    105 uint32_t sCubebPlaybackLatencyInMilliseconds = 100;
    106 uint32_t sCubebMTGLatencyInFrames = 512;
    107 // If sCubebForcedSampleRate is zero, PreferredSampleRate will return the
    108 // preferred sample-rate for the audio backend in use. Otherwise, it will be
    109 // used as the preferred sample-rate.
    110 Atomic<uint32_t> sCubebForcedSampleRate{0};
    111 bool sCubebPlaybackLatencyPrefSet = false;
    112 bool sCubebMTGLatencyPrefSet = false;
    113 bool sAudioStreamInitEverSucceeded = false;
    114 bool sCubebForceNullContext = false;
    115 bool sRouteOutputAsVoice = false;
    116 #ifdef MOZ_CUBEB_REMOTING
    117 bool sCubebSandbox = false;
    118 size_t sAudioIPCStackSize;
    119 size_t sAudioIPCShmAreaSize;
    120 #endif
    121 StaticAutoPtr<char> sBrandName;
    122 StaticAutoPtr<char> sCubebBackendName;
    123 StaticAutoPtr<char> sCubebOutputDeviceName;
    124 #ifdef MOZ_WIDGET_ANDROID
    125 // Counts the number of time a request for switching to global "communication
    126 // mode" has been received. If this is > 0, global communication mode is to be
    127 // enabled. If it is 0, the global communication mode is to be disabled.
    128 // This allows to correctly track the global behaviour to adopt accross
    129 // asynchronous GraphDriver changes, on Android.
    130 int sInCommunicationCount = 0;
    131 #endif
    132 
    133 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
    134 
    135 MOZ_RUNINIT std::unordered_map<std::string, BackendLabel>
    136    kTelemetryBackendLabel = {
    137        {"audiounit", BackendLabel::eAudiounit},
    138        {"audiounit-rust", BackendLabel::eAudiounitRust},
    139        {"aaudio", BackendLabel::eAaudio},
    140        {"opensl", BackendLabel::eOpensl},
    141        {"wasapi", BackendLabel::eWasapi},
    142        {"winmm", BackendLabel::eWinmm},
    143        {"alsa", BackendLabel::eAlsa},
    144        {"jack", BackendLabel::eJack},
    145        {"oss", BackendLabel::eOss},
    146        {"pulse", BackendLabel::ePulse},
    147        {"pulse-rust", BackendLabel::ePulseRust},
    148        {"sndio", BackendLabel::eSndio},
    149        {"sun", BackendLabel::eSunaudio},
    150 };
    151 
    152 // Prefered samplerate, in Hz (characteristic of the hardware, mixer, platform,
    153 // and API used).
    154 //
    155 // sMutex protects *initialization* of this, which must be performed from each
    156 // thread before fetching, after which it is safe to fetch without holding the
    157 // mutex because it is only written once per process execution (by the first
    158 // initialization to complete).  Since the init must have been called on a
    159 // given thread before fetching the value, it's guaranteed (via the mutex) that
    160 // sufficient memory barriers have occurred to ensure the correct value is
    161 // visible on the querying thread/CPU.
    162 static Atomic<uint32_t> sPreferredSampleRate{0};
    163 
    164 #ifdef MOZ_CUBEB_REMOTING
    165 // AudioIPC server handle
    166 void* sServerHandle = nullptr;
    167 
    168 // Initialized during early startup, protected by sMutex.
    169 StaticAutoPtr<ipc::FileDescriptor> sIPCConnection;
    170 
    171 static bool StartAudioIPCServer() {
    172  if (sCubebSandbox) {
    173    audioipc2::AudioIpcServerInitParams initParams{};
    174    initParams.mThreadCreateCallback = [](const char* aName) {
    175      PROFILER_REGISTER_THREAD(aName);
    176    };
    177    initParams.mThreadDestroyCallback = []() { PROFILER_UNREGISTER_THREAD(); };
    178 
    179    sServerHandle = audioipc2::audioipc2_server_start(
    180        sBrandName, sCubebBackendName, &initParams);
    181  }
    182  return sServerHandle != nullptr;
    183 }
    184 
    185 static void ShutdownAudioIPCServer() {
    186  if (!sServerHandle) {
    187    return;
    188  }
    189 
    190  audioipc2::audioipc2_server_stop(sServerHandle);
    191  sServerHandle = nullptr;
    192 }
    193 #endif  // MOZ_CUBEB_REMOTING
    194 }  // namespace
    195 
    196 static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
    197 // Consevative default that can work on all platforms.
    198 static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
    199 
    200 namespace CubebUtils {
    201 nsCString ProcessingParamsToString(cubeb_input_processing_params aParams) {
    202  if (aParams == CUBEB_INPUT_PROCESSING_PARAM_NONE) {
    203    return "NONE"_ns;
    204  }
    205  nsCString str;
    206  for (auto p : {CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    207                 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL,
    208                 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
    209                 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION}) {
    210    if (!(aParams & p)) {
    211      continue;
    212    }
    213    if (!str.IsEmpty()) {
    214      str.Append(" | ");
    215    }
    216    str.Append([&p] {
    217      switch (p) {
    218        case CUBEB_INPUT_PROCESSING_PARAM_NONE:
    219          // Handled above.
    220          MOZ_CRASH(
    221              "NONE is the absence of a param, thus not for logging here.");
    222        case CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION:
    223          return "AEC";
    224        case CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL:
    225          return "AGC";
    226        case CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION:
    227          return "NS";
    228        case CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION:
    229          return "VOICE";
    230      }
    231      MOZ_ASSERT_UNREACHABLE("Unexpected input processing param");
    232      return "<Unknown input processing param>";
    233    }());
    234  }
    235  return str;
    236 }
    237 
    238 RefPtr<CubebHandle> GetCubebUnlocked();
    239 
    240 void GetPrefAndSetString(const char* aPref, StaticAutoPtr<char>& aStorage) {
    241  nsAutoCString value;
    242  Preferences::GetCString(aPref, value);
    243  if (value.IsEmpty()) {
    244    aStorage = nullptr;
    245  } else {
    246    aStorage = new char[value.Length() + 1];
    247    PodCopy(aStorage.get(), value.get(), value.Length());
    248    aStorage[value.Length()] = 0;
    249  }
    250 }
    251 
    252 void PrefChanged(const char* aPref, void* aClosure) {
    253  if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
    254    nsAutoCString value;
    255    Preferences::GetCString(aPref, value);
    256    StaticMutexAutoLock lock(sMutex);
    257    if (value.IsEmpty()) {
    258      sVolumeScale = 1.0;
    259    } else {
    260      sVolumeScale = std::max<double>(0, PR_strtod(value.get(), nullptr));
    261    }
    262  } else if (strcmp(aPref, PREF_CUBEB_LATENCY_PLAYBACK) == 0) {
    263    StaticMutexAutoLock lock(sMutex);
    264    // Arbitrary default stream latency of 100ms.  The higher this
    265    // value, the longer stream volume changes will take to become
    266    // audible.
    267    sCubebPlaybackLatencyPrefSet = Preferences::HasUserValue(aPref);
    268    uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
    269    sCubebPlaybackLatencyInMilliseconds =
    270        std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
    271  } else if (strcmp(aPref, PREF_CUBEB_LATENCY_MTG) == 0) {
    272    StaticMutexAutoLock lock(sMutex);
    273    sCubebMTGLatencyPrefSet = Preferences::HasUserValue(aPref);
    274    uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_FRAMES);
    275    // 128 is the block size for the Web Audio API, which limits how low the
    276    // latency can be here.
    277    // We don't want to limit the upper limit too much, so that people can
    278    // experiment.
    279    sCubebMTGLatencyInFrames =
    280        std::min<uint32_t>(std::max<uint32_t>(value, 128), 1e6);
    281  } else if (strcmp(aPref, PREF_CUBEB_FORCE_SAMPLE_RATE) == 0) {
    282    StaticMutexAutoLock lock(sMutex);
    283    sCubebForcedSampleRate = Preferences::GetUint(aPref);
    284  } else if (strcmp(aPref, PREF_CUBEB_LOGGING_LEVEL) == 0) {
    285    LogLevel value =
    286        ToLogLevel(Preferences::GetInt(aPref, 0 /* LogLevel::Disabled */));
    287    if (value == LogLevel::Verbose) {
    288      cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
    289    } else if (value == LogLevel::Debug) {
    290      cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
    291    } else if (value == LogLevel::Disabled) {
    292      cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
    293    }
    294  } else if (strcmp(aPref, PREF_CUBEB_BACKEND) == 0) {
    295    StaticMutexAutoLock lock(sMutex);
    296    GetPrefAndSetString(aPref, sCubebBackendName);
    297  } else if (strcmp(aPref, PREF_CUBEB_OUTPUT_DEVICE) == 0) {
    298    StaticMutexAutoLock lock(sMutex);
    299    GetPrefAndSetString(aPref, sCubebOutputDeviceName);
    300  } else if (strcmp(aPref, PREF_CUBEB_FORCE_NULL_CONTEXT) == 0) {
    301    StaticMutexAutoLock lock(sMutex);
    302    sCubebForceNullContext = Preferences::GetBool(aPref, false);
    303    MOZ_LOG(gCubebLog, LogLevel::Verbose,
    304            ("%s: %s", PREF_CUBEB_FORCE_NULL_CONTEXT,
    305             sCubebForceNullContext ? "true" : "false"));
    306  }
    307 #ifdef ENABLE_MOCK_CUBEB
    308  else if (strcmp(aPref, PREF_CUBEB_FORCE_MOCK_CONTEXT) == 0) {
    309    if (Preferences::GetBool(aPref, false)) {
    310      MockCubeb* mock = new MockCubeb();
    311      constexpr const char* kGroupIds[] = {"group_id_1", "group_id_2",
    312                                           "group_id_3"};
    313      {
    314        constexpr size_t kNumDevices = 3;
    315        constexpr const char* kDeviceIds[] = {"mock_input_1", "mock_input_2",
    316                                              "mock_input_3"};
    317        constexpr const char* kDeviceNames[] = {
    318            "Fake Audio Input 1", "Fake Audio Input 2 (PREFERRED)",
    319            "Fake Audio Input 3"};
    320        for (size_t i = 0; i < kNumDevices; ++i) {
    321          cubeb_device_info devinfo{
    322              .devid = (cubeb_devid)(i + 1),
    323              .device_id = kDeviceIds[i],
    324              .friendly_name = kDeviceNames[i],
    325              .group_id = kGroupIds[i],
    326              .vendor_name = "Mozilla",
    327              .type = CUBEB_DEVICE_TYPE_INPUT,
    328              .state = CUBEB_DEVICE_STATE_ENABLED,
    329              .preferred =
    330                  i == 1 ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE,
    331              .format = CUBEB_DEVICE_FMT_F32NE,
    332              .default_format = CUBEB_DEVICE_FMT_F32NE,
    333              .max_channels = 2,
    334              .default_rate = 44100,
    335              .max_rate = 44100,
    336              .min_rate = 16000,
    337              .latency_lo = 256,
    338              .latency_hi = 1024,
    339          };
    340          mock->AddDevice(devinfo);
    341        }
    342      }
    343 
    344      {
    345        constexpr size_t kNumDevices = 2;
    346        constexpr const char* kDeviceIds[] = {"mock_output_0", "mock_output_1"};
    347        constexpr const char* kDeviceNames[] = {
    348            "Fake Audio Output 1 (PREFERRED)", "Fake Audio Output 2"};
    349        for (size_t i = 0; i < kNumDevices; ++i) {
    350          cubeb_device_info devinfo{
    351              .devid = (cubeb_devid)(i + 1),
    352              .device_id = kDeviceIds[i],
    353              .friendly_name = kDeviceNames[i],
    354              .group_id = kGroupIds[i],
    355              .vendor_name = "Mozilla",
    356              .type = CUBEB_DEVICE_TYPE_OUTPUT,
    357              .state = CUBEB_DEVICE_STATE_ENABLED,
    358              .preferred =
    359                  i == 0 ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE,
    360              .format = CUBEB_DEVICE_FMT_F32NE,
    361              .default_format = CUBEB_DEVICE_FMT_F32NE,
    362              .max_channels = 2,
    363              .default_rate = 44100,
    364              .max_rate = 44100,
    365              .min_rate = 16000,
    366              .latency_lo = 256,
    367              .latency_hi = 1024,
    368          };
    369          mock->AddDevice(devinfo);
    370        }
    371      }
    372      ForceSetCubebContext(mock->AsCubebContext());
    373    } else {
    374      ForceUnsetCubebContext();
    375    }
    376  }
    377 #endif
    378 #ifdef MOZ_CUBEB_REMOTING
    379  else if (strcmp(aPref, PREF_CUBEB_SANDBOX) == 0) {
    380    StaticMutexAutoLock lock(sMutex);
    381    sCubebSandbox = Preferences::GetBool(aPref);
    382    MOZ_LOG(gCubebLog, LogLevel::Verbose,
    383            ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
    384 #  if defined(MOZ_SANDBOX)
    385    if (!sCubebSandbox && IsContentSandboxEnabled()) {
    386      sCubebSandbox = true;
    387      MOZ_LOG(gCubebLog, LogLevel::Error,
    388              ("%s: false, but content sandbox enabled - forcing true",
    389               PREF_CUBEB_SANDBOX));
    390    }
    391 #  endif
    392  } else if (strcmp(aPref, PREF_AUDIOIPC_STACK_SIZE) == 0) {
    393    StaticMutexAutoLock lock(sMutex);
    394    sAudioIPCStackSize = Preferences::GetUint(PREF_AUDIOIPC_STACK_SIZE,
    395                                              AUDIOIPC_STACK_SIZE_DEFAULT);
    396  } else if (strcmp(aPref, PREF_AUDIOIPC_SHM_AREA_SIZE) == 0) {
    397    StaticMutexAutoLock lock(sMutex);
    398    sAudioIPCShmAreaSize = Preferences::GetUint(PREF_AUDIOIPC_SHM_AREA_SIZE);
    399  }
    400 #endif
    401  else if (strcmp(aPref, PREF_CUBEB_OUTPUT_VOICE_ROUTING) == 0) {
    402    StaticMutexAutoLock lock(sMutex);
    403    sRouteOutputAsVoice = Preferences::GetBool(aPref);
    404    MOZ_LOG(gCubebLog, LogLevel::Verbose,
    405            ("%s: %s", PREF_CUBEB_OUTPUT_VOICE_ROUTING,
    406             sRouteOutputAsVoice ? "true" : "false"));
    407  }
    408 }
    409 
    410 bool GetFirstStream() {
    411  static bool sFirstStream = true;
    412 
    413  StaticMutexAutoLock lock(sMutex);
    414  bool result = sFirstStream;
    415  sFirstStream = false;
    416  return result;
    417 }
    418 
    419 double GetVolumeScale() {
    420  StaticMutexAutoLock lock(sMutex);
    421  return sVolumeScale;
    422 }
    423 
    424 RefPtr<CubebHandle> GetCubeb() {
    425  StaticMutexAutoLock lock(sMutex);
    426  return GetCubebUnlocked();
    427 }
    428 
    429 #ifdef ENABLE_MOCK_CUBEB
    430 // This is only exported when running tests.
    431 void ForceSetCubebContext(cubeb* aCubebContext) {
    432  RefPtr<CubebHandle> oldHandle;  // For release without sMutex
    433  {
    434    StaticMutexAutoLock lock(sMutex);
    435    oldHandle = sCubebHandle.forget();
    436    sCubebHandle = aCubebContext ? new CubebHandle(aCubebContext) : nullptr;
    437    sCubebState = CubebState::Initialized;
    438  }
    439  CubebDeviceEnumerator::Shutdown();
    440 }
    441 
    442 void ForceUnsetCubebContext() {
    443  RefPtr<CubebHandle> oldHandle;  // For release without sMutex
    444  StaticMutexAutoLock lock(sMutex);
    445  oldHandle = sCubebHandle.forget();
    446  sCubebState = CubebState::Uninitialized;
    447 }
    448 #endif
    449 
    450 void SetInCommunication(bool aInCommunication) {
    451 #ifdef MOZ_WIDGET_ANDROID
    452  StaticMutexAutoLock lock(sMutex);
    453  if (aInCommunication) {
    454    sInCommunicationCount++;
    455  } else {
    456    MOZ_ASSERT(sInCommunicationCount > 0);
    457    sInCommunicationCount--;
    458  }
    459 
    460  if (sInCommunicationCount == 1) {
    461    java::GeckoAppShell::SetCommunicationAudioModeOn(true);
    462  } else if (sInCommunicationCount == 0) {
    463    java::GeckoAppShell::SetCommunicationAudioModeOn(false);
    464  }
    465 #endif
    466 }
    467 
    468 bool InitPreferredSampleRate() MOZ_REQUIRES(sMutex) {
    469  sMutex.AssertCurrentThreadOwns();
    470  if (sPreferredSampleRate != 0) {
    471    return true;
    472  }
    473 #ifdef MOZ_WIDGET_ANDROID
    474  int rate = AndroidGetAudioOutputSampleRate();
    475  if (rate > 0) {
    476    sPreferredSampleRate = rate;
    477    return true;
    478  } else {
    479    return false;
    480  }
    481 #else
    482  RefPtr<CubebHandle> handle = GetCubebUnlocked();
    483  if (!handle) {
    484    return false;
    485  }
    486  uint32_t rate;
    487  {
    488    StaticMutexAutoUnlock unlock(sMutex);
    489    if (cubeb_get_preferred_sample_rate(handle->Context(), &rate) != CUBEB_OK) {
    490      return false;
    491    }
    492  }
    493  sPreferredSampleRate = rate;
    494 #endif
    495  MOZ_ASSERT(sPreferredSampleRate);
    496  return true;
    497 }
    498 
    499 uint32_t PreferredSampleRate(bool aShouldResistFingerprinting) {
    500  StaticMutexAutoLock lock(sMutex);
    501  if (sCubebForcedSampleRate) {
    502    return sCubebForcedSampleRate;
    503  }
    504  if (aShouldResistFingerprinting) {
    505    return 44100;
    506  }
    507  if (!InitPreferredSampleRate()) {
    508    return 44100;
    509  }
    510  MOZ_ASSERT(sPreferredSampleRate);
    511  return sPreferredSampleRate;
    512 }
    513 
    514 int CubebStreamInit(cubeb* context, cubeb_stream** stream,
    515                    char const* stream_name, cubeb_devid input_device,
    516                    cubeb_stream_params* input_stream_params,
    517                    cubeb_devid output_device,
    518                    cubeb_stream_params* output_stream_params,
    519                    uint32_t latency_frames, cubeb_data_callback data_callback,
    520                    cubeb_state_callback state_callback, void* user_ptr) {
    521  uint32_t ms = StaticPrefs::media_cubeb_slow_stream_init_ms();
    522  if (ms) {
    523    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
    524  }
    525  cubeb_stream_params inputParamData;
    526  cubeb_stream_params outputParamData;
    527  cubeb_stream_params* inputParamPtr = input_stream_params;
    528  cubeb_stream_params* outputParamPtr = output_stream_params;
    529  if (input_stream_params && !output_stream_params) {
    530    inputParamData = *input_stream_params;
    531    inputParamData.rate = llround(
    532        static_cast<double>(StaticPrefs::media_cubeb_input_drift_factor()) *
    533        inputParamData.rate);
    534    MOZ_LOG(
    535        gCubebLog, LogLevel::Info,
    536        ("CubebStreamInit input stream rate %" PRIu32, inputParamData.rate));
    537    inputParamPtr = &inputParamData;
    538  } else if (output_stream_params && !input_stream_params) {
    539    outputParamData = *output_stream_params;
    540    outputParamData.rate = llround(
    541        static_cast<double>(StaticPrefs::media_cubeb_output_drift_factor()) *
    542        outputParamData.rate);
    543    MOZ_LOG(
    544        gCubebLog, LogLevel::Info,
    545        ("CubebStreamInit output stream rate %" PRIu32, outputParamData.rate));
    546    outputParamPtr = &outputParamData;
    547  }
    548 
    549  return cubeb_stream_init(
    550      context, stream, stream_name, input_device, inputParamPtr, output_device,
    551      outputParamPtr, latency_frames, data_callback, state_callback, user_ptr);
    552 }
    553 
    554 void InitBrandName() {
    555  if (sBrandName) {
    556    return;
    557  }
    558  nsAutoString brandName;
    559  nsCOMPtr<nsIStringBundleService> stringBundleService =
    560      mozilla::components::StringBundle::Service();
    561  if (stringBundleService) {
    562    nsCOMPtr<nsIStringBundle> brandBundle;
    563    nsresult rv = stringBundleService->CreateBundle(
    564        kBrandBundleURL, getter_AddRefs(brandBundle));
    565    if (NS_SUCCEEDED(rv)) {
    566      rv = brandBundle->GetStringFromName("brandShortName", brandName);
    567      NS_WARNING_ASSERTION(
    568          NS_SUCCEEDED(rv),
    569          "Could not get the program name for a cubeb stream.");
    570    }
    571  }
    572  NS_LossyConvertUTF16toASCII ascii(brandName);
    573  sBrandName = new char[ascii.Length() + 1];
    574  PodCopy(sBrandName.get(), ascii.get(), ascii.Length());
    575  sBrandName[ascii.Length()] = 0;
    576 }
    577 
    578 #ifdef MOZ_CUBEB_REMOTING
    579 void InitAudioIPCConnection() {
    580  MOZ_ASSERT(NS_IsMainThread());
    581  auto contentChild = dom::ContentChild::GetSingleton();
    582  auto promise = contentChild->SendCreateAudioIPCConnection();
    583  promise->Then(
    584      AbstractThread::MainThread(), __func__,
    585      [](dom::FileDescOrError&& aFD) {
    586        StaticMutexAutoLock lock(sMutex);
    587        MOZ_ASSERT(!sIPCConnection);
    588        if (aFD.type() == dom::FileDescOrError::Type::TFileDescriptor) {
    589          sIPCConnection = new ipc::FileDescriptor(std::move(aFD));
    590        } else {
    591          MOZ_LOG(gCubebLog, LogLevel::Error,
    592                  ("SendCreateAudioIPCConnection failed: invalid FD"));
    593        }
    594      },
    595      [](mozilla::ipc::ResponseRejectReason&& aReason) {
    596        MOZ_LOG(gCubebLog, LogLevel::Error,
    597                ("SendCreateAudioIPCConnection rejected: %d", int(aReason)));
    598      });
    599 }
    600 #endif
    601 
    602 #ifdef MOZ_CUBEB_REMOTING
    603 ipc::FileDescriptor CreateAudioIPCConnectionUnlocked() {
    604  MOZ_ASSERT(sCubebSandbox && XRE_IsParentProcess());
    605  if (!sServerHandle) {
    606    MOZ_LOG(gCubebLog, LogLevel::Debug, ("Starting cubeb server..."));
    607    if (!StartAudioIPCServer()) {
    608      MOZ_LOG(gCubebLog, LogLevel::Error, ("audioipc_server_start failed"));
    609      return ipc::FileDescriptor();
    610    }
    611  }
    612  MOZ_LOG(gCubebLog, LogLevel::Debug,
    613          ("%s: %d", PREF_AUDIOIPC_SHM_AREA_SIZE, (int)sAudioIPCShmAreaSize));
    614  MOZ_ASSERT(sServerHandle);
    615  ipc::FileDescriptor::PlatformHandleType rawFD;
    616  rawFD = audioipc2::audioipc2_server_new_client(sServerHandle,
    617                                                 sAudioIPCShmAreaSize);
    618  ipc::FileDescriptor fd(rawFD);
    619  if (!fd.IsValid()) {
    620    MOZ_LOG(gCubebLog, LogLevel::Error, ("audioipc_server_new_client failed"));
    621    return ipc::FileDescriptor();
    622  }
    623  // Close rawFD since FileDescriptor's ctor cloned it.
    624  // TODO: Find cleaner cross-platform way to close rawFD.
    625 #  ifdef XP_WIN
    626  CloseHandle(rawFD);
    627 #  else
    628  close(rawFD);
    629 #  endif
    630  return fd;
    631 }
    632 #endif
    633 
    634 ipc::FileDescriptor CreateAudioIPCConnection() {
    635 #ifdef MOZ_CUBEB_REMOTING
    636  StaticMutexAutoLock lock(sMutex);
    637  return CreateAudioIPCConnectionUnlocked();
    638 #else
    639  return ipc::FileDescriptor();
    640 #endif
    641 }
    642 
    643 RefPtr<CubebHandle> GetCubebUnlocked() {
    644  sMutex.AssertCurrentThreadOwns();
    645  if (sCubebForceNullContext) {
    646    // Pref set such that we should return a null context
    647    MOZ_LOG(gCubebLog, LogLevel::Debug,
    648            ("%s: returning null context due to %s!", __func__,
    649             PREF_CUBEB_FORCE_NULL_CONTEXT));
    650    return nullptr;
    651  }
    652  if (sCubebState != CubebState::Uninitialized) {
    653    // If we have already passed the initialization point (below), just return
    654    // the current context, which may be null (e.g., after error or shutdown.)
    655    return sCubebHandle;
    656  }
    657 
    658  if (!sBrandName && NS_IsMainThread()) {
    659    InitBrandName();
    660  } else {
    661    NS_WARNING_ASSERTION(
    662        sBrandName,
    663        "Did not initialize sbrandName, and not on the main thread?");
    664  }
    665 
    666  int rv = CUBEB_ERROR;
    667 #ifdef MOZ_CUBEB_REMOTING
    668  MOZ_LOG(gCubebLog, LogLevel::Info,
    669          ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
    670 
    671  if (sCubebSandbox) {
    672    if (XRE_IsParentProcess() && !sIPCConnection) {
    673      // TODO: Don't use audio IPC when within the same process.
    674      auto fd = CreateAudioIPCConnectionUnlocked();
    675      if (fd.IsValid()) {
    676        sIPCConnection = new ipc::FileDescriptor(fd);
    677      }
    678    }
    679    if (NS_WARN_IF(!sIPCConnection)) {
    680      // Either the IPC connection failed to init or we're still waiting for
    681      // InitAudioIPCConnection to complete (bug 1454782).
    682      return nullptr;
    683    }
    684 
    685    MOZ_LOG(gCubebLog, LogLevel::Debug,
    686            ("%s: %d", PREF_AUDIOIPC_STACK_SIZE, (int)sAudioIPCStackSize));
    687 
    688    audioipc2::AudioIpcInitParams initParams{};
    689    initParams.mStackSize = sAudioIPCStackSize;
    690    initParams.mServerConnection =
    691        sIPCConnection->ClonePlatformHandle().release();
    692    initParams.mThreadCreateCallback = [](const char* aName) {
    693      PROFILER_REGISTER_THREAD(aName);
    694    };
    695    initParams.mThreadDestroyCallback = []() { PROFILER_UNREGISTER_THREAD(); };
    696 
    697    cubeb* temp = nullptr;
    698    rv = audioipc2::audioipc2_client_init(&temp, sBrandName, &initParams);
    699    if (temp) {
    700      sCubebHandle = new CubebHandle(temp);
    701    }
    702  } else {
    703 #endif  // MOZ_CUBEB_REMOTING
    704 #ifdef XP_WIN
    705    mozilla::mscom::EnsureMTA([&]() -> void {
    706 #endif
    707      cubeb* temp = nullptr;
    708      rv = cubeb_init(&temp, sBrandName, sCubebBackendName);
    709      if (temp) {
    710        sCubebHandle = new CubebHandle(temp);
    711      }
    712 #ifdef XP_WIN
    713    });
    714 #endif
    715 #ifdef MOZ_CUBEB_REMOTING
    716  }
    717  sIPCConnection = nullptr;
    718 #endif  // MOZ_CUBEB_REMOTING
    719  NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context.");
    720  sCubebState =
    721      (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
    722 
    723  return sCubebHandle;
    724 }
    725 
    726 void ReportCubebBackendUsed() {
    727  RefPtr<CubebHandle> handle;
    728  {
    729    StaticMutexAutoLock lock(sMutex);
    730    sAudioStreamInitEverSucceeded = true;
    731    handle = sCubebHandle;
    732  }
    733 
    734  MOZ_RELEASE_ASSERT(handle.get());
    735 
    736  BackendLabel label = BackendLabel::eUnknown;
    737  auto backend =
    738      kTelemetryBackendLabel.find(cubeb_get_backend_id(handle->Context()));
    739  if (backend != kTelemetryBackendLabel.end()) {
    740    label = backend->second;
    741  }
    742 
    743  mozilla::glean::media_audio::backend.EnumGet(label).Add();
    744 }
    745 
    746 void ReportCubebStreamInitFailure(bool aIsFirst) {
    747  StaticMutexAutoLock lock(sMutex);
    748  if (!aIsFirst && !sAudioStreamInitEverSucceeded) {
    749    // This machine has no audio hardware, or it's in really bad shape, don't
    750    // send this info, since we want CUBEB_BACKEND_INIT_FAILURE_OTHER to detect
    751    // failures to open multiple streams in a process over time.
    752    return;
    753  }
    754  mozilla::glean::media_audio::init_failure
    755      .EnumGet(aIsFirst ? mozilla::glean::media_audio::InitFailureLabel::eFirst
    756                        : mozilla::glean::media_audio::InitFailureLabel::eOther)
    757      .Add();
    758 }
    759 
    760 uint32_t GetCubebPlaybackLatencyInMilliseconds() {
    761  StaticMutexAutoLock lock(sMutex);
    762  return sCubebPlaybackLatencyInMilliseconds;
    763 }
    764 
    765 bool CubebPlaybackLatencyPrefSet() {
    766  StaticMutexAutoLock lock(sMutex);
    767  return sCubebPlaybackLatencyPrefSet;
    768 }
    769 
    770 bool CubebMTGLatencyPrefSet() {
    771  StaticMutexAutoLock lock(sMutex);
    772  return sCubebMTGLatencyPrefSet;
    773 }
    774 
    775 uint32_t GetCubebMTGLatencyInFrames(cubeb_stream_params* params) {
    776  StaticMutexAutoLock lock(sMutex);
    777  if (sCubebMTGLatencyPrefSet) {
    778    MOZ_ASSERT(sCubebMTGLatencyInFrames > 0);
    779    return sCubebMTGLatencyInFrames;
    780  }
    781 
    782 #ifdef MOZ_WIDGET_ANDROID
    783  int32_t frames = AndroidGetAudioOutputFramesPerBuffer();
    784  // Allow extra time until audioipc threads are scheduled with higher
    785  // priority (bug 1931080).  768 was not sufficient on a Samsung SM-A528B
    786  // when switching to the home screen.
    787  return std::max(1024, frames);
    788 #else
    789  RefPtr<CubebHandle> handle = GetCubebUnlocked();
    790  if (!handle) {
    791    return sCubebMTGLatencyInFrames;  // default 512
    792  }
    793  uint32_t latency_frames = 0;
    794  int cubeb_result = CUBEB_OK;
    795 
    796  {
    797    StaticMutexAutoUnlock unlock(sMutex);
    798    cubeb_result =
    799        cubeb_get_min_latency(handle->Context(), params, &latency_frames);
    800  }
    801 
    802  if (cubeb_result != CUBEB_OK) {
    803    NS_WARNING("Could not get minimal latency from cubeb.");
    804    return sCubebMTGLatencyInFrames;  // default 512
    805  }
    806  return latency_frames;
    807 #endif
    808 }
    809 
    810 static const char* gInitCallbackPrefs[] = {
    811    PREF_VOLUME_SCALE,
    812    PREF_CUBEB_OUTPUT_DEVICE,
    813    PREF_CUBEB_LATENCY_PLAYBACK,
    814    PREF_CUBEB_LATENCY_MTG,
    815    PREF_CUBEB_BACKEND,
    816    PREF_CUBEB_FORCE_SAMPLE_RATE,
    817    PREF_CUBEB_FORCE_NULL_CONTEXT,
    818    PREF_CUBEB_FORCE_MOCK_CONTEXT,
    819    PREF_CUBEB_SANDBOX,
    820    PREF_AUDIOIPC_STACK_SIZE,
    821    PREF_AUDIOIPC_SHM_AREA_SIZE,
    822    nullptr,
    823 };
    824 
    825 static const char* gCallbackPrefs[] = {
    826    // We don't want to call the callback on startup, because the pref is the
    827    // empty string by default ("", which means "logging disabled"). Because the
    828    // logging can be enabled via environment variables (MOZ_LOG="module:5"),
    829    // calling this callback on init would immediately re-disable the logging.
    830    PREF_CUBEB_LOGGING_LEVEL,
    831    nullptr,
    832 };
    833 
    834 void InitLibrary() {
    835  Preferences::RegisterCallbacksAndCall(PrefChanged, gInitCallbackPrefs);
    836  Preferences::RegisterCallbacks(PrefChanged, gCallbackPrefs);
    837 
    838  if (MOZ_LOG_TEST(gCubebLog, LogLevel::Verbose)) {
    839    cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
    840  } else if (MOZ_LOG_TEST(gCubebLog, LogLevel::Error)) {
    841    cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
    842  }
    843 
    844 #ifndef MOZ_WIDGET_ANDROID
    845  NS_DispatchToMainThread(
    846      NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
    847 #endif
    848 #ifdef MOZ_CUBEB_REMOTING
    849  if (sCubebSandbox && XRE_IsContentProcess()) {
    850 #  if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
    851    if (atp_set_real_time_limit(0, 48000)) {
    852      MOZ_LOG(gCubebLog, LogLevel::Warning,
    853              ("could not set real-time limit in CubebUtils::InitLibrary"));
    854    }
    855    InstallSoftRealTimeLimitHandler();
    856 #  endif
    857    InitAudioIPCConnection();
    858  }
    859 #endif
    860 
    861  // Ensure the CallbackThreadRegistry is not created in an audio callback by
    862  // creating it now.
    863  (void)CallbackThreadRegistry::Get();
    864 }
    865 
    866 void ShutdownLibrary() {
    867  Preferences::UnregisterCallbacks(PrefChanged, gInitCallbackPrefs);
    868  Preferences::UnregisterCallbacks(PrefChanged, gCallbackPrefs);
    869 
    870  cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
    871  RefPtr<CubebHandle> trash;
    872  StaticMutexAutoLock lock(sMutex);
    873  trash = sCubebHandle.forget();
    874  sBrandName = nullptr;
    875  sCubebBackendName = nullptr;
    876  // This will ensure we don't try to re-create a context.
    877  sCubebState = CubebState::Shutdown;
    878 
    879  if (trash) {
    880    StaticMutexAutoUnlock unlock(sMutex);
    881    nsrefcnt count = trash.forget().take()->Release();
    882    MOZ_RELEASE_ASSERT(!count,
    883                       "ShutdownLibrary should be releasing the last reference "
    884                       "to the cubeb ctx!");
    885  }
    886 
    887 #ifdef MOZ_CUBEB_REMOTING
    888  sIPCConnection = nullptr;
    889  ShutdownAudioIPCServer();
    890 #endif
    891 }
    892 
    893 bool SandboxEnabled() {
    894 #ifdef MOZ_CUBEB_REMOTING
    895  StaticMutexAutoLock lock(sMutex);
    896  return !!sCubebSandbox;
    897 #else
    898  return false;
    899 #endif
    900 }
    901 
    902 already_AddRefed<SharedThreadPool> GetCubebOperationThread() {
    903  RefPtr<SharedThreadPool> pool = SharedThreadPool::Get("CubebOperation"_ns, 1);
    904  const uint32_t kIdleThreadTimeoutMs = 2000;
    905  pool->SetIdleThreadMaximumTimeout(
    906      PR_MillisecondsToInterval(kIdleThreadTimeoutMs));
    907  return pool.forget();
    908 }
    909 
    910 uint32_t MaxNumberOfChannels() {
    911  RefPtr<CubebHandle> handle = GetCubeb();
    912  uint32_t maxNumberOfChannels;
    913  if (handle && cubeb_get_max_channel_count(handle->Context(),
    914                                            &maxNumberOfChannels) == CUBEB_OK) {
    915    return maxNumberOfChannels;
    916  }
    917 
    918  return 0;
    919 }
    920 
    921 void GetCurrentBackend(nsAString& aBackend) {
    922  RefPtr<CubebHandle> handle = GetCubeb();
    923  if (handle) {
    924    const char* backend = cubeb_get_backend_id(handle->Context());
    925    if (backend) {
    926      aBackend.AssignASCII(backend);
    927      return;
    928    }
    929  }
    930  aBackend.AssignLiteral("unknown");
    931 }
    932 
    933 char* GetForcedOutputDevice() {
    934  StaticMutexAutoLock lock(sMutex);
    935  return sCubebOutputDeviceName;
    936 }
    937 
    938 cubeb_stream_prefs GetDefaultStreamPrefs(cubeb_device_type aType) {
    939  cubeb_stream_prefs prefs = CUBEB_STREAM_PREF_NONE;
    940 #ifdef XP_WIN
    941  if (StaticPrefs::media_cubeb_wasapi_raw() & static_cast<uint32_t>(aType)) {
    942    prefs |= CUBEB_STREAM_PREF_RAW;
    943  }
    944 #endif
    945  return prefs;
    946 }
    947 
    948 bool RouteOutputAsVoice() { return sRouteOutputAsVoice; }
    949 
    950 long datacb(cubeb_stream*, void*, const void*, void* out_buffer, long nframes) {
    951  PodZero(static_cast<float*>(out_buffer), nframes * 2);
    952  return nframes;
    953 }
    954 
    955 void statecb(cubeb_stream*, void*, cubeb_state) {}
    956 
    957 bool EstimatedLatencyDefaultDevices(double* aMean, double* aStdDev,
    958                                    Side aSide) {
    959  RefPtr<CubebHandle> handle = GetCubeb();
    960  if (!handle) {
    961    MOZ_LOG(gCubebLog, LogLevel::Error, ("No cubeb context, bailing."));
    962    return false;
    963  }
    964  bool includeInput = aSide & Side::Input;
    965  bool includeOutput = aSide & Side::Output;
    966  nsTArray<double> latencies;
    967  // Create a cubeb stream with the correct latency and default input/output
    968  // devices (mono/stereo channels). Wait for two seconds, get the latency a few
    969  // times.
    970  int rv;
    971  uint32_t rate;
    972  uint32_t latencyFrames;
    973  rv = cubeb_get_preferred_sample_rate(handle->Context(), &rate);
    974  if (rv != CUBEB_OK) {
    975    MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not get preferred rate"));
    976    return false;
    977  }
    978 
    979  cubeb_stream_params output_params;
    980  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    981  output_params.rate = rate;
    982  output_params.channels = 2;
    983  output_params.layout = CUBEB_LAYOUT_UNDEFINED;
    984  output_params.prefs = GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_OUTPUT);
    985  output_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
    986 
    987  latencyFrames = GetCubebMTGLatencyInFrames(&output_params);
    988 
    989  cubeb_stream_params input_params;
    990  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
    991  input_params.rate = rate;
    992  input_params.channels = 1;
    993  input_params.layout = CUBEB_LAYOUT_UNDEFINED;
    994  input_params.prefs = GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT);
    995  input_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
    996 
    997  cubeb_stream* stm;
    998  rv = cubeb_stream_init(handle->Context(), &stm,
    999                         "about:support latency estimation", NULL,
   1000                         &input_params, NULL, &output_params, latencyFrames,
   1001                         datacb, statecb, NULL);
   1002  if (rv != CUBEB_OK) {
   1003    MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not get init stream"));
   1004    return false;
   1005  }
   1006 
   1007  rv = cubeb_stream_start(stm);
   1008  if (rv != CUBEB_OK) {
   1009    MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not start stream"));
   1010    return false;
   1011  }
   1012  // +-2s
   1013  for (uint32_t i = 0; i < 40; i++) {
   1014    std::this_thread::sleep_for(std::chrono::milliseconds(50));
   1015    uint32_t inputLatency, outputLatency, rvIn, rvOut;
   1016    rvOut = cubeb_stream_get_latency(stm, &outputLatency);
   1017    if (rvOut) {
   1018      MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not get output latency"));
   1019    }
   1020    rvIn = cubeb_stream_get_input_latency(stm, &inputLatency);
   1021    if (rvIn) {
   1022      MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not get input latency"));
   1023    }
   1024    if (rvIn != CUBEB_OK || rvOut != CUBEB_OK) {
   1025      continue;
   1026    }
   1027 
   1028    double latency = static_cast<double>((includeInput ? inputLatency : 0) +
   1029                                         (includeOutput ? outputLatency : 0)) /
   1030                     rate;
   1031    latencies.AppendElement(latency);
   1032  }
   1033  rv = cubeb_stream_stop(stm);
   1034  if (rv != CUBEB_OK) {
   1035    MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not stop the stream"));
   1036  }
   1037 
   1038  *aMean = 0.0;
   1039  *aStdDev = 0.0;
   1040  double variance = 0.0;
   1041  for (uint32_t i = 0; i < latencies.Length(); i++) {
   1042    *aMean += latencies[i];
   1043  }
   1044 
   1045  *aMean /= latencies.Length();
   1046 
   1047  for (uint32_t i = 0; i < latencies.Length(); i++) {
   1048    variance += pow(latencies[i] - *aMean, 2.);
   1049  }
   1050  variance /= latencies.Length();
   1051 
   1052  *aStdDev = sqrt(variance);
   1053 
   1054  MOZ_LOG(gCubebLog, LogLevel::Debug,
   1055          ("Default devices latency in seconds %lf (stddev: %lf)", *aMean,
   1056           *aStdDev));
   1057 
   1058  cubeb_stream_destroy(stm);
   1059 
   1060  return true;
   1061 }
   1062 
   1063 #ifdef MOZ_WIDGET_ANDROID
   1064 int32_t AndroidGetAudioOutputSampleRate() {
   1065  if (java::GeckoAppShell::IsIsolatedProcess()) {
   1066    return 44100;  // TODO: Remote value; will be handled in following patch.
   1067  }
   1068 
   1069  int32_t sample_rate = java::GeckoAppShell::GetAudioOutputSampleRate();
   1070  return sample_rate;
   1071 }
   1072 int32_t AndroidGetAudioOutputFramesPerBuffer() {
   1073  if (java::GeckoAppShell::IsIsolatedProcess()) {
   1074    return 512;  // TODO: Remote value; will be handled in following patch.
   1075  }
   1076  int32_t frames = java::GeckoAppShell::GetAudioOutputFramesPerBuffer();
   1077  return frames;
   1078 }
   1079 #endif
   1080 
   1081 }  // namespace CubebUtils
   1082 }  // namespace mozilla
   1083 
   1084 #undef ENABLE_MOCK_CUBEB