Moves the display reconfiguration callback into a separate class,

so that it can be shared with the cursor monitor when single monitor capturing
is added (https://webrtc-codereview.appspot.com/4679005/).
This Cl should have no functionality change.

BUG=2253
R=henrike@webrtc.org, sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/7599004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5461 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jiayl@webrtc.org 2014-01-29 21:59:12 +00:00
parent 808b99b111
commit cf1b51b6fb
9 changed files with 232 additions and 95 deletions

View File

@ -36,6 +36,8 @@
"differ_block.h", "differ_block.h",
"mac/desktop_configuration.h", "mac/desktop_configuration.h",
"mac/desktop_configuration.mm", "mac/desktop_configuration.mm",
"mac/desktop_configuration_monitor.h",
"mac/desktop_configuration_monitor.cc",
"mac/scoped_pixel_buffer_object.cc", "mac/scoped_pixel_buffer_object.cc",
"mac/scoped_pixel_buffer_object.h", "mac/scoped_pixel_buffer_object.h",
"mouse_cursor.cc", "mouse_cursor.cc",

View File

@ -28,6 +28,9 @@ DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() {
DesktopCaptureOptions result; DesktopCaptureOptions result;
#if defined(USE_X11) #if defined(USE_X11)
result.set_x_display(SharedXDisplay::CreateDefault()); result.set_x_display(SharedXDisplay::CreateDefault());
#endif
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
result.set_configuration_monitor(new DesktopConfigurationMonitor());
#endif #endif
return result; return result;
} }

View File

@ -11,11 +11,16 @@
#define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
#include "webrtc/system_wrappers/interface/constructor_magic.h" #include "webrtc/system_wrappers/interface/constructor_magic.h"
#include "webrtc/system_wrappers/interface/scoped_refptr.h"
#if defined(USE_X11) #if defined(USE_X11)
#include "webrtc/modules/desktop_capture/x11/shared_x_display.h" #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
#endif #endif
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#endif
namespace webrtc { namespace webrtc {
// An object that stores initialization parameters for screen and window // An object that stores initialization parameters for screen and window
@ -38,6 +43,15 @@ class DesktopCaptureOptions {
} }
#endif #endif
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
DesktopConfigurationMonitor* configuration_monitor() const {
return configuration_monitor_;
}
void set_configuration_monitor(scoped_refptr<DesktopConfigurationMonitor> m) {
configuration_monitor_ = m;
}
#endif
// Flag indicating that the capturer should use screen change notifications. // Flag indicating that the capturer should use screen change notifications.
// Enables/disables use of XDAMAGE in the X11 capturer. // Enables/disables use of XDAMAGE in the X11 capturer.
bool use_update_notifications() const { return use_update_notifications_; } bool use_update_notifications() const { return use_update_notifications_; }
@ -56,6 +70,10 @@ class DesktopCaptureOptions {
#if defined(USE_X11) #if defined(USE_X11)
scoped_refptr<SharedXDisplay> x_display_; scoped_refptr<SharedXDisplay> x_display_;
#endif #endif
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
#endif
bool use_update_notifications_; bool use_update_notifications_;
bool disable_effects_; bool disable_effects_;
}; };

View File

@ -52,6 +52,9 @@ struct MacDesktopConfiguration {
// increase as you move up the screen) or Carbon-style "top-down" coordinates. // increase as you move up the screen) or Carbon-style "top-down" coordinates.
static MacDesktopConfiguration GetCurrent(Origin origin); static MacDesktopConfiguration GetCurrent(Origin origin);
// Returns true if the given desktop configuration equals this one.
bool Equals(const MacDesktopConfiguration& other);
// Bounds of the desktop in Density-Independent Pixels (DIPs). // Bounds of the desktop in Density-Independent Pixels (DIPs).
DesktopRect bounds; DesktopRect bounds;

View File

@ -143,4 +143,22 @@ MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
return desktop_config; return desktop_config;
} }
// For convenience of comparing MacDisplayConfigurations in
// MacDesktopConfiguration::Equals.
bool operator==(const MacDisplayConfiguration& left,
const MacDisplayConfiguration& right) {
return left.id == right.id &&
left.bounds.equals(right.bounds) &&
left.pixel_bounds.equals(right.pixel_bounds) &&
left.dip_to_pixel_scale == right.dip_to_pixel_scale;
}
bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
return bounds.equals(other.bounds) &&
pixel_bounds.equals(other.pixel_bounds) &&
dip_to_pixel_scale == other.dip_to_pixel_scale &&
displays == other.displays;
}
} // namespace webrtc } // namespace webrtc

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
// The amount of time allowed for displays to reconfigure.
static const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000;
DesktopConfigurationMonitor::DesktopConfigurationMonitor()
: ref_count_(0),
display_configuration_capture_event_(EventWrapper::Create()) {
CGError err = CGDisplayRegisterReconfigurationCallback(
DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
if (err != kCGErrorSuccess) {
LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
abort();
}
display_configuration_capture_event_->Set();
desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
MacDesktopConfiguration::TopLeftOrigin);
}
DesktopConfigurationMonitor::~DesktopConfigurationMonitor() {
CGError err = CGDisplayRemoveReconfigurationCallback(
DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
if (err != kCGErrorSuccess)
LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
}
void DesktopConfigurationMonitor::Lock() {
if (!display_configuration_capture_event_->Wait(
kDisplayConfigurationEventTimeoutMs)) {
LOG_F(LS_ERROR) << "Event wait timed out.";
abort();
}
}
void DesktopConfigurationMonitor::Unlock() {
display_configuration_capture_event_->Set();
}
// static
void DesktopConfigurationMonitor::DisplaysReconfiguredCallback(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *user_parameter) {
DesktopConfigurationMonitor* monitor =
reinterpret_cast<DesktopConfigurationMonitor*>(user_parameter);
monitor->DisplaysReconfigured(display, flags);
}
void DesktopConfigurationMonitor::DisplaysReconfigured(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags) {
if (flags & kCGDisplayBeginConfigurationFlag) {
if (reconfiguring_displays_.empty()) {
// If this is the first display to start reconfiguring then wait on
// |display_configuration_capture_event_| to block the capture thread
// from accessing display memory until the reconfiguration completes.
if (!display_configuration_capture_event_->Wait(
kDisplayConfigurationEventTimeoutMs)) {
LOG_F(LS_ERROR) << "Event wait timed out.";
abort();
}
}
reconfiguring_displays_.insert(display);
} else {
reconfiguring_displays_.erase(display);
if (reconfiguring_displays_.empty()) {
desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
MacDesktopConfiguration::TopLeftOrigin);
display_configuration_capture_event_->Set();
}
}
}
} // namespace webrtc

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2014 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 WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
#include <ApplicationServices/ApplicationServices.h>
#include <set>
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/system_wrappers/interface/atomic32.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
namespace webrtc {
class EventWrapper;
// The class provides functions to synchronize capturing and display
// reconfiguring across threads, and the up-to-date MacDesktopConfiguration.
class DesktopConfigurationMonitor {
public:
DesktopConfigurationMonitor();
// Acquires a lock on the current configuration.
void Lock();
// Releases the lock previously acquired.
void Unlock();
// Returns the current desktop configuration. Should only be called when the
// lock has been acquired.
const MacDesktopConfiguration& desktop_configuration() {
return desktop_configuration_;
}
void AddRef() { ++ref_count_; }
void Release() {
if (--ref_count_ == 0)
delete this;
}
private:
static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *user_parameter);
~DesktopConfigurationMonitor();
void DisplaysReconfigured(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags);
Atomic32 ref_count_;
std::set<CGDirectDisplayID> reconfiguring_displays_;
MacDesktopConfiguration desktop_configuration_;
scoped_ptr<EventWrapper> display_configuration_capture_event_;
DISALLOW_COPY_AND_ASSIGN(DesktopConfigurationMonitor);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_

View File

@ -26,11 +26,11 @@
#include "webrtc/modules/desktop_capture/desktop_geometry.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h"
#include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h" #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" #include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/tick_util.h" #include "webrtc/system_wrappers/interface/tick_util.h"
@ -120,13 +120,11 @@ bool IsOSLionOrLater() {
return darwin_version >= 11; return darwin_version >= 11;
} }
// The amount of time allowed for displays to reconfigure.
const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000;
// A class to perform video frame capturing for mac. // A class to perform video frame capturing for mac.
class ScreenCapturerMac : public ScreenCapturer { class ScreenCapturerMac : public ScreenCapturer {
public: public:
ScreenCapturerMac(); explicit ScreenCapturerMac(
scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor);
virtual ~ScreenCapturerMac(); virtual ~ScreenCapturerMac();
bool Init(); bool Init();
@ -160,8 +158,6 @@ class ScreenCapturerMac : public ScreenCapturer {
void ScreenUpdateMove(CGScreenUpdateMoveDelta delta, void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
size_t count, size_t count,
const CGRect *rect_array); const CGRect *rect_array);
void DisplaysReconfigured(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags);
static void ScreenRefreshCallback(CGRectCount count, static void ScreenRefreshCallback(CGRectCount count,
const CGRect *rect_array, const CGRect *rect_array,
void *user_parameter); void *user_parameter);
@ -169,10 +165,6 @@ class ScreenCapturerMac : public ScreenCapturer {
size_t count, size_t count,
const CGRect *rect_array, const CGRect *rect_array,
void *user_parameter); void *user_parameter);
static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *user_parameter);
void ReleaseBuffers(); void ReleaseBuffers();
Callback* callback_; Callback* callback_;
@ -184,9 +176,6 @@ class ScreenCapturerMac : public ScreenCapturer {
// Queue of the frames buffers. // Queue of the frames buffers.
ScreenCaptureFrameQueue queue_; ScreenCaptureFrameQueue queue_;
// Current display configuration.
MacDesktopConfiguration desktop_config_;
// A thread-safe list of invalid rectangles, and the size of the most // A thread-safe list of invalid rectangles, and the size of the most
// recently captured screen. // recently captured screen.
ScreenCapturerHelper helper_; ScreenCapturerHelper helper_;
@ -197,13 +186,12 @@ class ScreenCapturerMac : public ScreenCapturer {
// Contains an invalid region from the previous capture. // Contains an invalid region from the previous capture.
DesktopRegion last_invalid_region_; DesktopRegion last_invalid_region_;
// Used to ensure that frame captures do not take place while displays // Monitoring display reconfiguration.
// are being reconfigured. scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
scoped_ptr<EventWrapper> display_configuration_capture_event_;
// Records the Ids of attached displays which are being reconfigured. // The desktop configuration obtained from desktop_config_monitor_ the last
// Accessed on the thread on which we are notified of display events. // time of capturing.
std::set<CGDirectDisplayID> reconfiguring_displays_; MacDesktopConfiguration desktop_config_;
// Power management assertion to prevent the screen from sleeping. // Power management assertion to prevent the screen from sleeping.
IOPMAssertionID power_assertion_id_display_; IOPMAssertionID power_assertion_id_display_;
@ -258,11 +246,12 @@ DesktopFrame* CreateFrame(
return frame.release(); return frame.release();
} }
ScreenCapturerMac::ScreenCapturerMac() ScreenCapturerMac::ScreenCapturerMac(
scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
: callback_(NULL), : callback_(NULL),
mouse_shape_observer_(NULL), mouse_shape_observer_(NULL),
cgl_context_(NULL), cgl_context_(NULL),
display_configuration_capture_event_(EventWrapper::Create()), desktop_config_monitor_(desktop_config_monitor),
power_assertion_id_display_(kIOPMNullAssertionID), power_assertion_id_display_(kIOPMNullAssertionID),
power_assertion_id_user_(kIOPMNullAssertionID), power_assertion_id_user_(kIOPMNullAssertionID),
app_services_library_(NULL), app_services_library_(NULL),
@ -271,7 +260,6 @@ ScreenCapturerMac::ScreenCapturerMac()
cg_display_bits_per_pixel_(NULL), cg_display_bits_per_pixel_(NULL),
opengl_library_(NULL), opengl_library_(NULL),
cgl_set_full_screen_(NULL) { cgl_set_full_screen_(NULL) {
display_configuration_capture_event_->Set();
} }
ScreenCapturerMac::~ScreenCapturerMac() { ScreenCapturerMac::~ScreenCapturerMac() {
@ -286,11 +274,6 @@ ScreenCapturerMac::~ScreenCapturerMac() {
ReleaseBuffers(); ReleaseBuffers();
UnregisterRefreshAndMoveHandlers(); UnregisterRefreshAndMoveHandlers();
CGError err = CGDisplayRemoveReconfigurationCallback(
ScreenCapturerMac::DisplaysReconfiguredCallback, this);
if (err != kCGErrorSuccess)
LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
dlclose(app_services_library_); dlclose(app_services_library_);
dlclose(opengl_library_); dlclose(opengl_library_);
} }
@ -299,14 +282,6 @@ bool ScreenCapturerMac::Init() {
if (!RegisterRefreshAndMoveHandlers()) { if (!RegisterRefreshAndMoveHandlers()) {
return false; return false;
} }
CGError err = CGDisplayRegisterReconfigurationCallback(
ScreenCapturerMac::DisplaysReconfiguredCallback, this);
if (err != kCGErrorSuccess) {
LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
return false;
}
ScreenConfigurationChanged(); ScreenConfigurationChanged();
return true; return true;
} }
@ -351,14 +326,17 @@ void ScreenCapturerMac::Capture(
queue_.MoveToNextFrame(); queue_.MoveToNextFrame();
// Wait until the display configuration is stable. If one or more displays desktop_config_monitor_->Lock();
// are reconfiguring then |display_configuration_capture_event_| will not be MacDesktopConfiguration new_config =
// set until the reconfiguration completes. desktop_config_monitor_->desktop_configuration();
// TODO(wez): Replace this with an early-exit (See crbug.com/104542). if (!desktop_config_.Equals(new_config)) {
if (!display_configuration_capture_event_->Wait( desktop_config_ = new_config;
kDisplayConfigurationEventTimeoutMs)) { // If the display configuraiton has changed then refresh capturer data
LOG_F(LS_ERROR) << "Event wait timed out."; // structures. Occasionally, the refresh and move handlers are lost when
abort(); // the screen mode changes, so re-register them here.
UnregisterRefreshAndMoveHandlers();
RegisterRefreshAndMoveHandlers();
ScreenConfigurationChanged();
} }
DesktopRegion region; DesktopRegion region;
@ -400,7 +378,7 @@ void ScreenCapturerMac::Capture(
// Signal that we are done capturing data from the display framebuffer, // Signal that we are done capturing data from the display framebuffer,
// and accessing display structures. // and accessing display structures.
display_configuration_capture_event_->Set(); desktop_config_monitor_->Unlock();
// Capture the current cursor shape and notify |callback_| if it has changed. // Capture the current cursor shape and notify |callback_| if it has changed.
CaptureCursor(); CaptureCursor();
@ -693,14 +671,8 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
// Clear the dirty region, in case the display is down-sizing. // Clear the dirty region, in case the display is down-sizing.
helper_.ClearInvalidRegion(); helper_.ClearInvalidRegion();
// Refresh the cached desktop configuration.
desktop_config_ = MacDesktopConfiguration::GetCurrent(
MacDesktopConfiguration::TopLeftOrigin);
// Re-mark the entire desktop as dirty. // Re-mark the entire desktop as dirty.
helper_.InvalidateScreen( helper_.InvalidateScreen(desktop_config_.pixel_bounds.size());
DesktopSize(desktop_config_.pixel_bounds.width(),
desktop_config_.pixel_bounds.height()));
// Make sure the frame buffers will be reallocated. // Make sure the frame buffers will be reallocated.
queue_.Reset(); queue_.Reset();
@ -847,39 +819,6 @@ void ScreenCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
ScreenRefresh(count, refresh_rects); ScreenRefresh(count, refresh_rects);
} }
void ScreenCapturerMac::DisplaysReconfigured(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags) {
if (flags & kCGDisplayBeginConfigurationFlag) {
if (reconfiguring_displays_.empty()) {
// If this is the first display to start reconfiguring then wait on
// |display_configuration_capture_event_| to block the capture thread
// from accessing display memory until the reconfiguration completes.
if (!display_configuration_capture_event_->Wait(
kDisplayConfigurationEventTimeoutMs)) {
LOG_F(LS_ERROR) << "Event wait timed out.";
abort();
}
}
reconfiguring_displays_.insert(display);
} else {
reconfiguring_displays_.erase(display);
if (reconfiguring_displays_.empty()) {
// If no other displays are reconfiguring then refresh capturer data
// structures and un-block the capturer thread. Occasionally, the
// refresh and move handlers are lost when the screen mode changes,
// so re-register them here (the same does not appear to be true for
// the reconfiguration handler itself).
UnregisterRefreshAndMoveHandlers();
RegisterRefreshAndMoveHandlers();
ScreenConfigurationChanged();
display_configuration_capture_event_->Set();
}
}
}
void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count, void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
const CGRect* rect_array, const CGRect* rect_array,
void* user_parameter) { void* user_parameter) {
@ -900,20 +839,15 @@ void ScreenCapturerMac::ScreenUpdateMoveCallback(
capturer->ScreenUpdateMove(delta, count, rect_array); capturer->ScreenUpdateMove(delta, count, rect_array);
} }
void ScreenCapturerMac::DisplaysReconfiguredCallback(
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void* user_parameter) {
ScreenCapturerMac* capturer =
reinterpret_cast<ScreenCapturerMac*>(user_parameter);
capturer->DisplaysReconfigured(display, flags);
}
} // namespace } // namespace
// static // static
ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
scoped_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac()); if (!options.configuration_monitor())
return NULL;
scoped_ptr<ScreenCapturerMac> capturer(
new ScreenCapturerMac(options.configuration_monitor()));
if (!capturer->Init()) if (!capturer->Init())
capturer.reset(); capturer.reset();
return capturer.release(); return capturer.release();

View File

@ -11,6 +11,8 @@
#ifndef SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_ #ifndef SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
#define SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_ #define SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
#include <stddef.h>
namespace webrtc { namespace webrtc {
// Extracted from Chromium's src/base/memory/ref_counted.h. // Extracted from Chromium's src/base/memory/ref_counted.h.