OLD | NEW |
---|---|
(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 | |
OLD | NEW |