Chromium Code Reviews

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: Resolve review comments Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
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(
35 rect.left, rect.top, rect.right, rect.bottom);
36 }
37
38 } // namespace
39
40 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
41 const ComPtr<IDXGIOutput1>& output,
42 const DXGI_OUTPUT_DESC& desc)
43 : device_(device),
44 output_(output),
45 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
46 RTC_DCHECK(output_);
47 RTC_DCHECK(!desktop_rect_.is_empty());
48 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0);
49 }
50
51 DxgiOutputDuplicator::DxgiOutputDuplicator(
52 DxgiOutputDuplicator&& other) = default;
53
54 DxgiOutputDuplicator::~DxgiOutputDuplicator() {
55 if (duplication_) {
56 duplication_->ReleaseFrame();
57 }
58 texture_.reset();
59 }
60
61 bool DxgiOutputDuplicator::Initialize() {
62 if (DuplicateOutput()) {
63 if (desc_.DesktopImageInSystemMemory) {
64 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get()));
65 } else {
66 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_));
67 }
68 return true;
69 } else {
70 duplication_.Reset();
71 return false;
72 }
73 }
74
75 bool DxgiOutputDuplicator::DuplicateOutput() {
76 RTC_DCHECK(!duplication_);
77 _com_error error = output_->DuplicateOutput(
78 static_cast<IUnknown*>(device_.d3d_device()),
79 duplication_.GetAddressOf());
80 if (error.Error() != S_OK || !duplication_) {
81 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
82 << error.ErrorMessage() << ", with code " << error.Error();
83 return false;
84 }
85
86 memset(&desc_, 0, sizeof(desc_));
87 duplication_->GetDesc(&desc_);
88 if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
89 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
90 "format, which is required by downstream components, "
91 "format is "
92 << desc_.ModeDesc.Format;
93 return false;
94 }
95
96 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
97 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
98 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its "
99 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
100 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
101 << ", size returned by IDXGIOutput1 is "
102 << desktop_rect_.width() << " x " << desktop_rect_.height();
103 return false;
104 }
105
106 return true;
107 }
108
109 bool DxgiOutputDuplicator::ReleaseFrame() {
110 RTC_DCHECK(duplication_);
111 _com_error error = duplication_->ReleaseFrame();
112 if (error.Error() != S_OK) {
113 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, "
114 "error" << error.ErrorMessage() << ", code "
115 << error.Error();
116 return false;
117 }
118 return true;
119 }
120
121 bool DxgiOutputDuplicator::Duplicate(DesktopFrame* target,
122 const DesktopFrame* last_frame,
123 const DesktopVector offset) {
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 // We need to merge updated region with the one from last frame, since current
131 // frame contains the content one frame before.
132 DesktopRegion updated_region;
133 updated_region.AddRegion(updated_region_);
134 _com_error error = duplication_->AcquireNextFrame(
135 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
136 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
137 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
138 << ", code " << error.Error();
139 return false;
140 }
141
142 if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) {
143 DetectUpdatedRegion(frame_info, offset);
144 updated_region.AddRegion(updated_region_);
145 if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
146 return false;
147 }
148
149 const DesktopFrame& source = texture_->AsDesktopFrame();
150 DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
151 for (DesktopRegion::Iterator it(updated_region);
152 !it.IsAtEnd();
153 it.Advance()) {
154 if (!target_rect.ContainsRect(it.rect())) {
155 // target size is not large enough to copy the pixel from texture.
156 return false;
157 }
158 target->CopyPixelsFrom(source,
159 it.rect().top_left().subtract(offset),
160 it.rect());
161 }
162 target->mutable_updated_region()->AddRegion(updated_region);
163 return texture_->Release() && ReleaseFrame();
164 }
165
166 if (last_frame != nullptr) {
167 // DxgiOutputDuplicatorContainer::Duplicate makes sure target size and last
168 // frame size are consistent.
169 RTC_DCHECK(target->size().equals(last_frame->size()));
170 // No change since last frame or AcquireNextFrame timed out, we export last
171 // frame to the target.
172 updated_region_.Clear();
173 for (DesktopRegion::Iterator it(updated_region);
174 !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 if (DoDetectUpdatedRegion(frame_info, &updated_region_)) {
196 updated_region_.Translate(offset.x(), offset.y());
197 // Make sure even a region returned by Windows API is out of the scope of
198 // desktop_rect_, we still won't export it to the target DesktopFrame.
199 updated_region_.IntersectWith(TranslatedDesktopRect(offset));
200 } else {
201 updated_region_.SetRect(TranslatedDesktopRect(offset));
202 }
203 }
204
205 bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
206 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
207 DesktopRegion* updated_region) {
208 RTC_DCHECK(updated_region);
209 updated_region->Clear();
210 if (frame_info.TotalMetadataBufferSize == 0) {
211 // This should not happen, since frame_info.AccumulatedFrames > 0.
212 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
213 "but TotalMetadataBufferSize == 0";
214 return false;
215 }
216
217 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) {
218 metadata.clear(); // Avoid data copy
219 metadata.reserve(frame_info.TotalMetadataBufferSize);
220 }
221
222 UINT buff_size = 0;
223 DXGI_OUTDUPL_MOVE_RECT* move_rects =
224 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
225 size_t move_rects_count = 0;
226 _com_error error = _com_error(duplication_->GetFrameMoveRects(
227 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
228 if (error.Error() != S_OK) {
229 LOG(LS_ERROR) << "Failed to get move rectangles, error "
230 << error.ErrorMessage() << ", code " << error.Error();
231 return false;
232 }
233 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
234
235 RECT* dirty_rects =
236 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,
240 dirty_rects, &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(DesktopRect::MakeLTRB(
262 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 } // namespace webrtc
OLDNEW

Powered by Google App Engine