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}
This commit is contained in:
Julien Isorce
2019-04-19 14:32:56 -07:00
committed by Commit Bot
parent 5665572f47
commit 62acb5a8c0
17 changed files with 217 additions and 55 deletions

View File

@ -398,6 +398,8 @@ rtc_static_library("desktop_capture_generic") {
"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" ]

View File

@ -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, InitializedWithZeros) {
}
}
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

View File

@ -90,6 +90,7 @@ void DesktopFrame::CopyFrameInfoFrom(const DesktopFrame& other) {
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 @@ void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) {
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)

View File

@ -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 @@ class RTC_EXPORT DesktopFrame {
// 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 @@ class RTC_EXPORT DesktopFrame {
DesktopVector dpi_;
int64_t capture_time_ms_;
uint32_t capturer_id_;
std::vector<uint8_t> icc_profile_;
RTC_DISALLOW_COPY_AND_ASSIGN(DesktopFrame);
};

View File

@ -849,6 +849,10 @@ void BaseCapturerPipeWire::CaptureFrame() {
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));
}

View File

@ -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 @@ bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) {
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 @@ bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) {
// 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 @@ void ScreenCapturerX11::ScreenConfigurationChanged() {
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.";
}

View File

@ -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 @@ class ScreenCapturerX11 : public DesktopCapturer,
// current with the last buffer used.
DesktopRegion last_invalid_region_;
std::unique_ptr<XAtomCache> atom_cache_;
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerX11);
};

View File

@ -64,7 +64,7 @@ bool WindowCapturerX11::GetSourceList(SourceList* sources) {
}
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 @@ bool WindowCapturerX11::HandleXEvent(const XEvent& event) {
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.";
}

View File

@ -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 @@ DeferXFree::~DeferXFree() {
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 .

View File

@ -36,6 +36,10 @@ Atom XAtomCache::WindowTypeNormal() {
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) {

View File

@ -27,6 +27,7 @@ class XAtomCache final {
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 @@ class XAtomCache final {
Atom wm_state_ = None;
Atom window_type_ = None;
Atom window_type_normal_ = None;
Atom icc_profile_ = None;
};
} // namespace webrtc

View File

@ -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 @@ void XServerPixelBuffer::ReleaseSharedMemorySegment() {
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 @@ bool XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
SlowBlit(image, data, rect, frame);
}
if (!icc_profile_.empty())
frame->set_icc_profile(icc_profile_);
return true;
}

View File

@ -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 @@ class XServerPixelBuffer {
// 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 @@ class XServerPixelBuffer {
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);
};

View File

@ -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

View File

@ -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_

View File

@ -68,8 +68,21 @@ std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateFromCGImage(
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,

View File

@ -172,6 +172,10 @@ void ScreenCapturerWinDirectx::CaptureFrame() {
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;
}