desktop_and_cursor_composer.cc (10251B)
1 /* 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/desktop_capture/desktop_and_cursor_composer.h" 12 13 #include <cstdint> 14 #include <cstring> 15 #include <memory> 16 #include <utility> 17 18 #include "modules/desktop_capture/desktop_capture_metadata.h" 19 #include "modules/desktop_capture/desktop_capture_types.h" 20 #include "modules/desktop_capture/desktop_capturer.h" 21 #include "modules/desktop_capture/desktop_frame.h" 22 #include "modules/desktop_capture/desktop_geometry.h" 23 #include "modules/desktop_capture/mouse_cursor.h" 24 #include "modules/desktop_capture/mouse_cursor_monitor.h" 25 #include "modules/desktop_capture/shared_memory.h" 26 #include "rtc_base/checks.h" 27 #include "rtc_base/logging.h" 28 29 namespace webrtc { 30 31 namespace { 32 33 // Helper function that blends one image into another. Source image must be 34 // pre-multiplied with the alpha channel. Destination is assumed to be opaque. 35 void AlphaBlend(uint8_t* dest, 36 int dest_stride, 37 const uint8_t* src, 38 int src_stride, 39 const DesktopSize& size) { 40 for (int y = 0; y < size.height(); ++y) { 41 for (int x = 0; x < size.width(); ++x) { 42 uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3]; 43 if (base_alpha == 255) { 44 continue; 45 } else if (base_alpha == 0) { 46 memcpy(dest + x * DesktopFrame::kBytesPerPixel, 47 src + x * DesktopFrame::kBytesPerPixel, 48 DesktopFrame::kBytesPerPixel); 49 } else { 50 dest[x * DesktopFrame::kBytesPerPixel] = 51 dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 + 52 src[x * DesktopFrame::kBytesPerPixel]; 53 dest[x * DesktopFrame::kBytesPerPixel + 1] = 54 dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 + 55 src[x * DesktopFrame::kBytesPerPixel + 1]; 56 dest[x * DesktopFrame::kBytesPerPixel + 2] = 57 dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 + 58 src[x * DesktopFrame::kBytesPerPixel + 2]; 59 } 60 } 61 src += src_stride; 62 dest += dest_stride; 63 } 64 } 65 66 // DesktopFrame wrapper that draws mouse on a frame and restores original 67 // content before releasing the underlying frame. 68 class DesktopFrameWithCursor : public DesktopFrame { 69 public: 70 // Takes ownership of `frame`. 71 DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame, 72 const MouseCursor& cursor, 73 const DesktopVector& position, 74 const DesktopRect& previous_cursor_rect, 75 bool cursor_changed); 76 ~DesktopFrameWithCursor() override; 77 78 DesktopFrameWithCursor(const DesktopFrameWithCursor&) = delete; 79 DesktopFrameWithCursor& operator=(const DesktopFrameWithCursor&) = delete; 80 81 DesktopRect cursor_rect() const { return cursor_rect_; } 82 83 private: 84 const std::unique_ptr<DesktopFrame> original_frame_; 85 86 DesktopVector restore_position_; 87 std::unique_ptr<DesktopFrame> restore_frame_; 88 DesktopRect cursor_rect_; 89 }; 90 91 DesktopFrameWithCursor::DesktopFrameWithCursor( 92 std::unique_ptr<DesktopFrame> frame, 93 const MouseCursor& cursor, 94 const DesktopVector& position, 95 const DesktopRect& previous_cursor_rect, 96 bool cursor_changed) 97 : DesktopFrame(frame->size(), 98 frame->stride(), 99 frame->pixel_format(), 100 frame->data(), 101 frame->shared_memory()), 102 original_frame_(std::move(frame)) { 103 MoveFrameInfoFrom(original_frame_.get()); 104 105 DesktopVector image_pos = position.subtract(cursor.hotspot()); 106 cursor_rect_ = DesktopRect::MakeSize(cursor.image()->size()); 107 cursor_rect_.Translate(image_pos); 108 DesktopVector cursor_origin = cursor_rect_.top_left(); 109 cursor_rect_.IntersectWith(DesktopRect::MakeSize(size())); 110 111 if (!previous_cursor_rect.equals(cursor_rect_)) { 112 mutable_updated_region()->AddRect(cursor_rect_); 113 // TODO(crbug:1323241) Update this code to properly handle the case where 114 // |previous_cursor_rect| is outside of the boundaries of |frame|. 115 // Any boundary check has to take into account the fact that 116 // |previous_cursor_rect| can be in DPI or in pixels, based on the platform 117 // we're running on. 118 mutable_updated_region()->AddRect(previous_cursor_rect); 119 } else if (cursor_changed) { 120 mutable_updated_region()->AddRect(cursor_rect_); 121 } 122 123 if (cursor_rect_.is_empty()) 124 return; 125 126 // Copy original screen content under cursor to `restore_frame_`. 127 restore_position_ = cursor_rect_.top_left(); 128 restore_frame_.reset(new BasicDesktopFrame(cursor_rect_.size(), FOURCC_ARGB)); 129 restore_frame_->CopyPixelsFrom(*this, cursor_rect_.top_left(), 130 DesktopRect::MakeSize(restore_frame_->size())); 131 132 // Blit the cursor. 133 uint8_t* cursor_rect_data = 134 reinterpret_cast<uint8_t*>(data()) + cursor_rect_.top() * stride() + 135 cursor_rect_.left() * DesktopFrame::kBytesPerPixel; 136 DesktopVector origin_shift = cursor_rect_.top_left().subtract(cursor_origin); 137 AlphaBlend(cursor_rect_data, stride(), 138 cursor.image()->data() + 139 origin_shift.y() * cursor.image()->stride() + 140 origin_shift.x() * DesktopFrame::kBytesPerPixel, 141 cursor.image()->stride(), cursor_rect_.size()); 142 } 143 144 DesktopFrameWithCursor::~DesktopFrameWithCursor() { 145 // Restore original content of the frame. 146 if (restore_frame_) { 147 DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size()); 148 target_rect.Translate(restore_position_); 149 CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(), 150 target_rect); 151 } 152 } 153 154 } // namespace 155 156 DesktopAndCursorComposer::DesktopAndCursorComposer( 157 std::unique_ptr<DesktopCapturer> desktop_capturer, 158 const DesktopCaptureOptions& options) 159 : DesktopAndCursorComposer(desktop_capturer.release(), 160 MouseCursorMonitor::Create(options).release()) {} 161 162 DesktopAndCursorComposer::DesktopAndCursorComposer( 163 DesktopCapturer* desktop_capturer, 164 MouseCursorMonitor* mouse_monitor) 165 : desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) { 166 RTC_DCHECK(desktop_capturer_); 167 } 168 169 DesktopAndCursorComposer::~DesktopAndCursorComposer() = default; 170 171 std::unique_ptr<DesktopAndCursorComposer> 172 DesktopAndCursorComposer::CreateWithoutMouseCursorMonitor( 173 std::unique_ptr<DesktopCapturer> desktop_capturer) { 174 return std::unique_ptr<DesktopAndCursorComposer>( 175 new DesktopAndCursorComposer(desktop_capturer.release(), nullptr)); 176 } 177 178 void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) { 179 callback_ = callback; 180 if (mouse_monitor_) 181 mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION); 182 desktop_capturer_->Start(this); 183 } 184 185 void DesktopAndCursorComposer::SetMaxFrameRate(uint32_t max_frame_rate) { 186 desktop_capturer_->SetMaxFrameRate(max_frame_rate); 187 } 188 189 void DesktopAndCursorComposer::SetSharedMemoryFactory( 190 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) { 191 desktop_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory)); 192 } 193 194 void DesktopAndCursorComposer::CaptureFrame() { 195 if (mouse_monitor_) 196 mouse_monitor_->Capture(); 197 desktop_capturer_->CaptureFrame(); 198 } 199 200 void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) { 201 desktop_capturer_->SetExcludedWindow(window); 202 } 203 204 bool DesktopAndCursorComposer::GetSourceList(SourceList* sources) { 205 return desktop_capturer_->GetSourceList(sources); 206 } 207 208 bool DesktopAndCursorComposer::SelectSource(SourceId id) { 209 return desktop_capturer_->SelectSource(id); 210 } 211 212 bool DesktopAndCursorComposer::FocusOnSelectedSource() { 213 return desktop_capturer_->FocusOnSelectedSource(); 214 } 215 216 bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) { 217 return desktop_capturer_->IsOccluded(pos); 218 } 219 220 #if defined(WEBRTC_USE_GIO) 221 DesktopCaptureMetadata DesktopAndCursorComposer::GetMetadata() { 222 return desktop_capturer_->GetMetadata(); 223 } 224 #endif // defined(WEBRTC_USE_GIO) 225 226 void DesktopAndCursorComposer::OnFrameCaptureStart() { 227 callback_->OnFrameCaptureStart(); 228 } 229 230 void DesktopAndCursorComposer::OnCaptureResult( 231 DesktopCapturer::Result result, 232 std::unique_ptr<DesktopFrame> frame) { 233 if (frame && cursor_) { 234 if (!frame->may_contain_cursor() && 235 frame->rect().Contains(cursor_position_) && 236 !desktop_capturer_->IsOccluded(cursor_position_)) { 237 DesktopVector relative_position = 238 cursor_position_.subtract(frame->top_left()); 239 #if defined(WEBRTC_MAC) || defined(CHROMEOS) 240 // On OSX, the logical(DIP) and physical coordinates are used mixingly. 241 // For example, the captured cursor has its size in physical pixels(2x) 242 // and location in logical(DIP) pixels on Retina monitor. This will cause 243 // problem when the desktop is mixed with Retina and non-Retina monitors. 244 // So we use DIP pixel for all location info and compensate with the scale 245 // factor of current frame to the `relative_position`. 246 const float scale = frame->scale_factor(); 247 relative_position.set(relative_position.x() * scale, 248 relative_position.y() * scale); 249 #endif 250 auto frame_with_cursor = std::make_unique<DesktopFrameWithCursor>( 251 std::move(frame), *cursor_, relative_position, previous_cursor_rect_, 252 cursor_changed_); 253 previous_cursor_rect_ = frame_with_cursor->cursor_rect(); 254 cursor_changed_ = false; 255 frame = std::move(frame_with_cursor); 256 frame->set_may_contain_cursor(true); 257 } 258 } 259 260 callback_->OnCaptureResult(result, std::move(frame)); 261 } 262 263 void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) { 264 cursor_changed_ = true; 265 cursor_.reset(cursor); 266 } 267 268 void DesktopAndCursorComposer::OnMouseCursorPosition( 269 const DesktopVector& position) { 270 cursor_position_ = position; 271 } 272 273 } // namespace webrtc