desktop_capturer_differ_wrapper.cc (8799B)
1 /* 2 * Copyright (c) 2016 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_capturer_differ_wrapper.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/desktop_region.h" 24 #include "modules/desktop_capture/differ_block.h" 25 #include "modules/desktop_capture/shared_desktop_frame.h" 26 #include "modules/desktop_capture/shared_memory.h" 27 #include "rtc_base/checks.h" 28 #include "rtc_base/time_utils.h" 29 30 namespace webrtc { 31 32 namespace { 33 34 // Returns true if (0, 0) - (`width`, `height`) vector in `old_buffer` and 35 // `new_buffer` are equal. `width` should be less than 32 36 // (defined by kBlockSize), otherwise BlockDifference() should be used. 37 bool PartialBlockDifference(const uint8_t* old_buffer, 38 const uint8_t* new_buffer, 39 int width, 40 int height, 41 int stride) { 42 RTC_DCHECK_LT(width, kBlockSize); 43 const int width_bytes = width * DesktopFrame::kBytesPerPixel; 44 for (int i = 0; i < height; i++) { 45 if (memcmp(old_buffer, new_buffer, width_bytes) != 0) { 46 return true; 47 } 48 old_buffer += stride; 49 new_buffer += stride; 50 } 51 return false; 52 } 53 54 // Compares columns in the range of [`left`, `right`), in a row in the 55 // range of [`top`, `top` + `height`), starts from `old_buffer` and 56 // `new_buffer`, and outputs updated regions into `output`. `stride` is the 57 // DesktopFrame::stride(). 58 void CompareRow(const uint8_t* old_buffer, 59 const uint8_t* new_buffer, 60 const int left, 61 const int right, 62 const int top, 63 const int bottom, 64 const int stride, 65 DesktopRegion* const output) { 66 const int block_x_offset = kBlockSize * DesktopFrame::kBytesPerPixel; 67 const int width = right - left; 68 const int height = bottom - top; 69 const int block_count = (width - 1) / kBlockSize; 70 const int last_block_width = width - block_count * kBlockSize; 71 RTC_DCHECK_GT(last_block_width, 0); 72 RTC_DCHECK_LE(last_block_width, kBlockSize); 73 74 // The first block-column in a continuous dirty area in current block-row. 75 int first_dirty_x_block = -1; 76 77 // We always need to add dirty area into `output` in the last block, so handle 78 // it separatedly. 79 for (int x = 0; x < block_count; x++) { 80 if (BlockDifference(old_buffer, new_buffer, height, stride)) { 81 if (first_dirty_x_block == -1) { 82 // This is the first dirty block in a continuous dirty area. 83 first_dirty_x_block = x; 84 } 85 } else if (first_dirty_x_block != -1) { 86 // The block on the left is the last dirty block in a continuous 87 // dirty area. 88 output->AddRect( 89 DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top, 90 x * kBlockSize + left, bottom)); 91 first_dirty_x_block = -1; 92 } 93 old_buffer += block_x_offset; 94 new_buffer += block_x_offset; 95 } 96 97 bool last_block_diff; 98 if (last_block_width < kBlockSize) { 99 // The last one is a partial vector. 100 last_block_diff = PartialBlockDifference(old_buffer, new_buffer, 101 last_block_width, height, stride); 102 } else { 103 last_block_diff = BlockDifference(old_buffer, new_buffer, height, stride); 104 } 105 if (last_block_diff) { 106 if (first_dirty_x_block == -1) { 107 first_dirty_x_block = block_count; 108 } 109 output->AddRect(DesktopRect::MakeLTRB( 110 first_dirty_x_block * kBlockSize + left, top, right, bottom)); 111 } else if (first_dirty_x_block != -1) { 112 output->AddRect( 113 DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top, 114 block_count * kBlockSize + left, bottom)); 115 } 116 } 117 118 // Compares `rect` area in `old_frame` and `new_frame`, and outputs dirty 119 // regions into `output`. 120 void CompareFrames(const DesktopFrame& old_frame, 121 const DesktopFrame& new_frame, 122 DesktopRect rect, 123 DesktopRegion* const output) { 124 RTC_DCHECK(old_frame.size().equals(new_frame.size())); 125 RTC_DCHECK_EQ(old_frame.stride(), new_frame.stride()); 126 RTC_CHECK_EQ(old_frame.pixel_format(), new_frame.pixel_format()); 127 rect.IntersectWith(DesktopRect::MakeSize(old_frame.size())); 128 129 const int y_block_count = (rect.height() - 1) / kBlockSize; 130 const int last_y_block_height = rect.height() - y_block_count * kBlockSize; 131 // Offset from the start of one block-row to the next. 132 const int block_y_stride = old_frame.stride() * kBlockSize; 133 const uint8_t* prev_block_row_start = 134 old_frame.GetFrameDataAtPos(rect.top_left()); 135 const uint8_t* curr_block_row_start = 136 new_frame.GetFrameDataAtPos(rect.top_left()); 137 138 int top = rect.top(); 139 // The last row may have a different height, so we handle it separately. 140 for (int y = 0; y < y_block_count; y++) { 141 CompareRow(prev_block_row_start, curr_block_row_start, rect.left(), 142 rect.right(), top, top + kBlockSize, old_frame.stride(), output); 143 top += kBlockSize; 144 prev_block_row_start += block_y_stride; 145 curr_block_row_start += block_y_stride; 146 } 147 CompareRow(prev_block_row_start, curr_block_row_start, rect.left(), 148 rect.right(), top, top + last_y_block_height, old_frame.stride(), 149 output); 150 } 151 152 } // namespace 153 154 DesktopCapturerDifferWrapper::DesktopCapturerDifferWrapper( 155 std::unique_ptr<DesktopCapturer> base_capturer) 156 : base_capturer_(std::move(base_capturer)) { 157 RTC_DCHECK(base_capturer_); 158 } 159 160 DesktopCapturerDifferWrapper::~DesktopCapturerDifferWrapper() {} 161 162 void DesktopCapturerDifferWrapper::Start(DesktopCapturer::Callback* callback) { 163 callback_ = callback; 164 base_capturer_->Start(this); 165 } 166 167 void DesktopCapturerDifferWrapper::SetSharedMemoryFactory( 168 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) { 169 base_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory)); 170 } 171 172 void DesktopCapturerDifferWrapper::CaptureFrame() { 173 base_capturer_->CaptureFrame(); 174 } 175 176 void DesktopCapturerDifferWrapper::SetExcludedWindow(WindowId window) { 177 base_capturer_->SetExcludedWindow(window); 178 } 179 180 bool DesktopCapturerDifferWrapper::GetSourceList(SourceList* sources) { 181 return base_capturer_->GetSourceList(sources); 182 } 183 184 bool DesktopCapturerDifferWrapper::SelectSource(SourceId id) { 185 return base_capturer_->SelectSource(id); 186 } 187 188 bool DesktopCapturerDifferWrapper::FocusOnSelectedSource() { 189 return base_capturer_->FocusOnSelectedSource(); 190 } 191 192 bool DesktopCapturerDifferWrapper::IsOccluded(const DesktopVector& pos) { 193 return base_capturer_->IsOccluded(pos); 194 } 195 196 #if defined(WEBRTC_USE_GIO) 197 DesktopCaptureMetadata DesktopCapturerDifferWrapper::GetMetadata() { 198 return base_capturer_->GetMetadata(); 199 } 200 #endif // defined(WEBRTC_USE_GIO) 201 202 void DesktopCapturerDifferWrapper::OnCaptureResult( 203 Result result, 204 std::unique_ptr<DesktopFrame> input_frame) { 205 int64_t start_time_nanos = TimeNanos(); 206 if (!input_frame) { 207 callback_->OnCaptureResult(result, nullptr); 208 return; 209 } 210 RTC_DCHECK(result == Result::SUCCESS); 211 212 std::unique_ptr<SharedDesktopFrame> frame = 213 SharedDesktopFrame::Wrap(std::move(input_frame)); 214 if (last_frame_ && (last_frame_->size().width() != frame->size().width() || 215 last_frame_->size().height() != frame->size().height() || 216 last_frame_->stride() != frame->stride())) { 217 last_frame_.reset(); 218 } 219 220 if (last_frame_) { 221 DesktopRegion hints; 222 hints.Swap(frame->mutable_updated_region()); 223 for (DesktopRegion::Iterator it(hints); !it.IsAtEnd(); it.Advance()) { 224 CompareFrames(*last_frame_, *frame, it.rect(), 225 frame->mutable_updated_region()); 226 } 227 } else { 228 frame->mutable_updated_region()->SetRect( 229 DesktopRect::MakeSize(frame->size())); 230 } 231 last_frame_ = frame->Share(); 232 233 frame->set_capture_time_ms(frame->capture_time_ms() + 234 (TimeNanos() - start_time_nanos) / 235 kNumNanosecsPerMillisec); 236 callback_->OnCaptureResult(result, std::move(frame)); 237 } 238 239 } // namespace webrtc