mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
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:
parent
026249c357
commit
527c5c1a21
@ -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__);
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
95
dom/media/systemservices/video_engine/pipewire_camera_impl.h
Normal file
95
dom/media/systemservices/video_engine/pipewire_camera_impl.h
Normal 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_
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
||||
|
@ -319,6 +319,8 @@ parent:
|
||||
|
||||
async PFetch();
|
||||
|
||||
async RequestCameraAccess() returns (nsresult rv);
|
||||
|
||||
child:
|
||||
async PCache();
|
||||
async PCacheStreamControl();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user