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