Bug 1724900: Add PipeWire camera support r=pehrsons,ipc-reviewers,nika

Adds a PipeWire based camera support that was recently merged into
WebRTC. This should be an experimental feature for now and therefore it
is kept behind a config option.

Differential Revision: https://phabricator.services.mozilla.com/D176625
This commit is contained in:
Jan Grulich 2023-06-15 09:13:27 +00:00
parent 026249c357
commit 527c5c1a21
12 changed files with 443 additions and 73 deletions

View File

@ -41,6 +41,7 @@
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/media/MediaTaskUtils.h"
#include "nsAppDirectoryServiceDefs.h"
@ -1880,10 +1881,61 @@ RefPtr<DeviceSetPromise> MediaManager::EnumerateRawDevices(
const bool realDeviceRequested = (!hasFakeCams && hasVideo) ||
(!hasFakeMics && hasAudio) || hasAudioOutput;
RefPtr<Runnable> task = NewTaskFrom(
using NativePromise = MozPromise<nsresult, 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();
MozPromiseHolder<NativePromise> 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();
}
}
if (!deviceAccessPromise) {
// No device access request needed. Proceed directly.
deviceAccessPromise = NativePromise::CreateAndResolve(NS_OK, __func__);
}
deviceAccessPromise->Then(
mMediaThread, __func__,
[holder = std::move(holder), aVideoInputType, aAudioInputType,
hasFakeCams, hasFakeMics, videoLoopDev, audioLoopDev, hasVideo, hasAudio,
hasAudioOutput, realDeviceRequested]() mutable {
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)) {
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;
if (hasFakeCams || hasFakeMics) {
@ -1971,24 +2023,6 @@ RefPtr<DeviceSetPromise> MediaManager::EnumerateRawDevices(
holder.Resolve(std::move(devices), __func__);
});
if (realDeviceRequested &&
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();
obs->NotifyObservers(static_cast<nsIRunnable*>(task),
"getUserMedia:ask-device-permission", type);
} else {
// Don't need to ask permission to retrieve list of all devices;
// post the retrieval task immediately.
MediaManager::Dispatch(task.forget());
}
return promise;
}
@ -3006,8 +3040,9 @@ RefPtr<LocalDeviceSetPromise> MediaManager::EnumerateDevicesImpl(
[placeholderListener](RefPtr<MediaMgrError>&& aError) {
// EnumerateDevicesImpl may fail if a new doc has been set, in which
// case the OnNavigation() method should have removed all previous
// active listeners.
MOZ_ASSERT(placeholderListener->Stopped());
// active listeners, or if a platform device access request was not
// granted.
placeholderListener->Stop();
return LocalDeviceSetPromise::CreateAndReject(std::move(aError),
__func__);
});

View File

@ -24,6 +24,7 @@
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/media/MediaUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_permissions.h"
#include "nsIPermissionManager.h"
#include "nsIThread.h"
@ -33,6 +34,10 @@
#include "api/video/video_frame_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#if defined(WEBRTC_USE_PIPEWIRE)
# include "video_engine/pipewire_camera_impl.h"
#endif
#if defined(_WIN32)
# include <process.h>
# define getpid() _getpid()
@ -112,6 +117,11 @@ static int32_t sNumCamerasParents = 0;
// Video processing thread - where webrtc.org capturer code runs. Outlives the
// CamerasParent instances. IPC background thread only.
static StaticRefPtr<nsIThread> sVideoCaptureThread;
// VideoCaptureOptions holding PipeWire session connected to PipeWire socket
// using the file descriptor we received from camera portal request. Set
// initially by IPC background thread only and later accessed by video capture
// thread only. Outlives the CamerasParent instances.
static StaticAutoPtr<webrtc::VideoCaptureOptions> sVideoCaptureOptions;
static already_AddRefed<nsISerialEventTarget>
MakeAndAddRefVideoCaptureThreadAndSingletons() {
@ -375,7 +385,8 @@ void CamerasParent::CloseEngines() {
}
if (VideoEngine* engine = mEngines->ElementAt(CameraEngine); engine) {
auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
auto device_info =
engine->GetOrCreateVideoCaptureDeviceInfo(sVideoCaptureOptions.get());
MOZ_ASSERT(device_info);
if (device_info) {
device_info->DeRegisterVideoInputFeedBack(this);
@ -418,7 +429,8 @@ VideoEngine* CamerasParent::EnsureInitialized(int aEngine) {
}
if (capEngine == CameraEngine) {
auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
auto device_info =
engine->GetOrCreateVideoCaptureDeviceInfo(sVideoCaptureOptions.get());
MOZ_ASSERT(device_info);
if (device_info) {
device_info->RegisterVideoInputFeedBack(this);
@ -442,18 +454,18 @@ ipc::IPCResult CamerasParent::RecvNumberOfCaptureDevices(
LOG("CaptureEngine=%d", aCapEngine);
using Promise = MozPromise<int, bool, true>;
InvokeAsync(
mVideoCaptureThread, __func__,
[this, self = RefPtr(this), aCapEngine] {
int num = -1;
if (auto* engine = EnsureInitialized(aCapEngine)) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
num = static_cast<int>(devInfo->NumberOfDevices());
}
}
return Promise::CreateAndResolve(
num, "CamerasParent::RecvNumberOfCaptureDevices");
})
InvokeAsync(mVideoCaptureThread, __func__,
[this, self = RefPtr(this), aCapEngine] {
int num = -1;
if (auto* engine = EnsureInitialized(aCapEngine)) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo(
sVideoCaptureOptions.get())) {
num = static_cast<int>(devInfo->NumberOfDevices());
}
}
return Promise::CreateAndResolve(
num, "CamerasParent::RecvNumberOfCaptureDevices");
})
->Then(
mPBackgroundEventTarget, __func__,
[this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
@ -526,7 +538,8 @@ ipc::IPCResult CamerasParent::RecvNumberOfCapabilities(
[this, self = RefPtr(this), id = nsCString(aUniqueId), aCapEngine]() {
int num = -1;
if (auto* engine = EnsureInitialized(aCapEngine)) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo(
sVideoCaptureOptions.get())) {
num = devInfo->NumberOfCapabilities(id.get());
}
}
@ -573,7 +586,8 @@ ipc::IPCResult CamerasParent::RecvGetCaptureCapability(
webrtc::VideoCaptureCapability webrtcCaps;
int error = -1;
if (auto* engine = EnsureInitialized(aCapEngine)) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo(
sVideoCaptureOptions.get())) {
error = devInfo->GetCapability(id.get(), aIndex, webrtcCaps);
}
}
@ -631,31 +645,32 @@ ipc::IPCResult CamerasParent::RecvGetCaptureDevice(
using Data = std::tuple<nsCString, nsCString, pid_t, 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;
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);
}
}
if (error == 0) {
name.Assign(deviceName);
uniqueId.Assign(deviceUniqueId);
}
return Promise::CreateAndResolve(
std::make_tuple(std::move(name), std::move(uniqueId), devicePid,
error),
"CamerasParent::RecvGetCaptureDevice");
})
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;
int error = -1;
if (auto* engine = EnsureInitialized(aCapEngine)) {
if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo(
sVideoCaptureOptions.get())) {
error = devInfo->GetDeviceName(
aDeviceIndex, deviceName, sizeof(deviceName),
deviceUniqueId, sizeof(deviceUniqueId), nullptr, 0,
&devicePid);
}
}
if (error == 0) {
name.Assign(deviceName);
uniqueId.Assign(deviceUniqueId);
}
return Promise::CreateAndResolve(
std::make_tuple(std::move(name), std::move(uniqueId),
devicePid, error),
"CamerasParent::RecvGetCaptureDevice");
})
->Then(
mPBackgroundEventTarget, __func__,
[this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
@ -780,7 +795,8 @@ ipc::IPCResult CamerasParent::RecvAllocateCapture(
int error = -1;
if (allowed && EnsureInitialized(aCapEngine)) {
VideoEngine* engine = mEngines->ElementAt(aCapEngine);
captureId = engine->CreateVideoCapture(unique_id.get());
captureId = engine->CreateVideoCapture(
unique_id.get(), sVideoCaptureOptions.get());
engine->WithEntry(captureId,
[&error](VideoEngine::CaptureEntry& cap) {
if (cap.VideoCapture()) {
@ -1158,6 +1174,85 @@ CamerasParent::CamerasParent()
// after the constructor returns.
}
auto CamerasParent::RequestCameraAccess()
-> RefPtr<CameraAccessRequestPromise> {
ipc::AssertIsOnBackgroundThread();
#if defined(WEBRTC_USE_PIPEWIRE)
const bool usePipeWire =
mozilla::StaticPrefs::media_webrtc_camera_allow_pipewire_AtStartup();
#else
const bool usePipeWire = false;
#endif
if (!usePipeWire) {
// For non-PipeWire implementations the access is automatically granted
return CameraAccessRequestPromise::CreateAndResolve(
NS_OK, "CamerasParent::RequestCameraAccess Resolve");
}
#if defined(WEBRTC_USE_PIPEWIRE)
static StaticRefPtr<CameraAccessRequestPromise> sCameraAccessRequestPromise;
if (!sCameraAccessRequestPromise) {
auto options = MakeRefPtr<VideoCaptureOptionsImpl>();
auto portal = MakeRefPtr<CameraPortalImpl>();
sCameraAccessRequestPromise = RefPtr<CameraAccessRequestPromise>(
portal->Start()
->Then(
GetCurrentSerialEventTarget(),
"CamerasParent::RequestCameraAccess portal handler",
[options, portal](int aFD) { return options->Init(aFD); },
[](nsresult aRv) {
return VideoCaptureOptionsImpl::
VideoCaptureOptionsInitPromise::CreateAndReject(
aRv,
"CamerasParent::RequestCameraAccess Portal Reject");
})
->Then(
GetCurrentSerialEventTarget(),
"CamerasParent::RequestCameraAccess options init handler",
[options](nsresult aRv) {
MOZ_ASSERT(NS_SUCCEEDED(aRv));
MOZ_ASSERT(!sVideoCaptureOptions);
sVideoCaptureOptions = options->ReleaseOptions().release();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"CamerasParent::RequestCameraAccess",
[]() { ClearOnShutdown(&sVideoCaptureOptions); }));
return CameraAccessRequestPromise::CreateAndResolve(
aRv,
"CamerasParent::RequestCameraAccess options init "
"resolve");
},
[](nsresult aRv) {
MOZ_ASSERT(NS_FAILED(aRv));
return CameraAccessRequestPromise::CreateAndReject(
aRv,
"CamerasParent::RequestCameraAccess options 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) {
return CameraAccessRequestPromise::CreateAndResolve(
aRv, "CamerasParent::RequestCameraAccess resolve");
},
[promise = RefPtr(sCameraAccessRequestPromise.get())](nsresult aRv) {
if (promise == sCameraAccessRequestPromise) {
sCameraAccessRequestPromise = nullptr;
return CameraAccessRequestPromise::CreateAndReject(
aRv, "CamerasParent::RequestCameraAccess reject");
}
return CamerasParent::RequestCameraAccess();
});
#endif
}
// RecvPCamerasConstructor() is used because IPC messages, for
// Send__delete__(), cannot be sent from AllocPCamerasParent().
ipc::IPCResult CamerasParent::RecvPCamerasConstructor() {

View File

@ -50,17 +50,32 @@ class DeliverFrameRunnable;
class CamerasParent final : public PCamerasParent,
private webrtc::VideoInputFeedBack {
public:
using ShutdownMozPromise = media::ShutdownBlockingTicket::ShutdownMozPromise;
using CameraAccessRequestPromise =
MozPromise<nsresult, nsresult, /* IsExclusive = */ false>;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
CamerasParent, mPBackgroundEventTarget)
public:
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.
*/
static RefPtr<CameraAccessRequestPromise> RequestCameraAccess();
// Messages received from the child. These run on the IPC/PBackground thread.
mozilla::ipc::IPCResult RecvPCamerasConstructor();
mozilla::ipc::IPCResult RecvAllocateCapture(

View File

@ -43,7 +43,8 @@ int VideoEngine::SetAndroidObjects() {
}
#endif
int32_t VideoEngine::CreateVideoCapture(const char* aDeviceUniqueIdUTF8) {
int32_t VideoEngine::CreateVideoCapture(const char* aDeviceUniqueIdUTF8,
webrtc::VideoCaptureOptions* aOptions) {
LOG(("%s", __PRETTY_FUNCTION__));
MOZ_ASSERT(aDeviceUniqueIdUTF8);
@ -63,8 +64,13 @@ int32_t VideoEngine::CreateVideoCapture(const char* aDeviceUniqueIdUTF8) {
CaptureEntry entry = {-1, nullptr};
if (mCaptureDevInfo.type == CaptureDeviceType::Camera) {
entry = CaptureEntry(
id, webrtc::VideoCaptureFactory::Create(aDeviceUniqueIdUTF8));
if (aOptions) {
entry = CaptureEntry(id, webrtc::VideoCaptureFactory::Create(
aOptions, aDeviceUniqueIdUTF8));
} else {
entry = CaptureEntry(
id, webrtc::VideoCaptureFactory::Create(aDeviceUniqueIdUTF8));
}
if (entry.VideoCapture()) {
entry.VideoCapture()->SetApplyRotation(true);
}
@ -120,7 +126,8 @@ int VideoEngine::ReleaseVideoCapture(const int32_t aId) {
}
std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo>
VideoEngine::GetOrCreateVideoCaptureDeviceInfo() {
VideoEngine::GetOrCreateVideoCaptureDeviceInfo(
webrtc::VideoCaptureOptions* aOptions) {
LOG(("%s", __PRETTY_FUNCTION__));
webrtc::Timestamp currentTime = webrtc::Timestamp::Micros(0);
@ -161,7 +168,13 @@ VideoEngine::GetOrCreateVideoCaptureDeviceInfo() {
break;
}
#endif
mDeviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo());
if (aOptions) {
mDeviceInfo.reset(
webrtc::VideoCaptureFactory::CreateDeviceInfo(aOptions));
} else {
mDeviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo());
}
LOG(("CaptureDeviceType::Camera: Finished creating new device."));
break;
}

View File

@ -10,9 +10,11 @@
#include "MediaEngine.h"
#include "VideoFrameUtils.h"
#include "mozilla/media/MediaUtils.h"
#include "mozilla/StaticPrefs_media.h"
#include "modules/video_capture/video_capture_impl.h"
#include "modules/video_capture/video_capture_defines.h"
#include "modules/video_capture/video_capture_factory.h"
#include "modules/video_capture/video_capture_options.h"
#include <memory>
#include <functional>
@ -65,8 +67,13 @@ class VideoEngine {
#if defined(ANDROID)
static int SetAndroidObjects();
#endif
// Returns a non-negative capture identifier or -1 on failure.
int32_t CreateVideoCapture(const char* aDeviceUniqueIdUTF8);
/** Returns a non-negative capture identifier or -1 on failure.
* @value aOptions can be used to specify options for the new VideoCapture.
* This option is currently used only with PipeWire implementation and
* provides access to PipeWire remote for which we were granted permissions.
*/
int32_t CreateVideoCapture(const char* aDeviceUniqueIdUTF8,
webrtc::VideoCaptureOptions* aOptions = nullptr);
int ReleaseVideoCapture(const int32_t aId);
@ -78,12 +85,16 @@ class VideoEngine {
* of the hardware devices. Other types of capture, e.g. screen share info,
* are cached for 1 second. This could be handled in a more elegant way in
* the future.
* @value aOptions can be used to specify options for the new DeviceInfo.
* This option is currently used only with PipeWire implementation and
* provides access to PipeWire remote for which we were granted permissions.
* @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();
GetOrCreateVideoCaptureDeviceInfo(
webrtc::VideoCaptureOptions* aOptions = nullptr);
class CaptureEntry {
public:

View File

@ -56,6 +56,10 @@ if CONFIG["MOZ_WEBRTC"]:
"video_engine/tab_capturer.cc",
]
if "WEBRTC_USE_PIPEWIRE" in DEFINES:
UNIFIED_SOURCES += [
"video_engine/pipewire_camera_impl.cc",
]
if CONFIG["OS_TARGET"] == "Android":
DEFINES["WEBRTC_ANDROID"] = True

View File

@ -0,0 +1,72 @@
/* -*- 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/. */
#include "pipewire_camera_impl.h"
namespace mozilla {
using namespace webrtc;
auto CameraPortalImpl::Start() -> RefPtr<CameraPortalPromise> {
MOZ_ASSERT(!mPortal);
mPortal = std::make_unique<CameraPortal>(this);
mPortal->Start();
return mPromiseHolder.Ensure(__func__);
}
void CameraPortalImpl::OnCameraRequestResult(xdg_portal::RequestResponse result,
int fd) {
if (result == xdg_portal::RequestResponse::kSuccess) {
mPromiseHolder.Resolve(fd, __func__);
} else if (result == xdg_portal::RequestResponse::kUserCancelled) {
mPromiseHolder.Reject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, __func__);
} else {
mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__);
}
}
VideoCaptureOptionsImpl::VideoCaptureOptionsImpl()
: mCaptureOptions(std::make_unique<VideoCaptureOptions>()) {
mCaptureOptions->set_allow_pipewire(true);
}
auto VideoCaptureOptionsImpl::Init(int fd)
-> RefPtr<VideoCaptureOptionsInitPromise> {
MOZ_ASSERT(mCaptureOptions);
RefPtr<VideoCaptureOptionsInitPromise> promise =
mPromiseHolder.Ensure(__func__);
mCaptureOptions->set_pipewire_fd(fd);
mCaptureOptions->Init(this);
return promise;
}
std::unique_ptr<VideoCaptureOptions> VideoCaptureOptionsImpl::ReleaseOptions() {
return std::move(mCaptureOptions);
}
void VideoCaptureOptionsImpl::OnInitialized(
VideoCaptureOptions::Status status) {
switch (status) {
case VideoCaptureOptions::Status::SUCCESS:
mPromiseHolder.Resolve(NS_OK, __func__);
return;
case VideoCaptureOptions::Status::UNAVAILABLE:
mPromiseHolder.Reject(NS_ERROR_NOT_AVAILABLE, __func__);
return;
case VideoCaptureOptions::Status::DENIED:
mPromiseHolder.Reject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, __func__);
return;
default:
mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__);
return;
}
}
} // namespace mozilla

View File

@ -0,0 +1,95 @@
/* -*- 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 WEBRTC_MODULES_VIDEO_CAPTURE_PIPEWIRE_CAMERA_IMPL_H_
#define WEBRTC_MODULES_VIDEO_CAPTURE_PIPEWIRE_CAMERA_IMPL_H_
#include "modules/video_capture/linux/camera_portal.h"
#include "modules/video_capture/video_capture_options.h"
#include "mozilla/MozPromise.h"
namespace mozilla {
/**
* Implementation of webrtc::CameraPortal.
*
* This is intended to be used to request camera access using camera portal
* from xdg-desktop-portal and to obtain a file descriptor of a PipeWire
* socket.
*/
class CameraPortalImpl : public webrtc::CameraPortal::PortalNotifier {
public:
NS_INLINE_DECL_REFCOUNTING(CameraPortalImpl)
CameraPortalImpl() = default;
using CameraPortalPromise = MozPromise<int, nsresult, true>;
/**
* Initiate camera access request over xdg-desktop-portal.
*
* Resolves with a file descriptor of PipeWire socket or rejects with
* error based on the failure reason:
* 1) NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR - camera access has been
* rejected
* 3) NS_ERROR_FAILURE - generic error, e.g. xdg-desktop-portal not
* running
*/
RefPtr<CameraPortalPromise> Start();
private:
~CameraPortalImpl() = default;
void OnCameraRequestResult(webrtc::xdg_portal::RequestResponse result,
int fd) override;
std::unique_ptr<webrtc::CameraPortal> mPortal = nullptr;
MozPromiseHolder<CameraPortalPromise> mPromiseHolder;
};
/**
* Implementation of webrtc::VideoCaptureOptions.
*
* This class is intended to initialize and hold webrtc::PipeWireSession
* object after receiving a file descriptor of PipeWire socket using
* CameraPortalImpl and holds webrtc::VideoCaptureOptions object that needs
* to be further passed to webrtc::VideoCapture when PipeWire backend is
* used.
*/
class VideoCaptureOptionsImpl : webrtc::VideoCaptureOptions::Callback {
public:
NS_INLINE_DECL_REFCOUNTING(VideoCaptureOptionsImpl)
VideoCaptureOptionsImpl();
using VideoCaptureOptionsInitPromise = MozPromise<nsresult, nsresult, true>;
/**
* Request to initialize PipeWire session in order to get list of devices.
*
* Resolves with NS_OK when PipeWire session has been properly initialized
* or rejects with error based on the failure reason:
* 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<VideoCaptureOptionsInitPromise> Init(int fd);
/**
* Returns and release webrtc::VideoCaptureOptions object we hold.
*/
std::unique_ptr<webrtc::VideoCaptureOptions> ReleaseOptions();
private:
~VideoCaptureOptionsImpl() = default;
void OnInitialized(webrtc::VideoCaptureOptions::Status status) override;
std::unique_ptr<webrtc::VideoCaptureOptions> mCaptureOptions;
MozPromiseHolder<VideoCaptureOptionsInitPromise> mPromiseHolder;
};
} // namespace mozilla
#endif // WEBRTC_MODULES_VIDEO_CAPTURE_PIPEWIRE_CAMERA_IMPL_H_

View File

@ -1416,6 +1416,26 @@ BackgroundParentImpl::RecvEnsureUtilityProcessAndCreateBridge(
return IPC_OK();
}
mozilla::ipc::IPCResult BackgroundParentImpl::RecvRequestCameraAccess(
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());
}
});
#else
resolver(NS_ERROR_NOT_IMPLEMENTED);
#endif
return IPC_OK();
}
bool BackgroundParentImpl::DeallocPEndpointForReportParent(
PEndpointForReportParent* aActor) {
RefPtr<dom::EndpointForReportParent> actor =

View File

@ -388,6 +388,9 @@ class BackgroundParentImpl : public PBackgroundParent {
const RemoteDecodeIn& aLocation,
EnsureUtilityProcessAndCreateBridgeResolver&& aResolver) override;
mozilla::ipc::IPCResult RecvRequestCameraAccess(
RequestCameraAccessResolver&& aResolver) override;
bool DeallocPEndpointForReportParent(
PEndpointForReportParent* aActor) override;

View File

@ -319,6 +319,8 @@ parent:
async PFetch();
async RequestCameraAccess() returns (nsresult rv);
child:
async PCache();
async PCacheStreamControl();

View File

@ -10990,6 +10990,11 @@
type: RelaxedAtomicBool
value: true
mirror: always
- name: media.webrtc.camera.allow-pipewire
type: bool
value: false
mirror: once
#endif
- name: media.block-autoplay-until-in-foreground