Add ICC profile to DesktopFrame
And fill-in icc profile from the various window and screen capturers.
Done on WindowCapturerMac, ScreenCapturerMac, WindowCapturerX11
and ScreenCapturerX11. Follow-up CLs will do it on ScreenCapturerWinDirectx
and ScreenCapturerPipeWire.
Useful to build the gfx::ColorSpace in chromium, especially
from src/content/browser/media/capture/desktop_capture_device.cc.
We do not build the color space directly here to avoid duplicating
ui/gfx/icc_profile.h,cc code from chromium, which one implements
icc profile caching.
Bug: chromium:945468
Change-Id: Id6e3920233771e035f7578847406bf1f519dcd49
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/133580
Commit-Queue: Julien Isorce <julien.isorce@chromium.org>
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: Brave Yao <braveyao@webrtc.org>
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#27697}
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 57f7a34..e6797bf 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -398,6 +398,8 @@
"linux/x_error_trap.h",
"linux/x_server_pixel_buffer.cc",
"linux/x_server_pixel_buffer.h",
+ "linux/x_window_property.cc",
+ "linux/x_window_property.h",
]
configs += [ "//build/config/linux:x11" ]
diff --git a/modules/desktop_capture/cropped_desktop_frame_unittest.cc b/modules/desktop_capture/cropped_desktop_frame_unittest.cc
index fdf51ae..92207e4 100644
--- a/modules/desktop_capture/cropped_desktop_frame_unittest.cc
+++ b/modules/desktop_capture/cropped_desktop_frame_unittest.cc
@@ -14,6 +14,7 @@
#include "absl/memory/memory.h"
#include "modules/desktop_capture/cropped_desktop_frame.h"
#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
#include "test/gtest.h"
namespace webrtc {
@@ -77,4 +78,33 @@
}
}
+TEST(CroppedDesktopFrameTest, IccProfile) {
+ const uint8_t fake_icc_profile_data_array[] = {0x1a, 0x00, 0x2b, 0x00,
+ 0x3c, 0x00, 0x4d};
+ const std::vector<uint8_t> icc_profile(
+ fake_icc_profile_data_array,
+ fake_icc_profile_data_array + sizeof(fake_icc_profile_data_array));
+
+ std::unique_ptr<DesktopFrame> frame = CreateTestFrame();
+ EXPECT_EQ(frame->icc_profile().size(), 0UL);
+
+ frame->set_icc_profile(icc_profile);
+ EXPECT_EQ(frame->icc_profile().size(), 7UL);
+ EXPECT_EQ(frame->icc_profile(), icc_profile);
+
+ frame = CreateCroppedDesktopFrame(std::move(frame),
+ DesktopRect::MakeLTRB(2, 2, 8, 18));
+ EXPECT_EQ(frame->icc_profile().size(), 7UL);
+ EXPECT_EQ(frame->icc_profile(), icc_profile);
+
+ std::unique_ptr<SharedDesktopFrame> shared =
+ SharedDesktopFrame::Wrap(std::move(frame));
+ EXPECT_EQ(shared->icc_profile().size(), 7UL);
+ EXPECT_EQ(shared->icc_profile(), icc_profile);
+
+ std::unique_ptr<DesktopFrame> shared_other = shared->Share();
+ EXPECT_EQ(shared_other->icc_profile().size(), 7UL);
+ EXPECT_EQ(shared_other->icc_profile(), icc_profile);
+}
+
} // namespace webrtc
diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc
index eaee0aa..70af145 100644
--- a/modules/desktop_capture/desktop_frame.cc
+++ b/modules/desktop_capture/desktop_frame.cc
@@ -90,6 +90,7 @@
set_capturer_id(other.capturer_id());
*mutable_updated_region() = other.updated_region();
set_top_left(other.top_left());
+ set_icc_profile(other.icc_profile());
}
void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) {
@@ -98,6 +99,7 @@
set_capturer_id(other->capturer_id());
mutable_updated_region()->Swap(other->mutable_updated_region());
set_top_left(other->top_left());
+ set_icc_profile(other->icc_profile());
}
BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h
index ef5ed72..5658b1c 100644
--- a/modules/desktop_capture/desktop_frame.h
+++ b/modules/desktop_capture/desktop_frame.h
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <memory>
+#include <vector>
#include "modules/desktop_capture/desktop_geometry.h"
#include "modules/desktop_capture/desktop_region.h"
@@ -110,6 +111,15 @@
// DesktopFrameWithCursor.
void MoveFrameInfoFrom(DesktopFrame* other);
+ // Set and get the ICC profile of the frame data pixels. Useful to build the
+ // a ColorSpace object from clients of webrtc library like chromium. The
+ // format of an ICC profile is defined in the following specification
+ // http://www.color.org/specification/ICC1v43_2010-12.pdf.
+ const std::vector<uint8_t>& icc_profile() const { return icc_profile_; }
+ void set_icc_profile(const std::vector<uint8_t>& icc_profile) {
+ icc_profile_ = icc_profile;
+ }
+
protected:
DesktopFrame(DesktopSize size,
int stride,
@@ -131,6 +141,7 @@
DesktopVector dpi_;
int64_t capture_time_ms_;
uint32_t capturer_id_;
+ std::vector<uint8_t> icc_profile_;
RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrame);
};
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.cc b/modules/desktop_capture/linux/base_capturer_pipewire.cc
index 05ad86f..f79d0a1 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.cc
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.cc
@@ -849,6 +849,10 @@
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
return;
}
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on the
+ // frame, see ScreenCapturerX11::CaptureFrame.
+
callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
}
diff --git a/modules/desktop_capture/linux/screen_capturer_x11.cc b/modules/desktop_capture/linux/screen_capturer_x11.cc
index 6cd40c1..dc81393 100644
--- a/modules/desktop_capture/linux/screen_capturer_x11.cc
+++ b/modules/desktop_capture/linux/screen_capturer_x11.cc
@@ -19,6 +19,7 @@
#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 "modules/desktop_capture/desktop_frame.h"
@@ -51,6 +52,8 @@
TRACE_EVENT0("webrtc", "ScreenCapturerX11::Init");
options_ = options;
+ atom_cache_ = absl::make_unique<XAtomCache>(display());
+
root_window_ = RootWindow(display(), DefaultScreen(display()));
if (root_window_ == BadValue) {
RTC_LOG(LS_ERROR) << "Unable to get the root window";
@@ -79,7 +82,8 @@
// Register for changes to the dimensions of the root window.
XSelectInput(display(), root_window_, StructureNotifyMask);
- if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
return false;
}
@@ -272,7 +276,8 @@
queue_.Reset();
helper_.ClearInvalidRegion();
- if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
"configuration change.";
}
diff --git a/modules/desktop_capture/linux/screen_capturer_x11.h b/modules/desktop_capture/linux/screen_capturer_x11.h
index 6412a76..b3b6604 100644
--- a/modules/desktop_capture/linux/screen_capturer_x11.h
+++ b/modules/desktop_capture/linux/screen_capturer_x11.h
@@ -22,6 +22,7 @@
#include "modules/desktop_capture/desktop_frame.h"
#include "modules/desktop_capture/desktop_region.h"
#include "modules/desktop_capture/linux/shared_x_display.h"
+#include "modules/desktop_capture/linux/x_atom_cache.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"
@@ -116,6 +117,8 @@
// current with the last buffer used.
DesktopRegion last_invalid_region_;
+ std::unique_ptr<XAtomCache> atom_cache_;
+
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerX11);
};
diff --git a/modules/desktop_capture/linux/window_capturer_x11.cc b/modules/desktop_capture/linux/window_capturer_x11.cc
index 85a51a6..674c891 100644
--- a/modules/desktop_capture/linux/window_capturer_x11.cc
+++ b/modules/desktop_capture/linux/window_capturer_x11.cc
@@ -64,7 +64,7 @@
}
bool WindowCapturerX11::SelectSource(SourceId id) {
- if (!x_server_pixel_buffer_.Init(display(), id))
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, id))
return false;
// Tell the X server to send us window resizing events.
@@ -194,7 +194,7 @@
if (xce.window == selected_window_) {
if (!DesktopRectFromXAttributes(xce).equals(
x_server_pixel_buffer_.window_rect())) {
- if (!x_server_pixel_buffer_.Init(display(), selected_window_)) {
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, selected_window_)) {
RTC_LOG(LS_ERROR)
<< "Failed to initialize pixel buffer after resizing.";
}
diff --git a/modules/desktop_capture/linux/window_list_utils.cc b/modules/desktop_capture/linux/window_list_utils.cc
index 906042a..e71f28a 100644
--- a/modules/desktop_capture/linux/window_list_utils.cc
+++ b/modules/desktop_capture/linux/window_list_utils.cc
@@ -16,6 +16,7 @@
#include <algorithm>
#include "modules/desktop_capture/linux/x_error_trap.h"
+#include "modules/desktop_capture/linux/x_window_property.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/logging.h"
@@ -38,53 +39,6 @@
XFree(data_);
}
-// Convenience wrapper for XGetWindowProperty() results.
-template <class PropertyType>
-class XWindowProperty {
- public:
- XWindowProperty(Display* display, Window window, Atom property) {
- const int kBitsPerByte = 8;
- Atom actual_type;
- int actual_format;
- unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty
- int status = XGetWindowProperty(
- display, window, property, 0L, ~0L, False, AnyPropertyType,
- &actual_type, &actual_format, &size_, &bytes_after, &data_);
- if (status != Success) {
- data_ = nullptr;
- return;
- }
- if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
- size_ = 0;
- return;
- }
-
- is_valid_ = true;
- }
-
- ~XWindowProperty() {
- if (data_)
- XFree(data_);
- }
-
- // True if we got properly value successfully.
- bool is_valid() const { return is_valid_; }
-
- // Size and value of the property.
- size_t size() const { return size_; }
- const PropertyType* data() const {
- return reinterpret_cast<PropertyType*>(data_);
- }
- PropertyType* data() { return reinterpret_cast<PropertyType*>(data_); }
-
- private:
- bool is_valid_ = false;
- unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty
- unsigned char* data_ = nullptr;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
-};
-
// Iterates through |window| hierarchy to find first visible window, i.e. one
// that has WM_STATE property set to NormalState.
// See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
diff --git a/modules/desktop_capture/linux/x_atom_cache.cc b/modules/desktop_capture/linux/x_atom_cache.cc
index 832e616..4ea0249 100644
--- a/modules/desktop_capture/linux/x_atom_cache.cc
+++ b/modules/desktop_capture/linux/x_atom_cache.cc
@@ -36,6 +36,10 @@
return CreateIfNotExist(&window_type_normal_, "_NET_WM_WINDOW_TYPE_NORMAL");
}
+Atom XAtomCache::IccProfile() {
+ return CreateIfNotExist(&icc_profile_, "_ICC_PROFILE");
+}
+
Atom XAtomCache::CreateIfNotExist(Atom* atom, const char* name) {
RTC_DCHECK(atom);
if (*atom == None) {
diff --git a/modules/desktop_capture/linux/x_atom_cache.h b/modules/desktop_capture/linux/x_atom_cache.h
index 731b616..0249c15 100644
--- a/modules/desktop_capture/linux/x_atom_cache.h
+++ b/modules/desktop_capture/linux/x_atom_cache.h
@@ -27,6 +27,7 @@
Atom WmState();
Atom WindowType();
Atom WindowTypeNormal();
+ Atom IccProfile();
private:
// If |*atom| is None, this function uses XInternAtom() to retrieve an Atom.
@@ -36,6 +37,7 @@
Atom wm_state_ = None;
Atom window_type_ = None;
Atom window_type_normal_ = None;
+ Atom icc_profile_ = None;
};
} // namespace webrtc
diff --git a/modules/desktop_capture/linux/x_server_pixel_buffer.cc b/modules/desktop_capture/linux/x_server_pixel_buffer.cc
index 364c43b..9d8efdd 100644
--- a/modules/desktop_capture/linux/x_server_pixel_buffer.cc
+++ b/modules/desktop_capture/linux/x_server_pixel_buffer.cc
@@ -19,6 +19,7 @@
#include "modules/desktop_capture/desktop_frame.h"
#include "modules/desktop_capture/linux/window_list_utils.h"
#include "modules/desktop_capture/linux/x_error_trap.h"
+#include "modules/desktop_capture/linux/x_window_property.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@@ -172,15 +173,28 @@
shm_segment_info_ = nullptr;
}
-bool XServerPixelBuffer::Init(Display* display, Window window) {
+bool XServerPixelBuffer::Init(XAtomCache* cache, Window window) {
Release();
- display_ = display;
+ display_ = cache->display();
XWindowAttributes attributes;
if (!GetWindowRect(display_, window, &window_rect_, &attributes)) {
return false;
}
+ if (cache->IccProfile() != None) {
+ // |window| is the root window when doing screen capture.
+ XWindowProperty<uint8_t> icc_profile_property(cache->display(), window,
+ cache->IccProfile());
+ if (icc_profile_property.is_valid() && icc_profile_property.size() > 0) {
+ icc_profile_ = std::vector<uint8_t>(
+ icc_profile_property.data(),
+ icc_profile_property.data() + icc_profile_property.size());
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get icc profile";
+ }
+ }
+
window_ = window;
InitShm(attributes);
@@ -348,6 +362,9 @@
SlowBlit(image, data, rect, frame);
}
+ if (!icc_profile_.empty())
+ frame->set_icc_profile(icc_profile_);
+
return true;
}
diff --git a/modules/desktop_capture/linux/x_server_pixel_buffer.h b/modules/desktop_capture/linux/x_server_pixel_buffer.h
index a11ca20..98790e3 100644
--- a/modules/desktop_capture/linux/x_server_pixel_buffer.h
+++ b/modules/desktop_capture/linux/x_server_pixel_buffer.h
@@ -13,6 +13,9 @@
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_
#define MODULES_DESKTOP_CAPTURE_LINUX_X_SERVER_PIXEL_BUFFER_H_
+#include <memory>
+#include <vector>
+
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
@@ -22,6 +25,7 @@
namespace webrtc {
class DesktopFrame;
+class XAtomCache;
// A class to allow the X server's pixel buffer to be accessed as efficiently
// as possible.
@@ -34,7 +38,7 @@
// Allocate (or reallocate) the pixel buffer for |window|. Returns false in
// case of an error (e.g. window doesn't exist).
- bool Init(Display* display, Window window);
+ bool Init(XAtomCache* cache, Window window);
bool is_initialized() { return window_ != 0; }
@@ -75,6 +79,7 @@
Pixmap shm_pixmap_ = 0;
GC shm_gc_ = nullptr;
bool xshm_get_image_succeeded_ = false;
+ std::vector<uint8_t> icc_profile_;
RTC_DISALLOW_COPY_AND_ASSIGN(XServerPixelBuffer);
};
diff --git a/modules/desktop_capture/linux/x_window_property.cc b/modules/desktop_capture/linux/x_window_property.cc
new file mode 100644
index 0000000..ba25dee
--- /dev/null
+++ b/modules/desktop_capture/linux/x_window_property.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 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/x_window_property.h"
+
+namespace webrtc {
+
+XWindowPropertyBase::XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size) {
+ const int kBitsPerByte = 8;
+ Atom actual_type;
+ int actual_format;
+ unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty
+ int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
+ AnyPropertyType, &actual_type, &actual_format,
+ &size_, &bytes_after, &data_);
+ if (status != Success) {
+ data_ = nullptr;
+ return;
+ }
+ if ((expected_size * kBitsPerByte) != actual_format) {
+ size_ = 0;
+ return;
+ }
+
+ is_valid_ = true;
+}
+
+XWindowPropertyBase::~XWindowPropertyBase() {
+ if (data_)
+ XFree(data_);
+}
+
+} // namespace webrtc
diff --git a/modules/desktop_capture/linux/x_window_property.h b/modules/desktop_capture/linux/x_window_property.h
new file mode 100644
index 0000000..ef643b6
--- /dev/null
+++ b/modules/desktop_capture/linux/x_window_property.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 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_X_WINDOW_PROPERTY_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X_WINDOW_PROPERTY_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include "rtc_base/constructor_magic.h"
+
+namespace webrtc {
+
+class XWindowPropertyBase {
+ public:
+ XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size);
+ virtual ~XWindowPropertyBase();
+
+ // True if we got properly value successfully.
+ bool is_valid() const { return is_valid_; }
+
+ // Size and value of the property.
+ size_t size() const { return size_; }
+
+ protected:
+ unsigned char* data_ = nullptr;
+
+ private:
+ bool is_valid_ = false;
+ unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(XWindowPropertyBase);
+};
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty : public XWindowPropertyBase {
+ public:
+ XWindowProperty(Display* display, const Window window, const Atom property)
+ : XWindowPropertyBase(display, window, property, sizeof(PropertyType)) {}
+ ~XWindowProperty() override = default;
+
+ const PropertyType* data() const {
+ return reinterpret_cast<PropertyType*>(data_);
+ }
+ PropertyType* data() { return reinterpret_cast<PropertyType*>(data_); }
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X_WINDOW_PROPERTY_H_
diff --git a/modules/desktop_capture/mac/desktop_frame_cgimage.mm b/modules/desktop_capture/mac/desktop_frame_cgimage.mm
index 784b04f..dddf4a4 100644
--- a/modules/desktop_capture/mac/desktop_frame_cgimage.mm
+++ b/modules/desktop_capture/mac/desktop_frame_cgimage.mm
@@ -68,8 +68,21 @@
DesktopSize size(CGImageGetWidth(cg_image.get()), CGImageGetHeight(cg_image.get()));
int stride = CGImageGetBytesPerRow(cg_image.get());
- return std::unique_ptr<DesktopFrameCGImage>(
+ std::unique_ptr<DesktopFrameCGImage> frame(
new DesktopFrameCGImage(size, stride, data, cg_image, cg_data));
+
+ CGColorSpaceRef cg_color_space = CGImageGetColorSpace(cg_image.get());
+ if (cg_color_space) {
+ rtc::ScopedCFTypeRef<CFDataRef> cf_icc_profile(CGColorSpaceCopyICCProfile(cg_color_space));
+ const uint8_t* data_as_byte =
+ reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(cf_icc_profile.get()));
+ const size_t data_size = CFDataGetLength(cf_icc_profile.get());
+ if (data_as_byte && data_size > 0) {
+ frame->set_icc_profile(std::vector<uint8_t>(data_as_byte, data_as_byte + data_size));
+ }
+ }
+
+ return frame;
}
DesktopFrameCGImage::DesktopFrameCGImage(DesktopSize size,
diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.cc b/modules/desktop_capture/win/screen_capturer_win_directx.cc
index 7d4d0aa..ad62fb8 100644
--- a/modules/desktop_capture/win/screen_capturer_win_directx.cc
+++ b/modules/desktop_capture/win/screen_capturer_win_directx.cc
@@ -172,6 +172,10 @@
frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
rtc::kNumNanosecsPerMillisec);
frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx);
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
+ // the frame, see WindowCapturerMac::CaptureFrame.
+
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
break;
}