tor-browser

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

base_capturer_pipewire.cc (8708B)


      1 /*
      2 *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h"
     12 
     13 #include <sys/types.h>
     14 
     15 #include <cstdint>
     16 #include <memory>
     17 #include <utility>
     18 
     19 #include "modules/desktop_capture/delegated_source_list_controller.h"
     20 #include "modules/desktop_capture/desktop_capture_options.h"
     21 #include "modules/desktop_capture/desktop_capture_types.h"
     22 #include "modules/desktop_capture/desktop_capturer.h"
     23 #include "modules/desktop_capture/linux/wayland/restore_token_manager.h"
     24 #include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
     25 #include "modules/desktop_capture/linux/wayland/screencast_portal.h"
     26 #include "modules/portal/pipewire_utils.h"
     27 #include "modules/portal/portal_request_response.h"
     28 #include "modules/portal/xdg_session_details.h"
     29 #include "rtc_base/checks.h"
     30 #include "rtc_base/logging.h"
     31 #include "rtc_base/time_utils.h"
     32 #include "rtc_base/trace_event.h"
     33 
     34 namespace webrtc {
     35 
     36 namespace {
     37 
     38 using xdg_portal::RequestResponse;
     39 using xdg_portal::ScreenCapturePortalInterface;
     40 using xdg_portal::SessionDetails;
     41 
     42 }  // namespace
     43 
     44 // static
     45 bool BaseCapturerPipeWire::IsSupported() {
     46  // Unfortunately, the best way we have to check if PipeWire is available is
     47  // to try to initialize it.
     48  // InitializePipeWire should prevent us from repeatedly initializing PipeWire,
     49  // but we also don't really expect support to change without the application
     50  // restarting.
     51  static bool supported =
     52      DesktopCapturer::IsRunningUnderWayland() && InitializePipeWire();
     53  return supported;
     54 }
     55 
     56 BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options,
     57                                           CaptureType type)
     58    : BaseCapturerPipeWire(options,
     59                           std::make_unique<ScreenCastPortal>(type, this)) {
     60  is_screencast_portal_ = true;
     61 }
     62 
     63 BaseCapturerPipeWire::BaseCapturerPipeWire(
     64    const DesktopCaptureOptions& options,
     65    std::unique_ptr<ScreenCapturePortalInterface> portal)
     66    : options_(options),
     67      is_screencast_portal_(false),
     68      portal_(std::move(portal)) {
     69  source_id_ = RestoreTokenManager::GetInstance().GetUnusedId();
     70  options_.screencast_stream()->SetUseDamageRegion(
     71      options_.pipewire_use_damage_region());
     72 }
     73 
     74 BaseCapturerPipeWire::~BaseCapturerPipeWire() {
     75  options_.screencast_stream()->StopScreenCastStream();
     76 }
     77 
     78 void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
     79                                                     uint32_t stream_node_id,
     80                                                     int fd) {
     81  is_portal_open_ = false;
     82 
     83  // Reset the value of capturer_failed_ in case we succeed below. If we fail,
     84  // then it'll set it to the right value again soon enough.
     85  capturer_failed_ = false;
     86  if (result != RequestResponse::kSuccess ||
     87      !options_.screencast_stream() ||
     88      !options_.screencast_stream()->StartScreenCastStream(
     89          stream_node_id, fd, options_.get_width(), options_.get_height(),
     90          options_.prefer_cursor_embedded(),
     91          send_frames_immediately_ ? callback_ : nullptr)) {
     92    capturer_failed_ = true;
     93    RTC_LOG(LS_ERROR) << "ScreenCastPortal failed: "
     94                      << static_cast<uint>(result);
     95  } else if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
     96    if (!screencast_portal->RestoreToken().empty()) {
     97      const SourceId token_id =
     98          selected_source_id_ ? selected_source_id_ : source_id_;
     99      RestoreTokenManager::GetInstance().AddToken(
    100          token_id, screencast_portal->RestoreToken());
    101    }
    102  }
    103 
    104  if (!delegated_source_list_observer_)
    105    return;
    106 
    107  switch (result) {
    108    case RequestResponse::kUnknown:
    109      RTC_DCHECK_NOTREACHED();
    110      break;
    111    case RequestResponse::kSuccess:
    112      delegated_source_list_observer_->OnSelection();
    113      break;
    114    case RequestResponse::kUserCancelled:
    115      delegated_source_list_observer_->OnCancelled();
    116      break;
    117    case RequestResponse::kError:
    118      delegated_source_list_observer_->OnError();
    119      break;
    120  }
    121 }
    122 
    123 void BaseCapturerPipeWire::OnScreenCastSessionClosed() {
    124  if (!capturer_failed_) {
    125    options_.screencast_stream()->StopScreenCastStream();
    126  }
    127  capturer_failed_ = true;
    128 }
    129 
    130 void BaseCapturerPipeWire::UpdateResolution(uint32_t width, uint32_t height) {
    131  if (!capturer_failed_) {
    132    options_.screencast_stream()->UpdateScreenCastStreamResolution(width,
    133                                                                   height);
    134  }
    135 }
    136 
    137 void BaseCapturerPipeWire::SetMaxFrameRate(uint32_t max_frame_rate) {
    138  if (!capturer_failed_) {
    139    options_.screencast_stream()->UpdateScreenCastStreamFrameRate(
    140        max_frame_rate);
    141  }
    142 }
    143 
    144 void BaseCapturerPipeWire::Start(Callback* callback) {
    145  RTC_DCHECK(!callback_);
    146  RTC_DCHECK(callback);
    147 
    148  callback_ = callback;
    149 
    150  if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
    151    screencast_portal->SetPersistMode(
    152        ScreenCastPortal::PersistMode::kTransient);
    153    if (selected_source_id_) {
    154      screencast_portal->SetRestoreToken(
    155          RestoreTokenManager::GetInstance().GetToken(selected_source_id_));
    156    }
    157  }
    158 
    159  is_portal_open_ = true;
    160  portal_->Start();
    161 }
    162 
    163 void BaseCapturerPipeWire::CaptureFrame() {
    164  TRACE_EVENT0("webrtc", "BaseCapturerPipeWire::CaptureFrame");
    165  if (capturer_failed_) {
    166    // This could be recoverable if the source list is re-summoned; but for our
    167    // purposes this is fine, since it requires intervention to resolve and
    168    // essentially starts a new capture.
    169    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    170    return;
    171  }
    172 
    173  int64_t capture_start_time_nanos = TimeNanos();
    174  std::unique_ptr<DesktopFrame> frame =
    175      options_.screencast_stream()->CaptureFrame();
    176 
    177  if (!frame || !frame->data()) {
    178    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    179    return;
    180  }
    181 
    182  // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
    183  // the frame, see ScreenCapturerX11::CaptureFrame.
    184 
    185  frame->set_capturer_id(DesktopCapturerId::kWaylandCapturerLinux);
    186  frame->set_capture_time_ms((TimeNanos() - capture_start_time_nanos) /
    187                             kNumNanosecsPerMillisec);
    188  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    189 }
    190 
    191 bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
    192  RTC_DCHECK(sources->empty());
    193  // List of available screens is already presented by the xdg-desktop-portal,
    194  // so we just need a (valid) source id for any callers to pass around, even
    195  // though it doesn't mean anything to us. Until the user selects a source in
    196  // xdg-desktop-portal we'll just end up returning empty frames. Note that "0"
    197  // is often treated as a null/placeholder id, so we shouldn't use that.
    198  // TODO(https://crbug.com/1297671): Reconsider type of ID when plumbing
    199  // token that will enable stream re-use.
    200  sources->push_back({source_id_});
    201  return true;
    202 }
    203 
    204 bool BaseCapturerPipeWire::SelectSource(SourceId id) {
    205  // Screen selection is handled by the xdg-desktop-portal.
    206  selected_source_id_ = id;
    207  return true;
    208 }
    209 
    210 DelegatedSourceListController*
    211 BaseCapturerPipeWire::GetDelegatedSourceListController() {
    212  return this;
    213 }
    214 
    215 void BaseCapturerPipeWire::Observe(Observer* observer) {
    216  RTC_DCHECK(!delegated_source_list_observer_ || !observer);
    217  delegated_source_list_observer_ = observer;
    218 }
    219 
    220 void BaseCapturerPipeWire::EnsureVisible() {
    221  RTC_DCHECK(callback_);
    222  if (is_portal_open_)
    223    return;
    224 
    225  // Clear any previously selected state/capture
    226  portal_->Stop();
    227  options_.screencast_stream()->StopScreenCastStream();
    228 
    229  // Get a new source id to reflect that the source has changed.
    230  source_id_ = RestoreTokenManager::GetInstance().GetUnusedId();
    231 
    232  is_portal_open_ = true;
    233  portal_->Start();
    234 }
    235 
    236 void BaseCapturerPipeWire::EnsureHidden() {
    237  if (!is_portal_open_)
    238    return;
    239 
    240  is_portal_open_ = false;
    241  portal_->Stop();
    242 }
    243 
    244 SessionDetails BaseCapturerPipeWire::GetSessionDetails() {
    245  return portal_->GetSessionDetails();
    246 }
    247 
    248 ScreenCastPortal* BaseCapturerPipeWire::GetScreenCastPortal() {
    249  return is_screencast_portal_ ? static_cast<ScreenCastPortal*>(portal_.get())
    250                               : nullptr;
    251 }
    252 
    253 void BaseCapturerPipeWire::SendFramesImmediately(bool send_frames_immediately) {
    254  send_frames_immediately_ = send_frames_immediately;
    255 }
    256 
    257 }  // namespace webrtc