Bug 1876526: Make cubeb singleton refcounted. r=pehrsons

Differential Revision: https://phabricator.services.mozilla.com/D200178
This commit is contained in:
Byron Campen
2024-02-07 15:36:22 +00:00
parent d6a4677c1b
commit 03d62f28e2
10 changed files with 114 additions and 66 deletions

View File

@@ -1700,7 +1700,7 @@ mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
if (sandboxEnabled && !StaticPrefs::media_cubeb_sandbox()) {
// Pre-start audio before sandboxing; see bug 1443612.
Unused << CubebUtils::GetCubebContext();
Unused << CubebUtils::GetCubeb();
}
if (sandboxEnabled) {

View File

@@ -245,14 +245,15 @@ nsresult AudioStream::Init(AudioDeviceInfo* aSinkInfo)
// This is noop if MOZ_DUMP_AUDIO is not set.
mDumpFile.Open("AudioStream", mOutChannels, mAudioClock.GetInputRate());
cubeb* cubebContext = CubebUtils::GetCubebContext();
if (!cubebContext) {
RefPtr<CubebUtils::CubebHandle> handle = CubebUtils::GetCubeb();
if (!handle) {
LOGE("Can't get cubeb context!");
CubebUtils::ReportCubebStreamInitFailure(true);
return NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR;
}
return OpenCubeb(cubebContext, params, startTime,
mCubeb = handle;
return OpenCubeb(handle->Context(), params, startTime,
CubebUtils::GetFirstStream());
}

View File

@@ -337,6 +337,8 @@ class AudioStream final {
const uint32_t mOutChannels;
// mCubebStream holds a bare pointer to cubeb, so we hold a ref on its behalf
RefPtr<CubebUtils::CubebHandle> mCubeb;
// Owning reference to a cubeb_stream. Set in Init(), cleared in ShutDown, so
// no lock is needed to access.
UniquePtr<cubeb_stream, CubebDestroyPolicy> mCubebStream;

View File

@@ -83,8 +83,8 @@ UniquePtr<CubebInputStream> CubebInputStream::Create(cubeb_devid aDeviceId,
return nullptr;
}
cubeb* context = CubebUtils::GetCubebContext();
if (!context) {
RefPtr<CubebUtils::CubebHandle> handle = CubebUtils::GetCubeb();
if (!handle) {
LOGE("No valid cubeb context");
CubebUtils::ReportCubebStreamInitFailure(CubebUtils::GetFirstStream());
return nullptr;
@@ -98,9 +98,9 @@ UniquePtr<CubebInputStream> CubebInputStream::Create(cubeb_devid aDeviceId,
RefPtr<Listener> listener(aListener);
if (int r = CubebUtils::CubebStreamInit(
context, &cubebStream, "input-only stream", aDeviceId, &params,
nullptr, nullptr, latencyFrames, DataCallback_s, StateCallback_s,
listener.get());
handle->Context(), &cubebStream, "input-only stream", aDeviceId,
&params, nullptr, nullptr, latencyFrames, DataCallback_s,
StateCallback_s, listener.get());
r != CUBEB_OK) {
CubebUtils::ReportCubebStreamInitFailure(CubebUtils::GetFirstStream());
LOGE("Fail to create a cubeb stream. Error %d", r);
@@ -120,7 +120,9 @@ UniquePtr<CubebInputStream> CubebInputStream::Create(cubeb_devid aDeviceId,
CubebInputStream::CubebInputStream(
already_AddRefed<Listener>&& aListener,
UniquePtr<cubeb_stream, CubebDestroyPolicy>&& aStream)
: mListener(aListener), mStream(std::move(aStream)) {
: mListener(aListener),
mCubeb(CubebUtils::GetCubeb()),
mStream(std::move(aStream)) {
MOZ_ASSERT(mListener);
MOZ_ASSERT(mStream);
}

View File

@@ -76,6 +76,8 @@ class CubebInputStream final {
// mListener must outlive the life time of the mStream.
const RefPtr<Listener> mListener;
// So must mCubeb (mStream has a bare pointer to cubeb).
const RefPtr<CubebUtils::CubebHandle> mCubeb;
const UniquePtr<cubeb_stream, CubebDestroyPolicy> mStream;
};

View File

@@ -87,7 +87,7 @@ enum class CubebState {
Initialized,
Shutdown
} sCubebState = CubebState::Uninitialized;
cubeb* sCubebContext;
StaticRefPtr<CubebUtils::CubebHandle> sCubebHandle;
double sVolumeScale = 1.0;
uint32_t sCubebPlaybackLatencyInMilliseconds = 100;
uint32_t sCubebMTGLatencyInFrames = 512;
@@ -185,7 +185,7 @@ static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
namespace CubebUtils {
cubeb* GetCubebContextUnlocked();
RefPtr<CubebHandle> GetCubebUnlocked();
void GetPrefAndSetString(const char* aPref, StaticAutoPtr<char>& aStorage) {
nsAutoCString value;
@@ -292,18 +292,19 @@ double GetVolumeScale() {
return sVolumeScale;
}
cubeb* GetCubebContext() {
RefPtr<CubebHandle> GetCubeb() {
StaticMutexAutoLock lock(sMutex);
return GetCubebContextUnlocked();
return GetCubebUnlocked();
}
// This is only exported when running tests.
void ForceSetCubebContext(cubeb* aCubebContext) {
StaticMutexAutoLock lock(sMutex);
if (sCubebContext) {
cubeb_destroy(sCubebContext);
if (aCubebContext) {
sCubebHandle = new CubebHandle(aCubebContext);
} else {
sCubebHandle = nullptr;
}
sCubebContext = aCubebContext;
sCubebState = CubebState::Initialized;
}
@@ -339,12 +340,12 @@ bool InitPreferredSampleRate() {
return false;
}
#else
cubeb* context = GetCubebContextUnlocked();
if (!context) {
RefPtr<CubebHandle> handle = GetCubebUnlocked();
if (!handle) {
return false;
}
uint32_t rate;
if (cubeb_get_preferred_sample_rate(context, &rate) != CUBEB_OK) {
if (cubeb_get_preferred_sample_rate(handle->Context(), &rate) != CUBEB_OK) {
return false;
}
sPreferredSampleRate = rate;
@@ -473,7 +474,7 @@ ipc::FileDescriptor CreateAudioIPCConnection() {
#endif
}
cubeb* GetCubebContextUnlocked() {
RefPtr<CubebHandle> GetCubebUnlocked() {
sMutex.AssertCurrentThreadOwns();
if (sCubebForceNullContext) {
// Pref set such that we should return a null context
@@ -485,7 +486,7 @@ cubeb* GetCubebContextUnlocked() {
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;
return sCubebHandle;
}
if (!sBrandName && NS_IsMainThread()) {
@@ -527,14 +528,21 @@ cubeb* GetCubebContextUnlocked() {
};
initParams.mThreadDestroyCallback = []() { PROFILER_UNREGISTER_THREAD(); };
rv = audioipc2::audioipc2_client_init(&sCubebContext, sBrandName,
&initParams);
cubeb* temp = nullptr;
rv = audioipc2::audioipc2_client_init(&temp, sBrandName, &initParams);
if (temp) {
sCubebHandle = new CubebHandle(temp);
}
} else {
#endif // MOZ_CUBEB_REMOTING
#ifdef XP_WIN
mozilla::mscom::EnsureMTA([&]() -> void {
#endif
rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName);
cubeb* temp = nullptr;
rv = cubeb_init(&temp, sBrandName, sCubebBackendName);
if (temp) {
sCubebHandle = new CubebHandle(temp);
}
#ifdef XP_WIN
});
#endif
@@ -546,17 +554,21 @@ cubeb* GetCubebContextUnlocked() {
sCubebState =
(rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
return sCubebContext;
return sCubebHandle;
}
void ReportCubebBackendUsed() {
StaticMutexAutoLock lock(sMutex);
RefPtr<CubebHandle> handle;
sAudioStreamInitEverSucceeded = true;
handle = sCubebHandle;
MOZ_RELEASE_ASSERT(handle.get());
LABELS_MEDIA_AUDIO_BACKEND label = LABELS_MEDIA_AUDIO_BACKEND::unknown;
auto backend =
kTelemetryBackendLabel.find(cubeb_get_backend_id(sCubebContext));
kTelemetryBackendLabel.find(cubeb_get_backend_id(handle->Context()));
if (backend != kTelemetryBackendLabel.end()) {
label = backend->second;
}
@@ -605,12 +617,13 @@ uint32_t GetCubebMTGLatencyInFrames(cubeb_stream_params* params) {
return 512;
}
#else
cubeb* context = GetCubebContextUnlocked();
if (!context) {
RefPtr<CubebHandle> handle = GetCubebUnlocked();
if (!handle) {
return sCubebMTGLatencyInFrames; // default 512
}
uint32_t latency_frames = 0;
if (cubeb_get_min_latency(context, params, &latency_frames) != CUBEB_OK) {
if (cubeb_get_min_latency(handle->Context(), params, &latency_frames) !=
CUBEB_OK) {
NS_WARNING("Could not get minimal latency from cubeb.");
return sCubebMTGLatencyInFrames; // default 512
}
@@ -673,9 +686,11 @@ void ShutdownLibrary() {
StaticMutexAutoLock lock(sMutex);
cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
if (sCubebContext) {
cubeb_destroy(sCubebContext);
sCubebContext = nullptr;
if (sCubebHandle) {
nsrefcnt count = sCubebHandle.forget().take()->Release();
MOZ_RELEASE_ASSERT(!count,
"ShutdownLibrary should be releasing the last reference "
"to the cubeb ctx!");
}
sBrandName = nullptr;
sCubebBackendName = nullptr;
@@ -698,10 +713,10 @@ bool SandboxEnabled() {
}
uint32_t MaxNumberOfChannels() {
cubeb* cubebContext = GetCubebContext();
RefPtr<CubebHandle> handle = GetCubeb();
uint32_t maxNumberOfChannels;
if (cubebContext && cubeb_get_max_channel_count(
cubebContext, &maxNumberOfChannels) == CUBEB_OK) {
if (handle && cubeb_get_max_channel_count(handle->Context(),
&maxNumberOfChannels) == CUBEB_OK) {
return maxNumberOfChannels;
}
@@ -709,9 +724,9 @@ uint32_t MaxNumberOfChannels() {
}
void GetCurrentBackend(nsAString& aBackend) {
cubeb* cubebContext = GetCubebContext();
if (cubebContext) {
const char* backend = cubeb_get_backend_id(cubebContext);
RefPtr<CubebHandle> handle = GetCubeb();
if (handle) {
const char* backend = cubeb_get_backend_id(handle->Context());
if (backend) {
aBackend.AssignASCII(backend);
return;
@@ -745,6 +760,7 @@ long datacb(cubeb_stream*, void*, const void*, void* out_buffer, long nframes) {
void statecb(cubeb_stream*, void*, cubeb_state) {}
bool EstimatedRoundTripLatencyDefaultDevices(double* aMean, double* aStdDev) {
RefPtr<CubebHandle> handle = GetCubeb();
nsTArray<double> roundtripLatencies;
// Create a cubeb stream with the correct latency and default input/output
// devices (mono/stereo channels). Wait for two seconds, get the latency a few
@@ -752,7 +768,7 @@ bool EstimatedRoundTripLatencyDefaultDevices(double* aMean, double* aStdDev) {
int rv;
uint32_t rate;
uint32_t latencyFrames;
rv = cubeb_get_preferred_sample_rate(GetCubebContext(), &rate);
rv = cubeb_get_preferred_sample_rate(handle->Context(), &rate);
if (rv != CUBEB_OK) {
MOZ_LOG(gCubebLog, LogLevel::Error, ("Could not get preferred rate"));
return false;
@@ -775,7 +791,7 @@ bool EstimatedRoundTripLatencyDefaultDevices(double* aMean, double* aStdDev) {
input_params.prefs = GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT);
cubeb_stream* stm;
rv = cubeb_stream_init(GetCubebContext(), &stm,
rv = cubeb_stream_init(handle->Context(), &stm,
"about:support latency estimation", NULL,
&input_params, NULL, &output_params, latencyFrames,
datacb, statecb, NULL);

View File

@@ -11,6 +11,7 @@
# include "AudioSampleFormat.h"
# include "nsString.h"
# include "nsISupportsImpl.h"
class AudioDeviceInfo;
@@ -34,6 +35,23 @@ struct ToCubebFormat<AUDIO_FORMAT_S16> {
static const cubeb_sample_format value = CUBEB_SAMPLE_S16NE;
};
class CubebHandle {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CubebHandle)
explicit CubebHandle(cubeb* aCubeb) : mCubeb(aCubeb) {
MOZ_RELEASE_ASSERT(mCubeb);
};
CubebHandle(const CubebHandle&) = delete;
cubeb* Context() const { return mCubeb.get(); }
private:
struct CubebDeletePolicy {
void operator()(cubeb* aCubeb) { cubeb_destroy(aCubeb); }
};
const UniquePtr<cubeb, CubebDeletePolicy> mCubeb;
~CubebHandle() = default;
};
// Initialize Audio Library. Some Audio backends require initializing the
// library before using it.
void InitLibrary();
@@ -65,7 +83,7 @@ enum Side { Input, Output };
double GetVolumeScale();
bool GetFirstStream();
cubeb* GetCubebContext();
RefPtr<CubebHandle> GetCubeb();
void ReportCubebStreamInitFailure(bool aIsFirstStream);
void ReportCubebBackendUsed();
uint32_t GetCubebPlaybackLatencyInMilliseconds();

View File

@@ -521,8 +521,8 @@ void AudioCallbackDriver::Init(const nsCString& aStreamName) {
return;
}
bool fromFallback = fallbackState == FallbackDriverState::Running;
cubeb* cubebContext = CubebUtils::GetCubebContext();
if (!cubebContext) {
RefPtr<CubebUtils::CubebHandle> handle = CubebUtils::GetCubeb();
if (!handle) {
NS_WARNING("Could not get cubeb context.");
LOG(LogLevel::Warning, ("%s: Could not get cubeb context", __func__));
mAudioStreamState = AudioStreamState::None;
@@ -631,10 +631,11 @@ void AudioCallbackDriver::Init(const nsCString& aStreamName) {
CubebUtils::AudioDeviceID inputId = mInputDeviceID;
if (CubebUtils::CubebStreamInit(
cubebContext, &stream, streamName, inputId,
handle->Context(), &stream, streamName, inputId,
inputWanted ? &input : nullptr,
forcedOutputDeviceId ? forcedOutputDeviceId : outputId, &output,
latencyFrames, DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
mCubeb = handle;
mAudioStream.own(stream);
DebugOnly<int> rv =
cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());

View File

@@ -689,6 +689,9 @@ class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver {
* audio buffer cubeb passes us. This is only ever accessed on the audio
* callback thread. */
AudioCallbackBufferWrapper<AudioDataValue> mBuffer;
// mAudioStream (a cubeb_stream) has a bare pointer to the cubeb context, so
// we hold a strong reference on its behalf.
RefPtr<CubebUtils::CubebHandle> mCubeb;
/* cubeb stream for this graph. This is non-null after a successful
* cubeb_stream_init(). CubebOperation thread only. */
nsAutoRef<cubeb_stream> mAudioStream;

View File

@@ -58,8 +58,9 @@ CubebDeviceEnumerator::CubebDeviceEnumerator()
// before the MTA thread gets shutdown.
mozilla::mscom::EnsureMTA([&]() -> void {
#endif
RefPtr<CubebHandle> handle = GetCubeb();
int rv = cubeb_register_device_collection_changed(
GetCubebContext(), CUBEB_DEVICE_TYPE_OUTPUT,
handle->Context(), CUBEB_DEVICE_TYPE_OUTPUT,
&OutputAudioDeviceListChanged_s, this);
if (rv != CUBEB_OK) {
NS_WARNING(
@@ -68,7 +69,7 @@ CubebDeviceEnumerator::CubebDeviceEnumerator()
mManualOutputInvalidation = true;
}
rv = cubeb_register_device_collection_changed(
GetCubebContext(), CUBEB_DEVICE_TYPE_INPUT,
handle->Context(), CUBEB_DEVICE_TYPE_INPUT,
&InputAudioDeviceListChanged_s, this);
if (rv != CUBEB_OK) {
NS_WARNING(
@@ -93,19 +94,22 @@ CubebDeviceEnumerator::~CubebDeviceEnumerator() {
#ifdef XP_WIN
mozilla::mscom::EnsureMTA([&]() -> void {
#endif
int rv = cubeb_register_device_collection_changed(
GetCubebContext(), CUBEB_DEVICE_TYPE_OUTPUT, nullptr, this);
if (rv != CUBEB_OK) {
NS_WARNING(
"Could not unregister the audio output"
" device collection changed callback.");
}
rv = cubeb_register_device_collection_changed(
GetCubebContext(), CUBEB_DEVICE_TYPE_INPUT, nullptr, this);
if (rv != CUBEB_OK) {
NS_WARNING(
"Could not unregister the audio input"
" device collection changed callback.");
RefPtr<CubebHandle> handle = GetCubeb();
if (handle) {
int rv = cubeb_register_device_collection_changed(
handle->Context(), CUBEB_DEVICE_TYPE_OUTPUT, nullptr, this);
if (rv != CUBEB_OK) {
NS_WARNING(
"Could not unregister the audio output"
" device collection changed callback.");
}
rv = cubeb_register_device_collection_changed(
handle->Context(), CUBEB_DEVICE_TYPE_INPUT, nullptr, this);
if (rv != CUBEB_OK) {
NS_WARNING(
"Could not unregister the audio input"
" device collection changed callback.");
}
}
#ifdef XP_WIN
});
@@ -181,13 +185,13 @@ static uint16_t ConvertCubebFormat(cubeb_device_fmt aFormat) {
static RefPtr<AudioDeviceSet> GetDeviceCollection(Side aSide) {
RefPtr set = new AudioDeviceSet();
cubeb* context = GetCubebContext();
if (context) {
RefPtr<CubebHandle> handle = GetCubeb();
if (handle) {
cubeb_device_collection collection = {nullptr, 0};
# ifdef XP_WIN
mozilla::mscom::EnsureMTA([&]() -> void {
# endif
if (cubeb_enumerate_devices(context,
if (cubeb_enumerate_devices(handle->Context(),
aSide == Input ? CUBEB_DEVICE_TYPE_INPUT
: CUBEB_DEVICE_TYPE_OUTPUT,
&collection) == CUBEB_OK) {
@@ -209,7 +213,7 @@ static RefPtr<AudioDeviceSet> GetDeviceCollection(Side aSide) {
set->AppendElement(std::move(info));
}
}
cubeb_device_collection_destroy(context, &collection);
cubeb_device_collection_destroy(handle->Context(), &collection);
# ifdef XP_WIN
});
# endif
@@ -234,8 +238,7 @@ RefPtr<const AudioDeviceSet> CubebDeviceEnumerator::EnumerateAudioDevices(
manualInvalidation = mManualOutputInvalidation;
}
cubeb* context = GetCubebContext();
if (!context) {
if (!GetCubeb()) {
return new AudioDeviceSet();
}
if (!manualInvalidation) {