tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

video_capture_factory.cc (9592B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et ft=cpp : */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "video_capture_factory.h"
      8 
      9 #include "VideoEngine.h"
     10 #include "desktop_capture_impl.h"
     11 #include "fake_video_capture/device_info_fake.h"
     12 #include "fake_video_capture/video_capture_fake.h"
     13 #include "mozilla/StaticPrefs_media.h"
     14 
     15 #if defined(WEBRTC_USE_PIPEWIRE)
     16 #  include "video_engine/placeholder_device_info.h"
     17 #endif
     18 
     19 #if defined(WEBRTC_USE_PIPEWIRE) && defined(MOZ_ENABLE_DBUS)
     20 #  include "mozilla/widget/AsyncDBus.h"
     21 #endif
     22 
     23 #include <memory>
     24 
     25 namespace mozilla {
     26 
     27 VideoCaptureFactory::VideoCaptureFactory() {
     28 #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
     29  mVideoCaptureOptions = std::make_unique<webrtc::VideoCaptureOptions>();
     30  // In case pipewire is enabled, this acts as a fallback and can be always
     31  // enabled.
     32  mVideoCaptureOptions->set_allow_v4l2(true);
     33  bool allowPipeWire = false;
     34 #  if defined(WEBRTC_USE_PIPEWIRE)
     35  allowPipeWire =
     36      mozilla::StaticPrefs::media_webrtc_camera_allow_pipewire_AtStartup();
     37  mVideoCaptureOptions->set_allow_pipewire(allowPipeWire);
     38 #  endif
     39  if (!allowPipeWire) {
     40    // V4L2 backend can and should be initialized right away since there are no
     41    // permissions involved
     42    InitCameraBackend();
     43  }
     44 #endif
     45 }
     46 
     47 std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo>
     48 VideoCaptureFactory::CreateDeviceInfo(
     49    int32_t aId, mozilla::camera::CaptureDeviceType aType) {
     50  if (aType == mozilla::camera::CaptureDeviceType::Camera) {
     51    std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> deviceInfo;
     52    mUseFakeCamera = mUseFakeCamera.orElse([] {
     53      return Some(StaticPrefs::media_getusermedia_camera_fake_force());
     54    });
     55    if (*mUseFakeCamera) {
     56      deviceInfo.reset(new webrtc::videocapturemodule::DeviceInfoFake());
     57      return deviceInfo;
     58    }
     59 #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
     60 #  if defined(WEBRTC_USE_PIPEWIRE)
     61    // Special case when PipeWire is not initialized yet and we need to insert
     62    // a camera device placeholder based on camera device availability we get
     63    // from the camera portal
     64    if (!mCameraBackendInitialized && mVideoCaptureOptions->allow_pipewire()) {
     65      MOZ_ASSERT(mCameraAvailability != Unknown);
     66      deviceInfo.reset(
     67          new PlaceholderDeviceInfo(mCameraAvailability == Available));
     68      return deviceInfo;
     69    }
     70 #  endif
     71 
     72    deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo(
     73        mVideoCaptureOptions.get()));
     74 #else
     75    deviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo());
     76 #endif
     77    return deviceInfo;
     78  }
     79 
     80 #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
     81  MOZ_ASSERT("CreateDeviceInfo NO DESKTOP CAPTURE IMPL ON ANDROID" == nullptr);
     82  return nullptr;
     83 #else
     84  return webrtc::DesktopCaptureImpl::CreateDeviceInfo(aId, aType);
     85 #endif
     86 }
     87 
     88 VideoCaptureFactory::CreateVideoCaptureResult
     89 VideoCaptureFactory::CreateVideoCapture(
     90    int32_t aModuleId, const char* aUniqueId,
     91    mozilla::camera::CaptureDeviceType aType) {
     92  CreateVideoCaptureResult result;
     93  if (aType == mozilla::camera::CaptureDeviceType::Camera) {
     94    if (mUseFakeCamera.valueOr(false)) {
     95      nsCOMPtr<nsISerialEventTarget> target;
     96      NS_CreateBackgroundTaskQueue("VideoCaptureFake::mTarget",
     97                                   getter_AddRefs(target));
     98      result.mCapturer =
     99          webrtc::videocapturemodule::VideoCaptureFake::Create(target);
    100      return result;
    101    }
    102 #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
    103    result.mCapturer = webrtc::VideoCaptureFactory::Create(
    104        mVideoCaptureOptions.get(), aUniqueId);
    105 #else
    106    result.mCapturer = webrtc::VideoCaptureFactory::Create(aUniqueId);
    107 #endif
    108    return result;
    109  }
    110 
    111 #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
    112  MOZ_ASSERT("CreateVideoCapture NO DESKTOP CAPTURE IMPL ON ANDROID" ==
    113             nullptr);
    114 #else
    115  result.mDesktopImpl =
    116      webrtc::DesktopCaptureImpl::Create(aModuleId, aUniqueId, aType);
    117  result.mCapturer =
    118      webrtc::scoped_refptr<webrtc::VideoCaptureModule>(result.mDesktopImpl);
    119 #endif
    120 
    121  return result;
    122 }
    123 
    124 auto VideoCaptureFactory::InitCameraBackend()
    125    -> RefPtr<CameraBackendInitPromise> {
    126  if (!mPromise) {
    127    mPromise = mPromiseHolder.Ensure(__func__);
    128 #if (defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)) && !defined(WEBRTC_ANDROID)
    129    MOZ_ASSERT(mVideoCaptureOptions);
    130    mVideoCaptureOptions->Init(this);
    131 #  if defined(WEBRTC_USE_PIPEWIRE)
    132    mPromise = mPromise->Then(
    133        GetCurrentSerialEventTarget(), __func__,
    134        [this, self = RefPtr(this)](
    135            const CameraBackendInitPromise::ResolveOrRejectValue& aValue) {
    136          if (aValue.IsReject() &&
    137              aValue.RejectValue() != NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR) {
    138            // Fallback to V4L2 in case of PipeWire or camera portal failure.
    139            // There is nothing we need to do in order to initialize V4L2 so
    140            // consider the backend initialized and ready to be used.
    141            mVideoCaptureOptions->set_allow_pipewire(false);
    142            mCameraBackendInitialized = true;
    143 
    144            return CameraBackendInitPromise::CreateAndResolve(
    145                NS_OK,
    146                "VideoCaptureFactory::InitCameraBackend Resolve with "
    147                "fallback to V4L2");
    148          }
    149 
    150          return CameraBackendInitPromise::CreateAndResolveOrReject(
    151              aValue,
    152              "VideoCaptureFactory::InitCameraBackend Resolve or Reject");
    153        });
    154 #  endif
    155 #else
    156    mCameraBackendInitialized = true;
    157    mPromiseHolder.Resolve(NS_OK,
    158                           "VideoCaptureFactory::InitCameraBackend Resolve");
    159 #endif
    160  }
    161 
    162  return mPromise;
    163 }
    164 
    165 auto VideoCaptureFactory::HasCameraDevice()
    166    -> RefPtr<VideoCaptureFactory::HasCameraDevicePromise> {
    167 #if defined(WEBRTC_USE_PIPEWIRE) && defined(MOZ_ENABLE_DBUS)
    168  if (mVideoCaptureOptions && mVideoCaptureOptions->allow_pipewire()) {
    169    return widget::CreateDBusProxyForBus(
    170               G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
    171               /* aInterfaceInfo = */ nullptr, "org.freedesktop.portal.Desktop",
    172               "/org/freedesktop/portal/desktop",
    173               "org.freedesktop.portal.Camera")
    174        ->Then(
    175            GetCurrentSerialEventTarget(), __func__,
    176            [](RefPtr<GDBusProxy>&& aProxy) {
    177              GVariant* variant =
    178                  g_dbus_proxy_get_cached_property(aProxy, "IsCameraPresent");
    179              if (!variant) {
    180                return HasCameraDevicePromise::CreateAndReject(
    181                    NS_ERROR_NO_INTERFACE,
    182                    "VideoCaptureFactory::HasCameraDevice Reject");
    183              }
    184 
    185              if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
    186                return HasCameraDevicePromise::CreateAndReject(
    187                    NS_ERROR_UNEXPECTED,
    188                    "VideoCaptureFactory::HasCameraDevice Reject");
    189              }
    190 
    191              const bool hasCamera = g_variant_get_boolean(variant);
    192              g_variant_unref(variant);
    193              return HasCameraDevicePromise::CreateAndResolve(
    194                  hasCamera ? Available : NotAvailable,
    195                  "VideoCaptureFactory::HasCameraDevice Resolve");
    196            },
    197            [](GUniquePtr<GError>&& aError) {
    198              return HasCameraDevicePromise::CreateAndReject(
    199                  NS_ERROR_NO_INTERFACE,
    200                  "VideoCaptureFactory::HasCameraDevice Reject");
    201            });
    202  }
    203 #endif
    204  return HasCameraDevicePromise::CreateAndReject(
    205      NS_ERROR_NOT_IMPLEMENTED, "VideoCaptureFactory::HasCameraDevice Reject");
    206 }
    207 
    208 auto VideoCaptureFactory::UpdateCameraAvailability()
    209    -> RefPtr<UpdateCameraAvailabilityPromise> {
    210  return VideoCaptureFactory::HasCameraDevice()->Then(
    211      GetCurrentSerialEventTarget(), __func__,
    212      [this, self = RefPtr(this)](
    213          const HasCameraDevicePromise::ResolveOrRejectValue& aValue) {
    214        if (aValue.IsResolve()) {
    215          mCameraAvailability = aValue.ResolveValue();
    216 
    217          return HasCameraDevicePromise::CreateAndResolve(
    218              mCameraAvailability,
    219              "VideoCaptureFactory::UpdateCameraAvailability Resolve");
    220        }
    221 
    222        // We want to fallback to V4L2 at this point, therefore make sure a
    223        // camera device is announced so we can proceed with a gUM request,
    224        // where we can fallback to V4L2 backend.
    225        mCameraAvailability = Available;
    226 
    227        return HasCameraDevicePromise::CreateAndReject(
    228            aValue.RejectValue(),
    229            "VideoCaptureFactory::UpdateCameraAvailability Reject");
    230      });
    231 }
    232 
    233 void VideoCaptureFactory::OnInitialized(
    234    webrtc::VideoCaptureOptions::Status status) {
    235  switch (status) {
    236    case webrtc::VideoCaptureOptions::Status::SUCCESS:
    237      mCameraBackendInitialized = true;
    238      mPromiseHolder.Resolve(NS_OK, __func__);
    239      return;
    240    case webrtc::VideoCaptureOptions::Status::UNAVAILABLE:
    241      mPromiseHolder.Reject(NS_ERROR_NOT_AVAILABLE, __func__);
    242      return;
    243    case webrtc::VideoCaptureOptions::Status::DENIED:
    244      mPromiseHolder.Reject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, __func__);
    245      return;
    246    default:
    247      mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__);
    248      return;
    249  }
    250 }
    251 
    252 void VideoCaptureFactory::Invalidate() { mUseFakeCamera = Nothing(); }
    253 
    254 }  // namespace mozilla