Bug 1890873 - Prime a voice processing stream on macOS before showing gUM prompt. r=padenot

If priming is done when the gUM prompt is accepted by the user, creating the
voice processing stream is almost instantaneous since the primed stream can be
reused.

Differential Revision: https://phabricator.services.mozilla.com/D207213
This commit is contained in:
Andreas Pehrson 2024-04-17 10:13:32 +00:00
parent a35c77dd10
commit 89f4b44d7d
2 changed files with 86 additions and 0 deletions

View File

@ -15,8 +15,10 @@
#include "MediaTrackGraph.h"
#include "MediaTrackListener.h"
#include "VideoStreamTrack.h"
#include "Tracing.h"
#include "VideoUtils.h"
#include "mozilla/Base64.h"
#include "mozilla/EventTargetCapability.h"
#include "mozilla/MozPromise.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PeerIdentity.h"
@ -1458,9 +1460,63 @@ class GetUserMediaStreamTask final : public GetUserMediaTask {
const MediaStreamConstraints& GetConstraints() { return mConstraints; }
void PrimeVoiceProcessing() {
mPrimingStream = MakeAndAddRef<PrimingCubebVoiceInputStream>();
mPrimingStream->Init();
}
private:
void PrepareDOMStream();
class PrimingCubebVoiceInputStream {
class Listener final : public CubebInputStream::Listener {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Listener, override);
private:
~Listener() = default;
long DataCallback(const void*, long) override {
MOZ_CRASH("Unexpected data callback");
}
void StateCallback(cubeb_state) override {}
void DeviceChangedCallback() override {}
};
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
PrimingCubebVoiceInputStream, mCubebThread.GetEventTarget())
public:
void Init() {
mCubebThread.GetEventTarget()->Dispatch(
NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)] {
mCubebThread.AssertOnCurrentThread();
LOG("Priming voice processing with stream %p", this);
TRACE("PrimingCubebVoiceInputStream::Init");
const cubeb_devid default_device = nullptr;
const uint32_t mono = 1;
const uint32_t rate = CubebUtils::PreferredSampleRate(false);
const bool isVoice = true;
mCubebStream =
CubebInputStream::Create(default_device, mono, rate, isVoice,
MakeRefPtr<Listener>().get());
}));
}
private:
~PrimingCubebVoiceInputStream() {
mCubebThread.AssertOnCurrentThread();
LOG("Releasing primed voice processing stream %p", this);
mCubebStream = nullptr;
}
const EventTargetCapability<nsISerialEventTarget> mCubebThread =
EventTargetCapability<nsISerialEventTarget>(
TaskQueue::Create(CubebUtils::GetCubebOperationThread(),
"PrimingCubebInputStream::mCubebThread")
.get());
UniquePtr<CubebInputStream> mCubebStream MOZ_GUARDED_BY(mCubebThread);
};
// Constraints derived from those passed to getUserMedia() but adjusted for
// preferences, defaults, and security
const MediaStreamConstraints mConstraints;
@ -1473,6 +1529,7 @@ class GetUserMediaStreamTask final : public GetUserMediaTask {
// MediaDevices are set when selected and Allowed() by the UI.
RefPtr<LocalMediaDevice> mAudioDevice;
RefPtr<LocalMediaDevice> mVideoDevice;
RefPtr<PrimingCubebVoiceInputStream> mPrimingStream;
// Tracking id unique for a video frame source. Set when the corresponding
// device has been allocated.
Maybe<TrackingId> mVideoTrackingId;
@ -3044,6 +3101,27 @@ RefPtr<MediaManager::StreamPromise> MediaManager::GetUserMedia(
std::move(audioListener), std::move(videoListener), prefs,
principalInfo, aCallerType, focusSource);
// It is time to ask for user permission, prime voice processing
// now. Use a local lambda to enable a guard pattern.
[&] {
if (!StaticPrefs::
media_getusermedia_microphone_voice_stream_priming_enabled() ||
!StaticPrefs::
media_getusermedia_microphone_prefer_voice_stream_with_processing_enabled()) {
return;
}
if (const auto fc = FlattenedConstraints(
NormalizedConstraints(GetInvariant(c.mAudio)));
!fc.mEchoCancellation.Get(prefs.mAecOn) &&
!fc.mAutoGainControl.Get(prefs.mAgcOn && prefs.mAecOn) &&
!fc.mNoiseSuppression.Get(prefs.mNoiseOn && prefs.mAecOn)) {
return;
}
task->PrimeVoiceProcessing();
}();
size_t taskCount =
self->AddTaskAndGetCount(windowID, callID, std::move(task));

View File

@ -10884,6 +10884,14 @@
value: True
mirror: always
# Tell the audio backend to create a voice stream for later re-use before asking
# the user for microphone permissions, if approving those permissions would
# result in a voice stream when created later on.
- name: media.getusermedia.microphone.voice_stream_priming.enabled
type: RelaxedAtomicBool
value: @IS_XP_MACOSX@
mirror: always
# This pref turns on legacy (non-spec) exposure of camera and microphone
# information from enumerateDevices and devicechange ahead of successful
# getUserMedia calls. Should only be turned on to resolve web compat issues,