Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc

Issue 2099123002: [Chromoting] Improve DirectX capturer to support multiple outputs (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fix several lint errors Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/win/dxgi_output_duplicator.h"
12
13 #include <string.h>
14
15 #include <unknwn.h>
16 #include <DXGIFormat.h>
17 #include <Windows.h>
18
19 #include "webrtc/base/checks.h"
20 #include "webrtc/base/logging.h"
21 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h"
22 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h"
23
24 namespace webrtc {
25
26 using Microsoft::WRL::ComPtr;
27
28 namespace {
29
30 // Timeout for AcquireNextFrame() call.
31 const int kAcquireTimeoutMs = 10;
32
33 DesktopRect RECTToDesktopRect(const RECT& rect) {
34 return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
35 }
36
37 } // namespace
38
39 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
40 const ComPtr<IDXGIOutput1>& output,
41 const DXGI_OUTPUT_DESC& desc)
42 : device_(device),
43 output_(output),
44 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
45 RTC_DCHECK(output_);
46 RTC_DCHECK(!desktop_rect_.is_empty());
47 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0);
48 }
49
50 DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
51 default;
52
53 DxgiOutputDuplicator::~DxgiOutputDuplicator() {
54 if (duplication_) {
55 duplication_->ReleaseFrame();
56 }
57 texture_.reset();
58 }
59
60 bool DxgiOutputDuplicator::Initialize() {
61 if (DuplicateOutput()) {
62 if (desc_.DesktopImageInSystemMemory) {
63 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get()));
64 } else {
65 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_));
66 }
67 return true;
68 } else {
69 duplication_.Reset();
70 return false;
71 }
72 }
73
74 bool DxgiOutputDuplicator::DuplicateOutput() {
75 RTC_DCHECK(!duplication_);
76 _com_error error =
77 output_->DuplicateOutput(static_cast<IUnknown*>(device_.d3d_device()),
78 duplication_.GetAddressOf());
79 if (error.Error() != S_OK || !duplication_) {
80 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
81 << error.ErrorMessage() << ", with code " << error.Error();
82 return false;
83 }
84
85 memset(&desc_, 0, sizeof(desc_));
86 duplication_->GetDesc(&desc_);
87 if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
88 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
89 "format, which is required by downstream components, "
90 "format is "
91 << desc_.ModeDesc.Format;
92 return false;
93 }
94
95 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
96 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
97 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its "
98 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
99 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
100 << ", size returned by IDXGIOutput1 is "
101 << desktop_rect_.width() << " x " << desktop_rect_.height();
102 return false;
103 }
104
105 return true;
106 }
107
108 bool DxgiOutputDuplicator::ReleaseFrame() {
109 RTC_DCHECK(duplication_);
110 _com_error error = duplication_->ReleaseFrame();
111 if (error.Error() != S_OK) {
112 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, "
113 "error"
114 << error.ErrorMessage() << ", code " << error.Error();
115 return false;
116 }
117 return true;
118 }
119
120 bool DxgiOutputDuplicator::Duplicate(Context* context,
121 const DesktopFrame* last_frame,
122 const DesktopVector offset,
123 DesktopFrame* target) {
124 RTC_DCHECK(duplication_);
125 RTC_DCHECK(texture_);
126 RTC_DCHECK(target);
127 DXGI_OUTDUPL_FRAME_INFO frame_info;
128 memset(&frame_info, 0, sizeof(frame_info));
129 ComPtr<IDXGIResource> resource;
130 _com_error error = duplication_->AcquireNextFrame(
131 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
132 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
133 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
134 << ", code " << error.Error();
135 return false;
136 }
137
138 // We need to merge updated region with the one from last frame, since current
139 // frame contains the content one frame before. Note, this is for double
140 // buffering implementation, as what we have in ScreenCapturerWinDirectx. If
141 // a consumer uses single buffering, we should clear context->updated_region
142 // after it has been merged to updated_region.
143 DesktopRegion updated_region = context->updated_region;
144 if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) {
145 DetectUpdatedRegion(frame_info, offset, &context->updated_region);
146 SpreadContextChange(context);
147 updated_region.AddRegion(context->updated_region);
148 if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
149 return false;
150 }
151
152 const DesktopFrame& source = texture_->AsDesktopFrame();
153 DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
154 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
155 it.Advance()) {
156 if (!target_rect.ContainsRect(it.rect())) {
157 // target size is not large enough to copy the pixel from texture.
158 return false;
159 }
160 target->CopyPixelsFrom(source, it.rect().top_left().subtract(offset),
161 it.rect());
162 }
163 target->mutable_updated_region()->AddRegion(updated_region);
164 return texture_->Release() && ReleaseFrame();
165 }
166
167 if (last_frame != nullptr) {
168 // DxgiOutputDuplicatorContainer::Duplicate() makes sure target size and
169 // last frame size are consistent.
170 RTC_DCHECK(target->size().equals(last_frame->size()));
171 // No change since last frame or AcquireNextFrame() timed out, we will
172 // export last frame to the target.
173 context->updated_region.Clear();
174 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
175 it.Advance()) {
176 target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect());
177 }
178 target->mutable_updated_region()->AddRegion(updated_region);
179 }
180 // If AcquireNextFrame() failed with timeout error, we do not need to release
181 // the frame.
182 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
183 }
184
185 DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(
186 const DesktopVector offset) {
187 DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
188 result.Translate(offset);
189 return result;
190 }
191
192 void DxgiOutputDuplicator::DetectUpdatedRegion(
193 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
194 const DesktopVector offset,
195 DesktopRegion* updated_region) {
196 if (DoDetectUpdatedRegion(frame_info, updated_region)) {
197 updated_region->Translate(offset.x(), offset.y());
198 // Make sure even a region returned by Windows API is out of the scope of
199 // desktop_rect_, we still won't export it to the target DesktopFrame.
200 updated_region->IntersectWith(TranslatedDesktopRect(offset));
201 } else {
202 updated_region->SetRect(TranslatedDesktopRect(offset));
203 }
204 }
205
206 bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
207 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
208 DesktopRegion* updated_region) {
209 RTC_DCHECK(updated_region);
210 updated_region->Clear();
211 if (frame_info.TotalMetadataBufferSize == 0) {
212 // This should not happen, since frame_info.AccumulatedFrames > 0.
213 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
214 "but TotalMetadataBufferSize == 0";
215 return false;
216 }
217
218 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) {
219 metadata.clear(); // Avoid data copy
220 metadata.reserve(frame_info.TotalMetadataBufferSize);
221 }
222
223 UINT buff_size = 0;
224 DXGI_OUTDUPL_MOVE_RECT* move_rects =
225 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
226 size_t move_rects_count = 0;
227 _com_error error = _com_error(duplication_->GetFrameMoveRects(
228 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
229 if (error.Error() != S_OK) {
230 LOG(LS_ERROR) << "Failed to get move rectangles, error "
231 << error.ErrorMessage() << ", code " << error.Error();
232 return false;
233 }
234 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
235
236 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size);
237 size_t dirty_rects_count = 0;
238 error = _com_error(duplication_->GetFrameDirtyRects(
239 static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects,
240 &buff_size));
241 if (error.Error() != S_OK) {
242 LOG(LS_ERROR) << "Failed to get dirty rectangles, error "
243 << error.ErrorMessage() << ", code " << error.Error();
244 return false;
245 }
246 dirty_rects_count = buff_size / sizeof(RECT);
247
248 while (move_rects_count > 0) {
249 updated_region->AddRect(DesktopRect::MakeXYWH(
250 move_rects->SourcePoint.x, move_rects->SourcePoint.y,
251 move_rects->DestinationRect.right - move_rects->DestinationRect.left,
252 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
253 updated_region->AddRect(DesktopRect::MakeLTRB(
254 move_rects->DestinationRect.left, move_rects->DestinationRect.top,
255 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
256 move_rects++;
257 move_rects_count--;
258 }
259
260 while (dirty_rects_count > 0) {
261 updated_region->AddRect(
262 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top,
263 dirty_rects->right, dirty_rects->bottom));
264 dirty_rects++;
265 dirty_rects_count--;
266 }
267
268 return true;
269 }
270
271 void DxgiOutputDuplicator::Setup(Context* context) {
272 RTC_DCHECK(context->updated_region.is_empty());
273 // Always copy entire monitor during the first Duplicate() function call.
274 context->updated_region.AddRect(desktop_rect_);
275 for (size_t i = 0; i < contexts_.size(); i++) {
276 if (contexts_[i] == nullptr) {
277 contexts_[i] = context;
278 return;
279 }
280 }
281
282 contexts_.push_back(context);
283 }
284
285 void DxgiOutputDuplicator::Unregister(const Context* const context) {
286 for (size_t i = 0; i < contexts_.size(); i++) {
287 if (contexts_[i] == context) {
288 contexts_[i] = nullptr;
289 return;
290 }
291 }
292
293 RTC_NOTREACHED();
294 }
295
296 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
297 for (Context* dest : contexts_) {
298 if (dest != source) {
299 dest->updated_region.AddRegion(source->updated_region);
300 }
301 }
302 }
303
304 } // namespace webrtc
OLDNEW
« no previous file with comments | « webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h ('k') | webrtc/modules/desktop_capture/win/dxgi_texture.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698