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

Side by Side Diff: webrtc/modules/desktop_capture/win/dxgi_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: 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_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) {
Sergey Ulanov 2016/07/08 22:36:54 This is used in only one place. Do you really need
Hzj_jie 2016/07/11 00:54:59 But it's in the constructor, we have to use it, ot
34 return DesktopRect::MakeLTRB(
35 rect.left, rect.top, rect.right, rect.bottom);
36 }
37
38 } // namespace
39
40 DxgiDuplicator::DxgiDuplicator(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 DxgiDuplicator::~DxgiDuplicator() {
52 if (duplication_) {
53 duplication_->ReleaseFrame();
54 }
55 texture_.reset();
56 }
57
58 bool DxgiDuplicator::Initialize() {
59 if (DuplicateOutput()) {
60 if (desc_.DesktopImageInSystemMemory) {
61 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get()));
62 } else {
63 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_));
64 }
65 return true;
66 } else {
67 duplication_.Reset();
68 return false;
69 }
70 }
71
72 bool DxgiDuplicator::DuplicateOutput() {
73 RTC_DCHECK(!duplication_);
74 _com_error error = output_->DuplicateOutput(
75 static_cast<IUnknown*>(device_.d3d_device()),
76 duplication_.GetAddressOf());
77 if (error.Error() != S_OK || !duplication_) {
78 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
79 << error.ErrorMessage() << ", with code " << error.Error();
80 return false;
81 }
82
83 memset(&desc_, 0, sizeof(desc_));
Sergey Ulanov 2016/07/08 22:36:54 desc_ = {};
Hzj_jie 2016/07/11 00:55:00 DXGI_OUTDUPL_DESC is a complex structure (a struct
84 duplication_->GetDesc(&desc_);
85 if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
86 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
87 "format, which is required by downstream components, "
88 "format is "
89 << desc_.ModeDesc.Format;
90 return false;
91 }
92
93 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
94 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
95 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its "
96 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
97 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
98 << ", size returned by IDXGIOutput1 is "
99 << desktop_rect_.width() << " x " << desktop_rect_.height();
100 return false;
101 }
102
103 return true;
104 }
105
106 bool DxgiDuplicator::ReleaseFrame() {
107 RTC_DCHECK(duplication_);
108 _com_error error = duplication_->ReleaseFrame();
109 if (error.Error() != S_OK) {
110 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, "
111 "error" << error.ErrorMessage() << ", code "
112 << error.Error();
113 return false;
114 }
115 return true;
116 }
117
118 bool DxgiDuplicator::Duplicate(DesktopFrame* target,
119 const DesktopFrame* last_frame,
120 const DesktopVector& offset) {
121 RTC_DCHECK(duplication_);
122 RTC_DCHECK(texture_);
123 RTC_DCHECK(target);
124 DXGI_OUTDUPL_FRAME_INFO frame_info;
125 memset(&frame_info, 0, sizeof(frame_info));
Sergey Ulanov 2016/07/08 22:36:54 frame_info = {}
Hzj_jie 2016/07/11 00:54:59 Same reason as above.
126 ComPtr<IDXGIResource> resource;
127 // We need to merge updated region with the one from last frame, since current
128 // frame contains the content one frame before.
129 DesktopRegion updated_region;
130 updated_region.AddRegion(updated_region_);
131 _com_error error = duplication_->AcquireNextFrame(
132 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
133 if (error.Error() == S_OK) {
134 if (frame_info.AccumulatedFrames > 0) {
135 DetectUpdatedRegion(frame_info, offset);
136 updated_region.AddRegion(updated_region_);
137 if (texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
138 std::unique_ptr<DesktopFrame> source(texture_->AsDesktopFrame());
139 RTC_DCHECK(source);
140 DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
141 for (DesktopRegion::Iterator it(updated_region);
142 !it.IsAtEnd();
143 it.Advance()) {
144 if (!target_rect.ContainsRect(it.rect())) {
145 // target size is not large enough to copy the pixel from texture.
146 return false;
147 }
148 target->CopyPixelsFrom(*source,
149 it.rect().top_left().subtract(offset),
150 it.rect());
151 }
152 target->mutable_updated_region()->AddRegion(updated_region);
153 return texture_->Release() && ReleaseFrame();
Sergey Ulanov 2016/07/08 22:36:54 Do we really want to fail Duplicate() if Release()
Hzj_jie 2016/07/11 00:54:59 According to MSDN, there is zero possibility Relea
154 }
155 return false;
Sergey Ulanov 2016/07/08 22:36:54 Handle errors first: if (!texture_->CopyFrom(..
Hzj_jie 2016/07/11 00:54:59 Done.
156 }
157 } else if (error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
Sergey Ulanov 2016/07/08 22:36:54 Handle this case first. Then you wouldn't need els
Hzj_jie 2016/07/11 00:54:59 Done.
158 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
159 << ", code " << error.Error();
160 return false;
161 }
162
163 if (last_frame != nullptr) {
164 // DxgiDuplicatorContainer::Duplicate makes sure target size and last frame
165 // size are consistent.
166 RTC_DCHECK(target->size().equals(last_frame->size()));
167 // No change since last frame or AcquireNextFrame timed out, we export last
168 // frame to the target.
169 updated_region_.Clear();
170 for (DesktopRegion::Iterator it(updated_region);
171 !it.IsAtEnd();
172 it.Advance()) {
173 target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect());
174 }
175 target->mutable_updated_region()->AddRegion(updated_region);
176 }
177 // If AcquireNextFrame failed with timeout error, we do not need to release
178 // the frame.
179 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
180 }
181
182 DesktopRect DxgiDuplicator::TranslatedDesktopRect(const DesktopVector& offset) {
183 DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
184 result.Translate(offset);
185 return result;
186 }
187
188 void DxgiDuplicator::DetectUpdatedRegion(
189 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
190 const DesktopVector& offset) {
191 DesktopRegion untranslated;
192 if (DoDetectUpdatedRegion(frame_info, &untranslated)) {
193 updated_region_.Clear();
Sergey Ulanov 2016/07/08 22:36:54 Do I understand it correctly that if there are mul
Hzj_jie 2016/07/11 00:54:59 No, each monitor has its own DxgiDuplicator, and e
Sergey Ulanov 2016/07/20 19:05:17 My concern is about the case when there are two Sc
Hzj_jie 2016/07/22 18:43:43 Good point. I should address this issue.
194 for (DesktopRegion::Iterator it(untranslated);
195 !it.IsAtEnd();
196 it.Advance()) {
197 DesktopRect rect = it.rect();
198 rect.Translate(offset);
Sergey Ulanov 2016/07/08 22:36:54 There is DesktopRegion::Translate(). You don't nee
Hzj_jie 2016/07/11 00:55:00 Done.
199 updated_region_.AddRect(rect);
200 }
201 // Make sure even a region returned by Windows API is out of the scope of
202 // desktop_rect_, we still won't export it to the target DesktopFrame.
203 updated_region_.IntersectWith(TranslatedDesktopRect(offset));
204 } else {
205 updated_region_.SetRect(TranslatedDesktopRect(offset));
206 }
207 }
208
209 bool DxgiDuplicator::DoDetectUpdatedRegion(
210 const DXGI_OUTDUPL_FRAME_INFO& frame_info, DesktopRegion* updated_region) {
211 RTC_DCHECK(updated_region);
212 updated_region->Clear();
213 if (frame_info.TotalMetadataBufferSize == 0) {
214 // This should not happen, since frame_info.AccumulatedFrames > 0.
215 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
216 "but TotalMetadataBufferSize == 0";
217 return false;
218 }
219
220 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) {
221 metadata.clear(); // Avoid data copy
222 metadata.reserve(frame_info.TotalMetadataBufferSize);
223 }
224
225 UINT buff_size = 0;
226 DXGI_OUTDUPL_MOVE_RECT* move_rects =
227 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
228 size_t move_rects_count = 0;
229 _com_error error = _com_error(duplication_->GetFrameMoveRects(
230 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
231 if (error.Error() != S_OK) {
232 LOG(LS_ERROR) << "Failed to get move rectangles, error "
233 << error.ErrorMessage() << ", code " << error.Error();
234 return false;
235 }
236 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
237
238 RECT* dirty_rects =
239 reinterpret_cast<RECT*>(metadata.data() + buff_size);
240 size_t dirty_rects_count = 0;
241 error = _com_error(duplication_->GetFrameDirtyRects(
242 static_cast<UINT>(metadata.capacity()) - buff_size,
243 dirty_rects, &buff_size));
244 if (error.Error() != S_OK) {
245 LOG(LS_ERROR) << "Failed to get dirty rectangles, error "
246 << error.ErrorMessage() << ", code " << error.Error();
247 return false;
248 }
249 dirty_rects_count = buff_size / sizeof(RECT);
250
251 while (move_rects_count > 0) {
252 updated_region->AddRect(DesktopRect::MakeXYWH(
253 move_rects->SourcePoint.x, move_rects->SourcePoint.y,
254 move_rects->DestinationRect.right - move_rects->DestinationRect.left,
255 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
256 updated_region->AddRect(DesktopRect::MakeLTRB(
257 move_rects->DestinationRect.left, move_rects->DestinationRect.top,
258 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
259 move_rects++;
260 move_rects_count--;
261 }
262
263 while (dirty_rects_count > 0) {
264 updated_region->AddRect(DesktopRect::MakeLTRB(
265 dirty_rects->left, dirty_rects->top,
266 dirty_rects->right, dirty_rects->bottom));
267 dirty_rects++;
268 dirty_rects_count--;
269 }
270
271 return true;
272 }
273
274 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698