OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h" | 11 #include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h" |
12 | 12 |
13 #include <string.h> | 13 #include <string.h> |
14 | 14 |
15 #include <unknwn.h> | 15 #include <unknwn.h> |
16 #include <DXGI.h> | |
16 #include <DXGIFormat.h> | 17 #include <DXGIFormat.h> |
17 #include <Windows.h> | 18 #include <Windows.h> |
18 | 19 |
19 #include <algorithm> | 20 #include <algorithm> |
20 | 21 |
21 #include "webrtc/base/checks.h" | 22 #include "webrtc/base/checks.h" |
22 #include "webrtc/base/logging.h" | 23 #include "webrtc/base/logging.h" |
23 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h" | 24 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h" |
24 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h" | 25 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h" |
25 | 26 |
26 namespace webrtc { | 27 namespace webrtc { |
27 | 28 |
28 using Microsoft::WRL::ComPtr; | 29 using Microsoft::WRL::ComPtr; |
29 | 30 |
30 namespace { | 31 namespace { |
31 | 32 |
32 // Timeout for AcquireNextFrame() call. | 33 // Timeout for AcquireNextFrame() call. |
33 const int kAcquireTimeoutMs = 10; | 34 const int kAcquireTimeoutMs = 10; |
34 | 35 |
35 DesktopRect RECTToDesktopRect(const RECT& rect) { | 36 DesktopRect RECTToDesktopRect(const RECT& rect) { |
36 return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); | 37 return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); |
37 } | 38 } |
38 | 39 |
40 Rotation DXGI_MODE_ROTATIONToRotation(DXGI_MODE_ROTATION rotation) { | |
Sergey Ulanov
2016/11/28 23:12:35
Call it DxgiRotationToRotation()?
Hzj_jie
2016/11/29 01:21:39
Done.
| |
41 switch (rotation) { | |
42 case DXGI_MODE_ROTATION_IDENTITY: | |
43 case DXGI_MODE_ROTATION_UNSPECIFIED: | |
44 return Rotation::CLOCK_WISE_0; | |
45 case DXGI_MODE_ROTATION_ROTATE90: | |
46 return Rotation::CLOCK_WISE_90; | |
47 case DXGI_MODE_ROTATION_ROTATE180: | |
48 return Rotation::CLOCK_WISE_180; | |
49 case DXGI_MODE_ROTATION_ROTATE270: | |
50 return Rotation::CLOCK_WISE_270; | |
51 } | |
52 | |
53 RTC_NOTREACHED(); | |
54 return Rotation::CLOCK_WISE_0; | |
55 } | |
56 | |
57 // Translates |rect| with the reverse of |offset| | |
58 DesktopRect ReverseTranslate(DesktopRect rect, DesktopVector offset) { | |
59 rect.Translate(-offset.x(), -offset.y()); | |
Sergey Ulanov
2016/11/28 23:12:35
nit: This could be expressed Translate(DesktopVect
Hzj_jie
2016/11/29 01:21:39
Acknowledged.
| |
60 return rect; | |
61 } | |
62 | |
39 } // namespace | 63 } // namespace |
40 | 64 |
41 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, | 65 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, |
42 const ComPtr<IDXGIOutput1>& output, | 66 const ComPtr<IDXGIOutput1>& output, |
43 const DXGI_OUTPUT_DESC& desc) | 67 const DXGI_OUTPUT_DESC& desc) |
44 : device_(device), | 68 : device_(device), |
45 output_(output), | 69 output_(output), |
46 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { | 70 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { |
47 RTC_DCHECK(output_); | 71 RTC_DCHECK(output_); |
48 RTC_DCHECK(!desktop_rect_.is_empty()); | 72 RTC_DCHECK(!desktop_rect_.is_empty()); |
49 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0); | 73 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0); |
50 } | 74 } |
51 | 75 |
52 DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) = | 76 DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) = |
53 default; | 77 default; |
54 | 78 |
55 DxgiOutputDuplicator::~DxgiOutputDuplicator() { | 79 DxgiOutputDuplicator::~DxgiOutputDuplicator() { |
56 if (duplication_) { | 80 if (duplication_) { |
57 duplication_->ReleaseFrame(); | 81 duplication_->ReleaseFrame(); |
58 } | 82 } |
59 texture_.reset(); | 83 texture_.reset(); |
60 } | 84 } |
61 | 85 |
62 bool DxgiOutputDuplicator::Initialize() { | 86 bool DxgiOutputDuplicator::Initialize() { |
63 if (DuplicateOutput()) { | 87 if (DuplicateOutput()) { |
88 DesktopSize unrotated_size = | |
89 RotateSize(desktop_rect().size(), reverse_rotation_); | |
64 if (desc_.DesktopImageInSystemMemory) { | 90 if (desc_.DesktopImageInSystemMemory) { |
65 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get())); | 91 texture_.reset(new DxgiTextureMapping( |
92 unrotated_size, duplication_.Get())); | |
66 } else { | 93 } else { |
67 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_)); | 94 texture_.reset(new DxgiTextureStaging(unrotated_size, device_)); |
68 } | 95 } |
69 return true; | 96 return true; |
70 } else { | 97 } else { |
71 duplication_.Reset(); | 98 duplication_.Reset(); |
72 return false; | 99 return false; |
73 } | 100 } |
74 } | 101 } |
75 | 102 |
76 bool DxgiOutputDuplicator::DuplicateOutput() { | 103 bool DxgiOutputDuplicator::DuplicateOutput() { |
77 RTC_DCHECK(!duplication_); | 104 RTC_DCHECK(!duplication_); |
(...skipping 19 matching lines...) Expand all Loading... | |
97 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() || | 124 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() || |
98 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { | 125 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { |
99 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " | 126 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " |
100 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " | 127 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " |
101 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height | 128 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height |
102 << ", size returned by IDXGIOutput1 is " | 129 << ", size returned by IDXGIOutput1 is " |
103 << desktop_rect_.width() << " x " << desktop_rect_.height(); | 130 << desktop_rect_.width() << " x " << desktop_rect_.height(); |
104 return false; | 131 return false; |
105 } | 132 } |
106 | 133 |
134 rotation_ = DXGI_MODE_ROTATIONToRotation(desc_.Rotation); | |
135 reverse_rotation_ = ReverseRotation(rotation_); | |
136 unrotated_size_ = RotateSize(desktop_rect_.size(), reverse_rotation_); | |
137 | |
107 return true; | 138 return true; |
108 } | 139 } |
109 | 140 |
110 bool DxgiOutputDuplicator::ReleaseFrame() { | 141 bool DxgiOutputDuplicator::ReleaseFrame() { |
111 RTC_DCHECK(duplication_); | 142 RTC_DCHECK(duplication_); |
112 _com_error error = duplication_->ReleaseFrame(); | 143 _com_error error = duplication_->ReleaseFrame(); |
113 if (error.Error() != S_OK) { | 144 if (error.Error() != S_OK) { |
114 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " | 145 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " |
115 "error" | 146 "error" |
116 << error.ErrorMessage() << ", code " << error.Error(); | 147 << error.ErrorMessage() << ", code " << error.Error(); |
(...skipping 18 matching lines...) Expand all Loading... | |
135 memset(&frame_info, 0, sizeof(frame_info)); | 166 memset(&frame_info, 0, sizeof(frame_info)); |
136 ComPtr<IDXGIResource> resource; | 167 ComPtr<IDXGIResource> resource; |
137 _com_error error = duplication_->AcquireNextFrame( | 168 _com_error error = duplication_->AcquireNextFrame( |
138 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); | 169 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); |
139 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { | 170 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { |
140 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() | 171 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() |
141 << ", code " << error.Error(); | 172 << ", code " << error.Error(); |
142 return false; | 173 return false; |
143 } | 174 } |
144 | 175 |
145 // We need to merge updated region with the one from last frame, since current | 176 // We need to merge updated region with the one from context, but only spread |
146 // frame contains the content one frame before. Note, this is for double | 177 // updated region from current frame. So keeps a copy of updated region from |
147 // buffering implementation, as what we have in ScreenCapturerWinDirectx. If | 178 // context here. |
148 // a consumer uses single buffering, we should clear context->updated_region | |
149 // after it has been merged to updated_region. | |
150 DesktopRegion updated_region; | 179 DesktopRegion updated_region; |
151 updated_region.Swap(&context->updated_region); | 180 updated_region.Swap(&context->updated_region); |
152 if (error.Error() == S_OK && | 181 if (error.Error() == S_OK && |
153 frame_info.AccumulatedFrames > 0 && | 182 frame_info.AccumulatedFrames > 0 && |
154 resource) { | 183 resource) { |
155 DetectUpdatedRegion(frame_info, offset, &context->updated_region); | 184 DetectUpdatedRegion(frame_info, offset, &context->updated_region); |
156 if (!texture_->CopyFrom(frame_info, resource.Get(), | 185 if (!texture_->CopyFrom(frame_info, resource.Get())) { |
157 context->updated_region)) { | |
158 return false; | 186 return false; |
159 } | 187 } |
160 SpreadContextChange(context); | 188 SpreadContextChange(context); |
161 updated_region.AddRegion(context->updated_region); | 189 updated_region.AddRegion(context->updated_region); |
162 | 190 |
163 const DesktopFrame& source = texture_->AsDesktopFrame(); | 191 const DesktopFrame& source = texture_->AsDesktopFrame(); |
164 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | 192 if (rotation_ != Rotation::CLOCK_WISE_0) { |
165 it.Advance()) { | 193 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
166 target->CopyPixelsFrom( | 194 it.Advance()) { |
167 source, SourceRect(it.rect()).top_left(), it.rect()); | 195 const DesktopRect source_rect = |
196 RotateRect(ReverseTranslate(it.rect(), offset), | |
197 desktop_rect().size(), reverse_rotation_); | |
198 RotateDesktopFrame(source, | |
199 source_rect, | |
Sergey Ulanov
2016/11/28 23:12:35
"git cl format" please. It will put multiple args
Hzj_jie
2016/11/29 01:21:39
Done.
| |
200 rotation_, | |
201 offset, | |
202 target); | |
203 } | |
204 } else { | |
205 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | |
206 it.Advance()) { | |
207 target->CopyPixelsFrom( | |
208 source, ReverseTranslate(it.rect(), offset).top_left(), it.rect()); | |
209 } | |
168 } | 210 } |
169 last_frame_ = target->Share(); | 211 last_frame_ = target->Share(); |
170 last_frame_offset_ = offset; | 212 last_frame_offset_ = offset; |
171 target->mutable_updated_region()->AddRegion(updated_region); | 213 target->mutable_updated_region()->AddRegion(updated_region); |
172 return texture_->Release() && ReleaseFrame(); | 214 return texture_->Release() && ReleaseFrame(); |
173 } | 215 } |
174 | 216 |
175 if (last_frame_) { | 217 if (last_frame_) { |
176 // No change since last frame or AcquireNextFrame() timed out, we will | 218 // No change since last frame or AcquireNextFrame() timed out, we will |
177 // export last frame to the target. | 219 // export last frame to the target. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
211 DesktopRegion* updated_region) { | 253 DesktopRegion* updated_region) { |
212 RTC_DCHECK(updated_region); | 254 RTC_DCHECK(updated_region); |
213 updated_region->Clear(); | 255 updated_region->Clear(); |
214 if (frame_info.TotalMetadataBufferSize == 0) { | 256 if (frame_info.TotalMetadataBufferSize == 0) { |
215 // This should not happen, since frame_info.AccumulatedFrames > 0. | 257 // This should not happen, since frame_info.AccumulatedFrames > 0. |
216 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " | 258 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " |
217 "but TotalMetadataBufferSize == 0"; | 259 "but TotalMetadataBufferSize == 0"; |
218 return false; | 260 return false; |
219 } | 261 } |
220 | 262 |
221 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) { | 263 if (metadata_.capacity() < frame_info.TotalMetadataBufferSize) { |
222 metadata.clear(); // Avoid data copy | 264 metadata_.clear(); // Avoid data copy |
223 metadata.reserve(frame_info.TotalMetadataBufferSize); | 265 metadata_.reserve(frame_info.TotalMetadataBufferSize); |
224 } | 266 } |
225 | 267 |
226 UINT buff_size = 0; | 268 UINT buff_size = 0; |
227 DXGI_OUTDUPL_MOVE_RECT* move_rects = | 269 DXGI_OUTDUPL_MOVE_RECT* move_rects = |
228 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data()); | 270 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data()); |
229 size_t move_rects_count = 0; | 271 size_t move_rects_count = 0; |
230 _com_error error = duplication_->GetFrameMoveRects( | 272 _com_error error = duplication_->GetFrameMoveRects( |
231 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size); | 273 static_cast<UINT>(metadata_.capacity()), move_rects, &buff_size); |
232 if (error.Error() != S_OK) { | 274 if (error.Error() != S_OK) { |
233 LOG(LS_ERROR) << "Failed to get move rectangles, error " | 275 LOG(LS_ERROR) << "Failed to get move rectangles, error " |
234 << error.ErrorMessage() << ", code " << error.Error(); | 276 << error.ErrorMessage() << ", code " << error.Error(); |
235 return false; | 277 return false; |
236 } | 278 } |
237 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); | 279 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); |
238 | 280 |
239 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size); | 281 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size); |
240 size_t dirty_rects_count = 0; | 282 size_t dirty_rects_count = 0; |
241 error = duplication_->GetFrameDirtyRects( | 283 error = duplication_->GetFrameDirtyRects( |
242 static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects, | 284 static_cast<UINT>(metadata_.capacity()) - buff_size, dirty_rects, |
243 &buff_size); | 285 &buff_size); |
244 if (error.Error() != S_OK) { | 286 if (error.Error() != S_OK) { |
245 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " | 287 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " |
246 << error.ErrorMessage() << ", code " << error.Error(); | 288 << error.ErrorMessage() << ", code " << error.Error(); |
247 return false; | 289 return false; |
248 } | 290 } |
249 dirty_rects_count = buff_size / sizeof(RECT); | 291 dirty_rects_count = buff_size / sizeof(RECT); |
250 | 292 |
251 while (move_rects_count > 0) { | 293 while (move_rects_count > 0) { |
252 updated_region->AddRect(DesktopRect::MakeXYWH( | 294 updated_region->AddRect(RotateRect(DesktopRect::MakeXYWH( |
253 move_rects->SourcePoint.x, move_rects->SourcePoint.y, | 295 move_rects->SourcePoint.x, move_rects->SourcePoint.y, |
254 move_rects->DestinationRect.right - move_rects->DestinationRect.left, | 296 move_rects->DestinationRect.right - move_rects->DestinationRect.left, |
255 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); | 297 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top), |
256 updated_region->AddRect(DesktopRect::MakeLTRB( | 298 unrotated_size_, rotation_)); |
299 updated_region->AddRect(RotateRect(DesktopRect::MakeLTRB( | |
257 move_rects->DestinationRect.left, move_rects->DestinationRect.top, | 300 move_rects->DestinationRect.left, move_rects->DestinationRect.top, |
258 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); | 301 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom), |
302 unrotated_size_, rotation_)); | |
259 move_rects++; | 303 move_rects++; |
260 move_rects_count--; | 304 move_rects_count--; |
261 } | 305 } |
262 | 306 |
263 while (dirty_rects_count > 0) { | 307 while (dirty_rects_count > 0) { |
264 updated_region->AddRect( | 308 updated_region->AddRect(RotateRect( |
265 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, | 309 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, |
266 dirty_rects->right, dirty_rects->bottom)); | 310 dirty_rects->right, dirty_rects->bottom), |
311 unrotated_size_, rotation_)); | |
267 dirty_rects++; | 312 dirty_rects++; |
268 dirty_rects_count--; | 313 dirty_rects_count--; |
269 } | 314 } |
270 | 315 |
271 return true; | 316 return true; |
272 } | 317 } |
273 | 318 |
274 void DxgiOutputDuplicator::Setup(Context* context) { | 319 void DxgiOutputDuplicator::Setup(Context* context) { |
275 RTC_DCHECK(context->updated_region.is_empty()); | 320 RTC_DCHECK(context->updated_region.is_empty()); |
276 // Always copy entire monitor during the first Duplicate() function call. | 321 // Always copy entire monitor during the first Duplicate() function call. |
(...skipping 11 matching lines...) Expand all Loading... | |
288 | 333 |
289 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { | 334 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { |
290 for (Context* dest : contexts_) { | 335 for (Context* dest : contexts_) { |
291 RTC_DCHECK(dest); | 336 RTC_DCHECK(dest); |
292 if (dest != source) { | 337 if (dest != source) { |
293 dest->updated_region.AddRegion(source->updated_region); | 338 dest->updated_region.AddRegion(source->updated_region); |
294 } | 339 } |
295 } | 340 } |
296 } | 341 } |
297 | 342 |
298 DesktopRect DxgiOutputDuplicator::SourceRect(DesktopRect rect) { | |
299 // |texture_|->AsDesktopFrame() starts from (0, 0). | |
300 rect.Translate(-desktop_rect_.left(), -desktop_rect_.top()); | |
301 return rect; | |
302 } | |
303 | |
304 } // namespace webrtc | 343 } // namespace webrtc |
OLD | NEW |