| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef REMOTING_HOST_LINUX_PORTAL_CAPTURE_STREAM_MANAGER_H_ |
| #define REMOTING_HOST_LINUX_PORTAL_CAPTURE_STREAM_MANAGER_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/files/scoped_file.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/sequence_checker.h" |
| #include "remoting/host/linux/capture_stream_manager.h" |
| #include "remoting/host/linux/gdbus_connection_ref.h" |
| #include "remoting/host/linux/gvariant_ref.h" |
| #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" |
| #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" |
| |
| namespace remoting { |
| |
| class PipewireCaptureStream; |
| class ScopedPortalRequest; |
| |
| // This class is used to create and manage a list of Pipewire capture streams |
| // that are created through the XDG desktop portal. |
| // The current implementation does not support adding or removing virtual |
| // streams after the stream manager is initialized. |
| class PortalCaptureStreamManager final : public CaptureStreamManager { |
| public: |
| using InitCallbackSignature = void(base::expected<void, std::string>); |
| using InitCallback = base::OnceCallback<InitCallbackSignature>; |
| |
| PortalCaptureStreamManager(); |
| ~PortalCaptureStreamManager() override; |
| |
| PortalCaptureStreamManager(const PortalCaptureStreamManager&) = delete; |
| PortalCaptureStreamManager& operator=(const PortalCaptureStreamManager&) = |
| delete; |
| |
| // Initializes the stream manager. Must be called once before calling other |
| // methods of this class. |
| // |
| // If `create_virtual_monitor` is true, a new virtual monitor will be created. |
| // Otherwise, the stream manager will capture existing monitors instead. |
| // `remote_desktop_session_handle` is a session handle that has been created |
| // with the `org.freedesktop.portal.RemoteDesktop.CreateSession` method. This |
| // method will select the VIRTUAL source and call Start on the specified |
| // session handle. |
| void Init(bool create_virtual_monitor, |
| GDBusConnectionRef connection, |
| gvariant::ObjectPath remote_desktop_session_handle, |
| InitCallback callback); |
| |
| // CaptureStreamManager interface. |
| Observer::Subscription AddObserver(Observer* observer) override; |
| void RemoveObserver(Observer* observer); |
| base::WeakPtr<CaptureStream> GetStream(webrtc::ScreenId screen_id) override; |
| void AddVirtualStream(const ScreenResolution& initial_resolution, |
| AddStreamCallback callback) override; |
| void RemoveVirtualStream(webrtc::ScreenId screen_id) override; |
| base::flat_map<webrtc::ScreenId, base::WeakPtr<CaptureStream>> |
| GetActiveStreams() override; |
| |
| // Returns all active stream IDs and their initial desktop rects. They are |
| // reported by the Portal API when the stream just started, and are not |
| // updated afterwards. |
| base::flat_map<webrtc::ScreenId, const webrtc::DesktopRect*> |
| GetActiveStreamInitialRects(); |
| |
| base::WeakPtr<PortalCaptureStreamManager> GetWeakPtr(); |
| |
| private: |
| struct PendingStream { |
| uint32_t pipewire_node_id; |
| std::string mapping_id; |
| webrtc::DesktopRect initial_rect; |
| }; |
| |
| struct StreamInfo { |
| StreamInfo(); |
| StreamInfo(StreamInfo&&); |
| ~StreamInfo(); |
| |
| StreamInfo& operator=(StreamInfo&&); |
| |
| std::unique_ptr<PipewireCaptureStream> stream; |
| webrtc::DesktopRect initial_rect; |
| }; |
| |
| // Calls `success_method` if the returned callback is called with a result, |
| // otherwise calls OnInitError(). |
| // Resets `request` iff it is not a nullptr (i.e. pointing to a unique_ptr). |
| template <typename SuccessType, typename String> |
| GDBusConnectionRef::CallCallback<SuccessType> CheckInitResultAndContinue( |
| void (PortalCaptureStreamManager::*success_method)(SuccessType), |
| std::unique_ptr<ScopedPortalRequest>* request, |
| String&& error_context); |
| |
| // No-op if the returned callback is called with a result, otherwise resets |
| // `request` and calls OnInitError(). |
| template <typename String> |
| GDBusConnectionRef::CallCallback<GVariantRef<"(o)">> ResetRequestOnFailure( |
| std::unique_ptr<ScopedPortalRequest>& request, |
| String&& error_context); |
| |
| void OnInitError(std::string_view what, Loggable why); |
| |
| // DBus callback handlers: |
| void OnSelectSourcesResponse(gvariant::GVariantRef<"a{sv}"> result); |
| void OnStartResponse(gvariant::GVariantRef<"a{sv}"> result); |
| void OnPipeWireStreamOpened( |
| std::pair<std::tuple<GDBusFdList::Handle>, GDBusFdList> result); |
| |
| std::vector<PendingStream> pending_streams_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // We are using the PipeWire node ID as the screen ID. |
| base::flat_map<webrtc::ScreenId, StreamInfo> streams_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| InitCallback init_callback_; |
| gvariant::ObjectPath remote_desktop_session_handle_; |
| GDBusConnectionRef connection_; |
| base::ObserverList<Observer> observers_ GUARDED_BY_CONTEXT(sequence_checker_); |
| base::ScopedFD pipewire_fd_; |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| std::unique_ptr<ScopedPortalRequest> select_sources_request_; |
| std::unique_ptr<ScopedPortalRequest> start_request_; |
| |
| base::WeakPtrFactory<PortalCaptureStreamManager> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace remoting |
| |
| #endif // REMOTING_HOST_LINUX_PORTAL_CAPTURE_STREAM_MANAGER_H_ |