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

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. 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(
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,
Sergey Ulanov 2016/07/26 00:48:52 indentation
Hzj_jie 2016/07/26 02:13:02 Done.
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(DxgiContext* context,
122 DesktopFrame* target,
123 const DesktopFrame* last_frame,
124 const DesktopVector offset) {
125 RTC_DCHECK(duplication_);
126 RTC_DCHECK(texture_);
127 RTC_DCHECK(target);
128 DXGI_OUTDUPL_FRAME_INFO frame_info;
129 memset(&frame_info, 0, sizeof(frame_info));
130 ComPtr<IDXGIResource> resource;
Sergey Ulanov 2016/07/26 00:48:51 Move this just before AcquireNextFrame() call.
Hzj_jie 2016/07/26 02:13:02 I should move updated_region and the following lin
131 // We need to merge updated region with the one from last frame, since current
132 // frame contains the content one frame before.
133 DesktopRegion updated_region;
134 updated_region.AddRegion(context->updated_region_);
Sergey Ulanov 2016/07/26 00:48:51 Add an empty line after this one as the comment ab
Sergey Ulanov 2016/07/26 00:48:51 updated_region = context->updated_region_; No need
Hzj_jie 2016/07/26 02:13:02 Acknowledged.
Hzj_jie 2016/07/26 02:13:02 Done.
135 _com_error error = duplication_->AcquireNextFrame(
136 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
137 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
138 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
139 << ", code " << error.Error();
140 return false;
141 }
142
143 if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) {
144 DetectUpdatedRegion(frame_info, offset, &context->updated_region_);
Sergey Ulanov 2016/07/26 00:48:51 I do not understand how this will work with multip
Hzj_jie 2016/07/26 02:13:02 Two capturers will have two DxgiContext instances.
Hzj_jie 2016/07/26 03:45:35 Sorry, this is not correct. Instantiating several
145 updated_region.AddRegion(context->updated_region_);
146 if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
147 return false;
148 }
149
150 const DesktopFrame& source = texture_->AsDesktopFrame();
151 DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
152 for (DesktopRegion::Iterator it(updated_region);
153 !it.IsAtEnd();
154 it.Advance()) {
155 if (!target_rect.ContainsRect(it.rect())) {
156 // target size is not large enough to copy the pixel from texture.
157 return false;
158 }
159 target->CopyPixelsFrom(source,
160 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 last
Sergey Ulanov 2016/07/26 00:48:52 s/Duplicate/Duplicate()/ Same for other comments.
Hzj_jie 2016/07/26 02:13:02 Done.
169 // frame size are consistent.
170 RTC_DCHECK(target->size().equals(last_frame->size()));
171 // No change since last frame or AcquireNextFrame timed out, we export last
172 // frame to the target.
173 context->updated_region_.Clear();
174 for (DesktopRegion::Iterator it(updated_region);
175 !it.IsAtEnd();
176 it.Advance()) {
177 target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect());
178 }
179 target->mutable_updated_region()->AddRegion(updated_region);
180 }
181 // If AcquireNextFrame failed with timeout error, we do not need to release
182 // the frame.
183 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
184 }
185
186 DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(
187 const DesktopVector offset) {
188 DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
189 result.Translate(offset);
190 return result;
191 }
192
193 void DxgiOutputDuplicator::DetectUpdatedRegion(
194 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
195 const DesktopVector offset,
196 DesktopRegion* updated_region) {
197 if (DoDetectUpdatedRegion(frame_info, updated_region)) {
198 updated_region->Translate(offset.x(), offset.y());
199 // Make sure even a region returned by Windows API is out of the scope of
200 // desktop_rect_, we still won't export it to the target DesktopFrame.
201 updated_region->IntersectWith(TranslatedDesktopRect(offset));
202 } else {
203 updated_region->SetRect(TranslatedDesktopRect(offset));
204 }
205 }
206
207 bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
208 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
209 DesktopRegion* updated_region) {
210 RTC_DCHECK(updated_region);
211 updated_region->Clear();
212 if (frame_info.TotalMetadataBufferSize == 0) {
213 // This should not happen, since frame_info.AccumulatedFrames > 0.
Sergey Ulanov 2016/07/26 00:48:52 Should this be a DCHECK if it's not supposed to ha
Hzj_jie 2016/07/26 02:13:02 I have not seen a clear comment in MSDN this won't
214 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
215 "but TotalMetadataBufferSize == 0";
216 return false;
217 }
218
219 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) {
220 metadata.clear(); // Avoid data copy
221 metadata.reserve(frame_info.TotalMetadataBufferSize);
222 }
223
224 UINT buff_size = 0;
225 DXGI_OUTDUPL_MOVE_RECT* move_rects =
226 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
227 size_t move_rects_count = 0;
228 _com_error error = _com_error(duplication_->GetFrameMoveRects(
229 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
230 if (error.Error() != S_OK) {
231 LOG(LS_ERROR) << "Failed to get move rectangles, error "
232 << error.ErrorMessage() << ", code " << error.Error();
233 return false;
234 }
235 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
236
237 RECT* dirty_rects =
238 reinterpret_cast<RECT*>(metadata.data() + buff_size);
239 size_t dirty_rects_count = 0;
240 error = _com_error(duplication_->GetFrameDirtyRects(
241 static_cast<UINT>(metadata.capacity()) - buff_size,
242 dirty_rects, &buff_size));
243 if (error.Error() != S_OK) {
244 LOG(LS_ERROR) << "Failed to get dirty rectangles, error "
245 << error.ErrorMessage() << ", code " << error.Error();
246 return false;
247 }
248 dirty_rects_count = buff_size / sizeof(RECT);
249
250 while (move_rects_count > 0) {
251 updated_region->AddRect(DesktopRect::MakeXYWH(
252 move_rects->SourcePoint.x, move_rects->SourcePoint.y,
253 move_rects->DestinationRect.right - move_rects->DestinationRect.left,
254 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
255 updated_region->AddRect(DesktopRect::MakeLTRB(
256 move_rects->DestinationRect.left, move_rects->DestinationRect.top,
257 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
258 move_rects++;
259 move_rects_count--;
260 }
261
262 while (dirty_rects_count > 0) {
263 updated_region->AddRect(DesktopRect::MakeLTRB(
264 dirty_rects->left, dirty_rects->top,
265 dirty_rects->right, dirty_rects->bottom));
266 dirty_rects++;
267 dirty_rects_count--;
268 }
269
270 return true;
271 }
272
273 void DxgiOutputDuplicator::Setup(DxgiContext* context) {
274 RTC_DCHECK(context->contexts_.empty());
275 RTC_DCHECK(context->updated_region_.is_empty());
276 }
277
278 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698