summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaManager.cpp')
-rw-r--r--dom/media/MediaManager.cpp257
1 files changed, 196 insertions, 61 deletions
diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp
index fb4384c826..0eb1a0977d 100644
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -10,13 +10,16 @@
#include "AudioDeviceInfo.h"
#include "AudioStreamTrack.h"
#include "CubebDeviceEnumerator.h"
+#include "CubebInputStream.h"
#include "MediaTimer.h"
#include "MediaTrackConstraints.h"
#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"
@@ -291,6 +294,72 @@ void MediaManager::CallOnSuccess(GetUserMediaSuccessCallback& aCallback,
aCallback.Call(aStream);
}
+enum class PersistentPermissionState : uint32_t {
+ Unknown = nsIPermissionManager::UNKNOWN_ACTION,
+ Allow = nsIPermissionManager::ALLOW_ACTION,
+ Deny = nsIPermissionManager::DENY_ACTION,
+ Prompt = nsIPermissionManager::PROMPT_ACTION,
+};
+
+static PersistentPermissionState CheckPermission(
+ PersistentPermissionState aPermission) {
+ switch (aPermission) {
+ case PersistentPermissionState::Unknown:
+ case PersistentPermissionState::Allow:
+ case PersistentPermissionState::Deny:
+ case PersistentPermissionState::Prompt:
+ return aPermission;
+ }
+ MOZ_CRASH("Unexpected permission value");
+}
+
+struct WindowPersistentPermissionState {
+ PersistentPermissionState mCameraPermission;
+ PersistentPermissionState mMicrophonePermission;
+};
+
+static Result<WindowPersistentPermissionState, nsresult>
+GetPersistentPermissions(uint64_t aWindowId) {
+ auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
+ if (NS_WARN_IF(!window) || NS_WARN_IF(!window->GetPrincipal())) {
+ return Err(NS_ERROR_INVALID_ARG);
+ }
+
+ Document* doc = window->GetExtantDoc();
+ if (NS_WARN_IF(!doc)) {
+ return Err(NS_ERROR_INVALID_ARG);
+ }
+
+ nsIPrincipal* principal = window->GetPrincipal();
+ if (NS_WARN_IF(!principal)) {
+ return Err(NS_ERROR_INVALID_ARG);
+ }
+
+ nsresult rv;
+ RefPtr<PermissionDelegateHandler> permDelegate =
+ doc->GetPermissionDelegateHandler();
+ if (NS_WARN_IF(!permDelegate)) {
+ return Err(NS_ERROR_INVALID_ARG);
+ }
+
+ uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
+ uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
+ {
+ rv = permDelegate->GetPermission("microphone"_ns, &audio, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return Err(rv);
+ }
+ rv = permDelegate->GetPermission("camera"_ns, &video, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return Err(rv);
+ }
+ }
+
+ return WindowPersistentPermissionState{
+ CheckPermission(static_cast<PersistentPermissionState>(video)),
+ CheckPermission(static_cast<PersistentPermissionState>(audio))};
+}
+
/**
* DeviceListener has threadsafe refcounting for use across the main, media and
* MTG threads. But it has a non-threadsafe SupportsWeakPtr for WeakPtr usage
@@ -1458,9 +1527,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 +1596,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;
@@ -2220,6 +2344,7 @@ MediaManager::MediaManager(already_AddRefed<TaskQueue> aMediaThread)
mPrefs.mWidth = 0; // adaptive default
mPrefs.mHeight = 0; // adaptive default
mPrefs.mFPS = MediaEnginePrefs::DEFAULT_VIDEO_FPS;
+ mPrefs.mUsePlatformProcessing = false;
mPrefs.mAecOn = false;
mPrefs.mUseAecMobile = false;
mPrefs.mAgcOn = false;
@@ -2272,14 +2397,14 @@ static void ForeachObservedPref(const Function& aFunction) {
aFunction("media.video_loopback_dev"_ns);
aFunction("media.getusermedia.fake-camera-name"_ns);
#ifdef MOZ_WEBRTC
- aFunction("media.getusermedia.aec_enabled"_ns);
- aFunction("media.getusermedia.aec"_ns);
- aFunction("media.getusermedia.agc_enabled"_ns);
- aFunction("media.getusermedia.agc"_ns);
- aFunction("media.getusermedia.hpf_enabled"_ns);
- aFunction("media.getusermedia.noise_enabled"_ns);
- aFunction("media.getusermedia.noise"_ns);
- aFunction("media.getusermedia.channels"_ns);
+ aFunction("media.getusermedia.audio.processing.aec.enabled"_ns);
+ aFunction("media.getusermedia.audio.processing.aec"_ns);
+ aFunction("media.getusermedia.audio.processing.agc.enabled"_ns);
+ aFunction("media.getusermedia.audio.processing.agc"_ns);
+ aFunction("media.getusermedia.audio.processing.hpf.enabled"_ns);
+ aFunction("media.getusermedia.audio.processing.noise.enabled"_ns);
+ aFunction("media.getusermedia.audio.processing.noise"_ns);
+ aFunction("media.getusermedia.audio.max_channels"_ns);
aFunction("media.navigator.streams.fake"_ns);
#endif
}
@@ -2392,7 +2517,7 @@ void MediaManager::Dispatch(already_AddRefed<Runnable> task) {
template <typename MozPromiseType, typename FunctionType>
/* static */
-RefPtr<MozPromiseType> MediaManager::Dispatch(const char* aName,
+RefPtr<MozPromiseType> MediaManager::Dispatch(StaticString aName,
FunctionType&& aFunction) {
MozPromiseHolder<MozPromiseType> holder;
RefPtr<MozPromiseType> promise = holder.Ensure(aName);
@@ -2851,7 +2976,7 @@ RefPtr<MediaManager::StreamPromise> MediaManager::GetUserMedia(
case MediaSourceEnum::AudioCapture:
// Only enable AudioCapture if the pref is enabled. If it's not, we can
// deny right away.
- if (!Preferences::GetBool("media.getusermedia.audiocapture.enabled")) {
+ if (!Preferences::GetBool("media.getusermedia.audio.capture.enabled")) {
return StreamPromise::CreateAndReject(
MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
__func__);
@@ -3044,6 +3169,36 @@ 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;
+ }
+
+ if (GetPersistentPermissions(windowID)
+ .map([](auto&& aState) {
+ return aState.mMicrophonePermission ==
+ PersistentPermissionState::Deny;
+ })
+ .unwrapOr(true)) {
+ return;
+ }
+
+ task->PrimeVoiceProcessing();
+ }();
+
size_t taskCount =
self->AddTaskAndGetCount(windowID, callID, std::move(task));
@@ -3474,14 +3629,19 @@ void MediaManager::GetPrefs(nsIPrefBranch* aBranch, const char* aData) {
GetPref(aBranch, "media.navigator.audio.fake_frequency", aData,
&mPrefs.mFreq);
#ifdef MOZ_WEBRTC
- GetPrefBool(aBranch, "media.getusermedia.aec_enabled", aData, &mPrefs.mAecOn);
- GetPrefBool(aBranch, "media.getusermedia.agc_enabled", aData, &mPrefs.mAgcOn);
- GetPrefBool(aBranch, "media.getusermedia.hpf_enabled", aData, &mPrefs.mHPFOn);
- GetPrefBool(aBranch, "media.getusermedia.noise_enabled", aData,
- &mPrefs.mNoiseOn);
- GetPrefBool(aBranch, "media.getusermedia.transient_enabled", aData,
- &mPrefs.mTransientOn);
- GetPrefBool(aBranch, "media.getusermedia.agc2_forced", aData,
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.platform.enabled",
+ aData, &mPrefs.mUsePlatformProcessing);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.aec.enabled", aData,
+ &mPrefs.mAecOn);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.agc.enabled", aData,
+ &mPrefs.mAgcOn);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.hpf.enabled", aData,
+ &mPrefs.mHPFOn);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.noise.enabled",
+ aData, &mPrefs.mNoiseOn);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.transient.enabled",
+ aData, &mPrefs.mTransientOn);
+ GetPrefBool(aBranch, "media.getusermedia.audio.processing.agc2.forced", aData,
&mPrefs.mAgc2Forced);
// Use 0 or 1 to force to false or true
// EchoCanceller3Config::echo_removal_control.has_clock_drift.
@@ -3489,14 +3649,19 @@ void MediaManager::GetPrefs(nsIPrefBranch* aBranch, const char* aData) {
// deemed appropriate.
GetPref(aBranch, "media.getusermedia.audio.processing.aec.expect_drift",
aData, &mPrefs.mExpectDrift);
- GetPref(aBranch, "media.getusermedia.agc", aData, &mPrefs.mAgc);
- GetPref(aBranch, "media.getusermedia.noise", aData, &mPrefs.mNoise);
- GetPref(aBranch, "media.getusermedia.channels", aData, &mPrefs.mChannels);
+ GetPref(aBranch, "media.getusermedia.audio.processing.agc", aData,
+ &mPrefs.mAgc);
+ GetPref(aBranch, "media.getusermedia.audio.processing.noise", aData,
+ &mPrefs.mNoise);
+ GetPref(aBranch, "media.getusermedia.audio.max_channels", aData,
+ &mPrefs.mChannels);
#endif
- LOG("%s: default prefs: %dx%d @%dfps, %dHz test tones, aec: %s, "
- "agc: %s, hpf: %s, noise: %s, drift: %s, agc level: %d, agc version: %s, "
- "noise level: %d, transient: %s, channels %d",
+ LOG("%s: default prefs: %dx%d @%dfps, %dHz test tones, platform processing: "
+ "%s, aec: %s, agc: %s, hpf: %s, noise: %s, drift: %s, agc level: %d, agc "
+ "version: "
+ "%s, noise level: %d, transient: %s, channels %d",
__FUNCTION__, mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mFreq,
+ mPrefs.mUsePlatformProcessing ? "on" : "off",
mPrefs.mAecOn ? "on" : "off", mPrefs.mAgcOn ? "on" : "off",
mPrefs.mHPFOn ? "on" : "off", mPrefs.mNoiseOn ? "on" : "off",
mPrefs.mExpectDrift < 0 ? "auto"
@@ -3948,43 +4113,13 @@ bool MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId) {
// Or are persistent permissions (audio or video) granted?
- auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
- if (NS_WARN_IF(!window) || NS_WARN_IF(!window->GetPrincipal())) {
- return false;
- }
-
- Document* doc = window->GetExtantDoc();
- if (NS_WARN_IF(!doc)) {
- return false;
- }
-
- nsIPrincipal* principal = window->GetPrincipal();
- if (NS_WARN_IF(!principal)) {
- return false;
- }
-
- // Check if this site has persistent permissions.
- nsresult rv;
- RefPtr<PermissionDelegateHandler> permDelegate =
- doc->GetPermissionDelegateHandler();
- if (NS_WARN_IF(!permDelegate)) {
- return false;
- }
-
- uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
- uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
- {
- rv = permDelegate->GetPermission("microphone"_ns, &audio, true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return false;
- }
- rv = permDelegate->GetPermission("camera"_ns, &video, true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return false;
- }
- }
- return audio == nsIPermissionManager::ALLOW_ACTION ||
- video == nsIPermissionManager::ALLOW_ACTION;
+ return GetPersistentPermissions(aWindowId)
+ .map([](auto&& aState) {
+ return aState.mMicrophonePermission ==
+ PersistentPermissionState::Allow ||
+ aState.mCameraPermission == PersistentPermissionState::Allow;
+ })
+ .unwrapOr(false);
}
DeviceListener::DeviceListener()