author | Jan Grulich <jgrulich@redhat.com> |
Thu, 30 Nov 2023 11:49:36 +0000 (19 months ago) | |
changeset 687779 | fef894fcc4294027fdfb62af3997b87411bef554 |
parent 687778 | ed31b2acb5fbca3e2d0691a64bc52e65952070c0 |
child 687780 | ae5d8a4c6f186bba953930b247d145d0a39ff343 |
push id | 41379 |
push user | nbeleuzu@mozilla.com |
push date | Thu, 30 Nov 2023 17:23:44 +0000 (19 months ago) |
treeherder | mozilla-central@1d1fb9d5a497 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | pehrsons |
bugs | 1844020 |
milestone | 122.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -37,16 +37,17 @@ #include "mozilla/dom/MediaStreamBinding.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/UserActivation.h" #include "mozilla/dom/WindowContext.h" #include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/media/CamerasTypes.h" #include "mozilla/media/MediaChild.h" #include "mozilla/media/MediaTaskUtils.h" #include "nsAppDirectoryServiceDefs.h" #include "nsArray.h" #include "nsContentUtils.h" #include "nsGlobalWindowInner.h" #include "nsHashPropertyBag.h" #include "nsIEventTarget.h" @@ -146,16 +147,17 @@ namespace mozilla { LazyLogModule gMediaManagerLog("MediaManager"); #define LOG(...) MOZ_LOG(gMediaManagerLog, LogLevel::Debug, (__VA_ARGS__)) class GetUserMediaStreamTask; class LocalTrackSource; class SelectAudioOutputTask; +using camera::CamerasAccessStatus; using dom::BFCacheStatus; using dom::CallerType; using dom::ConstrainDOMStringParameters; using dom::ConstrainDoubleRange; using dom::ConstrainLongRange; using dom::DisplayMediaStreamConstraints; using dom::Document; using dom::Element; @@ -853,26 +855,28 @@ class AudioCaptureTrackSource : public L /** * nsIMediaDevice implementation. */ NS_IMPL_ISUPPORTS(LocalMediaDevice, nsIMediaDevice) MediaDevice::MediaDevice(MediaEngine* aEngine, MediaSourceEnum aMediaSource, const nsString& aRawName, const nsString& aRawID, const nsString& aRawGroupID, IsScary aIsScary, - const OsPromptable canRequestOsLevelPrompt) + const OsPromptable canRequestOsLevelPrompt, + const IsPlaceholder aIsPlaceholder) : mEngine(aEngine), mAudioDeviceInfo(nullptr), mMediaSource(aMediaSource), mKind(MediaEngineSource::IsVideo(aMediaSource) ? MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput), mScary(aIsScary == IsScary::Yes), mCanRequestOsLevelPrompt(canRequestOsLevelPrompt == OsPromptable::Yes), mIsFake(mEngine->IsFake()), + mIsPlaceholder(aIsPlaceholder == IsPlaceholder::Yes), mType( NS_ConvertASCIItoUTF16(dom::MediaDeviceKindValues::GetString(mKind))), mRawID(aRawID), mRawGroupID(aRawGroupID), mRawName(aRawName) { MOZ_ASSERT(mEngine); } @@ -885,30 +889,32 @@ MediaDevice::MediaDevice(MediaEngine* aE ? MediaSourceEnum::Microphone : MediaSourceEnum::Other), mKind(mMediaSource == MediaSourceEnum::Microphone ? MediaDeviceKind::Audioinput : MediaDeviceKind::Audiooutput), mScary(false), mCanRequestOsLevelPrompt(false), mIsFake(false), + mIsPlaceholder(false), mType( NS_ConvertASCIItoUTF16(dom::MediaDeviceKindValues::GetString(mKind))), mRawID(aRawID), mRawGroupID(mAudioDeviceInfo->GroupID()), mRawName(mAudioDeviceInfo->Name()) {} /* static */ RefPtr<MediaDevice> MediaDevice::CopyWithNewRawGroupId( const RefPtr<MediaDevice>& aOther, const nsString& aRawGroupID) { MOZ_ASSERT(!aOther->mAudioDeviceInfo, "device not supported"); return new MediaDevice(aOther->mEngine, aOther->mMediaSource, aOther->mRawName, aOther->mRawID, aRawGroupID, IsScary(aOther->mScary), - OsPromptable(aOther->mCanRequestOsLevelPrompt)); + OsPromptable(aOther->mCanRequestOsLevelPrompt), + IsPlaceholder(aOther->mIsPlaceholder)); } MediaDevice::~MediaDevice() = default; LocalMediaDevice::LocalMediaDevice(RefPtr<const MediaDevice> aRawDevice, const nsString& aID, const nsString& aGroupID, const nsString& aName) @@ -1814,25 +1820,25 @@ class DeviceSetPromiseHolderWithFallback } } }; // Class to hold the promise used to request device access and to resolve // even if |task| does not run, which can happen in case there is no // observer for the ask-device-permission event. class DeviceAccessRequestPromiseHolderWithFallback - : public MozPromiseHolder< - MozPromise<nsresult, mozilla::ipc::ResponseRejectReason, true>> { + : public MozPromiseHolder<MozPromise< + CamerasAccessStatus, mozilla::ipc::ResponseRejectReason, true>> { public: DeviceAccessRequestPromiseHolderWithFallback() = default; DeviceAccessRequestPromiseHolderWithFallback( DeviceAccessRequestPromiseHolderWithFallback&&) = default; ~DeviceAccessRequestPromiseHolderWithFallback() { if (!IsEmpty()) { - Resolve(NS_OK, __func__); + Resolve(CamerasAccessStatus::Granted, __func__); } } }; } // anonymous namespace /** * EnumerateRawDevices - Enumerate a list of audio & video devices that @@ -1887,65 +1893,77 @@ RefPtr<DeviceSetPromise> MediaManager::E hasFakeMics = fakeByPref && audioLoopDev.IsEmpty(); } } // True if at least one of video input or audio input is a real device // or there is audio output. const bool realDeviceRequested = (!hasFakeCams && hasVideo) || (!hasFakeMics && hasAudio) || hasAudioOutput; - using NativePromise = MozPromise<nsresult, mozilla::ipc::ResponseRejectReason, - /* IsExclusive = */ true>; + using NativePromise = + MozPromise<CamerasAccessStatus, mozilla::ipc::ResponseRejectReason, + /* IsExclusive = */ true>; RefPtr<NativePromise> deviceAccessPromise; if (realDeviceRequested && - aFlags.contains(EnumerationFlag::AllowPermissionRequest)) { - if (Preferences::GetBool("media.navigator.permission.device", false)) { - // Need to ask permission to retrieve list of all devices; - // notify frontend observer and wait for callback notification to post - // task. - const char16_t* const type = - (aVideoInputType != MediaSourceEnum::Camera) ? u"audio" - : (aAudioInputType != MediaSourceEnum::Microphone) ? u"video" - : u"all"; - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - DeviceAccessRequestPromiseHolderWithFallback deviceAccessPromiseHolder; - deviceAccessPromise = deviceAccessPromiseHolder.Ensure(__func__); - RefPtr task = NS_NewRunnableFunction( - __func__, [holder = std::move(deviceAccessPromiseHolder)]() mutable { - holder.Resolve(NS_OK, "getUserMedia:got-device-permission"); - }); - obs->NotifyObservers(static_cast<nsIRunnable*>(task), - "getUserMedia:ask-device-permission", type); - } else if (hasVideo && aVideoInputType == MediaSourceEnum::Camera) { - ipc::PBackgroundChild* backgroundChild = - ipc::BackgroundChild::GetOrCreateForCurrentThread(); - deviceAccessPromise = backgroundChild->SendRequestCameraAccess(); - } + aFlags.contains(EnumerationFlag::AllowPermissionRequest) && + Preferences::GetBool("media.navigator.permission.device", false)) { + // Need to ask permission to retrieve list of all devices; + // notify frontend observer and wait for callback notification to post + // task. + const char16_t* const type = + (aVideoInputType != MediaSourceEnum::Camera) ? u"audio" + : (aAudioInputType != MediaSourceEnum::Microphone) ? u"video" + : u"all"; + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + DeviceAccessRequestPromiseHolderWithFallback deviceAccessPromiseHolder; + deviceAccessPromise = deviceAccessPromiseHolder.Ensure(__func__); + RefPtr task = NS_NewRunnableFunction( + __func__, [holder = std::move(deviceAccessPromiseHolder)]() mutable { + holder.Resolve(CamerasAccessStatus::Granted, + "getUserMedia:got-device-permission"); + }); + obs->NotifyObservers(static_cast<nsIRunnable*>(task), + "getUserMedia:ask-device-permission", type); + } else if (realDeviceRequested && hasVideo && + aVideoInputType == MediaSourceEnum::Camera) { + ipc::PBackgroundChild* backgroundChild = + ipc::BackgroundChild::GetOrCreateForCurrentThread(); + deviceAccessPromise = backgroundChild->SendRequestCameraAccess( + aFlags.contains(EnumerationFlag::AllowPermissionRequest)); } if (!deviceAccessPromise) { // No device access request needed. Proceed directly. - deviceAccessPromise = NativePromise::CreateAndResolve(NS_OK, __func__); + deviceAccessPromise = + NativePromise::CreateAndResolve(CamerasAccessStatus::Granted, __func__); } deviceAccessPromise->Then( mMediaThread, __func__, [holder = std::move(holder), aVideoInputType, aAudioInputType, hasFakeCams, hasFakeMics, videoLoopDev, audioLoopDev, hasVideo, hasAudio, hasAudioOutput, realDeviceRequested]( NativePromise::ResolveOrRejectValue&& aValue) mutable { if (aValue.IsReject()) { // IPC failure probably means we're in shutdown. Resolve with // an empty set, so that callers do not need to handle rejection. holder.Resolve(new MediaDeviceSetRefCnt(), "EnumerateRawDevices: ipc failure"); return; } - if (nsresult value = aValue.ResolveValue(); NS_FAILED(value)) { + const CamerasAccessStatus value = aValue.ResolveValue(); + if (value == CamerasAccessStatus::Rejected || + value == CamerasAccessStatus::Error) { + LOG("Request to camera access %s", + value == CamerasAccessStatus::Rejected ? "was rejected" + : "failed"); + if (value == CamerasAccessStatus::Error) { + NS_WARNING("Failed to request camera access"); + } holder.Reject( MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError), "EnumerateRawDevices: camera access rejected"); return; } // Only enumerate what's asked for, and only fake cams and mics. RefPtr<MediaEngine> fakeBackend, realBackend;
--- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -78,20 +78,26 @@ class MediaDevice final { */ enum class IsScary { No, Yes }; /** * Whether source device can use OS level selection prompt */ enum class OsPromptable { No, Yes }; + /** + * Whether source device is just a placeholder + */ + enum class IsPlaceholder { No, Yes }; + MediaDevice(MediaEngine* aEngine, dom::MediaSourceEnum aMediaSource, const nsString& aRawName, const nsString& aRawID, const nsString& aRawGroupID, IsScary aIsScary, - const OsPromptable canRequestOsLevelPrompt); + const OsPromptable canRequestOsLevelPrompt, + const IsPlaceholder aIsPlaceholder = IsPlaceholder::No); MediaDevice(MediaEngine* aEngine, const RefPtr<AudioDeviceInfo>& aAudioDeviceInfo, const nsString& aRawID); static RefPtr<MediaDevice> CopyWithNewRawGroupId( const RefPtr<MediaDevice>& aOther, const nsString& aRawGroupID); @@ -103,16 +109,17 @@ class MediaDevice final { public: const RefPtr<MediaEngine> mEngine; const RefPtr<AudioDeviceInfo> mAudioDeviceInfo; const dom::MediaSourceEnum mMediaSource; const dom::MediaDeviceKind mKind; const bool mScary; const bool mCanRequestOsLevelPrompt; const bool mIsFake; + const bool mIsPlaceholder; const nsString mType; const nsString mRawID; const nsString mRawGroupID; const nsString mRawName; }; /** * Device info that is specific to a particular Window. If the device is a
--- a/dom/media/systemservices/CamerasChild.cpp +++ b/dom/media/systemservices/CamerasChild.cpp @@ -284,48 +284,55 @@ mozilla::ipc::IPCResult CamerasChild::Re mReplyCapability->maxFPS = ipcCapability.maxFPS(); mReplyCapability->videoType = static_cast<webrtc::VideoType>(ipcCapability.videoType()); mReplyCapability->interlaced = ipcCapability.interlaced(); monitor.Notify(); return IPC_OK(); } -int CamerasChild::GetCaptureDevice( - CaptureEngine aCapEngine, unsigned int list_number, char* device_nameUTF8, - const unsigned int device_nameUTF8Length, char* unique_idUTF8, - const unsigned int unique_idUTF8Length, bool* scary) { +int CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine, + unsigned int list_number, + char* device_nameUTF8, + const unsigned int device_nameUTF8Length, + char* unique_idUTF8, + const unsigned int unique_idUTF8Length, + bool* scary, bool* device_is_placeholder) { LOG(("%s", __PRETTY_FUNCTION__)); nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine, unsigned int>( "camera::PCamerasChild::SendGetCaptureDevice", this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number); LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); if (dispatcher.Success()) { base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length); base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length); if (scary) { *scary = mReplyScary; } + if (device_is_placeholder) { + *device_is_placeholder = mReplyDeviceIsPlaceholder; + } LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8)); } return dispatcher.ReturnValue(); } mozilla::ipc::IPCResult CamerasChild::RecvReplyGetCaptureDevice( const nsACString& device_name, const nsACString& device_id, - const bool& scary) { + const bool& scary, const bool& device_is_placeholder) { LOG(("%s", __PRETTY_FUNCTION__)); MonitorAutoLock monitor(mReplyMonitor); mReceivedReply = true; mReplySuccess = true; mReplyDeviceName = device_name; mReplyDeviceID = device_id; mReplyScary = scary; + mReplyDeviceIsPlaceholder = device_is_placeholder; monitor.Notify(); return IPC_OK(); } int CamerasChild::AllocateCapture(CaptureEngine aCapEngine, const char* unique_idUTF8, uint64_t aWindowID) { LOG(("%s", __PRETTY_FUNCTION__));
--- a/dom/media/systemservices/CamerasChild.h +++ b/dom/media/systemservices/CamerasChild.h @@ -155,17 +155,17 @@ class CamerasChild final : public PCamer // these are response messages to our outgoing requests mozilla::ipc::IPCResult RecvReplyNumberOfCaptureDevices(const int&) override; mozilla::ipc::IPCResult RecvReplyNumberOfCapabilities(const int&) override; mozilla::ipc::IPCResult RecvReplyAllocateCapture(const int&) override; mozilla::ipc::IPCResult RecvReplyGetCaptureCapability( const VideoCaptureCapability& capability) override; mozilla::ipc::IPCResult RecvReplyGetCaptureDevice( const nsACString& device_name, const nsACString& device_id, - const bool& scary) override; + const bool& scary, const bool& device_is_placeholder) override; mozilla::ipc::IPCResult RecvReplyFailure(void) override; mozilla::ipc::IPCResult RecvReplySuccess(void) override; void ActorDestroy(ActorDestroyReason aWhy) override; // the webrtc.org ViECapture calls are mirrored here, but with access // to a specific PCameras instance to communicate over. These also // run on the MediaManager thread int NumberOfCaptureDevices(CaptureEngine aCapEngine); @@ -182,18 +182,18 @@ class CamerasChild final : public PCamer uint64_t aWindowID); int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8, const unsigned int capability_number, webrtc::VideoCaptureCapability* capability); int GetCaptureDevice(CaptureEngine aCapEngine, unsigned int list_number, char* device_nameUTF8, const unsigned int device_nameUTF8Length, char* unique_idUTF8, - const unsigned int unique_idUTF8Length, - bool* scary = nullptr); + const unsigned int unique_idUTF8Length, bool* scary, + bool* device_is_placeholder); int EnsureInitialized(CaptureEngine aCapEngine); template <typename This> int ConnectDeviceListChangeListener(MediaEventListener* aListener, AbstractThread* aTarget, This* aThis, void (This::*aMethod)()) { // According to the spec, if the script sets // navigator.mediaDevices.ondevicechange and the permission state is @@ -248,15 +248,16 @@ class CamerasChild final : public PCamer // Async responses data contents; bool mReplySuccess; const int mZero; int mReplyInteger; webrtc::VideoCaptureCapability* mReplyCapability = nullptr; nsCString mReplyDeviceName; nsCString mReplyDeviceID; bool mReplyScary; + bool mReplyDeviceIsPlaceholder; MediaEventProducer<void> mDeviceListChangeEvent; }; } // namespace camera } // namespace mozilla #endif // mozilla_CamerasChild_h
--- a/dom/media/systemservices/CamerasParent.cpp +++ b/dom/media/systemservices/CamerasParent.cpp @@ -2,16 +2,17 @@ /* vim: set sw=2 ts=8 et ft=cpp : */ /* 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 "CamerasParent.h" #include <atomic> +#include "CamerasTypes.h" #include "MediaEngineSource.h" #include "PerformanceRecorder.h" #include "VideoFrameUtils.h" #include "mozilla/AppShutdown.h" #include "mozilla/Assertions.h" #include "mozilla/BasePrincipal.h" #include "mozilla/ProfilerMarkers.h" @@ -645,62 +646,64 @@ ipc::IPCResult CamerasParent::RecvGetCap ipc::IPCResult CamerasParent::RecvGetCaptureDevice( const CaptureEngine& aCapEngine, const int& aDeviceIndex) { MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread()); MOZ_ASSERT(!mDestroyed); LOG_FUNCTION(); - using Data = std::tuple<nsCString, nsCString, pid_t, int>; + using Data = std::tuple<nsCString, nsCString, pid_t, bool, int>; using Promise = MozPromise<Data, bool, true>; InvokeAsync( mVideoCaptureThread, __func__, [this, self = RefPtr(this), aCapEngine, aDeviceIndex] { char deviceName[MediaEngineSource::kMaxDeviceNameLength]; char deviceUniqueId[MediaEngineSource::kMaxUniqueIdLength]; nsCString name; nsCString uniqueId; pid_t devicePid = 0; + bool placeholder = false; int error = -1; if (auto* engine = EnsureInitialized(aCapEngine)) { if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) { error = devInfo->GetDeviceName( aDeviceIndex, deviceName, sizeof(deviceName), deviceUniqueId, - sizeof(deviceUniqueId), nullptr, 0, &devicePid); + sizeof(deviceUniqueId), nullptr, 0, &devicePid, &placeholder); } } if (error == 0) { name.Assign(deviceName); uniqueId.Assign(deviceUniqueId); } return Promise::CreateAndResolve( std::make_tuple(std::move(name), std::move(uniqueId), devicePid, - error), + placeholder, error), "CamerasParent::RecvGetCaptureDevice"); }) ->Then( mPBackgroundEventTarget, __func__, [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) { - const auto& [name, uniqueId, devicePid, error] = + const auto& [name, uniqueId, devicePid, placeholder, error] = aValue.ResolveValue(); if (mDestroyed) { return; } if (error != 0) { LOG("GetCaptureDevice failed: %d", error); Unused << SendReplyFailure(); return; } bool scary = (devicePid == getpid()); LOG("Returning %s name %s id (pid = %d)%s", name.get(), uniqueId.get(), devicePid, (scary ? " (scary)" : "")); - Unused << SendReplyGetCaptureDevice(name, uniqueId, scary); + Unused << SendReplyGetCaptureDevice(name, uniqueId, scary, + placeholder); }); return IPC_OK(); } // Find out whether the given window with id has permission to use the // camera. If the permission is not persistent, we'll make it a one-shot by // removing the (session) permission. static bool HasCameraPermission(const uint64_t& aWindowId) { @@ -1174,60 +1177,97 @@ CamerasParent::CamerasParent() "GetCurrentThreadEventTarget failed"); LOG("CamerasParent: %p", this); // Don't dispatch from the constructor a runnable that may toggle the // reference count, because the IPC thread does not get a reference until // after the constructor returns. } -auto CamerasParent::RequestCameraAccess() +/* static */ +auto CamerasParent::RequestCameraAccess(bool aAllowPermissionRequest) -> RefPtr<CameraAccessRequestPromise> { ipc::AssertIsOnBackgroundThread(); + // Special case for PipeWire where we at this point just need to make sure + // we have information about camera availabilty through the camera portal + if (!aAllowPermissionRequest) { + return EnsureVideoCaptureFactory()->UpdateCameraAvailability()->Then( + GetCurrentSerialEventTarget(), + "CamerasParent::RequestCameraAccess update camera availability", + [](const VideoCaptureFactory::UpdateCameraAvailabilityPromise:: + ResolveOrRejectValue& aValue) { + LOG("Camera availability updated to %s", + aValue.IsResolve() + ? aValue.ResolveValue() == + VideoCaptureFactory::CameraAvailability::Available + ? "available" + : "not available" + : "still unknown"); + return CameraAccessRequestPromise::CreateAndResolve( + CamerasAccessStatus::RequestRequired, + "CamerasParent::RequestCameraAccess camera availability updated"); + }); + } + static StaticRefPtr<CameraAccessRequestPromise> sCameraAccessRequestPromise; if (!sCameraAccessRequestPromise) { sCameraAccessRequestPromise = RefPtr<CameraAccessRequestPromise>( EnsureVideoCaptureFactory()->InitCameraBackend()->Then( GetCurrentSerialEventTarget(), "CamerasParent::RequestCameraAccess camera backend init handler", [](nsresult aRv) mutable { MOZ_ASSERT(NS_SUCCEEDED(aRv)); + if (sVideoCaptureThread) { + MOZ_ASSERT(sEngines); + MOZ_ALWAYS_SUCCEEDS( + sVideoCaptureThread->Dispatch(NS_NewRunnableFunction( + __func__, [engines = RefPtr(sEngines.get())] { + if (VideoEngine* engine = + engines->ElementAt(CameraEngine)) { + engine->ClearVideoCaptureDeviceInfo(); + } + }))); + } return CameraAccessRequestPromise::CreateAndResolve( - aRv, + CamerasAccessStatus::Granted, "CamerasParent::RequestCameraAccess camera backend init " "resolve"); }, [](nsresult aRv) mutable { MOZ_ASSERT(NS_FAILED(aRv)); - return CameraAccessRequestPromise::CreateAndReject( - aRv, + return CameraAccessRequestPromise::CreateAndResolve( + aRv == NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR + ? CamerasAccessStatus::Rejected + : CamerasAccessStatus::Error, "CamerasParent::RequestCameraAccess camera backend init " "reject"); })); static nsresult clearingRv = NS_DispatchToMainThread(NS_NewRunnableFunction( __func__, [] { ClearOnShutdown(&sCameraAccessRequestPromise); })); Unused << clearingRv; } // If camera acess is granted, all is jolly. But we need to handle rejection. return sCameraAccessRequestPromise->Then( GetCurrentSerialEventTarget(), "CamerasParent::CameraAccessRequestPromise rejection handler", - [](nsresult aRv) { + [](CamerasAccessStatus aStatus) { return CameraAccessRequestPromise::CreateAndResolve( - aRv, "CamerasParent::RequestCameraAccess resolve"); + aStatus, "CamerasParent::RequestCameraAccess resolve"); }, - [promise = RefPtr(sCameraAccessRequestPromise.get())](nsresult aRv) { + [promise = RefPtr(sCameraAccessRequestPromise.get()), + aAllowPermissionRequest](void_t aRv) { if (promise == sCameraAccessRequestPromise) { sCameraAccessRequestPromise = nullptr; - return CameraAccessRequestPromise::CreateAndReject( - aRv, "CamerasParent::RequestCameraAccess reject"); + return CameraAccessRequestPromise::CreateAndResolve( + CamerasAccessStatus::Error, + "CamerasParent::RequestCameraAccess reject"); } - return CamerasParent::RequestCameraAccess(); + return CamerasParent::RequestCameraAccess(aAllowPermissionRequest); }); } // RecvPCamerasConstructor() is used because IPC messages, for // Send__delete__(), cannot be sent from AllocPCamerasParent(). ipc::IPCResult CamerasParent::RecvPCamerasConstructor() { MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
--- a/dom/media/systemservices/CamerasParent.h +++ b/dom/media/systemservices/CamerasParent.h @@ -51,39 +51,41 @@ class CallbackHelper : public rtc::Video }; class DeliverFrameRunnable; class CamerasParent final : public PCamerasParent, private webrtc::VideoInputFeedBack { public: using ShutdownMozPromise = media::ShutdownBlockingTicket::ShutdownMozPromise; - using CameraAccessRequestPromise = - MozPromise<nsresult, nsresult, /* IsExclusive = */ false>; + + using CameraAccessRequestPromise = MozPromise<CamerasAccessStatus, void_t, + /* IsExclusive = */ false>; NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET( CamerasParent, mPBackgroundEventTarget) class VideoEngineArray; friend DeliverFrameRunnable; static already_AddRefed<CamerasParent> Create(); /** * Request camera access - * Currently used only on desktop. This will make an xdg-desktop-portal - * call to request access to camera when PipeWire is used, otherwise it - * automatically grants access for other implementations. - * - * 1) NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR - access to camera has been - * rejected by the user. - * 2) NS_ERROR_FAILURE - generic error, for instance with pipewire most - * likely the xdg-desktop-portal request failed. + * Currently only used on desktop. If @value + * aAllowPermissionRequest is true, a request for full camera access may be + * made and the returned promise may be blocked on user input on a modal + * dialog. If @value aAllowPermissionRequest is false, only a request to + * check camera device presence will be made. If any camera device is + * present, we will enumerate a single placeholder device until a successful + * RequestCameraAccess with a true aAllowPermissionRequest. + * The returned promise will never be rejected. */ - static RefPtr<CameraAccessRequestPromise> RequestCameraAccess(); + static RefPtr<CameraAccessRequestPromise> RequestCameraAccess( + bool aAllowPermissionRequest); // Messages received from the child. These run on the IPC/PBackground thread. mozilla::ipc::IPCResult RecvPCamerasConstructor(); mozilla::ipc::IPCResult RecvAllocateCapture( const CaptureEngine& aCapEngine, const nsACString& aUniqueIdUTF8, const uint64_t& aWindowID) override; mozilla::ipc::IPCResult RecvReleaseCapture(const CaptureEngine& aCapEngine, const int& aCaptureId) override;
--- a/dom/media/systemservices/CamerasTypes.h +++ b/dom/media/systemservices/CamerasTypes.h @@ -16,23 +16,46 @@ enum CaptureEngine : int { InvalidEngine = 0, ScreenEngine, BrowserEngine, WinEngine, CameraEngine, MaxEngine }; +enum class CamerasAccessStatus { + // We have full access to cameras, either because it was granted, or because + // requesting it from the user was not necessary. + Granted = 1, + // A permission request to the platform is required before we know the + // camera access status. Enumeration will result in a single placeholder + // device, should any cameras be present on the system. The placeholder + // device cannot be captured. + RequestRequired, + // A permission request was made and was rejected by the platform. + Rejected, + // Generic error while doing the request, for instance with pipewire most + // likely the xdg-desktop-portal request failed. + Error, +}; + TrackingId::Source CaptureEngineToTrackingSourceStr( const CaptureEngine& aEngine); } // namespace mozilla::camera namespace IPC { template <> struct ParamTraits<mozilla::camera::CaptureEngine> : public ContiguousEnumSerializer< mozilla::camera::CaptureEngine, mozilla::camera::CaptureEngine::InvalidEngine, mozilla::camera::CaptureEngine::MaxEngine> {}; + +template <> +struct ParamTraits<mozilla::camera::CamerasAccessStatus> + : public ContiguousEnumSerializer< + mozilla::camera::CamerasAccessStatus, + mozilla::camera::CamerasAccessStatus::Granted, + mozilla::camera::CamerasAccessStatus::Error> {}; } // namespace IPC #endif // mozilla_CamerasTypes_h
--- a/dom/media/systemservices/PCameras.ipdl +++ b/dom/media/systemservices/PCameras.ipdl @@ -57,17 +57,17 @@ child: // transfers ownership of |buffer| from parent to child async DeliverFrame(CaptureEngine capEngine, int streamId, Shmem buffer, VideoFrameProperties props); async DeviceChange(); async ReplyNumberOfCaptureDevices(int deviceCount); async ReplyNumberOfCapabilities(int capabilityCount); async ReplyAllocateCapture(int captureId); async ReplyGetCaptureCapability(VideoCaptureCapability cap); - async ReplyGetCaptureDevice(nsCString device_name, nsCString device_id, bool scary); + async ReplyGetCaptureDevice(nsCString device_name, nsCString device_id, bool scary, bool placeholder); async ReplyFailure(); async ReplySuccess(); async __delete__(); parent: async NumberOfCaptureDevices(CaptureEngine engine); async NumberOfCapabilities(CaptureEngine engine, nsCString deviceUniqueIdUTF8);
--- a/dom/media/systemservices/VideoEngine.cpp +++ b/dom/media/systemservices/VideoEngine.cpp @@ -153,16 +153,21 @@ VideoEngine::GetOrCreateVideoCaptureDevi mDeviceInfo = mVideoCaptureFactory->CreateDeviceInfo(mId, mCaptureDevInfo.type); LOG(("EXIT %s", __PRETTY_FUNCTION__)); return mDeviceInfo; } +void VideoEngine::ClearVideoCaptureDeviceInfo() { + LOG(("%s", __PRETTY_FUNCTION__)); + mDeviceInfo.reset(); +} + already_AddRefed<VideoEngine> VideoEngine::Create( const CaptureDeviceType& aCaptureDeviceType, RefPtr<VideoCaptureFactory> aVideoCaptureFactory) { LOG(("%s", __PRETTY_FUNCTION__)); return do_AddRef( new VideoEngine(aCaptureDeviceType, std::move(aVideoCaptureFactory))); }
--- a/dom/media/systemservices/VideoEngine.h +++ b/dom/media/systemservices/VideoEngine.h @@ -84,16 +84,22 @@ class VideoEngine { * the future. * @return on failure the shared_ptr will be null, otherwise it will contain * a DeviceInfo. * @see bug 1305212 https://bugzilla.mozilla.org/show_bug.cgi?id=1305212 */ std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> GetOrCreateVideoCaptureDeviceInfo(); + /** + * Destroys existing DeviceInfo. + * The DeviceInfo will be recreated the next time it is needed. + */ + void ClearVideoCaptureDeviceInfo(); + class CaptureEntry { public: CaptureEntry(int32_t aCapnum, rtc::scoped_refptr<webrtc::VideoCaptureModule> aCapture); int32_t Capnum() const; rtc::scoped_refptr<webrtc::VideoCaptureModule> VideoCapture(); private:
--- a/dom/media/systemservices/moz.build +++ b/dom/media/systemservices/moz.build @@ -53,16 +53,19 @@ if CONFIG["MOZ_WEBRTC"]: if CONFIG["OS_TARGET"] != "Android": UNIFIED_SOURCES += [ "video_engine/desktop_capture_impl.cc", "video_engine/desktop_device_info.cc", "video_engine/tab_capturer.cc", ] if "WEBRTC_USE_PIPEWIRE" in DEFINES: + UNIFIED_SOURCES += [ + "video_engine/placeholder_device_info.cc", + ] CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] if CONFIG["OS_TARGET"] == "Android": DEFINES["WEBRTC_ANDROID"] = True if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": UNIFIED_SOURCES += ["OSXRunLoopSingleton.cpp"] EXPORTS += ["OSXRunLoopSingleton.h"]
new file mode 100644 --- /dev/null +++ b/dom/media/systemservices/video_engine/placeholder_device_info.cc @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=cpp : */ +/* 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 "placeholder_device_info.h" +#include "modules/video_capture/video_capture_factory.h" + +namespace mozilla { + +PlaceholderDeviceInfo::PlaceholderDeviceInfo(bool aCameraPresent) + : mCameraPresent(aCameraPresent) {} + +PlaceholderDeviceInfo::~PlaceholderDeviceInfo() = default; + +uint32_t PlaceholderDeviceInfo::NumberOfDevices() { return mCameraPresent; } + +int32_t PlaceholderDeviceInfo::Init() { return 0; } + +int32_t PlaceholderDeviceInfo::GetDeviceName( + uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameLength, + char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Length, + char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Length, + pid_t* aPid, bool* aDeviceIsPlaceholder) { + // Check whether there is camera device reported by the Camera portal + // When the promise is resolved, it means there is a camera available + // but we have to use a placeholder device. + if (!mCameraPresent) { + return -1; + } + + // Making these empty to follow the specs for non-legacy enumeration: + // https://w3c.github.io/mediacapture-main/#access-control-model + memset(aDeviceNameUTF8, 0, aDeviceNameLength); + memset(aDeviceUniqueIdUTF8, 0, aDeviceUniqueIdUTF8Length); + + if (aProductUniqueIdUTF8) { + memset(aProductUniqueIdUTF8, 0, aProductUniqueIdUTF8Length); + } + + if (aDeviceIsPlaceholder) { + *aDeviceIsPlaceholder = true; + } + + return 0; +} + +int32_t PlaceholderDeviceInfo::CreateCapabilityMap( + const char* /*aDeviceUniqueIdUTF8*/) { + return -1; +} + +int32_t PlaceholderDeviceInfo::DisplayCaptureSettingsDialogBox( + const char* /*deviceUniqueIdUTF8*/, const char* /*dialogTitleUTF8*/, + void* /*parentWindow*/, uint32_t /*positionX*/, uint32_t /*positionY*/) { + return -1; +} + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/media/systemservices/video_engine/placeholder_device_info.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_MEDIA_SYSTEMSERVICES_VIDEO_ENGINE_PLACEHOLDER_DEVICE_INFO_H_ +#define DOM_MEDIA_SYSTEMSERVICES_VIDEO_ENGINE_PLACEHOLDER_DEVICE_INFO_H_ + +#include "modules/video_capture/device_info_impl.h" +#include "modules/video_capture/video_capture.h" +#include "modules/video_capture/video_capture_impl.h" + +namespace mozilla { + +class PlaceholderDeviceInfo + : public webrtc::videocapturemodule::DeviceInfoImpl { + public: + explicit PlaceholderDeviceInfo(bool aCameraPresent); + ~PlaceholderDeviceInfo() override; + + uint32_t NumberOfDevices() override; + int32_t GetDeviceName(uint32_t aDeviceNumber, char* aDeviceNameUTF8, + uint32_t aDeviceNameLength, char* aDeviceUniqueIdUTF8, + uint32_t aDeviceUniqueIdUTF8Length, + char* aProductUniqueIdUTF8 = nullptr, + uint32_t aProductUniqueIdUTF8Length = 0, + pid_t* aPid = nullptr, + bool* aDeviceIsPlaceholder = nullptr) override; + + int32_t CreateCapabilityMap(const char* aDeviceUniqueIdUTF8) override; + int32_t DisplayCaptureSettingsDialogBox(const char* aDeviceUniqueIdUTF8, + const char* aDialogTitleUTF8, + void* aParentWindow, + uint32_t aPositionX, + uint32_t aPositionY) override; + int32_t Init() override; + + private: + const bool mCameraPresent; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_SYSTEMSERVICES_VIDEO_ENGINE_PLACEHOLDER_DEVICE_INFO_H_
--- a/dom/media/systemservices/video_engine/video_capture_factory.cc +++ b/dom/media/systemservices/video_engine/video_capture_factory.cc @@ -5,25 +5,29 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "video_capture_factory.h" #include "mozilla/StaticPrefs_media.h" #include "desktop_capture_impl.h" #include "VideoEngine.h" +#if defined(WEBRTC_USE_PIPEWIRE) +# include "video_engine/placeholder_device_info.h" +#endif + #if defined(MOZ_ENABLE_DBUS) # include "mozilla/widget/AsyncDBus.h" #endif #include <memory> namespace mozilla { -VideoCaptureFactory::VideoCaptureFactory() : mCameraBackendInitialized(false) { +VideoCaptureFactory::VideoCaptureFactory() { #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID) mVideoCaptureOptions = std::make_unique<webrtc::VideoCaptureOptions>(); // In case pipewire is enabled, this acts as a fallback and can be always // enabled. mVideoCaptureOptions->set_allow_v4l2(true); bool allowPipeWire = false; # if defined(WEBRTC_USE_PIPEWIRE) allowPipeWire = @@ -39,16 +43,26 @@ VideoCaptureFactory::VideoCaptureFactory } std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> VideoCaptureFactory::CreateDeviceInfo( int32_t aId, mozilla::camera::CaptureDeviceType aType) { if (aType == mozilla::camera::CaptureDeviceType::Camera) { std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> deviceInfo; #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID) + // Special case when PipeWire is not initialized yet and we need to insert + // a camera device placeholder based on camera device availability we get + // from the camera portal + if (!mCameraBackendInitialized) { + MOZ_ASSERT(mCameraAvailability != Unknown); + deviceInfo.reset( + new PlaceholderDeviceInfo(mCameraAvailability == Available)); + return deviceInfo; + } + deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo( mVideoCaptureOptions.get())); #else deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo()); #endif return deviceInfo; } @@ -95,18 +109,18 @@ auto VideoCaptureFactory::InitCameraBack mPromiseHolder.Resolve(NS_OK, "VideoCaptureFactory::InitCameraBackend Resolve"); #endif } return mPromise; } -RefPtr<VideoCaptureFactory::HasCameraDevicePromise> -VideoCaptureFactory::HasCameraDevice() { +auto VideoCaptureFactory::HasCameraDevice() + -> RefPtr<VideoCaptureFactory::HasCameraDevicePromise> { #if defined(WEBRTC_USE_PIPEWIRE) && defined(MOZ_ENABLE_DBUS) if (mVideoCaptureOptions && mVideoCaptureOptions->allow_pipewire()) { return widget::CreateDBusProxyForBus( G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /* aInterfaceInfo = */ nullptr, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.Camera") ->Then( @@ -124,29 +138,51 @@ VideoCaptureFactory::HasCameraDevice() { return HasCameraDevicePromise::CreateAndReject( NS_ERROR_UNEXPECTED, "VideoCaptureFactory::HasCameraDevice Reject"); } const bool hasCamera = g_variant_get_boolean(variant); g_variant_unref(variant); return HasCameraDevicePromise::CreateAndResolve( - hasCamera, "VideoCaptureFactory::HasCameraDevice Resolve"); + hasCamera ? Available : NotAvailable, + "VideoCaptureFactory::HasCameraDevice Resolve"); }, [](GUniquePtr<GError>&& aError) { return HasCameraDevicePromise::CreateAndReject( NS_ERROR_NO_INTERFACE, "VideoCaptureFactory::HasCameraDevice Reject"); }); } #endif return HasCameraDevicePromise::CreateAndReject( NS_ERROR_NOT_IMPLEMENTED, "VideoCaptureFactory::HasCameraDevice Resolve"); } +auto VideoCaptureFactory::UpdateCameraAvailability() + -> RefPtr<UpdateCameraAvailabilityPromise> { + return VideoCaptureFactory::HasCameraDevice()->Then( + GetCurrentSerialEventTarget(), __func__, + [this, self = RefPtr(this)]( + const HasCameraDevicePromise::ResolveOrRejectValue& aValue) { + if (aValue.IsResolve()) { + mCameraAvailability = aValue.ResolveValue(); + + return HasCameraDevicePromise::CreateAndResolve( + mCameraAvailability, + "VideoCaptureFactory::UpdateCameraAvailability Resolve"); + } + + mCameraAvailability = Unknown; + return HasCameraDevicePromise::CreateAndReject( + aValue.RejectValue(), + "VideoCaptureFactory::UpdateCameraAvailability Reject"); + }); +} + void VideoCaptureFactory::OnInitialized( webrtc::VideoCaptureOptions::Status status) { switch (status) { case webrtc::VideoCaptureOptions::Status::SUCCESS: mCameraBackendInitialized = true; mPromiseHolder.Resolve(NS_OK, __func__); return; case webrtc::VideoCaptureOptions::Status::UNAVAILABLE:
--- a/dom/media/systemservices/video_engine/video_capture_factory.h +++ b/dom/media/systemservices/video_engine/video_capture_factory.h @@ -18,17 +18,19 @@ enum class CaptureDeviceType; } namespace mozilla { /** * NOTE: This class must be accessed only on a single SerialEventTarget */ class VideoCaptureFactory : webrtc::VideoCaptureOptions::Callback { public: - NS_INLINE_DECL_REFCOUNTING(VideoCaptureFactory); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoCaptureFactory); + + enum CameraAvailability { Unknown, Available, NotAvailable }; VideoCaptureFactory(); std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> CreateDeviceInfo( int32_t aId, mozilla::camera::CaptureDeviceType aType); rtc::scoped_refptr<webrtc::VideoCaptureModule> CreateVideoCapture( int32_t aModuleId, const char* aUniqueId, @@ -43,35 +45,43 @@ class VideoCaptureFactory : webrtc::Vide * supported by PipeWire, all the errors are PipeWire specific: * 1) NS_ERROR_NOT_AVAILABLE - PipeWire libraries are not available on * the system * 2) NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR - camera access has been rejected * 3) NS_ERROR_FAILURE - generic error, usually a PipeWire failure */ RefPtr<CameraBackendInitPromise> InitCameraBackend(); + /** + * Updates information about camera availability + */ + using UpdateCameraAvailabilityPromise = + MozPromise<CameraAvailability, nsresult, true>; + RefPtr<UpdateCameraAvailabilityPromise> UpdateCameraAvailability(); + private: ~VideoCaptureFactory() = default; // aka OnCameraBackendInitialized // this method override has to follow webrtc::VideoCaptureOptions::Callback void OnInitialized(webrtc::VideoCaptureOptions::Status status) override; /** * Resolves with true or false depending on whether there is a camera device * advertised by the xdg-desktop-portal (Camera portal). Rejects with one * of the following errors: * 1) NS_ERROR_NOT_IMPLEMENTED - support for the Camera portal is not * implemented or enabled * 2) NS_ERROR_NO_INTERFACE - the camera portal is not available * 3) NS_ERROR_UNEXPECTED - the camera portal returned wrong value */ - using HasCameraDevicePromise = MozPromise<bool, nsresult, true>; + using HasCameraDevicePromise = MozPromise<CameraAvailability, nsresult, true>; RefPtr<HasCameraDevicePromise> HasCameraDevice(); std::atomic<bool> mCameraBackendInitialized = false; + CameraAvailability mCameraAvailability = Unknown; #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID) std::unique_ptr<webrtc::VideoCaptureOptions> mVideoCaptureOptions; #endif MozPromiseHolder<CameraBackendInitPromise> mPromiseHolder; RefPtr<CameraBackendInitPromise> mPromise; }; } // namespace mozilla
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -97,25 +97,26 @@ void MediaEngineWebRTC::EnumerateVideoDe camera::CaptureEngine capEngine = MediaEngineRemoteVideoSource::CaptureEngine(aMediaSource); num = GetChildAndCall(&CamerasChild::NumberOfCaptureDevices, capEngine); for (int i = 0; i < num; i++) { char deviceName[MediaEngineSource::kMaxDeviceNameLength]; char uniqueId[MediaEngineSource::kMaxUniqueIdLength]; bool scarySource = false; + bool placeholder = false; // paranoia deviceName[0] = '\0'; uniqueId[0] = '\0'; int error; error = GetChildAndCall(&CamerasChild::GetCaptureDevice, capEngine, i, deviceName, sizeof(deviceName), uniqueId, - sizeof(uniqueId), &scarySource); + sizeof(uniqueId), &scarySource, &placeholder); if (error) { LOG(("camera:GetCaptureDevice: Failed %d", error)); continue; } #ifdef DEBUG LOG((" Capture Device Index %d, Name %s", i, deviceName)); webrtc::CaptureCapability cap; @@ -132,21 +133,23 @@ void MediaEngineWebRTC::EnumerateVideoDe } #endif NS_ConvertUTF8toUTF16 name(deviceName); NS_ConvertUTF8toUTF16 uuid(uniqueId); // The remote video backend doesn't implement group id. We return the // device name and higher layers will correlate this with the name of // audio devices. - aDevices->EmplaceBack(new MediaDevice( - this, aMediaSource, name, uuid, uuid, - MediaDevice::IsScary(scaryKind || scarySource), - canRequestOsLevelPrompt ? MediaDevice::OsPromptable::Yes - : MediaDevice::OsPromptable::No)); + + aDevices->EmplaceBack( + new MediaDevice(this, aMediaSource, name, uuid, uuid, + MediaDevice::IsScary(scaryKind || scarySource), + canRequestOsLevelPrompt ? MediaDevice::OsPromptable::Yes + : MediaDevice::OsPromptable::No, + MediaDevice::IsPlaceholder(placeholder))); } } void MediaEngineWebRTC::EnumerateMicrophoneDevices( nsTArray<RefPtr<MediaDevice>>* aDevices) { AssertIsOnOwningThread(); RefPtr<const AudioDeviceSet> devices =
--- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -1354,29 +1354,30 @@ BackgroundParentImpl::RecvEnsureUtilityP resolver(Type(NS_OK, std::move(aValue.ResolveValue()))); }); } })); return IPC_OK(); } mozilla::ipc::IPCResult BackgroundParentImpl::RecvRequestCameraAccess( + const bool& aAllowPermissionRequest, RequestCameraAccessResolver&& aResolver) { #ifdef MOZ_WEBRTC - mozilla::camera::CamerasParent::RequestCameraAccess()->Then( - GetCurrentSerialEventTarget(), __func__, - [resolver = std::move(aResolver)]( - const mozilla::camera::CamerasParent::CameraAccessRequestPromise:: - ResolveOrRejectValue& aValue) { - if (aValue.IsResolve()) { - resolver(aValue.ResolveValue()); - } else { - resolver(aValue.RejectValue()); - } - }); + mozilla::camera::CamerasParent::RequestCameraAccess(aAllowPermissionRequest) + ->Then(GetCurrentSerialEventTarget(), __func__, + [resolver = std::move(aResolver)]( + const mozilla::camera::CamerasParent:: + CameraAccessRequestPromise::ResolveOrRejectValue& aValue) { + if (aValue.IsResolve()) { + resolver(aValue.ResolveValue()); + } else { + resolver(CamerasAccessStatus::Error); + } + }); #else aResolver(NS_ERROR_NOT_IMPLEMENTED); #endif return IPC_OK(); } bool BackgroundParentImpl::DeallocPEndpointForReportParent( PEndpointForReportParent* aActor) {
--- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -348,16 +348,17 @@ class BackgroundParentImpl : public PBac mozilla::ipc::IPCResult RecvEnsureRDDProcessAndCreateBridge( EnsureRDDProcessAndCreateBridgeResolver&& aResolver) override; mozilla::ipc::IPCResult RecvEnsureUtilityProcessAndCreateBridge( const RemoteDecodeIn& aLocation, EnsureUtilityProcessAndCreateBridgeResolver&& aResolver) override; mozilla::ipc::IPCResult RecvRequestCameraAccess( + const bool& aAllowPermissionRequest, RequestCameraAccessResolver&& aResolver) override; bool DeallocPEndpointForReportParent( PEndpointForReportParent* aActor) override; mozilla::ipc::IPCResult RecvRemoveEndpoint( const nsAString& aGroupName, const nsACString& aEndpointURL, const PrincipalInfo& aPrincipalInfo) override;
--- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -70,16 +70,18 @@ include "mozilla/layers/LayersMessageUti using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h"; using class mozilla::dom::SSCacheCopy from "mozilla/dom/PBackgroundSessionStorageCache.h"; using mozilla::RemoteDecodeIn from "mozilla/RemoteDecoderManagerChild.h"; +using mozilla::camera::CamerasAccessStatus from "mozilla/media/CamerasTypes.h"; + namespace mozilla { namespace ipc { [NeedsOtherPid, ChildImpl=virtual, ParentImpl=virtual] sync protocol PBackground { manages PBackgroundIDBFactory; manages PBackgroundIndexedDBUtils; @@ -276,17 +278,17 @@ parent: async EnsureUtilityProcessAndCreateBridge(RemoteDecodeIn aLocation) returns (nsresult rv, Endpoint<PRemoteDecoderManagerChild> aEndpoint); async PLockManager(nsIPrincipal aPrincipalInfo, nsID aClientId); async PFetch(); - async RequestCameraAccess() returns (nsresult rv); + async RequestCameraAccess(bool aAllowPermissionRequest) returns (CamerasAccessStatus rv); child: async PCache(); async PCacheStreamControl(); async PRemoteWorker(RemoteWorkerData data); };