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 DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) { |
| 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()); |
| 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(), ReverseRotation(rotation_)); |
64 if (desc_.DesktopImageInSystemMemory) { | 90 if (desc_.DesktopImageInSystemMemory) { |
65 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get())); | 91 texture_.reset( |
| 92 new DxgiTextureMapping(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_ = DxgiRotationToRotation(desc_.Rotation); |
| 135 unrotated_size_ = |
| 136 RotateSize(desktop_rect_.size(), ReverseRotation(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(), ReverseRotation(rotation_)); |
| 198 RotateDesktopFrame(source, source_rect, rotation_, offset, target); |
| 199 } |
| 200 } else { |
| 201 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 202 it.Advance()) { |
| 203 target->CopyPixelsFrom( |
| 204 source, ReverseTranslate(it.rect(), offset).top_left(), it.rect()); |
| 205 } |
168 } | 206 } |
169 last_frame_ = target->Share(); | 207 last_frame_ = target->Share(); |
170 last_frame_offset_ = offset; | 208 last_frame_offset_ = offset; |
171 target->mutable_updated_region()->AddRegion(updated_region); | 209 target->mutable_updated_region()->AddRegion(updated_region); |
172 return texture_->Release() && ReleaseFrame(); | 210 return texture_->Release() && ReleaseFrame(); |
173 } | 211 } |
174 | 212 |
175 if (last_frame_) { | 213 if (last_frame_) { |
176 // No change since last frame or AcquireNextFrame() timed out, we will | 214 // No change since last frame or AcquireNextFrame() timed out, we will |
177 // export last frame to the target. | 215 // export last frame to the target. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 DesktopRegion* updated_region) { | 249 DesktopRegion* updated_region) { |
212 RTC_DCHECK(updated_region); | 250 RTC_DCHECK(updated_region); |
213 updated_region->Clear(); | 251 updated_region->Clear(); |
214 if (frame_info.TotalMetadataBufferSize == 0) { | 252 if (frame_info.TotalMetadataBufferSize == 0) { |
215 // This should not happen, since frame_info.AccumulatedFrames > 0. | 253 // This should not happen, since frame_info.AccumulatedFrames > 0. |
216 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " | 254 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " |
217 "but TotalMetadataBufferSize == 0"; | 255 "but TotalMetadataBufferSize == 0"; |
218 return false; | 256 return false; |
219 } | 257 } |
220 | 258 |
221 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) { | 259 if (metadata_.capacity() < frame_info.TotalMetadataBufferSize) { |
222 metadata.clear(); // Avoid data copy | 260 metadata_.clear(); // Avoid data copy |
223 metadata.reserve(frame_info.TotalMetadataBufferSize); | 261 metadata_.reserve(frame_info.TotalMetadataBufferSize); |
224 } | 262 } |
225 | 263 |
226 UINT buff_size = 0; | 264 UINT buff_size = 0; |
227 DXGI_OUTDUPL_MOVE_RECT* move_rects = | 265 DXGI_OUTDUPL_MOVE_RECT* move_rects = |
228 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data()); | 266 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data()); |
229 size_t move_rects_count = 0; | 267 size_t move_rects_count = 0; |
230 _com_error error = duplication_->GetFrameMoveRects( | 268 _com_error error = duplication_->GetFrameMoveRects( |
231 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size); | 269 static_cast<UINT>(metadata_.capacity()), move_rects, &buff_size); |
232 if (error.Error() != S_OK) { | 270 if (error.Error() != S_OK) { |
233 LOG(LS_ERROR) << "Failed to get move rectangles, error " | 271 LOG(LS_ERROR) << "Failed to get move rectangles, error " |
234 << error.ErrorMessage() << ", code " << error.Error(); | 272 << error.ErrorMessage() << ", code " << error.Error(); |
235 return false; | 273 return false; |
236 } | 274 } |
237 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); | 275 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); |
238 | 276 |
239 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size); | 277 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size); |
240 size_t dirty_rects_count = 0; | 278 size_t dirty_rects_count = 0; |
241 error = duplication_->GetFrameDirtyRects( | 279 error = duplication_->GetFrameDirtyRects( |
242 static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects, | 280 static_cast<UINT>(metadata_.capacity()) - buff_size, dirty_rects, |
243 &buff_size); | 281 &buff_size); |
244 if (error.Error() != S_OK) { | 282 if (error.Error() != S_OK) { |
245 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " | 283 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " |
246 << error.ErrorMessage() << ", code " << error.Error(); | 284 << error.ErrorMessage() << ", code " << error.Error(); |
247 return false; | 285 return false; |
248 } | 286 } |
249 dirty_rects_count = buff_size / sizeof(RECT); | 287 dirty_rects_count = buff_size / sizeof(RECT); |
250 | 288 |
251 while (move_rects_count > 0) { | 289 while (move_rects_count > 0) { |
252 updated_region->AddRect(DesktopRect::MakeXYWH( | 290 updated_region->AddRect( |
253 move_rects->SourcePoint.x, move_rects->SourcePoint.y, | 291 RotateRect(DesktopRect::MakeXYWH(move_rects->SourcePoint.x, |
254 move_rects->DestinationRect.right - move_rects->DestinationRect.left, | 292 move_rects->SourcePoint.y, |
255 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); | 293 move_rects->DestinationRect.right - |
256 updated_region->AddRect(DesktopRect::MakeLTRB( | 294 move_rects->DestinationRect.left, |
257 move_rects->DestinationRect.left, move_rects->DestinationRect.top, | 295 move_rects->DestinationRect.bottom - |
258 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); | 296 move_rects->DestinationRect.top), |
| 297 unrotated_size_, rotation_)); |
| 298 updated_region->AddRect( |
| 299 RotateRect(DesktopRect::MakeLTRB(move_rects->DestinationRect.left, |
| 300 move_rects->DestinationRect.top, |
| 301 move_rects->DestinationRect.right, |
| 302 move_rects->DestinationRect.bottom), |
| 303 unrotated_size_, rotation_)); |
259 move_rects++; | 304 move_rects++; |
260 move_rects_count--; | 305 move_rects_count--; |
261 } | 306 } |
262 | 307 |
263 while (dirty_rects_count > 0) { | 308 while (dirty_rects_count > 0) { |
264 updated_region->AddRect( | 309 updated_region->AddRect(RotateRect( |
265 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, | 310 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, |
266 dirty_rects->right, dirty_rects->bottom)); | 311 dirty_rects->right, dirty_rects->bottom), |
| 312 unrotated_size_, rotation_)); |
267 dirty_rects++; | 313 dirty_rects++; |
268 dirty_rects_count--; | 314 dirty_rects_count--; |
269 } | 315 } |
270 | 316 |
271 return true; | 317 return true; |
272 } | 318 } |
273 | 319 |
274 void DxgiOutputDuplicator::Setup(Context* context) { | 320 void DxgiOutputDuplicator::Setup(Context* context) { |
275 RTC_DCHECK(context->updated_region.is_empty()); | 321 RTC_DCHECK(context->updated_region.is_empty()); |
276 // Always copy entire monitor during the first Duplicate() function call. | 322 // Always copy entire monitor during the first Duplicate() function call. |
(...skipping 11 matching lines...) Expand all Loading... |
288 | 334 |
289 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { | 335 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { |
290 for (Context* dest : contexts_) { | 336 for (Context* dest : contexts_) { |
291 RTC_DCHECK(dest); | 337 RTC_DCHECK(dest); |
292 if (dest != source) { | 338 if (dest != source) { |
293 dest->updated_region.AddRegion(source->updated_region); | 339 dest->updated_region.AddRegion(source->updated_region); |
294 } | 340 } |
295 } | 341 } |
296 } | 342 } |
297 | 343 |
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 | 344 } // namespace webrtc |
OLD | NEW |