summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/video_engine/video_capture_factory.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/video_engine/video_capture_factory.cc')
-rw-r--r--dom/media/systemservices/video_engine/video_capture_factory.cc230
1 files changed, 230 insertions, 0 deletions
diff --git a/dom/media/systemservices/video_engine/video_capture_factory.cc b/dom/media/systemservices/video_engine/video_capture_factory.cc
new file mode 100644
index 0000000000..e4ca505fa6
--- /dev/null
+++ b/dom/media/systemservices/video_engine/video_capture_factory.cc
@@ -0,0 +1,230 @@
+/* -*- 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 "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(WEBRTC_USE_PIPEWIRE) && defined(MOZ_ENABLE_DBUS)
+# include "mozilla/widget/AsyncDBus.h"
+#endif
+
+#include <memory>
+
+namespace mozilla {
+
+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 =
+ mozilla::StaticPrefs::media_webrtc_camera_allow_pipewire_AtStartup();
+ mVideoCaptureOptions->set_allow_pipewire(allowPipeWire);
+# endif
+ if (!allowPipeWire) {
+ // V4L2 backend can and should be initialized right away since there are no
+ // permissions involved
+ InitCameraBackend();
+ }
+#endif
+}
+
+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)
+# if defined(WEBRTC_USE_PIPEWIRE)
+ // 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 && mVideoCaptureOptions->allow_pipewire()) {
+ MOZ_ASSERT(mCameraAvailability != Unknown);
+ deviceInfo.reset(
+ new PlaceholderDeviceInfo(mCameraAvailability == Available));
+ return deviceInfo;
+ }
+# endif
+
+ deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo(
+ mVideoCaptureOptions.get()));
+#else
+ deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo());
+#endif
+ return deviceInfo;
+ }
+
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+ MOZ_ASSERT("CreateDeviceInfo NO DESKTOP CAPTURE IMPL ON ANDROID" == nullptr);
+ return nullptr;
+#else
+ return webrtc::DesktopCaptureImpl::CreateDeviceInfo(aId, aType);
+#endif
+}
+
+rtc::scoped_refptr<webrtc::VideoCaptureModule>
+VideoCaptureFactory::CreateVideoCapture(
+ int32_t aModuleId, const char* aUniqueId,
+ mozilla::camera::CaptureDeviceType aType) {
+ if (aType == mozilla::camera::CaptureDeviceType::Camera) {
+#if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
+ return webrtc::VideoCaptureFactory::Create(mVideoCaptureOptions.get(),
+ aUniqueId);
+#else
+ return webrtc::VideoCaptureFactory::Create(aUniqueId);
+#endif
+ }
+
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+ MOZ_ASSERT("CreateVideoCapture NO DESKTOP CAPTURE IMPL ON ANDROID" ==
+ nullptr);
+ return nullptr;
+#else
+ return rtc::scoped_refptr<webrtc::VideoCaptureModule>(
+ webrtc::DesktopCaptureImpl::Create(aModuleId, aUniqueId, aType));
+#endif
+}
+
+auto VideoCaptureFactory::InitCameraBackend()
+ -> RefPtr<CameraBackendInitPromise> {
+ if (!mPromise) {
+ mPromise = mPromiseHolder.Ensure(__func__);
+#if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
+ MOZ_ASSERT(mVideoCaptureOptions);
+ mVideoCaptureOptions->Init(this);
+# if defined(WEBRTC_USE_PIPEWIRE)
+ mPromise = mPromise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [this, self = RefPtr(this)](
+ const CameraBackendInitPromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsReject() &&
+ aValue.RejectValue() != NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR) {
+ // Fallback to V4L2 in case of PipeWire or camera portal failure.
+ // There is nothing we need to do in order to initialize V4L2 so
+ // consider the backend initialized and ready to be used.
+ mVideoCaptureOptions->set_allow_pipewire(false);
+ mCameraBackendInitialized = true;
+
+ return CameraBackendInitPromise::CreateAndResolve(
+ NS_OK,
+ "VideoCaptureFactory::InitCameraBackend Resolve with "
+ "fallback to V4L2");
+ }
+
+ return CameraBackendInitPromise::CreateAndResolveOrReject(
+ aValue,
+ "VideoCaptureFactory::InitCameraBackend Resolve or Reject");
+ });
+# endif
+#else
+ mCameraBackendInitialized = true;
+ mPromiseHolder.Resolve(NS_OK,
+ "VideoCaptureFactory::InitCameraBackend Resolve");
+#endif
+ }
+
+ return mPromise;
+}
+
+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(
+ GetCurrentSerialEventTarget(), __func__,
+ [](RefPtr<GDBusProxy>&& aProxy) {
+ GVariant* variant =
+ g_dbus_proxy_get_cached_property(aProxy, "IsCameraPresent");
+ if (!variant) {
+ return HasCameraDevicePromise::CreateAndReject(
+ NS_ERROR_NO_INTERFACE,
+ "VideoCaptureFactory::HasCameraDevice Reject");
+ }
+
+ if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
+ 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 ? 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 Reject");
+}
+
+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");
+ }
+
+ // We want to fallback to V4L2 at this point, therefore make sure a
+ // camera device is announced so we can proceed with a gUM request,
+ // where we can fallback to V4L2 backend.
+ mCameraAvailability = Available;
+
+ 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:
+ mPromiseHolder.Reject(NS_ERROR_NOT_AVAILABLE, __func__);
+ return;
+ case webrtc::VideoCaptureOptions::Status::DENIED:
+ mPromiseHolder.Reject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, __func__);
+ return;
+ default:
+ mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__);
+ return;
+ }
+}
+
+} // namespace mozilla