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