123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cindent: */
- /* 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 <stdint.h>
- #include <algorithm>
- #include "nsIStringBundle.h"
- #include "nsDebug.h"
- #include "nsString.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/Services.h"
- #include "mozilla/Sprintf.h"
- #include "mozilla/StaticMutex.h"
- #include "mozilla/StaticPtr.h"
- #include "mozilla/Telemetry.h"
- #include "mozilla/Logging.h"
- #include "nsThreadUtils.h"
- #include "CubebUtils.h"
- #include "nsAutoRef.h"
- #include "prdtoa.h"
- #define PREF_VOLUME_SCALE "media.volume_scale"
- #define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
- #define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
- namespace mozilla {
- namespace {
- LazyLogModule gCubebLog("cubeb");
- void CubebLogCallback(const char* aFmt, ...)
- {
- char buffer[256];
- va_list arglist;
- va_start(arglist, aFmt);
- VsprintfLiteral (buffer, aFmt, arglist);
- MOZ_LOG(gCubebLog, LogLevel::Error, ("%s", buffer));
- va_end(arglist);
- }
- // This mutex protects the variables below.
- StaticMutex sMutex;
- enum class CubebState {
- Uninitialized = 0,
- Initialized,
- Shutdown
- } sCubebState = CubebState::Uninitialized;
- cubeb* sCubebContext;
- double sVolumeScale;
- uint32_t sCubebPlaybackLatencyInMilliseconds;
- uint32_t sCubebMSGLatencyInFrames;
- bool sCubebPlaybackLatencyPrefSet;
- bool sCubebMSGLatencyPrefSet;
- bool sAudioStreamInitEverSucceeded = false;
- StaticAutoPtr<char> sBrandName;
- const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
- const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
- "jack",
- "pulse",
- "alsa",
- "audiounit",
- "audioqueue",
- "wasapi",
- "winmm",
- "directsound",
- "sndio",
- "opensl",
- "audiotrack",
- "kai"
- };
- /* Index for failures to create an audio stream the first time. */
- const int CUBEB_BACKEND_INIT_FAILURE_FIRST =
- ArrayLength(AUDIOSTREAM_BACKEND_ID_STR);
- /* Index for failures to create an audio stream after the first time */
- const int CUBEB_BACKEND_INIT_FAILURE_OTHER = CUBEB_BACKEND_INIT_FAILURE_FIRST + 1;
- /* Index for an unknown backend. */
- const int CUBEB_BACKEND_UNKNOWN = CUBEB_BACKEND_INIT_FAILURE_FIRST + 2;
- // Prefered samplerate, in Hz (characteristic of the hardware, mixer, platform,
- // and API used).
- //
- // sMutex protects *initialization* of this, which must be performed from each
- // thread before fetching, after which it is safe to fetch without holding the
- // mutex because it is only written once per process execution (by the first
- // initialization to complete). Since the init must have been called on a
- // given thread before fetching the value, it's guaranteed (via the mutex) that
- // sufficient memory barriers have occurred to ensure the correct value is
- // visible on the querying thread/CPU.
- uint32_t sPreferredSampleRate;
- } // namespace
- extern LazyLogModule gAudioStreamLog;
- static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
- // Consevative default that can work on all platforms.
- static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
- namespace CubebUtils {
- void PrefChanged(const char* aPref, void* aClosure)
- {
- if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
- nsAdoptingString value = Preferences::GetString(aPref);
- StaticMutexAutoLock lock(sMutex);
- if (value.IsEmpty()) {
- sVolumeScale = 1.0;
- } else {
- NS_ConvertUTF16toUTF8 utf8(value);
- sVolumeScale = std::max<double>(0, PR_strtod(utf8.get(), nullptr));
- }
- } else if (strcmp(aPref, PREF_CUBEB_LATENCY_PLAYBACK) == 0) {
- // Arbitrary default stream latency of 100ms. The higher this
- // value, the longer stream volume changes will take to become
- // audible.
- sCubebPlaybackLatencyPrefSet = Preferences::HasUserValue(aPref);
- uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
- StaticMutexAutoLock lock(sMutex);
- sCubebPlaybackLatencyInMilliseconds = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
- } else if (strcmp(aPref, PREF_CUBEB_LATENCY_MSG) == 0) {
- sCubebMSGLatencyPrefSet = Preferences::HasUserValue(aPref);
- uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_FRAMES);
- StaticMutexAutoLock lock(sMutex);
- // 128 is the block size for the Web Audio API, which limits how low the
- // latency can be here.
- // We don't want to limit the upper limit too much, so that people can
- // experiment.
- sCubebMSGLatencyInFrames = std::min<uint32_t>(std::max<uint32_t>(value, 128), 1e6);
- }
- }
- bool GetFirstStream()
- {
- static bool sFirstStream = true;
- StaticMutexAutoLock lock(sMutex);
- bool result = sFirstStream;
- sFirstStream = false;
- return result;
- }
- double GetVolumeScale()
- {
- StaticMutexAutoLock lock(sMutex);
- return sVolumeScale;
- }
- cubeb* GetCubebContext()
- {
- StaticMutexAutoLock lock(sMutex);
- return GetCubebContextUnlocked();
- }
- bool InitPreferredSampleRate()
- {
- StaticMutexAutoLock lock(sMutex);
- if (sPreferredSampleRate != 0) {
- return true;
- }
- cubeb* context = GetCubebContextUnlocked();
- if (!context) {
- return false;
- }
- if (cubeb_get_preferred_sample_rate(context,
- &sPreferredSampleRate) != CUBEB_OK) {
- return false;
- }
- MOZ_ASSERT(sPreferredSampleRate);
- return true;
- }
- uint32_t PreferredSampleRate()
- {
- if (!InitPreferredSampleRate()) {
- return 44100;
- }
- MOZ_ASSERT(sPreferredSampleRate);
- return sPreferredSampleRate;
- }
- void InitBrandName()
- {
- if (sBrandName) {
- return;
- }
- nsXPIDLString brandName;
- nsCOMPtr<nsIStringBundleService> stringBundleService =
- mozilla::services::GetStringBundleService();
- if (stringBundleService) {
- nsCOMPtr<nsIStringBundle> brandBundle;
- nsresult rv = stringBundleService->CreateBundle(kBrandBundleURL,
- getter_AddRefs(brandBundle));
- if (NS_SUCCEEDED(rv)) {
- rv = brandBundle->GetStringFromName(u"brandShortName",
- getter_Copies(brandName));
- NS_WARNING_ASSERTION(
- NS_SUCCEEDED(rv), "Could not get the program name for a cubeb stream.");
- }
- }
- NS_LossyConvertUTF16toASCII ascii(brandName);
- sBrandName = new char[ascii.Length() + 1];
- PodCopy(sBrandName.get(), ascii.get(), ascii.Length());
- sBrandName[ascii.Length()] = 0;
- }
- cubeb* GetCubebContextUnlocked()
- {
- sMutex.AssertCurrentThreadOwns();
- if (sCubebState != CubebState::Uninitialized) {
- // If we have already passed the initialization point (below), just return
- // the current context, which may be null (e.g., after error or shutdown.)
- return sCubebContext;
- }
- if (!sBrandName && NS_IsMainThread()) {
- InitBrandName();
- } else {
- NS_WARNING_ASSERTION(
- sBrandName, "Did not initialize sbrandName, and not on the main thread?");
- }
- int rv = cubeb_init(&sCubebContext, sBrandName);
- NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context.");
- sCubebState = (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
- if (MOZ_LOG_TEST(gCubebLog, LogLevel::Verbose)) {
- cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
- } else if (MOZ_LOG_TEST(gCubebLog, LogLevel::Error)) {
- cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
- }
- return sCubebContext;
- }
- uint32_t GetCubebPlaybackLatencyInMilliseconds()
- {
- StaticMutexAutoLock lock(sMutex);
- return sCubebPlaybackLatencyInMilliseconds;
- }
- bool CubebPlaybackLatencyPrefSet()
- {
- StaticMutexAutoLock lock(sMutex);
- return sCubebPlaybackLatencyPrefSet;
- }
- bool CubebMSGLatencyPrefSet()
- {
- StaticMutexAutoLock lock(sMutex);
- return sCubebMSGLatencyPrefSet;
- }
- Maybe<uint32_t> GetCubebMSGLatencyInFrames()
- {
- StaticMutexAutoLock lock(sMutex);
- if (!sCubebMSGLatencyPrefSet) {
- return Maybe<uint32_t>();
- }
- MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
- return Some(sCubebMSGLatencyInFrames);
- }
- void InitLibrary()
- {
- PrefChanged(PREF_VOLUME_SCALE, nullptr);
- Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
- PrefChanged(PREF_CUBEB_LATENCY_PLAYBACK, nullptr);
- PrefChanged(PREF_CUBEB_LATENCY_MSG, nullptr);
- Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
- Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
- NS_DispatchToMainThread(NS_NewRunnableFunction(&InitBrandName));
- }
- void ShutdownLibrary()
- {
- Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
- Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
- Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
- StaticMutexAutoLock lock(sMutex);
- if (sCubebContext) {
- cubeb_destroy(sCubebContext);
- sCubebContext = nullptr;
- }
- sBrandName = nullptr;
- // This will ensure we don't try to re-create a context.
- sCubebState = CubebState::Shutdown;
- }
- uint32_t MaxNumberOfChannels()
- {
- cubeb* cubebContext = GetCubebContext();
- uint32_t maxNumberOfChannels;
- if (cubebContext &&
- cubeb_get_max_channel_count(cubebContext,
- &maxNumberOfChannels) == CUBEB_OK) {
- return maxNumberOfChannels;
- }
- return 0;
- }
- void GetCurrentBackend(nsAString& aBackend)
- {
- cubeb* cubebContext = GetCubebContext();
- if (cubebContext) {
- const char* backend = cubeb_get_backend_id(cubebContext);
- if (backend) {
- aBackend.AssignASCII(backend);
- return;
- }
- }
- aBackend.AssignLiteral("unknown");
- }
- } // namespace CubebUtils
- } // namespace mozilla
|