diff -up firefox-60.3.0/config/system-headers.mozbuild.pipewire firefox-60.3.0/config/system-headers.mozbuild --- firefox-60.3.0/config/system-headers.mozbuild.pipewire 2018-10-17 22:39:27.000000000 +0200 +++ firefox-60.3.0/config/system-headers.mozbuild 2019-01-07 14:53:17.200061752 +0100 @@ -314,6 +314,7 @@ system_headers = [ 'Gestalt.h', 'getopt.h', 'gio/gio.h', + 'gio/gunixfdlist.h', 'glibconfig.h', 'glib.h', 'glib-object.h', @@ -607,6 +608,7 @@ system_headers = [ 'Pgenerr.h', 'PGenErr.h', 'Ph.h', + 'pipewire/pipewire.h', 'pixman.h', 'pk11func.h', 'pk11pqg.h', diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc.pipewire 2019-01-07 14:53:17.200061752 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc 2019-01-07 14:53:17.200061752 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" + +#if defined(USE_X11) +#include "webrtc/modules/desktop_capture/app_capturer_x11.h" +#endif // defined(USE_X11) + +namespace webrtc { + +// static +AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) { +#if defined(USE_X11) + return AppCapturerX11::Create(options); +#endif // defined(USE_X11) + + return nullptr; +} + +// static +std::unique_ptr DesktopCapturer::CreateRawAppCapturer( + const DesktopCaptureOptions& options) { +#if defined(USE_X11) + return AppCapturerX11::CreateRawAppCapturer(options); +#endif // defined(USE_X11) + + return nullptr; +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc 2019-01-07 14:53:17.201061764 +0100 @@ -7,9 +7,6 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/desktop_capture/app_capturer.h" -#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" -#include "webrtc/modules/desktop_capture/x11/shared_x_util.h" #include #include @@ -21,80 +18,19 @@ #include +#include "webrtc/modules/desktop_capture/app_capturer_x11.h" + #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/x11/shared_x_util.h" #include "webrtc/modules/desktop_capture/x11/x_error_trap.h" #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" #include "webrtc/system_wrappers/include/logging.h" namespace webrtc { -namespace { - -class ScreenCapturerProxy : DesktopCapturer::Callback { -public: - ScreenCapturerProxy() - : screen_capturer_(DesktopCapturer::CreateScreenCapturer(DesktopCaptureOptions::CreateDefault())) { - screen_capturer_->SelectSource(kFullDesktopScreenId); - screen_capturer_->Start(this); - } - void CaptureFrame() { screen_capturer_->CaptureFrame(); } - std::unique_ptr GetFrame() { return std::move(frame_); } - - // Callback interface - virtual void OnCaptureResult(DesktopCapturer::Result result, - std::unique_ptr frame) { - frame_ = std::move(frame); - } - -protected: - std::unique_ptr screen_capturer_; - std::unique_ptr frame_; -}; - -class AppCapturerLinux : public AppCapturer { -public: - AppCapturerLinux(const DesktopCaptureOptions& options); - virtual ~AppCapturerLinux(); - - // AppCapturer interface. - virtual bool GetAppList(AppList* apps) override; - virtual bool SelectApp(ProcessId processId) override; - virtual bool BringAppToFront() override; - - // DesktopCapturer interface. - virtual void Start(Callback* callback) override; - virtual void Stop() override; - virtual void CaptureFrame() override; - virtual bool SelectSource(SourceId id) override - { - return SelectApp(static_cast(id)); - } - -protected: - Display* GetDisplay() { return x_display_->display(); } - bool UpdateRegions(); - - void FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame,Region rgn, uint32_t color); -private: - Callback* callback_; - ProcessId selected_process_; - - // Sample Mode - ScreenCapturerProxy screen_capturer_proxy_; - // Mask of foreground (non-app windows in front of selected) - Region rgn_mask_; - // Region of selected windows - Region rgn_visual_; - // Mask of background (desktop, non-app windows behind selected) - Region rgn_background_; - - rtc::scoped_refptr x_display_; - RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux); -}; - -AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options) +AppCapturerX11::AppCapturerX11(const DesktopCaptureOptions& options) : callback_(NULL), selected_process_(0), x_display_(options.x_display()) { @@ -103,7 +39,7 @@ AppCapturerLinux::AppCapturerLinux(const rgn_background_ = XCreateRegion(); } -AppCapturerLinux::~AppCapturerLinux() { +AppCapturerX11::~AppCapturerX11() { if (rgn_mask_) { XDestroyRegion(rgn_mask_); } @@ -116,32 +52,32 @@ AppCapturerLinux::~AppCapturerLinux() { } // AppCapturer interface. -bool AppCapturerLinux::GetAppList(AppList* apps) { +bool AppCapturerX11::GetAppList(AppList* apps) { // Implemented in DesktopDeviceInfo return true; } -bool AppCapturerLinux::SelectApp(ProcessId processId) { +bool AppCapturerX11::SelectApp(ProcessId processId) { selected_process_ = processId; return true; } -bool AppCapturerLinux::BringAppToFront() { +bool AppCapturerX11::BringAppToFront() { // Not implemented yet: See Bug 1036653 return true; } // DesktopCapturer interface. -void AppCapturerLinux::Start(Callback* callback) { +void AppCapturerX11::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } -void AppCapturerLinux::Stop() { +void AppCapturerX11::Stop() { callback_ = NULL; } -void AppCapturerLinux::CaptureFrame() { +void AppCapturerX11::CaptureFrame() { XErrorTrap error_trap(GetDisplay()); //Capture screen >> set root window as capture window @@ -169,7 +105,7 @@ void AppCapturerLinux::CaptureFrame() { } } -void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) { +void AppCapturerX11::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) { XErrorTrap error_trap(GetDisplay()); if (!pDesktopFrame) { @@ -192,7 +128,7 @@ void AppCapturerLinux::FillDesktopFrameR } } -bool AppCapturerLinux::UpdateRegions() { +bool AppCapturerX11::UpdateRegions() { XErrorTrap error_trap(GetDisplay()); XSubtractRegion(rgn_visual_, rgn_visual_, rgn_visual_); @@ -269,21 +205,19 @@ bool AppCapturerLinux::UpdateRegions() { return true; } -} // namespace - // static -AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) { - return new AppCapturerLinux(options); +AppCapturer* AppCapturerX11::Create(const DesktopCaptureOptions& options) { + return new AppCapturerX11(options); } // static -std::unique_ptr DesktopCapturer::CreateRawAppCapturer( +std::unique_ptr AppCapturerX11::CreateRawAppCapturer( const DesktopCaptureOptions& options) { if (!options.x_display()) return nullptr; - std::unique_ptr capturer(new AppCapturerLinux(options)); + std::unique_ptr capturer(new AppCapturerX11(options)); return std::unique_ptr(std::move(capturer)); } diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.h.pipewire 2019-01-07 14:53:17.201061764 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.h 2019-01-07 14:53:17.201061764 +0100 @@ -0,0 +1,98 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_APP_CAPTURER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_APP_CAPTURER_X11_H_ + +#include +#include +#include +#include + +#include "webrtc/modules/desktop_capture/app_capturer.h" +#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" +#include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { + +class ScreenCapturerProxy : DesktopCapturer::Callback { +public: + ScreenCapturerProxy() + : screen_capturer_(DesktopCapturer::CreateScreenCapturer(DesktopCaptureOptions::CreateDefault())) { + screen_capturer_->SelectSource(kFullDesktopScreenId); + screen_capturer_->Start(this); + } + void CaptureFrame() { screen_capturer_->CaptureFrame(); } + std::unique_ptr GetFrame() { return std::move(frame_); } + + // Callback interface + virtual void OnCaptureResult(DesktopCapturer::Result result, + std::unique_ptr frame) { + frame_ = std::move(frame); + } + +protected: + std::unique_ptr screen_capturer_; + std::unique_ptr frame_; +}; + +class AppCapturerX11 : public AppCapturer { +public: + AppCapturerX11(const DesktopCaptureOptions& options); + virtual ~AppCapturerX11(); + + static AppCapturer* Create(const DesktopCaptureOptions& options); + static std::unique_ptr CreateRawAppCapturer(const DesktopCaptureOptions& options); + + // AppCapturer interface. + virtual bool GetAppList(AppList* apps) override; + virtual bool SelectApp(ProcessId processId) override; + virtual bool BringAppToFront() override; + + // DesktopCapturer interface. + virtual void Start(Callback* callback) override; + virtual void Stop() override; + virtual void CaptureFrame() override; + virtual bool SelectSource(SourceId id) override + { + return SelectApp(static_cast(id)); + } + +protected: + Display* GetDisplay() { return x_display_->display(); } + bool UpdateRegions(); + + void FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame,Region rgn, uint32_t color); +private: + Callback* callback_; + ProcessId selected_process_; + + // Sample Mode + ScreenCapturerProxy screen_capturer_proxy_; + // Mask of foreground (non-app windows in front of selected) + Region rgn_mask_; + // Region of selected windows + Region rgn_visual_; + // Mask of background (desktop, non-app windows behind selected) + Region rgn_background_; + + rtc::scoped_refptr x_display_; + RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerX11); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_APP_CAPTURER_X11_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.cc.pipewire 2019-01-07 14:53:17.201061764 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.cc 2019-01-07 14:53:17.201061764 +0100 @@ -0,0 +1,849 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "webrtc/modules/desktop_capture/base_capturer_pipewire.h" + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" + +namespace webrtc { + +const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; +const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; +const char kDesktopRequestObjectPath[] = + "/org/freedesktop/portal/desktop/request"; +const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; +const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; +const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; + +const int kBytesPerPixelPw = 4; + +// static +void BaseCapturerPipeWire::OnStateChanged(void* data, + pw_remote_state old_state, + pw_remote_state state, + const char* error_message) { + BaseCapturerPipeWire* that = static_cast(data); + RTC_DCHECK(that); + + switch (state) { + case PW_REMOTE_STATE_ERROR: + LOG(LS_ERROR) << "PipeWire remote state error: " << error_message; + break; + case PW_REMOTE_STATE_CONNECTED: + LOG(LS_INFO) << "PipeWire remote state: connected."; + that->CreateReceivingStream(); + break; + case PW_REMOTE_STATE_CONNECTING: + LOG(LS_INFO) << "PipeWire remote state: connecting."; + break; + case PW_REMOTE_STATE_UNCONNECTED: + LOG(LS_INFO) << "PipeWire remote state: unconnected."; + break; + } +} + +// static +void BaseCapturerPipeWire::OnStreamStateChanged(void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message) { + BaseCapturerPipeWire* that = static_cast(data); + RTC_DCHECK(that); + + switch (state) { + case PW_STREAM_STATE_ERROR: + LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; + break; + case PW_STREAM_STATE_CONFIGURE: + pw_stream_set_active(that->pw_stream_, true); + break; + case PW_STREAM_STATE_UNCONNECTED: + case PW_STREAM_STATE_CONNECTING: + case PW_STREAM_STATE_READY: + case PW_STREAM_STATE_PAUSED: + case PW_STREAM_STATE_STREAMING: + break; + } +} + +// static +void BaseCapturerPipeWire::OnStreamFormatChanged( + void* data, + const struct spa_pod* format) { + BaseCapturerPipeWire* that = static_cast(data); + RTC_DCHECK(that); + + LOG(LS_INFO) << "PipeWire stream format changed."; + + if (!format) { + pw_stream_finish_format(that->pw_stream_, /*res=*/0, /*params=*/nullptr, + /*n_params=*/0); + return; + } + + that->spa_video_format_ = new spa_video_info_raw(); + spa_format_video_raw_parse(format, that->spa_video_format_, + &that->pw_type_->format_video); + + auto width = that->spa_video_format_->size.width; + auto height = that->spa_video_format_->size.height; + auto stride = SPA_ROUND_UP_N(width * kBytesPerPixelPw, 4); + auto size = height * stride; + + uint8_t buffer[1024] = {}; + auto builder = spa_pod_builder{buffer, sizeof(buffer)}; + + // Setup buffers and meta header for new format. + const struct spa_pod* params[2]; + params[0] = reinterpret_cast(spa_pod_builder_object( + &builder, + // id to enumerate buffer requirements + that->pw_core_type_->param.idBuffers, + that->pw_core_type_->param_buffers.Buffers, + // Size: specified as integer (i) and set to specified size + ":", that->pw_core_type_->param_buffers.size, "i", size, + // Stride: specified as integer (i) and set to specified stride + ":", that->pw_core_type_->param_buffers.stride, "i", stride, + // Buffers: specifies how many buffers we want to deal with, set as + // integer (i) where preferred number is 8, then allowed number is defined + // as range (r) from min and max values and it is undecided (u) to allow + // negotiation + ":", that->pw_core_type_->param_buffers.buffers, "iru", 8, + SPA_POD_PROP_MIN_MAX(1, 32), + // Align: memory alignment of the buffer, set as integer (i) to specified + // value + ":", that->pw_core_type_->param_buffers.align, "i", 16)); + params[1] = reinterpret_cast(spa_pod_builder_object( + &builder, + // id to enumerate supported metadata + that->pw_core_type_->param.idMeta, that->pw_core_type_->param_meta.Meta, + // Type: specified as id or enum (I) + ":", that->pw_core_type_->param_meta.type, "I", + that->pw_core_type_->meta.Header, + // Size: size of the metadata, specified as integer (i) + ":", that->pw_core_type_->param_meta.size, "i", + sizeof(struct spa_meta_header))); + + pw_stream_finish_format(that->pw_stream_, /*res=*/0, params, /*n_params=*/2); +} + +// static +void BaseCapturerPipeWire::OnStreamProcess(void* data) { + BaseCapturerPipeWire* that = static_cast(data); + RTC_DCHECK(that); + + pw_buffer* buf = nullptr; + + if (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) { + return; + } + + that->HandleBuffer(buf); + + pw_stream_queue_buffer(that->pw_stream_, buf); +} + +BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type) + : capture_source_type_(source_type) {} + +BaseCapturerPipeWire::~BaseCapturerPipeWire() { + if (pw_main_loop_) { + pw_thread_loop_stop(pw_main_loop_); + } + + if (pw_type_) { + delete pw_type_; + } + + if (spa_video_format_) { + delete spa_video_format_; + } + + if (pw_stream_) { + pw_stream_destroy(pw_stream_); + } + + if (pw_remote_) { + pw_remote_destroy(pw_remote_); + } + + if (pw_core_) { + pw_core_destroy(pw_core_); + } + + if (pw_main_loop_) { + pw_thread_loop_destroy(pw_main_loop_); + } + + if (pw_loop_) { + pw_loop_destroy(pw_loop_); + } + + if (current_frame_) { + free(current_frame_); + } + + if (start_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); + } + if (sources_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, + sources_request_signal_id_); + } + if (session_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, + session_request_signal_id_); + } + + if (session_handle_) { + GDBusMessage* message = g_dbus_message_new_method_call( + kDesktopBusName, session_handle_, kSessionInterfaceName, "Close"); + if (message) { + GError* error = nullptr; + g_dbus_connection_send_message(connection_, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + /*out_serial=*/nullptr, &error); + if (error) { + LOG(LS_ERROR) << "Failed to close the session: " << error->message; + g_error_free(error); + } + g_object_unref(message); + } + } + + g_free(start_handle_); + g_free(sources_handle_); + g_free(session_handle_); + g_free(portal_handle_); + + if (proxy_) { + g_clear_object(&proxy_); + } +} + +void BaseCapturerPipeWire::InitPortals() { + g_dbus_proxy_new_for_bus( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, + kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, + /*cancellable=*/nullptr, + reinterpret_cast(OnProxyRequested), this); +} + +void BaseCapturerPipeWire::InitPipeWire() { + pw_init(/*argc=*/nullptr, /*argc=*/nullptr); + + pw_loop_ = pw_loop_new(/*properties=*/nullptr); + pw_main_loop_ = pw_thread_loop_new(pw_loop_, "pipewire-main-loop"); + + pw_core_ = pw_core_new(pw_loop_, /*properties=*/nullptr); + pw_core_type_ = pw_core_get_type(pw_core_); + pw_remote_ = pw_remote_new(pw_core_, nullptr, /*user_data_size=*/0); + + InitPipeWireTypes(); + + // Initialize event handlers, remote end and stream-related. + pw_remote_events_.version = PW_VERSION_REMOTE_EVENTS; + pw_remote_events_.state_changed = &OnStateChanged; + + pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; + pw_stream_events_.state_changed = &OnStreamStateChanged; + pw_stream_events_.format_changed = &OnStreamFormatChanged; + pw_stream_events_.process = &OnStreamProcess; + + pw_remote_add_listener(pw_remote_, &spa_remote_listener_, &pw_remote_events_, + this); + pw_remote_connect_fd(pw_remote_, pw_fd_); + + if (pw_thread_loop_start(pw_main_loop_) < 0) { + LOG(LS_ERROR) << "Failed to start main PipeWire loop"; + portal_init_failed_ = true; + } +} + +void BaseCapturerPipeWire::InitPipeWireTypes() { + spa_type_map* map = pw_core_type_->map; + pw_type_ = new PipeWireType(); + + spa_type_media_type_map(map, &pw_type_->media_type); + spa_type_media_subtype_map(map, &pw_type_->media_subtype); + spa_type_format_video_map(map, &pw_type_->format_video); + spa_type_video_format_map(map, &pw_type_->video_format); +} + +void BaseCapturerPipeWire::CreateReceivingStream() { + spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1}; + spa_rectangle pwScreenBounds = + spa_rectangle{static_cast(desktop_size_.width()), + static_cast(desktop_size_.height())}; + + spa_fraction pwFrameRateMin = spa_fraction{0, 1}; + spa_fraction pwFrameRateMax = spa_fraction{60, 1}; + + pw_properties* reuseProps = pw_properties_new("pipewire.client.reuse", "1", + /*end of varargs*/ nullptr); + pw_stream_ = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps); + + uint8_t buffer[1024] = {}; + const spa_pod* params[1]; + spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; + params[0] = reinterpret_cast(spa_pod_builder_object( + &builder, + // id to enumerate formats + pw_core_type_->param.idEnumFormat, pw_core_type_->spa_format, "I", + pw_type_->media_type.video, "I", pw_type_->media_subtype.raw, + // Video format: specified as id or enum (I), preferred format is BGRx, + // then allowed formats are enumerated (e) and the format is undecided (u) + // to allow negotiation + ":", pw_type_->format_video.format, "Ieu", pw_type_->video_format.BGRx, + SPA_POD_PROP_ENUM(2, pw_type_->video_format.RGBx, + pw_type_->video_format.BGRx), + // Video size: specified as rectangle (R), preferred size is specified as + // first parameter, then allowed size is defined as range (r) from min and + // max values and the format is undecided (u) to allow negotiation + ":", pw_type_->format_video.size, "Rru", &pwScreenBounds, 2, + &pwMinScreenBounds, &pwScreenBounds, + // Frame rate: specified as fraction (F) and set to minimum frame rate + // value + ":", pw_type_->format_video.framerate, "F", &pwFrameRateMin, + // Max frame rate: specified as fraction (F), preferred frame rate is set + // to maximum value, then allowed frame rate is defined as range (r) from + // min and max values and it is undecided (u) to allow negotiation + ":", pw_type_->format_video.max_framerate, "Fru", &pwFrameRateMax, 2, + &pwFrameRateMin, &pwFrameRateMax)); + + pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_, + this); + pw_stream_flags flags = static_cast( + PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | + PW_STREAM_FLAG_MAP_BUFFERS); + if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, /*port_path=*/nullptr, + flags, params, + /*n_params=*/1) != 0) { + LOG(LS_ERROR) << "Could not connect receiving stream."; + portal_init_failed_ = true; + return; + } +} + +void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) { + spa_buffer* spaBuffer = buffer->buffer; + void* src = nullptr; + + if (!(src = spaBuffer->datas[0].data)) { + return; + } + + uint32_t maxSize = spaBuffer->datas[0].maxsize; + int32_t srcStride = spaBuffer->datas[0].chunk->stride; + if (srcStride != (desktop_size_.width() * kBytesPerPixelPw)) { + LOG(LS_ERROR) << "Got buffer with stride different from screen stride: " + << srcStride + << " != " << (desktop_size_.width() * kBytesPerPixelPw); + portal_init_failed_ = true; + return; + } + + if (!current_frame_) { + current_frame_ = static_cast(malloc(maxSize)); + } + RTC_DCHECK(current_frame_ != nullptr); + + // If both sides decided to go with the RGBx format we need to convert it to + // BGRx to match color format expected by WebRTC. + if (spa_video_format_->format == pw_type_->video_format.RGBx) { + uint8_t* tempFrame = static_cast(malloc(maxSize)); + std::memcpy(tempFrame, src, maxSize); + ConvertRGBxToBGRx(tempFrame, maxSize); + std::memcpy(current_frame_, tempFrame, maxSize); + free(tempFrame); + } else { + std::memcpy(current_frame_, src, maxSize); + } +} + +void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) { + // Change color format for KDE KWin which uses RGBx and not BGRx + for (uint32_t i = 0; i < size; i += 4) { + uint8_t tempR = frame[i]; + uint8_t tempB = frame[i + 2]; + frame[i] = tempB; + frame[i + 2] = tempR; + } +} + +guint BaseCapturerPipeWire::SetupRequestResponseSignal( + const gchar* object_path, + GDBusSignalCallback callback) { + return g_dbus_connection_signal_subscribe( + connection_, kDesktopBusName, kRequestInterfaceName, "Response", + object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + callback, this, /*user_data_free_func=*/nullptr); +} + +// static +void BaseCapturerPipeWire::OnProxyRequested(GObject* /*object*/, + GAsyncResult* result, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + GError* error = nullptr; + that->proxy_ = g_dbus_proxy_new_finish(result, &error); + if (!that->proxy_) { + LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " + << error->message; + g_error_free(error); + that->portal_init_failed_ = true; + return; + } + that->connection_ = g_dbus_proxy_get_connection(that->proxy_); + + LOG(LS_INFO) << "Created proxy for the screen cast portal."; + that->SessionRequest(); +} + +// static +gchar* BaseCapturerPipeWire::PrepareSignalHandle(GDBusConnection* connection, + const gchar* token) { + gchar* sender = g_strdup(g_dbus_connection_get_unique_name(connection) + 1); + for (int i = 0; sender[i]; i++) { + if (sender[i] == '.') { + sender[i] = '_'; + } + } + + gchar* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender, "/", + token, /*end of varargs*/ nullptr); + g_free(sender); + + return handle; +} + +void BaseCapturerPipeWire::SessionRequest() { + GVariantBuilder builder; + gchar* variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = + g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "session_handle_token", + g_variant_new_string(variant_string)); + g_free(variant_string); + variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string)); + + portal_handle_ = PrepareSignalHandle(connection_, variant_string); + session_request_signal_id_ = SetupRequestResponseSignal( + portal_handle_, OnSessionRequestResponseSignal); + g_free(variant_string); + + LOG(LS_INFO) << "Screen cast session requested."; + g_dbus_proxy_call( + proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, + reinterpret_cast(OnSessionRequested), this); +} + +// static +void BaseCapturerPipeWire::OnSessionRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + GError* error = nullptr; + GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); + if (!variant) { + LOG(LS_ERROR) << "Failed to create a screen cast session: " + << error->message; + g_error_free(error); + that->portal_init_failed_ = true; + return; + } + LOG(LS_INFO) << "Initializing the screen cast session."; + + gchar* handle = nullptr; + g_variant_get_child(variant, 0, "o", &handle); + g_variant_unref(variant); + if (!handle) { + LOG(LS_ERROR) << "Failed to initialize the screen cast session."; + if (that->session_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection, + that->session_request_signal_id_); + that->session_request_signal_id_ = 0; + } + that->portal_init_failed_ = true; + return; + } + + g_free(handle); + + LOG(LS_INFO) << "Subscribing to the screen cast session."; +} + +// static +void BaseCapturerPipeWire::OnSessionRequestResponseSignal( + GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + + LOG(LS_INFO) + << "Received response for the screen cast session subscription."; + + guint32 portal_response; + GVariant* response_data; + g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); + g_variant_lookup(response_data, "session_handle", "s", + &that->session_handle_); + g_variant_unref(response_data); + + if (!that->session_handle_ || portal_response) { + LOG(LS_ERROR) + << "Failed to request the screen cast session subscription."; + that->portal_init_failed_ = true; + return; + } + + that->SourcesRequest(); +} + +void BaseCapturerPipeWire::SourcesRequest() { + GVariantBuilder builder; + gchar* variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + // We want to record monitor content. + g_variant_builder_add(&builder, "{sv}", "types", + g_variant_new_uint32(capture_source_type_)); + // We don't want to allow selection of multiple sources. + g_variant_builder_add(&builder, "{sv}", "multiple", + g_variant_new_boolean(false)); + variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string)); + + sources_handle_ = PrepareSignalHandle(connection_, variant_string); + sources_request_signal_id_ = SetupRequestResponseSignal( + sources_handle_, OnSourcesRequestResponseSignal); + g_free(variant_string); + + LOG(LS_INFO) << "Requesting sources from the screen cast session."; + g_dbus_proxy_call( + proxy_, "SelectSources", + g_variant_new("(oa{sv})", session_handle_, &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, + reinterpret_cast(OnSourcesRequested), this); +} + +// static +void BaseCapturerPipeWire::OnSourcesRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + GError* error = nullptr; + GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); + if (!variant) { + LOG(LS_ERROR) << "Failed to request the sources: " << error->message; + g_error_free(error); + that->portal_init_failed_ = true; + return; + } + + LOG(LS_INFO) << "Sources requested from the screen cast session."; + + gchar* handle = nullptr; + g_variant_get_child(variant, 0, "o", &handle); + g_variant_unref(variant); + if (!handle) { + LOG(LS_ERROR) << "Failed to initialize the screen cast session."; + if (that->sources_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection, + that->sources_request_signal_id_); + that->sources_request_signal_id_ = 0; + } + that->portal_init_failed_ = true; + return; + } + + g_free(handle); + + LOG(LS_INFO) << "Subscribed to sources signal."; +} + +// static +void BaseCapturerPipeWire::OnSourcesRequestResponseSignal( + GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + guint32 portal_response; + g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); + if (portal_response) { + LOG(LS_ERROR) + << "Failed to select sources for the screen cast session."; + that->portal_init_failed_ = true; + return; + } + + LOG(LS_INFO) << "Received sources signal from session."; + that->StartRequest(); +} + +void BaseCapturerPipeWire::StartRequest() { + GVariantBuilder builder; + gchar* variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string)); + + start_handle_ = PrepareSignalHandle(connection_, variant_string); + start_request_signal_id_ = + SetupRequestResponseSignal(start_handle_, OnStartRequestResponseSignal); + g_free(variant_string); + + // "Identifier for the application window", this is Wayland, so not "x11:...". + const gchar parent_window[] = ""; + + LOG(LS_INFO) << "Starting the screen cast session."; + g_dbus_proxy_call( + proxy_, "Start", + g_variant_new("(osa{sv})", session_handle_, parent_window, &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, + reinterpret_cast(OnStartRequested), this); +} + +// static +void BaseCapturerPipeWire::OnStartRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + GError* error = nullptr; + GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); + if (!variant) { + LOG(LS_ERROR) << "Failed to start the screen cast session: " + << error->message; + g_error_free(error); + that->portal_init_failed_ = true; + return; + } + + LOG(LS_INFO) << "Initializing the start of the screen cast session."; + + gchar* handle = nullptr; + g_variant_get_child(variant, 0, "o", &handle); + g_variant_unref(variant); + if (!handle) { + LOG(LS_ERROR) + << "Failed to initialize the start of the screen cast session."; + if (that->start_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection, + that->start_request_signal_id_); + that->start_request_signal_id_ = 0; + } + that->portal_init_failed_ = true; + return; + } + + g_free(handle); + + LOG(LS_INFO) << "Subscribed to the start signal."; +} + +// static +void BaseCapturerPipeWire::OnStartRequestResponseSignal( + GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + LOG(LS_INFO) << "Start signal received."; + guint32 portal_response; + GVariant* response_data; + GVariantIter* iter = nullptr; + g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); + if (portal_response || !response_data) { + LOG(LS_ERROR) << "Failed to start the screen cast session."; + that->portal_init_failed_ = true; + return; + } + + // Array of PipeWire streams. See + // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml + // documentation for . + if (g_variant_lookup(response_data, "streams", "a(ua{sv})", &iter)) { + GVariant* variant; + + while (g_variant_iter_next(iter, "@(ua{sv})", &variant)) { + guint32 stream_id; + gint32 width; + gint32 height; + GVariant* options; + + g_variant_get(variant, "(u@a{sv})", &stream_id, &options); + RTC_DCHECK(options != nullptr); + + g_variant_lookup(options, "size", "(ii)", &width, &height); + + that->desktop_size_.set(width, height); + + g_variant_unref(options); + g_variant_unref(variant); + } + } + g_variant_iter_free(iter); + g_variant_unref(response_data); + + that->OpenPipeWireRemote(); +} + +void BaseCapturerPipeWire::OpenPipeWireRemote() { + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + + LOG(LS_INFO) << "Opening the PipeWire remote."; + + g_dbus_proxy_call_with_unix_fd_list( + proxy_, "OpenPipeWireRemote", + g_variant_new("(oa{sv})", session_handle_, &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, + /*cancellable=*/nullptr, + reinterpret_cast(OnOpenPipeWireRemoteRequested), + this); +} + +// static +void BaseCapturerPipeWire::OnOpenPipeWireRemoteRequested( + GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data) { + BaseCapturerPipeWire* that = + static_cast(user_data); + RTC_DCHECK(that); + + GError* error = nullptr; + GUnixFDList* outlist = nullptr; + GVariant* variant = g_dbus_proxy_call_with_unix_fd_list_finish( + that->proxy_, &outlist, result, &error); + if (!variant) { + LOG(LS_ERROR) << "Failed to open the PipeWire remote: " + << error->message; + g_error_free(error); + that->portal_init_failed_ = true; + return; + } + + gint32 index; + g_variant_get(variant, "(h)", &index); + + if ((that->pw_fd_ = g_unix_fd_list_get(outlist, index, &error)) == -1) { + LOG(LS_ERROR) << "Failed to get file descriptor from the list: " + << error->message; + g_error_free(error); + g_variant_unref(variant); + that->portal_init_failed_ = true; + return; + } + + g_variant_unref(variant); + g_object_unref(outlist); + + that->InitPipeWire(); + LOG(LS_INFO) << "PipeWire remote opened."; +} + +void BaseCapturerPipeWire::Start(Callback* callback) { + RTC_DCHECK(!callback_); + RTC_DCHECK(callback); + + InitPortals(); + + callback_ = callback; +} + +void BaseCapturerPipeWire::CaptureFrame() { + if (portal_init_failed_) { + callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); + return; + } + + if (!current_frame_) { + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + + std::unique_ptr result(new BasicDesktopFrame(desktop_size_)); + result->CopyPixelsFrom( + current_frame_, (desktop_size_.width() * kBytesPerPixelPw), + DesktopRect::MakeWH(desktop_size_.width(), desktop_size_.height())); + if (!result) { + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); +} + +bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) { + RTC_DCHECK(sources->size() == 0); + // List of available screens is already presented by the xdg-desktop-portal. + // But we have to add an empty source as the code expects it. + sources->push_back({0}); + return true; +} + +bool BaseCapturerPipeWire::SelectSource(SourceId id) { + // Screen selection is handled by the xdg-desktop-portal. + return true; +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.h.pipewire 2019-01-07 14:53:17.201061764 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.h 2019-01-07 14:53:17.201061764 +0100 @@ -0,0 +1,167 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_BASE_CAPTURER_PIPEWIRE_H_ +#define MODULES_DESKTOP_CAPTURE_BASE_CAPTURER_PIPEWIRE_H_ + +#include +#define typeof __typeof__ +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" + +namespace webrtc { + +class PipeWireType { + public: + spa_type_media_type media_type; + spa_type_media_subtype media_subtype; + spa_type_format_video format_video; + spa_type_video_format video_format; +}; + +class BaseCapturerPipeWire : public DesktopCapturer { + public: + enum CaptureSourceType { Screen = 1, Window }; + + explicit BaseCapturerPipeWire(CaptureSourceType source_type); + ~BaseCapturerPipeWire() override; + + // DesktopCapturer interface. + void Start(Callback* delegate) override; + void Stop() override { callback_ = nullptr; } + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + + private: + // PipeWire types --> + pw_core* pw_core_ = nullptr; + pw_type* pw_core_type_ = nullptr; + pw_stream* pw_stream_ = nullptr; + pw_remote* pw_remote_ = nullptr; + pw_loop* pw_loop_ = nullptr; + pw_thread_loop* pw_main_loop_ = nullptr; + PipeWireType* pw_type_ = nullptr; + + spa_hook spa_stream_listener_ = {}; + spa_hook spa_remote_listener_ = {}; + + pw_stream_events pw_stream_events_ = {}; + pw_remote_events pw_remote_events_ = {}; + + spa_video_info_raw* spa_video_format_ = nullptr; + + gint32 pw_fd_ = -1; + + CaptureSourceType capture_source_type_ = + BaseCapturerPipeWire::CaptureSourceType::Screen; + + // <-- end of PipeWire types + + GDBusConnection* connection_ = nullptr; + GDBusProxy* proxy_ = nullptr; + gchar* portal_handle_ = nullptr; + gchar* session_handle_ = nullptr; + gchar* sources_handle_ = nullptr; + gchar* start_handle_ = nullptr; + guint session_request_signal_id_ = 0; + guint sources_request_signal_id_ = 0; + guint start_request_signal_id_ = 0; + + DesktopSize desktop_size_ = {}; + DesktopCaptureOptions options_ = {}; + + uint8_t* current_frame_ = nullptr; + Callback* callback_ = nullptr; + + bool portal_init_failed_ = false; + + void InitPortals(); + void InitPipeWire(); + void InitPipeWireTypes(); + + void CreateReceivingStream(); + void HandleBuffer(pw_buffer* buffer); + + void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); + + static void OnStateChanged(void* data, + pw_remote_state old_state, + pw_remote_state state, + const char* error); + static void OnStreamStateChanged(void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message); + + static void OnStreamFormatChanged(void* data, const struct spa_pod* format); + static void OnStreamProcess(void* data); + static void OnNewBuffer(void* data, uint32_t id); + + guint SetupRequestResponseSignal(const gchar* object_path, + GDBusSignalCallback callback); + + static void OnProxyRequested(GObject* object, + GAsyncResult* result, + gpointer user_data); + + static gchar* PrepareSignalHandle(GDBusConnection* connection, + const gchar* token); + + void SessionRequest(); + static void OnSessionRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data); + static void OnSessionRequestResponseSignal(GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data); + + void SourcesRequest(); + static void OnSourcesRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data); + static void OnSourcesRequestResponseSignal(GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data); + + void StartRequest(); + static void OnStartRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data); + static void OnStartRequestResponseSignal(GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data); + + void OpenPipeWireRemote(); + static void OnOpenPipeWireRemoteRequested(GDBusConnection* connection, + GAsyncResult* result, + gpointer user_data); + + RTC_DISALLOW_COPY_AND_ASSIGN(BaseCapturerPipeWire); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_BASE_CAPTURER_PIPEWIRE_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build.pipewire 2019-01-07 14:53:17.201061764 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build 2019-01-07 14:56:57.674544724 +0100 @@ -144,14 +144,26 @@ if CONFIG["OS_TARGET"] == "FreeBSD": "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc" ] +# Common Linux stuff between X11 and PipeWire if CONFIG["OS_TARGET"] == "Linux": DEFINES["USE_NSS_CERTS"] = "1" - DEFINES["USE_X11"] = "1" DEFINES["WEBRTC_LINUX"] = True DEFINES["WEBRTC_POSIX"] = True DEFINES["_FILE_OFFSET_BITS"] = "64" + UNIFIED_SOURCES += [ + "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc", + ] + +# X11 specific files +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_X11"] = "1" + OS_LIBS += [ "rt", "X11", @@ -178,6 +190,28 @@ if CONFIG["OS_TARGET"] == "Linux": "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc" ] +# PipeWire specific files +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["WEBRTC_USE_PIPEWIRE"] = "1" + + OS_LIBS += [ + "rt", + "pipewire-0.2", + "glib-2.0", + "gio-2.0", + "gobject-2.0" + ] + + CXXFLAGS += CONFIG['TK_CFLAGS'] + + UNIFIED_SOURCES += [ + "/media/webrtc/trunk/webrtc/modules/desktop_capture/base_capturer_pipewire.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.cc" + ] + + if CONFIG["OS_TARGET"] == "NetBSD": DEFINES["USE_X11"] = "1" diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h 2019-01-07 14:53:17.202061775 +0100 @@ -107,6 +107,11 @@ class DesktopCaptureOptions { } #endif +#if defined(WEBRTC_USE_PIPEWIRE) + bool allow_pipewire() const { return allow_pipewire_; } + void set_allow_pipewire(bool allow) { allow_pipewire_ = allow; } +#endif + private: #if defined(USE_X11) rtc::scoped_refptr x_display_; @@ -129,6 +134,9 @@ class DesktopCaptureOptions { #endif bool disable_effects_ = true; bool detect_updated_region_ = false; +#if defined(WEBRTC_USE_PIPEWIRE) + bool allow_pipewire_ = true; +#endif }; } // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc 2019-01-07 14:53:17.202061775 +0100 @@ -66,4 +66,17 @@ std::unique_ptr Desktop return capturer; } +#if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11) +bool DesktopCapturer::IsRunningUnderWayland() { + const char* xdg_session_type = getenv("XDG_SESSION_TYPE"); + if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0) + return false; + + if (!(getenv("WAYLAND_DISPLAY"))) + return false; + + return true; +} +#endif + } // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h 2019-01-07 14:53:17.202061775 +0100 @@ -129,6 +129,10 @@ class DesktopCapturer { static std::unique_ptr CreateAppCapturer( const DesktopCaptureOptions& options); +#if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11) + static bool IsRunningUnderWayland(); +#endif + protected: // CroppingWindowCapturer needs to create raw capturers without wrappers, so // the following two functions are protected. diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc.pipewire 2019-01-07 14:53:17.202061775 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc 2019-01-07 14:53:17.202061775 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" + +#if defined(USE_X11) +#include "webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h" +#endif // defined(USE_X11) + +namespace webrtc { + +// static +MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( + const DesktopCaptureOptions& options, WindowId window) { +#if defined(USE_X11) + return MouseCursorMonitorX11::CreateForWindow(options, window); +#else + return nullptr; +#endif // defined(USE_X11) +} + +// static +MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( + const DesktopCaptureOptions& options, + ScreenId screen) { +#if defined(USE_X11) + return MouseCursorMonitorX11::CreateForScreen(options, screen); +#else + return nullptr; +#endif // defined(USE_X11) +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc 2019-01-07 14:53:17.202061775 +0100 @@ -16,6 +16,8 @@ #include #include +#include "webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h" + #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/mouse_cursor.h" @@ -59,38 +61,6 @@ Window GetTopLevelWindow(Display* displa namespace webrtc { -class MouseCursorMonitorX11 : public MouseCursorMonitor, - public SharedXDisplay::XEventHandler { - public: - MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window, Window inner_window); - ~MouseCursorMonitorX11() override; - - void Start(Callback* callback, Mode mode) override; - void Stop() override; - void Capture() override; - - private: - // SharedXDisplay::XEventHandler interface. - bool HandleXEvent(const XEvent& event) override; - - Display* display() { return x_display_->display(); } - - // Captures current cursor shape and stores it in |cursor_shape_|. - void CaptureCursor(); - - rtc::scoped_refptr x_display_; - Callback* callback_; - Mode mode_; - Window window_; - Window inner_window_; - - bool have_xfixes_; - int xfixes_event_base_; - int xfixes_error_base_; - - std::unique_ptr cursor_shape_; -}; - MouseCursorMonitorX11::MouseCursorMonitorX11( const DesktopCaptureOptions& options, Window window, Window inner_window) @@ -244,7 +214,7 @@ void MouseCursorMonitorX11::CaptureCurso } // static -MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( +MouseCursorMonitor* MouseCursorMonitorX11::CreateForWindow( const DesktopCaptureOptions& options, WindowId window) { if (!options.x_display()) return NULL; @@ -254,7 +224,7 @@ MouseCursorMonitor* MouseCursorMonitor:: return new MouseCursorMonitorX11(options, outer_window, window); } -MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( +MouseCursorMonitor* MouseCursorMonitorX11::CreateForScreen( const DesktopCaptureOptions& options, ScreenId screen) { if (!options.x_display()) diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h.pipewire 2019-01-07 14:53:17.202061775 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.h 2019-01-07 14:53:17.202061775 +0100 @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_X11_H_ +#define MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_X11_H_ + +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capture_types.h" +#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { + +class MouseCursorMonitorX11 : public MouseCursorMonitor, + public SharedXDisplay::XEventHandler { + public: + MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window, Window inner_window); + ~MouseCursorMonitorX11() override; + + void Start(Callback* callback, Mode mode) override; + void Stop() override; + void Capture() override; + + static MouseCursorMonitor* CreateForWindow(const DesktopCaptureOptions& options, WindowId window); + static MouseCursorMonitor* CreateForScreen(const DesktopCaptureOptions& options, ScreenId screen); + + private: + // SharedXDisplay::XEventHandler interface. + bool HandleXEvent(const XEvent& event) override; + + Display* display() { return x_display_->display(); } + + // Captures current cursor shape and stores it in |cursor_shape_|. + void CaptureCursor(); + + rtc::scoped_refptr x_display_; + Callback* callback_; + Mode mode_; + Window window_; + Window inner_window_; + + bool have_xfixes_; + int xfixes_event_base_; + int xfixes_error_base_; + + std::unique_ptr cursor_shape_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_X11_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.pipewire 2019-01-07 14:53:17.203061786 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc 2019-01-07 14:53:17.203061786 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" + +#if defined(WEBRTC_USE_PIPEWIRE) +#include "webrtc/modules/desktop_capture/screen_capturer_pipewire.h" +#endif // defined(WEBRTC_USE_PIPEWIRE) + +#if defined(USE_X11) +#include "webrtc/modules/desktop_capture/screen_capturer_x11.h" +#endif // defined(USE_X11) + +namespace webrtc { + +// static +std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( + const DesktopCaptureOptions& options) { +#if defined(WEBRTC_USE_PIPEWIRE) + if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { + return ScreenCapturerPipeWire::CreateRawScreenCapturer(options); + } +#endif // defined(WEBRTC_USE_PIPEWIRE) + +#if defined(USE_X11) + return ScreenCapturerX11::CreateRawScreenCapturer(options); +#endif // defined(USE_X11) + + return nullptr; +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc.pipewire 2019-01-07 14:53:17.203061786 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc 2019-01-07 14:53:17.203061786 +0100 @@ -0,0 +1,31 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/screen_capturer_pipewire.h" + +#include + +namespace webrtc { + +ScreenCapturerPipeWire::ScreenCapturerPipeWire() + : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Screen) {} +ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {} + +// static +std::unique_ptr +ScreenCapturerPipeWire::CreateRawScreenCapturer( + const DesktopCaptureOptions& options) { + std::unique_ptr capturer( + new ScreenCapturerPipeWire()); + + return std::move(capturer); +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.h.pipewire 2019-01-07 14:53:17.203061786 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.h 2019-01-07 14:53:17.203061786 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_PIPEWIRE_H_ +#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_PIPEWIRE_H_ + +#include + +#include "webrtc/modules/desktop_capture/base_capturer_pipewire.h" + +namespace webrtc { + +class ScreenCapturerPipeWire : public BaseCapturerPipeWire { + public: + ScreenCapturerPipeWire(); + ~ScreenCapturerPipeWire() override; + + static std::unique_ptr CreateRawScreenCapturer( + const DesktopCaptureOptions& options); + + RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerPipeWire); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_PIPEWIRE_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc 2019-01-07 14:53:17.203061786 +0100 @@ -19,6 +19,8 @@ #include #include +#include "webrtc/modules/desktop_capture/screen_capturer_x11.h" + #include "webrtc/base/checks.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/timeutils.h" @@ -32,100 +34,12 @@ #include "webrtc/system_wrappers/include/logging.h" namespace webrtc { -namespace { - -// A class to perform video frame capturing for Linux. -// -// If XDamage is used, this class sets DesktopFrame::updated_region() according -// to the areas reported by XDamage. Otherwise this class does not detect -// DesktopFrame::updated_region(), the field is always set to the entire frame -// rectangle. ScreenCapturerDifferWrapper should be used if that functionality -// is necessary. -class ScreenCapturerLinux : public DesktopCapturer, - public SharedXDisplay::XEventHandler { - public: - ScreenCapturerLinux(); - ~ScreenCapturerLinux() override; - - // TODO(ajwong): Do we really want this to be synchronous? - bool Init(const DesktopCaptureOptions& options); - - // DesktopCapturer interface. - void Start(Callback* delegate) override; - void Stop() override; - void CaptureFrame() override; - bool GetSourceList(SourceList* sources) override; - bool SelectSource(SourceId id) override; - - private: - Display* display() { return options_.x_display()->display(); } - - // SharedXDisplay::XEventHandler interface. - bool HandleXEvent(const XEvent& event) override; - - void InitXDamage(); - - // Capture screen pixels to the current buffer in the queue. In the DAMAGE - // case, the ScreenCapturerHelper already holds the list of invalid rectangles - // from HandleXEvent(). In the non-DAMAGE case, this captures the - // whole screen, then calculates some invalid rectangles that include any - // differences between this and the previous capture. - std::unique_ptr CaptureScreen(); - - // Called when the screen configuration is changed. - void ScreenConfigurationChanged(); - - // Synchronize the current buffer with |last_buffer_|, by copying pixels from - // the area of |last_invalid_rects|. - // Note this only works on the assumption that kNumBuffers == 2, as - // |last_invalid_rects| holds the differences from the previous buffer and - // the one prior to that (which will then be the current buffer). - void SynchronizeFrame(); - - void DeinitXlib(); - - DesktopCaptureOptions options_; - - Callback* callback_ = nullptr; - - // X11 graphics context. - GC gc_ = nullptr; - Window root_window_ = BadValue; - - // XFixes. - bool has_xfixes_ = false; - int xfixes_event_base_ = -1; - int xfixes_error_base_ = -1; - - // XDamage information. - bool use_damage_ = false; - Damage damage_handle_ = 0; - int damage_event_base_ = -1; - int damage_error_base_ = -1; - XserverRegion damage_region_ = 0; - - // Access to the X Server's pixel buffer. - XServerPixelBuffer x_server_pixel_buffer_; - - // A thread-safe list of invalid rectangles, and the size of the most - // recently captured screen. - ScreenCapturerHelper helper_; - - // Queue of the frames buffers. - ScreenCaptureFrameQueue queue_; - - // Invalid region from the previous capture. This is used to synchronize the - // current with the last buffer used. - DesktopRegion last_invalid_region_; - RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux); -}; - -ScreenCapturerLinux::ScreenCapturerLinux() { +ScreenCapturerX11::ScreenCapturerX11() { helper_.SetLogGridSize(4); } -ScreenCapturerLinux::~ScreenCapturerLinux() { +ScreenCapturerX11::~ScreenCapturerX11() { options_.x_display()->RemoveEventHandler(ConfigureNotify, this); if (use_damage_) { options_.x_display()->RemoveEventHandler( @@ -134,7 +48,7 @@ ScreenCapturerLinux::~ScreenCapturerLinu DeinitXlib(); } -bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) { +bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) { options_ = options; root_window_ = RootWindow(display(), DefaultScreen(display())); @@ -177,7 +91,7 @@ bool ScreenCapturerLinux::Init(const Des return true; } -void ScreenCapturerLinux::InitXDamage() { +void ScreenCapturerX11::InitXDamage() { // Our use of XDamage requires XFixes. if (!has_xfixes_) { return; @@ -218,18 +132,18 @@ void ScreenCapturerLinux::InitXDamage() LOG(LS_INFO) << "Using XDamage extension."; } -void ScreenCapturerLinux::Start(Callback* callback) { +void ScreenCapturerX11::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); callback_ = callback; } -void ScreenCapturerLinux::Stop() { +void ScreenCapturerX11::Stop() { callback_ = NULL; } -void ScreenCapturerLinux::CaptureFrame() { +void ScreenCapturerX11::CaptureFrame() { int64_t capture_start_time_nanos = rtc::TimeNanos(); queue_.MoveToNextFrame(); @@ -243,6 +157,7 @@ void ScreenCapturerLinux::CaptureFrame() // in a good shape. if (!x_server_pixel_buffer_.is_initialized()) { // We failed to initialize pixel buffer. + LOG(LS_ERROR) << "Pixel buffer is not initialized."; callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); return; } @@ -258,6 +173,7 @@ void ScreenCapturerLinux::CaptureFrame() std::unique_ptr result = CaptureScreen(); if (!result) { + LOG(LS_WARNING) << "Temporarily failed to capture screen."; callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); return; } @@ -268,19 +184,19 @@ void ScreenCapturerLinux::CaptureFrame() callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); } -bool ScreenCapturerLinux::GetSourceList(SourceList* sources) { +bool ScreenCapturerX11::GetSourceList(SourceList* sources) { RTC_DCHECK(sources->size() == 0); // TODO(jiayl): implement screen enumeration. sources->push_back({0}); return true; } -bool ScreenCapturerLinux::SelectSource(SourceId id) { +bool ScreenCapturerX11::SelectSource(SourceId id) { // TODO(jiayl): implement screen selection. return true; } -bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) { +bool ScreenCapturerX11::HandleXEvent(const XEvent& event) { if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) { const XDamageNotifyEvent* damage_event = reinterpret_cast(&event); @@ -295,7 +211,7 @@ bool ScreenCapturerLinux::HandleXEvent(c return false; } -std::unique_ptr ScreenCapturerLinux::CaptureScreen() { +std::unique_ptr ScreenCapturerX11::CaptureScreen() { std::unique_ptr frame = queue_.current_frame()->Share(); RTC_DCHECK(x_server_pixel_buffer_.window_size().equals(frame->size())); @@ -345,14 +261,15 @@ std::unique_ptr ScreenCapt // Doing full-screen polling, or this is the first capture after a // screen-resolution change. In either case, need a full-screen capture. DesktopRect screen_rect = DesktopRect::MakeSize(frame->size()); - x_server_pixel_buffer_.CaptureRect(screen_rect, frame.get()); + if (!x_server_pixel_buffer_.CaptureRect(screen_rect, frame.get())) + return nullptr; updated_region->SetRect(screen_rect); } return std::move(frame); } -void ScreenCapturerLinux::ScreenConfigurationChanged() { +void ScreenCapturerX11::ScreenConfigurationChanged() { // Make sure the frame buffers will be reallocated. queue_.Reset(); @@ -363,7 +280,7 @@ void ScreenCapturerLinux::ScreenConfigur } } -void ScreenCapturerLinux::SynchronizeFrame() { +void ScreenCapturerX11::SynchronizeFrame() { // Synchronize the current buffer with the previous one since we do not // capture the entire desktop. Note that encoder may be reading from the // previous buffer at this time so thread access complaints are false @@ -383,7 +300,7 @@ void ScreenCapturerLinux::SynchronizeFra } } -void ScreenCapturerLinux::DeinitXlib() { +void ScreenCapturerX11::DeinitXlib() { if (gc_) { XFreeGC(display(), gc_); gc_ = nullptr; @@ -404,20 +321,18 @@ void ScreenCapturerLinux::DeinitXlib() { } } -} // namespace - // static -std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( +std::unique_ptr ScreenCapturerX11::CreateRawScreenCapturer( const DesktopCaptureOptions& options) { if (!options.x_display()) return nullptr; - std::unique_ptr capturer(new ScreenCapturerLinux()); + std::unique_ptr capturer(new ScreenCapturerX11()); if (!capturer.get()->Init(options)) { return nullptr; } - return std::unique_ptr(capturer.release()); + return std::move(capturer); } } // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.h.pipewire 2019-01-07 14:53:17.203061786 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.h 2019-01-07 14:53:17.203061786 +0100 @@ -0,0 +1,124 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_X11_H_ + +#include +#include +#include +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" +#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" +#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" +#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" +#include "webrtc/base/constructormagic.h" + +namespace webrtc { + +// A class to perform video frame capturing for Linux on X11. +// +// If XDamage is used, this class sets DesktopFrame::updated_region() according +// to the areas reported by XDamage. Otherwise this class does not detect +// DesktopFrame::updated_region(), the field is always set to the entire frame +// rectangle. ScreenCapturerDifferWrapper should be used if that functionality +// is necessary. +class ScreenCapturerX11 : public DesktopCapturer, + public SharedXDisplay::XEventHandler { + public: + ScreenCapturerX11(); + ~ScreenCapturerX11() override; + + static std::unique_ptr CreateRawScreenCapturer(const DesktopCaptureOptions& options); + + // TODO(ajwong): Do we really want this to be synchronous? + bool Init(const DesktopCaptureOptions& options); + + // DesktopCapturer interface. + void Start(Callback* delegate) override; + void Stop() override; + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + + private: + Display* display() { return options_.x_display()->display(); } + + // SharedXDisplay::XEventHandler interface. + bool HandleXEvent(const XEvent& event) override; + + void InitXDamage(); + + // Capture screen pixels to the current buffer in the queue. In the DAMAGE + // case, the ScreenCapturerHelper already holds the list of invalid rectangles + // from HandleXEvent(). In the non-DAMAGE case, this captures the + // whole screen, then calculates some invalid rectangles that include any + // differences between this and the previous capture. + std::unique_ptr CaptureScreen(); + + // Called when the screen configuration is changed. + void ScreenConfigurationChanged(); + + // Synchronize the current buffer with |last_buffer_|, by copying pixels from + // the area of |last_invalid_rects|. + // Note this only works on the assumption that kNumBuffers == 2, as + // |last_invalid_rects| holds the differences from the previous buffer and + // the one prior to that (which will then be the current buffer). + void SynchronizeFrame(); + + void DeinitXlib(); + + DesktopCaptureOptions options_; + + Callback* callback_ = nullptr; + + // X11 graphics context. + GC gc_ = nullptr; + Window root_window_ = BadValue; + + // XFixes. + bool has_xfixes_ = false; + int xfixes_event_base_ = -1; + int xfixes_error_base_ = -1; + + // XDamage information. + bool use_damage_ = false; + Damage damage_handle_ = 0; + int damage_event_base_ = -1; + int damage_error_base_ = -1; + XserverRegion damage_region_ = 0; + + // Access to the X Server's pixel buffer. + XServerPixelBuffer x_server_pixel_buffer_; + + // A thread-safe list of invalid rectangles, and the size of the most + // recently captured screen. + ScreenCapturerHelper helper_; + + // Queue of the frames buffers. + ScreenCaptureFrameQueue queue_; + + // Invalid region from the previous capture. This is used to synchronize the + // current with the last buffer used. + DesktopRegion last_invalid_region_; + + RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerX11); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_X11_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.pipewire 2019-01-07 14:53:17.203061786 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc 2019-01-07 14:53:17.203061786 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" + +#if defined(WEBRTC_USE_PIPEWIRE) +#include "webrtc/modules/desktop_capture/window_capturer_pipewire.h" +#endif // defined(WEBRTC_USE_PIPEWIRE) + +#if defined(USE_X11) +#include "webrtc/modules/desktop_capture/window_capturer_x11.h" +#endif // defined(USE_X11) + +namespace webrtc { + +// static +std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { +#if defined(WEBRTC_USE_PIPEWIRE) + if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) { + return WindowCapturerPipeWire::CreateRawWindowCapturer(options); + } +#endif // defined(WEBRTC_USE_PIPEWIRE) + +#if defined(USE_X11) + return WindowCapturerX11::CreateRawWindowCapturer(options); +#endif // defined(USE_X11) + + return nullptr; +} + +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.cc.pipewire 2019-01-07 14:53:17.204061797 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.cc 2019-01-07 14:53:17.204061797 +0100 @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/window_capturer_pipewire.h" + +namespace webrtc { + +WindowCapturerPipeWire::WindowCapturerPipeWire() + : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Window) {} +WindowCapturerPipeWire::~WindowCapturerPipeWire() {} + +// static +std::unique_ptr +WindowCapturerPipeWire::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + std::unique_ptr capturer( + new WindowCapturerPipeWire()); + + return std::move(capturer); +} +} // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.h.pipewire 2019-01-07 14:53:17.204061797 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_pipewire.h 2019-01-07 14:53:17.204061797 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_PIPEWIRE_H_ +#define MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_PIPEWIRE_H_ + +#include + +#include "webrtc/modules/desktop_capture/base_capturer_pipewire.h" + +namespace webrtc { + +class WindowCapturerPipeWire : public BaseCapturerPipeWire { + public: + WindowCapturerPipeWire(); + ~WindowCapturerPipeWire() override; + + static std::unique_ptr CreateRawWindowCapturer( + const DesktopCaptureOptions& options); + + RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerPipeWire); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_PIPEWIRE_H_ diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc.pipewire 2018-10-17 22:39:32.000000000 +0200 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc 2019-01-07 14:53:17.204061797 +0100 @@ -17,6 +17,8 @@ #include +#include "webrtc/modules/desktop_capture/window_capturer_x11.h" + #include "webrtc/base/constructormagic.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/modules/desktop_capture/desktop_capturer.h" @@ -30,58 +32,7 @@ namespace webrtc { -namespace { - -class WindowCapturerLinux : public DesktopCapturer, - public SharedXDisplay::XEventHandler { - public: - WindowCapturerLinux(const DesktopCaptureOptions& options); - ~WindowCapturerLinux() override; - - // DesktopCapturer interface. - void Start(Callback* callback) override; - void Stop() override; - void CaptureFrame() override; - bool GetSourceList(SourceList* sources) override; - bool SelectSource(SourceId id) override; - bool FocusOnSelectedSource() override; - - // SharedXDisplay::XEventHandler interface. - bool HandleXEvent(const XEvent& event) override; - - private: - Display* display() { return x_display_->display(); } - - // Iterates through |window| hierarchy to find first visible window, i.e. one - // that has WM_STATE property set to NormalState. - // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 . - ::Window GetApplicationWindow(::Window window); - - // Returns true if the |window| is a desktop element. - bool IsDesktopElement(::Window window); - - // Returns window title for the specified X |window|. - bool GetWindowTitle(::Window window, std::string* title); - - // Returns the id of the owning process. - int GetWindowProcessID(::Window window); - - Callback* callback_ = nullptr; - - rtc::scoped_refptr x_display_; - - Atom wm_state_atom_; - Atom window_type_atom_; - Atom normal_window_type_atom_; - bool has_composite_extension_ = false; - - ::Window selected_window_ = 0; - XServerPixelBuffer x_server_pixel_buffer_; - - RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux); -}; - -WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options) +WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options) : x_display_(options.x_display()) { // Create Atoms so we don't need to do it every time they are used. wm_state_atom_ = XInternAtom(display(), "WM_STATE", True); @@ -102,11 +53,11 @@ WindowCapturerLinux::WindowCapturerLinux x_display_->AddEventHandler(ConfigureNotify, this); } -WindowCapturerLinux::~WindowCapturerLinux() { +WindowCapturerX11::~WindowCapturerX11() { x_display_->RemoveEventHandler(ConfigureNotify, this); } -bool WindowCapturerLinux::GetSourceList(SourceList* sources) { +bool WindowCapturerX11::GetSourceList(SourceList* sources) { SourceList result; XErrorTrap error_trap(display()); @@ -159,7 +110,7 @@ bool WindowCapturerLinux::GetSourceList( return true; } -bool WindowCapturerLinux::SelectSource(SourceId id) { +bool WindowCapturerX11::SelectSource(SourceId id) { if (!x_server_pixel_buffer_.Init(display(), id)) return false; @@ -180,7 +131,7 @@ bool WindowCapturerLinux::SelectSource(S return true; } -bool WindowCapturerLinux::FocusOnSelectedSource() { +bool WindowCapturerX11::FocusOnSelectedSource() { if (!selected_window_) return false; @@ -229,18 +180,18 @@ bool WindowCapturerLinux::FocusOnSelecte return true; } -void WindowCapturerLinux::Start(Callback* callback) { +void WindowCapturerX11::Start(Callback* callback) { assert(!callback_); assert(callback); callback_ = callback; } -void WindowCapturerLinux::Stop() { +void WindowCapturerX11::Stop() { callback_ = NULL; } -void WindowCapturerLinux::CaptureFrame() { +void WindowCapturerX11::CaptureFrame() { x_display_->ProcessPendingXEvents(); if (!x_server_pixel_buffer_.IsWindowValid()) { @@ -274,7 +225,7 @@ void WindowCapturerLinux::CaptureFrame() callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); } -bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { +bool WindowCapturerX11::HandleXEvent(const XEvent& event) { if (event.type == ConfigureNotify) { XConfigureEvent xce = event.xconfigure; if (!DesktopSize(xce.width, xce.height).equals( @@ -288,7 +239,7 @@ bool WindowCapturerLinux::HandleXEvent(c return false; } -::Window WindowCapturerLinux::GetApplicationWindow(::Window window) { +::Window WindowCapturerX11::GetApplicationWindow(::Window window) { // Get WM_STATE property of the window. XWindowProperty window_state(display(), window, wm_state_atom_); @@ -326,7 +277,7 @@ bool WindowCapturerLinux::HandleXEvent(c return app_window; } -bool WindowCapturerLinux::IsDesktopElement(::Window window) { +bool WindowCapturerX11::IsDesktopElement(::Window window) { if (window == 0) return false; @@ -361,7 +312,7 @@ bool WindowCapturerLinux::IsDesktopEleme return result; } -bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) { +bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) { int status; bool result = false; XTextProperty window_name; @@ -390,9 +341,7 @@ bool WindowCapturerLinux::GetWindowTitle return result; } -} // namespace - -int WindowCapturerLinux::GetWindowProcessID(::Window window) { +int WindowCapturerX11::GetWindowProcessID(::Window window) { // Get _NET_WM_PID property of the window. Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True); XWindowProperty process_id(display(), window, process_atom); @@ -401,11 +350,11 @@ int WindowCapturerLinux::GetWindowProces } // static -std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( +std::unique_ptr WindowCapturerX11::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { if (!options.x_display()) return nullptr; - return std::unique_ptr(new WindowCapturerLinux(options)); + return std::unique_ptr(new WindowCapturerX11(options)); } } // namespace webrtc diff -up firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.h.pipewire firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.h --- firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.h.pipewire 2019-01-07 14:53:17.204061797 +0100 +++ firefox-60.3.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.h 2019-01-07 14:53:17.204061797 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_X11_H_ +#define MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_X11_H_ + +#include +#include +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "webrtc/modules/desktop_capture/desktop_capturer.h" +#include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { + +class WindowCapturerX11 : public DesktopCapturer, + public SharedXDisplay::XEventHandler { + public: + explicit WindowCapturerX11(const DesktopCaptureOptions& options); + ~WindowCapturerX11() override; + + static std::unique_ptr CreateRawWindowCapturer( + const DesktopCaptureOptions& options); + + // DesktopCapturer interface. + void Start(Callback* callback) override; + void Stop() override; + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + bool FocusOnSelectedSource() override; + + // SharedXDisplay::XEventHandler interface. + bool HandleXEvent(const XEvent& event) override; + + private: + Display* display() { return x_display_->display(); } + + // Iterates through |window| hierarchy to find first visible window, i.e. one + // that has WM_STATE property set to NormalState. + // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 . + ::Window GetApplicationWindow(::Window window); + + // Returns true if the |window| is a desktop element. + bool IsDesktopElement(::Window window); + + // Returns window title for the specified X |window|. + bool GetWindowTitle(::Window window, std::string* title); + + // Returns the id of the owning process. + int GetWindowProcessID(::Window window); + + Callback* callback_ = nullptr; + + rtc::scoped_refptr x_display_; + + Atom wm_state_atom_; + Atom window_type_atom_; + Atom normal_window_type_atom_; + bool has_composite_extension_ = false; + + ::Window selected_window_ = 0; + XServerPixelBuffer x_server_pixel_buffer_; + + RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerX11); +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_X11_H_