mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-17 15:47:53 +01:00
Add support for screen sharing with PipeWire on Wayland
Currently, when users want to use the screen sharing and are using the Wayland display server (the default on Fedora distribution), then it doesn't work, because the WebRTC only includes the X11 implementation. This change adds the support by using the PipeWire multimedia server. The PipeWire implementation in WebRTC stays in screen-capturer-pipewire.c and is guarded by the rtc_use_pipewire build flag that is automatically enabled on Linux. More information are included in the relevant commit messages. Tested on the current Chromium master and Firefox. The sysroot changes are requested in: https://chromium-review.googlesource.com/c/chromium/src/+/1258174 Co-authored-by: Jan Grulich <grulja@gmail.com> Co-authored-by: Eike Rathke <erathke@redhat.com> Change-Id: I212074a4bc437b99a77bf383266026c5bfae7c4a BUG=chromium:682122 Change-Id: I212074a4bc437b99a77bf383266026c5bfae7c4a Reviewed-on: https://webrtc-review.googlesource.com/c/103504 Commit-Queue: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Brave Yao <braveyao@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25461}
This commit is contained in:
parent
7f4dfa4106
commit
dd20c9c1e3
35 changed files with 1719 additions and 276 deletions
3
AUTHORS
3
AUTHORS
|
@ -72,6 +72,9 @@ Sergio Garcia Murillo <sergio.garcia.murillo@gmail.com>
|
|||
Maxim Pavlov <pavllovmax@gmail.com>
|
||||
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
Piasy Xu <xz4215@gmail.com>
|
||||
Tomas Popela <tomas.popela@gmail.com>
|
||||
Jan Grulich <grulja@gmail.com>
|
||||
Eike Rathke <erathke@redhat.com>
|
||||
|
||||
&yet LLC <*@andyet.com>
|
||||
Agora IO <*@agora.io>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
import("//build/config/linux/pkg_config.gni")
|
||||
import("//build/config/ui.gni")
|
||||
import("../../webrtc.gni")
|
||||
|
||||
|
@ -165,6 +166,23 @@ if (rtc_include_tests) {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_linux) {
|
||||
if (rtc_use_pipewire) {
|
||||
pkg_config("pipewire") {
|
||||
packages = [ "libpipewire-0.2" ]
|
||||
|
||||
defines = [ "WEBRTC_USE_PIPEWIRE" ]
|
||||
}
|
||||
|
||||
pkg_config("gio") {
|
||||
packages = [
|
||||
"gio-2.0",
|
||||
"gio-unix-2.0",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtc_source_set("desktop_capture") {
|
||||
visibility = [ "*" ]
|
||||
public_deps = [
|
||||
|
@ -322,39 +340,69 @@ rtc_static_library("desktop_capture_generic") {
|
|||
]
|
||||
}
|
||||
|
||||
if (rtc_use_x11 || rtc_use_pipewire) {
|
||||
sources += [
|
||||
"mouse_cursor_monitor_linux.cc",
|
||||
"screen_capturer_linux.cc",
|
||||
"window_capturer_linux.cc",
|
||||
]
|
||||
|
||||
if (build_with_mozilla) {
|
||||
sources += [ "app_capturer_linux.cc" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_use_x11) {
|
||||
sources += [
|
||||
"mouse_cursor_monitor_x11.cc",
|
||||
"screen_capturer_x11.cc",
|
||||
"window_capturer_x11.cc",
|
||||
"window_finder_x11.cc",
|
||||
"window_finder_x11.h",
|
||||
"x11/shared_x_display.cc",
|
||||
"x11/shared_x_display.h",
|
||||
"x11/window_list_utils.cc",
|
||||
"x11/window_list_utils.h",
|
||||
"x11/x_atom_cache.cc",
|
||||
"x11/x_atom_cache.h",
|
||||
"x11/x_error_trap.cc",
|
||||
"x11/x_error_trap.h",
|
||||
"x11/x_server_pixel_buffer.cc",
|
||||
"x11/x_server_pixel_buffer.h",
|
||||
"linux/mouse_cursor_monitor_x11.cc",
|
||||
"linux/mouse_cursor_monitor_x11.h",
|
||||
"linux/screen_capturer_x11.cc",
|
||||
"linux/screen_capturer_x11.h",
|
||||
"linux/shared_x_display.cc",
|
||||
"linux/shared_x_display.h",
|
||||
"linux/window_capturer_x11.cc",
|
||||
"linux/window_capturer_x11.h",
|
||||
"linux/window_finder_x11.cc",
|
||||
"linux/window_finder_x11.h",
|
||||
"linux/window_list_utils.cc",
|
||||
"linux/window_list_utils.h",
|
||||
"linux/x_atom_cache.cc",
|
||||
"linux/x_atom_cache.h",
|
||||
"linux/x_error_trap.cc",
|
||||
"linux/x_error_trap.h",
|
||||
"linux/x_server_pixel_buffer.cc",
|
||||
"linux/x_server_pixel_buffer.h",
|
||||
]
|
||||
configs += [ "//build/config/linux:x11" ]
|
||||
|
||||
if (build_with_mozilla) {
|
||||
sources += [
|
||||
"app_capturer_x11.cc",
|
||||
"app_capturer_x11.h",
|
||||
"x11/desktop_device_info_x11.cc",
|
||||
"x11/desktop_device_info_x11.h",
|
||||
"x11/shared_x_util.cc",
|
||||
"x11/shared_x_util.h",
|
||||
"linux/app_capturer_x11.cc",
|
||||
"linux/desktop_device_info_linux.cc",
|
||||
"linux/desktop_device_info_linux.h",
|
||||
"linux/shared_x_util.cc",
|
||||
"linux/shared_x_util.h",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_win && !is_mac && !rtc_use_x11) {
|
||||
if (rtc_use_pipewire) {
|
||||
sources += [
|
||||
"linux/base_capturer_pipewire.cc",
|
||||
"linux/base_capturer_pipewire.h",
|
||||
"linux/screen_capturer_pipewire.cc",
|
||||
"linux/screen_capturer_pipewire.h",
|
||||
"linux/window_capturer_pipewire.cc",
|
||||
"linux/window_capturer_pipewire.h",
|
||||
]
|
||||
|
||||
configs += [
|
||||
":gio",
|
||||
":pipewire",
|
||||
]
|
||||
}
|
||||
|
||||
if (!is_win && !is_mac && !rtc_use_x11 && !rtc_use_pipewire) {
|
||||
sources += [
|
||||
"mouse_cursor_monitor_null.cc",
|
||||
"screen_capturer_null.cc",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "modules/desktop_capture/x11/shared_x_display.h"
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
|
@ -115,6 +115,11 @@ class RTC_EXPORT 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<SharedXDisplay> x_display_;
|
||||
|
@ -138,6 +143,9 @@ class RTC_EXPORT DesktopCaptureOptions {
|
|||
#endif
|
||||
bool disable_effects_ = true;
|
||||
bool detect_updated_region_ = false;
|
||||
#if defined(WEBRTC_USE_PIPEWIRE)
|
||||
bool allow_pipewire_ = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -60,4 +60,17 @@ std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
|
|||
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 // defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -135,6 +135,10 @@ class RTC_EXPORT DesktopCapturer {
|
|||
static std::unique_ptr<DesktopCapturer> CreateScreenCapturer(
|
||||
const DesktopCaptureOptions& options);
|
||||
|
||||
#if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
|
||||
static bool IsRunningUnderWayland();
|
||||
#endif // defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
|
||||
|
||||
protected:
|
||||
// CroppingWindowCapturer needs to create raw capturers without wrappers, so
|
||||
// the following two functions are protected.
|
||||
|
|
843
modules/desktop_capture/linux/base_capturer_pipewire.cc
Normal file
843
modules/desktop_capture/linux/base_capturer_pipewire.cc
Normal file
|
@ -0,0 +1,843 @@
|
|||
/*
|
||||
* 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 "modules/desktop_capture/linux/base_capturer_pipewire.h"
|
||||
|
||||
#include <gio/gunixfdlist.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <spa/param/format-utils.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <spa/param/video/raw-utils.h>
|
||||
#include <spa/support/type-map.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_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 kBytesPerPixel = 4;
|
||||
|
||||
// static
|
||||
void BaseCapturerPipeWire::OnStateChanged(void* data,
|
||||
pw_remote_state old_state,
|
||||
pw_remote_state state,
|
||||
const char* error_message) {
|
||||
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
switch (state) {
|
||||
case PW_REMOTE_STATE_ERROR:
|
||||
RTC_LOG(LS_ERROR) << "PipeWire remote state error: " << error_message;
|
||||
break;
|
||||
case PW_REMOTE_STATE_CONNECTED:
|
||||
RTC_LOG(LS_INFO) << "PipeWire remote state: connected.";
|
||||
that->CreateReceivingStream();
|
||||
break;
|
||||
case PW_REMOTE_STATE_CONNECTING:
|
||||
RTC_LOG(LS_INFO) << "PipeWire remote state: connecting.";
|
||||
break;
|
||||
case PW_REMOTE_STATE_UNCONNECTED:
|
||||
RTC_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<BaseCapturerPipeWire*>(data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
switch (state) {
|
||||
case PW_STREAM_STATE_ERROR:
|
||||
RTC_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<BaseCapturerPipeWire*>(data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
RTC_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 * kBytesPerPixel, 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*>(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*>(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<BaseCapturerPipeWire*>(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) {
|
||||
RTC_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::InitPortal() {
|
||||
g_dbus_proxy_new_for_bus(
|
||||
G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr,
|
||||
kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName,
|
||||
/*cancellable=*/nullptr,
|
||||
reinterpret_cast<GAsyncReadyCallback>(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) {
|
||||
RTC_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<uint32_t>(desktop_size_.width()),
|
||||
static_cast<uint32_t>(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*>(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_flags>(
|
||||
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) {
|
||||
RTC_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() * kBytesPerPixel)) {
|
||||
RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
|
||||
<< srcStride
|
||||
<< " != " << (desktop_size_.width() * kBytesPerPixel);
|
||||
portal_init_failed_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current_frame_) {
|
||||
current_frame_ = static_cast<uint8_t*>(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<uint8_t*>(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<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
GError* error = nullptr;
|
||||
that->proxy_ = g_dbus_proxy_new_finish(result, &error);
|
||||
if (!that->proxy_) {
|
||||
RTC_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_);
|
||||
|
||||
RTC_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);
|
||||
|
||||
RTC_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<GAsyncReadyCallback>(OnSessionRequested), this);
|
||||
}
|
||||
|
||||
// static
|
||||
void BaseCapturerPipeWire::OnSessionRequested(GDBusConnection* connection,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
GError* error = nullptr;
|
||||
GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error);
|
||||
if (!variant) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: "
|
||||
<< error->message;
|
||||
g_error_free(error);
|
||||
that->portal_init_failed_ = true;
|
||||
return;
|
||||
}
|
||||
RTC_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) {
|
||||
RTC_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);
|
||||
|
||||
RTC_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<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
RTC_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) {
|
||||
RTC_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);
|
||||
|
||||
RTC_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<GAsyncReadyCallback>(OnSourcesRequested), this);
|
||||
}
|
||||
|
||||
// static
|
||||
void BaseCapturerPipeWire::OnSourcesRequested(GDBusConnection* connection,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
GError* error = nullptr;
|
||||
GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error);
|
||||
if (!variant) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message;
|
||||
g_error_free(error);
|
||||
that->portal_init_failed_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_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) {
|
||||
RTC_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);
|
||||
|
||||
RTC_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<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
RTC_LOG(LS_INFO) << "Received sources signal from session.";
|
||||
|
||||
guint32 portal_response;
|
||||
g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
|
||||
if (portal_response) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "Failed to select sources for the screen cast session.";
|
||||
that->portal_init_failed_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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[] = "";
|
||||
|
||||
RTC_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<GAsyncReadyCallback>(OnStartRequested), this);
|
||||
}
|
||||
|
||||
// static
|
||||
void BaseCapturerPipeWire::OnStartRequested(GDBusConnection* connection,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
GError* error = nullptr;
|
||||
GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error);
|
||||
if (!variant) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: "
|
||||
<< error->message;
|
||||
g_error_free(error);
|
||||
that->portal_init_failed_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_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) {
|
||||
RTC_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);
|
||||
|
||||
RTC_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<BaseCapturerPipeWire*>(user_data);
|
||||
RTC_DCHECK(that);
|
||||
|
||||
RTC_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) {
|
||||
RTC_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 <method name="Start">.
|
||||
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);
|
||||
|
||||
RTC_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<GAsyncReadyCallback>(OnOpenPipeWireRemoteRequested),
|
||||
this);
|
||||
}
|
||||
|
||||
// static
|
||||
void BaseCapturerPipeWire::OnOpenPipeWireRemoteRequested(
|
||||
GDBusConnection* connection,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(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) {
|
||||
RTC_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) {
|
||||
RTC_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();
|
||||
RTC_LOG(LS_INFO) << "PipeWire remote opened.";
|
||||
}
|
||||
|
||||
void BaseCapturerPipeWire::Start(Callback* callback) {
|
||||
RTC_DCHECK(!callback_);
|
||||
RTC_DCHECK(callback);
|
||||
|
||||
InitPortal();
|
||||
|
||||
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<DesktopFrame> result(new BasicDesktopFrame(desktop_size_));
|
||||
result->CopyPixelsFrom(
|
||||
current_frame_, (desktop_size_.width() * kBytesPerPixel),
|
||||
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
|
167
modules/desktop_capture/linux/base_capturer_pipewire.h
Normal file
167
modules/desktop_capture/linux/base_capturer_pipewire.h
Normal file
|
@ -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_LINUX_BASE_CAPTURER_PIPEWIRE_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_
|
||||
|
||||
#include <gio/gio.h>
|
||||
#define typeof __typeof__
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "rtc_base/constructormagic.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 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 InitPortal();
|
||||
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_LINUX_BASE_CAPTURER_PIPEWIRE_H_
|
|
@ -8,19 +8,20 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "modules/desktop_capture/linux/mouse_cursor_monitor_x11.h"
|
||||
|
||||
#include "modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capture_types.h"
|
||||
#include "modules/desktop_capture/desktop_frame.h"
|
||||
#include "modules/desktop_capture/linux/x_error_trap.h"
|
||||
#include "modules/desktop_capture/mouse_cursor.h"
|
||||
#include "modules/desktop_capture/x11/x_error_trap.h"
|
||||
#include "modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace {
|
||||
|
@ -59,36 +60,6 @@ Window GetTopLevelWindow(Display* display, Window window) {
|
|||
|
||||
namespace webrtc {
|
||||
|
||||
class MouseCursorMonitorX11 : public MouseCursorMonitor,
|
||||
public SharedXDisplay::XEventHandler {
|
||||
public:
|
||||
MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window);
|
||||
~MouseCursorMonitorX11() override;
|
||||
|
||||
void Init(Callback* callback, Mode mode) 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<SharedXDisplay> x_display_;
|
||||
Callback* callback_;
|
||||
Mode mode_;
|
||||
Window window_;
|
||||
|
||||
bool have_xfixes_;
|
||||
int xfixes_event_base_;
|
||||
int xfixes_error_base_;
|
||||
|
||||
std::unique_ptr<MouseCursor> cursor_shape_;
|
||||
};
|
||||
|
||||
MouseCursorMonitorX11::MouseCursorMonitorX11(
|
||||
const DesktopCaptureOptions& options,
|
||||
Window window)
|
||||
|
@ -231,12 +202,11 @@ void MouseCursorMonitorX11::CaptureCursor() {
|
|||
std::unique_ptr<DesktopFrame> image(
|
||||
new BasicDesktopFrame(DesktopSize(img->width, img->height)));
|
||||
|
||||
// Xlib stores 32-bit data in longs, even if longs are 64-bits long.
|
||||
unsigned long* src = img->pixels;
|
||||
uint64_t* src = reinterpret_cast<uint64_t*>(img->pixels);
|
||||
uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
|
||||
uint32_t* dst_end = dst + (img->width * img->height);
|
||||
while (dst < dst_end) {
|
||||
*dst++ = static_cast<uint32_t>(*src++);
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
DesktopVector hotspot(std::min(img->width, img->xhot),
|
||||
|
@ -248,7 +218,7 @@ void MouseCursorMonitorX11::CaptureCursor() {
|
|||
}
|
||||
|
||||
// static
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
||||
MouseCursorMonitor* MouseCursorMonitorX11::CreateForWindow(
|
||||
const DesktopCaptureOptions& options,
|
||||
WindowId window) {
|
||||
if (!options.x_display())
|
||||
|
@ -259,7 +229,7 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
|
|||
return new MouseCursorMonitorX11(options, window);
|
||||
}
|
||||
|
||||
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
||||
MouseCursorMonitor* MouseCursorMonitorX11::CreateForScreen(
|
||||
const DesktopCaptureOptions& options,
|
||||
ScreenId screen) {
|
||||
if (!options.x_display())
|
||||
|
@ -268,7 +238,7 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
|
|||
options, DefaultRootWindow(options.x_display()->display()));
|
||||
}
|
||||
|
||||
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
|
||||
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitorX11::Create(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return std::unique_ptr<MouseCursorMonitor>(
|
||||
CreateForScreen(options, kFullDesktopScreenId));
|
64
modules/desktop_capture/linux/mouse_cursor_monitor_x11.h
Normal file
64
modules/desktop_capture/linux/mouse_cursor_monitor_x11.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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_LINUX_MOUSE_CURSOR_MONITOR_X11_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_X11_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
#include "modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MouseCursorMonitorX11 : public MouseCursorMonitor,
|
||||
public SharedXDisplay::XEventHandler {
|
||||
public:
|
||||
MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window);
|
||||
~MouseCursorMonitorX11() override;
|
||||
|
||||
static MouseCursorMonitor* CreateForWindow(
|
||||
const DesktopCaptureOptions& options,
|
||||
WindowId window);
|
||||
static MouseCursorMonitor* CreateForScreen(
|
||||
const DesktopCaptureOptions& options,
|
||||
ScreenId screen);
|
||||
static std::unique_ptr<MouseCursorMonitor> Create(
|
||||
const DesktopCaptureOptions& options);
|
||||
|
||||
void Init(Callback* callback, Mode mode) 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<SharedXDisplay> x_display_;
|
||||
Callback* callback_;
|
||||
Mode mode_;
|
||||
Window window_;
|
||||
|
||||
bool have_xfixes_;
|
||||
int xfixes_event_base_;
|
||||
int xfixes_error_base_;
|
||||
|
||||
std::unique_ptr<MouseCursor> cursor_shape_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_MOUSE_CURSOR_MONITOR_X11_H_
|
30
modules/desktop_capture/linux/screen_capturer_pipewire.cc
Normal file
30
modules/desktop_capture/linux/screen_capturer_pipewire.cc
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 "modules/desktop_capture/linux/screen_capturer_pipewire.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
ScreenCapturerPipeWire::ScreenCapturerPipeWire()
|
||||
: BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Screen) {}
|
||||
ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {}
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer>
|
||||
ScreenCapturerPipeWire::CreateRawScreenCapturer(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return absl::make_unique<ScreenCapturerPipeWire>();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
33
modules/desktop_capture/linux/screen_capturer_pipewire.h
Normal file
33
modules/desktop_capture/linux/screen_capturer_pipewire.h
Normal file
|
@ -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_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "modules/desktop_capture/linux/base_capturer_pipewire.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ScreenCapturerPipeWire : public BaseCapturerPipeWire {
|
||||
public:
|
||||
ScreenCapturerPipeWire();
|
||||
~ScreenCapturerPipeWire() override;
|
||||
|
||||
static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
|
||||
const DesktopCaptureOptions& options);
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerPipeWire);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_PIPEWIRE_H_
|
|
@ -8,124 +8,38 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "modules/desktop_capture/linux/screen_capturer_x11.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/desktop_frame.h"
|
||||
#include "modules/desktop_capture/linux/x_server_pixel_buffer.h"
|
||||
#include "modules/desktop_capture/screen_capture_frame_queue.h"
|
||||
#include "modules/desktop_capture/screen_capturer_helper.h"
|
||||
#include "modules/desktop_capture/shared_desktop_frame.h"
|
||||
#include "modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/timeutils.h"
|
||||
#include "rtc_base/trace_event.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 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<DesktopFrame> 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<SharedDesktopFrame> 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(damage_event_base_ + XDamageNotify,
|
||||
|
@ -134,8 +48,8 @@ ScreenCapturerLinux::~ScreenCapturerLinux() {
|
|||
DeinitXlib();
|
||||
}
|
||||
|
||||
bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerLinux::Init");
|
||||
bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerX11::Init");
|
||||
options_ = options;
|
||||
|
||||
root_window_ = RootWindow(display(), DefaultScreen(display()));
|
||||
|
@ -178,7 +92,7 @@ bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ScreenCapturerLinux::InitXDamage() {
|
||||
void ScreenCapturerX11::InitXDamage() {
|
||||
// Our use of XDamage requires XFixes.
|
||||
if (!has_xfixes_) {
|
||||
return;
|
||||
|
@ -219,15 +133,15 @@ void ScreenCapturerLinux::InitXDamage() {
|
|||
RTC_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::CaptureFrame() {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerLinux::CaptureFrame");
|
||||
void ScreenCapturerX11::CaptureFrame() {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerX11::CaptureFrame");
|
||||
int64_t capture_start_time_nanos = rtc::TimeNanos();
|
||||
|
||||
queue_.MoveToNextFrame();
|
||||
|
@ -268,19 +182,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<const XDamageNotifyEvent*>(&event);
|
||||
|
@ -295,7 +209,7 @@ bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<DesktopFrame> ScreenCapturerLinux::CaptureScreen() {
|
||||
std::unique_ptr<DesktopFrame> ScreenCapturerX11::CaptureScreen() {
|
||||
std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
|
||||
RTC_DCHECK(x_server_pixel_buffer_.window_size().equals(frame->size()));
|
||||
|
||||
|
@ -353,8 +267,8 @@ std::unique_ptr<DesktopFrame> ScreenCapturerLinux::CaptureScreen() {
|
|||
return std::move(frame);
|
||||
}
|
||||
|
||||
void ScreenCapturerLinux::ScreenConfigurationChanged() {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerLinux::ScreenConfigurationChanged");
|
||||
void ScreenCapturerX11::ScreenConfigurationChanged() {
|
||||
TRACE_EVENT0("webrtc", "ScreenCapturerX11::ScreenConfigurationChanged");
|
||||
// Make sure the frame buffers will be reallocated.
|
||||
queue_.Reset();
|
||||
|
||||
|
@ -365,7 +279,7 @@ void ScreenCapturerLinux::ScreenConfigurationChanged() {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -385,7 +299,7 @@ void ScreenCapturerLinux::SynchronizeFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
void ScreenCapturerLinux::DeinitXlib() {
|
||||
void ScreenCapturerX11::DeinitXlib() {
|
||||
if (gc_) {
|
||||
XFreeGC(display(), gc_);
|
||||
gc_ = nullptr;
|
||||
|
@ -406,15 +320,13 @@ void ScreenCapturerLinux::DeinitXlib() {
|
|||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
|
||||
std::unique_ptr<DesktopCapturer> ScreenCapturerX11::CreateRawScreenCapturer(
|
||||
const DesktopCaptureOptions& options) {
|
||||
if (!options.x_display())
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
|
||||
std::unique_ptr<ScreenCapturerX11> capturer(new ScreenCapturerX11());
|
||||
if (!capturer.get()->Init(options)) {
|
||||
return nullptr;
|
||||
}
|
123
modules/desktop_capture/linux/screen_capturer_x11.h
Normal file
123
modules/desktop_capture/linux/screen_capturer_x11.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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_LINUX_SCREEN_CAPTURER_X11_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_SCREEN_CAPTURER_X11_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/desktop_frame.h"
|
||||
#include "modules/desktop_capture/linux/x_server_pixel_buffer.h"
|
||||
#include "modules/desktop_capture/screen_capture_frame_queue.h"
|
||||
#include "modules/desktop_capture/screen_capturer_helper.h"
|
||||
#include "modules/desktop_capture/shared_desktop_frame.h"
|
||||
#include "rtc_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<DesktopCapturer> 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 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<DesktopFrame> 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<SharedDesktopFrame> 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_LINUX_SCREEN_CAPTURER_X11_H_
|
|
@ -8,7 +8,7 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/x11/shared_x_display.h"
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
@ -78,4 +78,4 @@ class SharedXDisplay : public rtc::RefCountedBase {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_X11_SHARED_X_DISPLAY_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_SHARED_X_DISPLAY_H_
|
30
modules/desktop_capture/linux/window_capturer_pipewire.cc
Normal file
30
modules/desktop_capture/linux/window_capturer_pipewire.cc
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 "modules/desktop_capture/linux/window_capturer_pipewire.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
WindowCapturerPipeWire::WindowCapturerPipeWire()
|
||||
: BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Window) {}
|
||||
WindowCapturerPipeWire::~WindowCapturerPipeWire() {}
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer>
|
||||
WindowCapturerPipeWire::CreateRawWindowCapturer(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return absl::make_unique<WindowCapturerPipeWire>();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
33
modules/desktop_capture/linux/window_capturer_pipewire.h
Normal file
33
modules/desktop_capture/linux/window_capturer_pipewire.h
Normal file
|
@ -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_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "modules/desktop_capture/linux/base_capturer_pipewire.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class WindowCapturerPipeWire : public BaseCapturerPipeWire {
|
||||
public:
|
||||
WindowCapturerPipeWire();
|
||||
~WindowCapturerPipeWire() override;
|
||||
|
||||
static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
|
||||
const DesktopCaptureOptions& options);
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerPipeWire);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_PIPEWIRE_H_
|
|
@ -8,70 +8,28 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "modules/desktop_capture/linux/window_capturer_x11.h"
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/desktop_frame.h"
|
||||
#include "modules/desktop_capture/window_finder_x11.h"
|
||||
#include "modules/desktop_capture/x11/shared_x_display.h"
|
||||
#include "modules/desktop_capture/x11/window_list_utils.h"
|
||||
#include "modules/desktop_capture/x11/x_atom_cache.h"
|
||||
#include "modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
#include "modules/desktop_capture/linux/window_finder_x11.h"
|
||||
#include "modules/desktop_capture/linux/window_list_utils.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/scoped_ref_ptr.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
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 CaptureFrame() override;
|
||||
bool GetSourceList(SourceList* sources) override;
|
||||
bool SelectSource(SourceId id) override;
|
||||
bool FocusOnSelectedSource() override;
|
||||
bool IsOccluded(const DesktopVector& pos) override;
|
||||
|
||||
// SharedXDisplay::XEventHandler interface.
|
||||
bool HandleXEvent(const XEvent& event) override;
|
||||
|
||||
private:
|
||||
Display* display() { return x_display_->display(); }
|
||||
|
||||
// Returns window title for the specified X |window|.
|
||||
bool GetWindowTitle(::Window window, std::string* title);
|
||||
|
||||
Callback* callback_ = nullptr;
|
||||
|
||||
rtc::scoped_refptr<SharedXDisplay> x_display_;
|
||||
|
||||
bool has_composite_extension_ = false;
|
||||
|
||||
::Window selected_window_ = 0;
|
||||
XServerPixelBuffer x_server_pixel_buffer_;
|
||||
XAtomCache atom_cache_;
|
||||
WindowFinderX11 window_finder_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
|
||||
};
|
||||
|
||||
WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
|
||||
WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options)
|
||||
: x_display_(options.x_display()),
|
||||
atom_cache_(display()),
|
||||
window_finder_(&atom_cache_) {
|
||||
|
@ -88,11 +46,11 @@ WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
|
|||
x_display_->AddEventHandler(ConfigureNotify, this);
|
||||
}
|
||||
|
||||
WindowCapturerLinux::~WindowCapturerLinux() {
|
||||
WindowCapturerX11::~WindowCapturerX11() {
|
||||
x_display_->RemoveEventHandler(ConfigureNotify, this);
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::GetSourceList(SourceList* sources) {
|
||||
bool WindowCapturerX11::GetSourceList(SourceList* sources) {
|
||||
return GetWindowList(&atom_cache_, [this, sources](::Window window) {
|
||||
Source w;
|
||||
w.id = window;
|
||||
|
@ -103,7 +61,7 @@ bool WindowCapturerLinux::GetSourceList(SourceList* sources) {
|
|||
});
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::SelectSource(SourceId id) {
|
||||
bool WindowCapturerX11::SelectSource(SourceId id) {
|
||||
if (!x_server_pixel_buffer_.Init(display(), id))
|
||||
return false;
|
||||
|
||||
|
@ -124,7 +82,7 @@ bool WindowCapturerLinux::SelectSource(SourceId id) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::FocusOnSelectedSource() {
|
||||
bool WindowCapturerX11::FocusOnSelectedSource() {
|
||||
if (!selected_window_)
|
||||
return false;
|
||||
|
||||
|
@ -170,15 +128,15 @@ bool WindowCapturerLinux::FocusOnSelectedSource() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void WindowCapturerLinux::Start(Callback* callback) {
|
||||
void WindowCapturerX11::Start(Callback* callback) {
|
||||
RTC_DCHECK(!callback_);
|
||||
RTC_DCHECK(callback);
|
||||
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void WindowCapturerLinux::CaptureFrame() {
|
||||
TRACE_EVENT0("webrtc", "WindowCapturerLinux::CaptureFrame");
|
||||
void WindowCapturerX11::CaptureFrame() {
|
||||
TRACE_EVENT0("webrtc", "WindowCapturerX11::CaptureFrame");
|
||||
|
||||
if (!x_server_pixel_buffer_.IsWindowValid()) {
|
||||
RTC_LOG(LS_ERROR) << "The window is no longer valid.";
|
||||
|
@ -223,12 +181,12 @@ void WindowCapturerLinux::CaptureFrame() {
|
|||
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::IsOccluded(const DesktopVector& pos) {
|
||||
bool WindowCapturerX11::IsOccluded(const DesktopVector& pos) {
|
||||
return window_finder_.GetWindowUnderPoint(pos) !=
|
||||
static_cast<WindowId>(selected_window_);
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
|
||||
bool WindowCapturerX11::HandleXEvent(const XEvent& event) {
|
||||
if (event.type == ConfigureNotify) {
|
||||
XConfigureEvent xce = event.xconfigure;
|
||||
if (xce.window == selected_window_) {
|
||||
|
@ -246,7 +204,7 @@ bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
|
||||
bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) {
|
||||
int status;
|
||||
bool result = false;
|
||||
XTextProperty window_name;
|
||||
|
@ -275,14 +233,12 @@ bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
|
|||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
|
||||
std::unique_ptr<DesktopCapturer> WindowCapturerX11::CreateRawWindowCapturer(
|
||||
const DesktopCaptureOptions& options) {
|
||||
if (!options.x_display())
|
||||
return nullptr;
|
||||
return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options));
|
||||
return std::unique_ptr<DesktopCapturer>(new WindowCapturerX11(options));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
68
modules/desktop_capture/linux/window_capturer_x11.h
Normal file
68
modules/desktop_capture/linux/window_capturer_x11.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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_LINUX_WINDOW_CAPTURER_X11_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_X11_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/linux/window_finder_x11.h"
|
||||
#include "modules/desktop_capture/linux/x_atom_cache.h"
|
||||
#include "modules/desktop_capture/linux/x_server_pixel_buffer.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class WindowCapturerX11 : public DesktopCapturer,
|
||||
public SharedXDisplay::XEventHandler {
|
||||
public:
|
||||
explicit WindowCapturerX11(const DesktopCaptureOptions& options);
|
||||
~WindowCapturerX11() override;
|
||||
|
||||
static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
|
||||
const DesktopCaptureOptions& options);
|
||||
|
||||
// DesktopCapturer interface.
|
||||
void Start(Callback* callback) override;
|
||||
void CaptureFrame() override;
|
||||
bool GetSourceList(SourceList* sources) override;
|
||||
bool SelectSource(SourceId id) override;
|
||||
bool FocusOnSelectedSource() override;
|
||||
bool IsOccluded(const DesktopVector& pos) override;
|
||||
|
||||
// SharedXDisplay::XEventHandler interface.
|
||||
bool HandleXEvent(const XEvent& event) override;
|
||||
|
||||
private:
|
||||
Display* display() { return x_display_->display(); }
|
||||
|
||||
// Returns window title for the specified X |window|.
|
||||
bool GetWindowTitle(::Window window, std::string* title);
|
||||
|
||||
Callback* callback_ = nullptr;
|
||||
|
||||
rtc::scoped_refptr<SharedXDisplay> x_display_;
|
||||
|
||||
bool has_composite_extension_ = false;
|
||||
|
||||
::Window selected_window_ = 0;
|
||||
XServerPixelBuffer x_server_pixel_buffer_;
|
||||
XAtomCache atom_cache_;
|
||||
WindowFinderX11 window_finder_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerX11);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_CAPTURER_X11_H_
|
|
@ -8,10 +8,12 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/window_finder_x11.h"
|
||||
#include "modules/desktop_capture/linux/window_finder_x11.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "modules/desktop_capture/x11/window_list_utils.h"
|
||||
#include "modules/desktop_capture/linux/window_list_utils.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
|
@ -8,8 +8,8 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_X11_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_X11_H_
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_
|
||||
|
||||
#include "modules/desktop_capture/window_finder.h"
|
||||
|
||||
|
@ -32,4 +32,4 @@ class WindowFinderX11 final : public WindowFinder {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_X11_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_FINDER_X11_H_
|
|
@ -8,7 +8,7 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/x11/window_list_utils.h"
|
||||
#include "modules/desktop_capture/linux/window_list_utils.h"
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "modules/desktop_capture/x11/x_error_trap.h"
|
||||
#include "modules/desktop_capture/linux/x_error_trap.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/logging.h"
|
|
@ -8,13 +8,13 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "modules/desktop_capture/desktop_geometry.h"
|
||||
#include "modules/desktop_capture/x11/x_atom_cache.h"
|
||||
#include "modules/desktop_capture/linux/x_atom_cache.h"
|
||||
#include "rtc_base/function_view.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -51,4 +51,4 @@ DesktopRect DesktopRectFromXAttributes(const T& attributes) {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WINDOW_LIST_UTILS_H_
|
|
@ -8,7 +8,7 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/x11/x_atom_cache.h"
|
||||
#include "modules/desktop_capture/linux/x_atom_cache.h"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
@ -40,4 +40,4 @@ class XAtomCache final {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_ATOM_CACHE_H_
|
|
@ -8,7 +8,7 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/x11/x_error_trap.h"
|
||||
#include "modules/desktop_capture/linux/x_error_trap.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
|
@ -8,8 +8,8 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
|
@ -36,4 +36,4 @@ class XErrorTrap {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_ERROR_TRAP_H_
|
|
@ -8,14 +8,14 @@
|
|||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
||||
#include "modules/desktop_capture/linux/x_server_pixel_buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include "modules/desktop_capture/desktop_frame.h"
|
||||
#include "modules/desktop_capture/x11/window_list_utils.h"
|
||||
#include "modules/desktop_capture/x11/x_error_trap.h"
|
||||
#include "modules/desktop_capture/linux/window_list_utils.h"
|
||||
#include "modules/desktop_capture/linux/x_error_trap.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
|
@ -10,15 +10,15 @@
|
|||
|
||||
// Don't include this file in any .h files because it pulls in some X headers.
|
||||
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_X11_X_SERVER_PIXEL_BUFFER_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_X11_X_SERVER_PIXEL_BUFFER_H_
|
||||
|
||||
#include "modules/desktop_capture/desktop_geometry.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#include "modules/desktop_capture/desktop_geometry.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class DesktopFrame;
|
||||
|
@ -81,4 +81,4 @@ class XServerPixelBuffer {
|
|||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_X11_X_SERVER_PIXEL_BUFFER_H_
|
||||
#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_
|
51
modules/desktop_capture/mouse_cursor_monitor_linux.cc
Normal file
51
modules/desktop_capture/mouse_cursor_monitor_linux.cc
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 "modules/desktop_capture/mouse_cursor_monitor.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "modules/desktop_capture/linux/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)
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
|
||||
const DesktopCaptureOptions& options) {
|
||||
#if defined(USE_X11)
|
||||
return MouseCursorMonitorX11::Create(options);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif // defined(USE_X11)
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
40
modules/desktop_capture/screen_capturer_linux.cc
Normal file
40
modules/desktop_capture/screen_capturer_linux.cc
Normal file
|
@ -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 "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
|
||||
#if defined(WEBRTC_USE_PIPEWIRE)
|
||||
#include "modules/desktop_capture/linux/screen_capturer_pipewire.h"
|
||||
#endif // defined(WEBRTC_USE_PIPEWIRE)
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "modules/desktop_capture/linux/screen_capturer_x11.h"
|
||||
#endif // defined(USE_X11)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer> 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
|
|
@ -14,9 +14,9 @@
|
|||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
#include "modules/desktop_capture/screen_drawer.h"
|
||||
#include "modules/desktop_capture/screen_drawer_lock_posix.h"
|
||||
#include "modules/desktop_capture/x11/shared_x_display.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "system_wrappers/include/sleep.h"
|
||||
|
||||
|
|
40
modules/desktop_capture/window_capturer_linux.cc
Normal file
40
modules/desktop_capture/window_capturer_linux.cc
Normal file
|
@ -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 "modules/desktop_capture/desktop_capture_options.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
|
||||
#if defined(WEBRTC_USE_PIPEWIRE)
|
||||
#include "modules/desktop_capture/linux/window_capturer_pipewire.h"
|
||||
#endif // defined(WEBRTC_USE_PIPEWIRE)
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "modules/desktop_capture/linux/window_capturer_x11.h"
|
||||
#endif // defined(USE_X11)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer> 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
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
#if defined(USE_X11)
|
||||
#include "absl/memory/memory.h"
|
||||
#include "modules/desktop_capture/x11/shared_x_display.h"
|
||||
#include "modules/desktop_capture/x11/x_atom_cache.h"
|
||||
#include "modules/desktop_capture/linux/shared_x_display.h"
|
||||
#include "modules/desktop_capture/linux/x_atom_cache.h"
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
|
|
|
@ -9,6 +9,7 @@ import("//build/config/arm.gni")
|
|||
import("//build/config/features.gni")
|
||||
import("//build/config/mips.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
import("//build/config/sysroot.gni")
|
||||
import("//build/config/ui.gni")
|
||||
import("//build_overrides/build.gni")
|
||||
|
||||
|
@ -107,6 +108,9 @@ declare_args() {
|
|||
# Set this to false to skip building code that requires X11.
|
||||
rtc_use_x11 = use_x11
|
||||
|
||||
# Set this to use PipeWire on the Wayland display server.
|
||||
rtc_use_pipewire = false
|
||||
|
||||
# Enable to use the Mozilla internal settings.
|
||||
build_with_mozilla = false
|
||||
|
||||
|
@ -229,7 +233,8 @@ rtc_libvpx_dir = "//third_party/libvpx"
|
|||
rtc_opus_dir = "//third_party/opus"
|
||||
|
||||
# Desktop capturer is supported only on Windows, OSX and Linux.
|
||||
rtc_desktop_capture_supported = is_win || is_mac || (is_linux && rtc_use_x11)
|
||||
rtc_desktop_capture_supported =
|
||||
is_win || is_mac || (is_linux && (rtc_use_x11 || rtc_use_pipewire))
|
||||
|
||||
###############################################################################
|
||||
# Templates
|
||||
|
|
Loading…
Reference in a new issue