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/screen_capturer_win_directx.h" | |
12 | |
13 #include <string.h> | |
14 | |
15 #include <comdef.h> | |
16 #include <wincodec.h> | |
17 #include <DXGI.h> | |
18 | |
19 #include "webrtc/base/checks.h" | |
20 #include "webrtc/base/criticalsection.h" | |
21 #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" | |
22 #include "webrtc/system_wrappers/include/logging.h" | |
23 #include "webrtc/system_wrappers/include/tick_util.h" | |
24 | |
25 namespace webrtc { | |
26 | |
27 using Microsoft::WRL::ComPtr; | |
28 using rtc::CriticalSection; | |
29 using rtc::CritScope; | |
30 | |
31 namespace { | |
32 | |
33 // In milliseconds, consistent with windows api. | |
Sergey Ulanov
2016/04/19 23:51:07
The comment should make it clear the purpose of th
Hzj_jie
2016/04/26 23:00:07
Done.
| |
34 const int kAcquireTimeoutMs = 10; | |
35 | |
36 // Wait time between two DuplicateOutput operations, DuplicateOutput may fail if | |
37 // display mode is changing. | |
38 const int kDuplicateOutputWait = 50; | |
Sergey Ulanov
2016/04/19 23:51:08
Units should be clear from the name. As I understa
Hzj_jie
2016/04/26 23:00:08
AFAICT, the DuplicateOutput should only fail when
| |
39 | |
40 // How many times we attempt to DuplicateOutput before returning an error to | |
41 // upstream components. | |
42 const int kDuplicateOutputAttempts = 10; | |
43 | |
44 rtc::GlobalLockPod g_initialize_lock; | |
45 | |
46 // A container of all the objects we need to call Windows API. Note, one | |
47 // application can only have one IDXGIOutputDuplication instance, that's the | |
48 // reason the container is singleton. | |
49 struct DxgiContainer { | |
50 CriticalSection duplication_lock; | |
51 CriticalSection acquire_lock; | |
52 bool initialize_result GUARDED_BY(g_initialize_lock) = false; | |
53 ID3D11Device* device GUARDED_BY(g_initialize_lock) = nullptr; | |
54 ID3D11DeviceContext* context GUARDED_BY(g_initialize_lock) = nullptr; | |
55 IDXGIOutput1* output1 GUARDED_BY(g_initialize_lock) = nullptr; | |
56 ComPtr<IDXGIOutputDuplication> duplication | |
57 GUARDED_BY(duplication_lock); | |
58 DesktopSize desktop_size; | |
Sergey Ulanov
2016/04/19 23:51:08
Why do we need to store desktop_size here? What if
Hzj_jie
2016/04/26 23:00:08
I did not see any comments on msdn to describe the
| |
59 std::vector<BYTE> metadata GUARDED_BY(acquire_lock); | |
Sergey Ulanov
2016/04/19 23:51:08
please use uint8_t instead of BYTE
Hzj_jie
2016/04/26 23:00:07
Done.
| |
60 }; | |
61 | |
62 DxgiContainer* g_container GUARDED_BY(g_initialize_lock); | |
63 | |
64 // A DesktopFrame which does not own the data buffer, and also does not have | |
65 // shared memory. This uses in IT2ME scenario only. | |
66 class DxgiDesktopFrame : public DesktopFrame { | |
67 public: | |
68 DxgiDesktopFrame(DesktopSize size, int stride, uint8_t* data) : | |
69 DesktopFrame(size, stride, data, nullptr) {} | |
70 | |
71 // A valid but empty frame, uses before we captured the first screenshot. | |
72 DxgiDesktopFrame() | |
73 : DesktopFrame(g_container->desktop_size, 0, nullptr, nullptr) {} | |
74 | |
75 virtual ~DxgiDesktopFrame() {} | |
76 }; | |
77 | |
78 } // namespace | |
79 | |
80 // A pair of an ID3D11Texture2D and an IDXGISurface. We need an | |
81 // ID3D11Texture2D instance to copy GPU texture to RAM, but an IDXGISurface to | |
82 // map the texture into a bitmap buffer. These two instances are always | |
83 // pointing to a same object. | |
84 // | |
85 // This class is not thread safe. | |
86 class ScreenCapturerWinDirectx::Texture { | |
87 public: | |
88 // Captures a frame represented by frame_info and resource. Returns false if | |
89 // anything wrong. | |
90 // Before the first Capture is called, this instance is still workable, but | |
91 // the data in underlying buffer (SharedMemory) is undefined. | |
92 bool Capture(const DXGI_OUTDUPL_FRAME_INFO& frame_info, | |
Sergey Ulanov
2016/04/19 23:51:07
This method copies the image from GPU to in-memory
Hzj_jie
2016/04/26 23:00:08
Done.
| |
93 IDXGIResource* resource) { | |
94 if (!resource || frame_info.AccumulatedFrames == 0) { | |
95 // Nothing updated, but current data is still valid. | |
96 return false; | |
97 } | |
98 | |
99 ComPtr<ID3D11Texture2D> texture; | |
100 _com_error error = resource->QueryInterface( | |
101 __uuidof(ID3D11Texture2D), | |
102 reinterpret_cast<void**>(texture.GetAddressOf())); | |
103 if (error.Error() != S_OK || !texture) { | |
104 LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D, " | |
105 "error " | |
106 << error.ErrorMessage() << ", code " << error.Error(); | |
107 return false; | |
108 } | |
109 | |
110 // AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to | |
111 // make a copy. | |
112 if (!CreateTexture(texture.Get())) { | |
Sergey Ulanov
2016/04/19 23:51:08
It looks strange that you have CreateTexture() fun
Hzj_jie
2016/04/26 23:00:07
Not only screen size change, there are bunch of pa
| |
113 return false; | |
114 } | |
115 | |
116 g_container->context->CopyResource( | |
Sergey Ulanov
2016/04/19 23:51:08
Can we use CopySubresourceRegion() here? (see http
Hzj_jie
2016/04/26 23:00:07
Done.
| |
117 static_cast<ID3D11Resource*>(stage_.Get()), | |
118 static_cast<ID3D11Resource*>(texture.Get())); | |
119 | |
120 rect_ = {0}; | |
121 error = _com_error(surface_->Map(&rect_, DXGI_MAP_READ)); | |
122 if (error.Error() != S_OK) { | |
123 LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap, error " | |
124 << error.ErrorMessage() << ", code " << error.Error(); | |
125 return false; | |
126 } | |
127 | |
128 // surface_->Unmap() will be called next time we capture an image to avoid | |
129 // memory copy without shared_memory. | |
130 return true; | |
131 } | |
132 | |
133 // Create a DesktopFrame from current instance. | |
134 std::unique_ptr<DesktopFrame> CreateDesktopFrame() const { | |
135 return std::unique_ptr<DesktopFrame>( | |
136 new DxgiDesktopFrame(desktop_size(), pitch(), bits())); | |
137 } | |
138 | |
139 uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); } | |
140 int pitch() const { return static_cast<int>(rect_.Pitch); } | |
141 const DesktopSize& desktop_size() const { return desktop_size_; } | |
Sergey Ulanov
2016/04/19 23:51:08
Please call this size(), instead of desktop_size()
Hzj_jie
2016/04/26 23:00:07
Done.
| |
142 | |
143 private: | |
144 // Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false | |
145 // if it fails to execute windows api. | |
146 bool CreateTexture(ID3D11Texture2D* texture) { | |
147 RTC_DCHECK(texture); | |
148 D3D11_TEXTURE2D_DESC desc = {0}; | |
149 texture->GetDesc(&desc); | |
150 desc.BindFlags = 0; | |
151 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; | |
152 desc.MiscFlags = 0; | |
153 desc.Usage = D3D11_USAGE_STAGING; | |
154 if (stage_) { | |
155 // Make sure stage_ and surface_ are always pointing to a same object. | |
156 // We need an ID3D11Texture2D instance for | |
157 // ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for | |
158 // IDXGISurface::Map. | |
159 { | |
160 ComPtr<IUnknown> left; | |
161 ComPtr<IUnknown> right; | |
162 bool left_result = SUCCEEDED(stage_.As(&left)); | |
163 bool right_result = SUCCEEDED(surface_.As(&right)); | |
164 RTC_DCHECK(left_result); | |
165 RTC_DCHECK(right_result); | |
166 RTC_DCHECK(left.Get() == right.Get()); | |
167 } | |
168 | |
169 // This buffer should be used already. | |
170 _com_error error = _com_error(surface_->Unmap()); | |
171 if (error.Error() == S_OK) { | |
172 D3D11_TEXTURE2D_DESC orgi_desc; | |
Sergey Ulanov
2016/04/19 23:51:08
orgi?
Also style guide discourages names like orig
Hzj_jie
2016/04/26 23:00:07
Sorry, typo. Updated.
| |
173 stage_->GetDesc(&orgi_desc); | |
174 if (memcmp(&desc, &orgi_desc, sizeof(D3D11_TEXTURE2D_DESC)) == 0) { | |
175 return true; | |
176 } | |
177 } else { | |
178 // Let's recreate stage_ and surface_ later. | |
179 LOG(LS_ERROR) << "Failed to unmap surface, error " | |
180 << error.ErrorMessage() << ", code " << error.Error(); | |
181 } | |
182 | |
183 stage_.Reset(); | |
184 surface_.Reset(); | |
185 } else { | |
186 RTC_DCHECK(!surface_); | |
187 } | |
188 | |
189 _com_error error = _com_error(g_container->device->CreateTexture2D( | |
190 &desc, nullptr, stage_.GetAddressOf())); | |
191 if (error.Error() != S_OK || !stage_) { | |
192 LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage, " | |
193 "error " | |
194 << error.ErrorMessage() << ", code " << error.Error(); | |
195 return false; | |
196 } | |
197 | |
198 error = _com_error(stage_.As(&surface_)); | |
199 if (error.Error() != S_OK || !surface_) { | |
200 LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface, " | |
201 "error " | |
202 << error.ErrorMessage() << ", code " << error.Error(); | |
203 return false; | |
204 } | |
205 | |
206 desktop_size_.set(static_cast<int>(desc.Width), | |
207 static_cast<int>(desc.Height)); | |
Sergey Ulanov
2016/04/19 23:51:08
I don't think you need these casts
Hzj_jie
2016/04/26 23:00:07
Done.
| |
208 return true; | |
209 } | |
210 | |
211 Microsoft::WRL::ComPtr<ID3D11Texture2D> stage_; | |
212 Microsoft::WRL::ComPtr<IDXGISurface> surface_; | |
213 DXGI_MAPPED_RECT rect_; | |
214 DesktopSize desktop_size_; | |
215 }; | |
216 | |
217 bool ScreenCapturerWinDirectx::Initialize() { | |
218 if (!g_container) { | |
219 rtc::GlobalLockScope lock(&g_initialize_lock); | |
220 if (!g_container) { | |
221 g_container = new DxgiContainer(); | |
222 g_container->initialize_result = DoInitialize(); | |
223 if (g_container->initialize_result) { | |
224 return true; | |
225 } | |
226 | |
227 // Clean up if DirectX cannot work on the system. | |
228 if (g_container->duplication) { | |
229 g_container->duplication.Reset(); | |
230 } | |
231 | |
232 if (g_container->output1) { | |
233 g_container->output1->Release(); | |
234 g_container->output1 = nullptr; | |
235 } | |
236 | |
237 if (g_container->context) { | |
238 g_container->context->Release(); | |
239 g_container->context = nullptr; | |
240 } | |
241 | |
242 if (g_container->device) { | |
243 g_container->device->Release(); | |
244 g_container->device = nullptr; | |
245 } | |
246 | |
247 return false; | |
248 } | |
249 } | |
250 | |
251 return g_container->initialize_result; | |
252 } | |
253 | |
254 bool ScreenCapturerWinDirectx::DoInitialize() { | |
255 D3D_FEATURE_LEVEL feature_level; | |
256 _com_error error = D3D11CreateDevice( | |
257 nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, | |
258 D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED, | |
259 nullptr, 0, D3D11_SDK_VERSION, &g_container->device, &feature_level, | |
260 &g_container->context); | |
261 if (error.Error() != S_OK || !g_container->device || !g_container->context) { | |
262 LOG(LS_WARNING) << "D3D11CreateDeivce returns error " | |
263 << error.ErrorMessage() << " with code " << error.Error(); | |
264 return false; | |
265 } | |
266 | |
267 if (feature_level < D3D_FEATURE_LEVEL_11_0) { | |
268 LOG(LS_WARNING) << "D3D11CreateDevice returns an instance without DirectX " | |
269 "11 support, level " | |
270 << feature_level; | |
271 return false; | |
272 } | |
273 | |
274 ComPtr<IDXGIDevice> device; | |
275 error = _com_error(g_container->device->QueryInterface( | |
276 __uuidof(IDXGIDevice), reinterpret_cast<void**>(device.GetAddressOf()))); | |
277 if (error.Error() != S_OK || !device) { | |
278 LOG(LS_WARNING) << "ID3D11Device is not an implementation of IDXGIDevice, " | |
279 "this usually means the system does not support DirectX " | |
280 "11"; | |
281 return false; | |
282 } | |
283 | |
284 ComPtr<IDXGIAdapter> adapter; | |
285 error = _com_error(device->GetAdapter(adapter.GetAddressOf())); | |
286 if (error.Error() != S_OK || !adapter) { | |
287 LOG(LS_WARNING) << "Failed to get an IDXGIAdapter implementation from " | |
288 "IDXGIDevice."; | |
289 return false; | |
290 } | |
291 | |
292 ComPtr<IDXGIOutput> output; | |
293 for (int i = 0;; i++) { | |
294 error = _com_error(adapter->EnumOutputs(i, output.GetAddressOf())); | |
295 if (error.Error() == DXGI_ERROR_NOT_FOUND) { | |
296 LOG(LS_WARNING) << "No output detected."; | |
297 return false; | |
298 } | |
299 if (error.Error() == S_OK && output) { | |
300 DXGI_OUTPUT_DESC desc; | |
301 error = _com_error(output->GetDesc(&desc)); | |
302 if (error.Error() == S_OK) { | |
303 if (desc.AttachedToDesktop) { | |
304 // Current output instance is the device attached to desktop. | |
305 break; | |
306 } | |
307 } else { | |
308 LOG(LS_WARNING) << "Failed to get output description of device " << i | |
309 << ", ignore."; | |
310 } | |
311 } | |
312 } | |
313 | |
314 RTC_DCHECK(output); | |
315 error = _com_error(output.CopyTo( | |
316 __uuidof(IDXGIOutput1), reinterpret_cast<void**>(&g_container->output1))); | |
317 if (error.Error() != S_OK || !g_container->output1) { | |
318 LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, this " | |
319 "usually means the system does not support DirectX 11"; | |
320 return false; | |
321 } | |
322 | |
323 return DuplicateOutput(); | |
324 } | |
325 | |
326 bool ScreenCapturerWinDirectx::DuplicateOutput() { | |
327 // We are updating the instance. | |
328 CritScope lock(&g_container->duplication_lock); | |
329 // Make sure nobody is using current instance. | |
330 CritScope lock2(&g_container->acquire_lock); | |
331 if (g_container->duplication) { | |
332 g_container->duplication->ReleaseFrame(); | |
333 g_container->duplication.Reset(); | |
334 } | |
335 | |
336 for (int i = 0; i < kDuplicateOutputAttempts; i++) { | |
337 _com_error error = g_container->output1->DuplicateOutput( | |
338 static_cast<IUnknown*>(g_container->device), | |
339 g_container->duplication.GetAddressOf()); | |
340 if (error.Error() == S_OK && g_container->duplication) { | |
341 DXGI_OUTDUPL_DESC desc; | |
342 g_container->duplication->GetDesc(&desc); | |
343 if (desc.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) { | |
344 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) " | |
345 "format, which is required by downstream components, " | |
346 "format is " | |
347 << desc.ModeDesc.Format; | |
348 return false; | |
349 } | |
350 | |
351 g_container->desktop_size.set(desc.ModeDesc.Width, desc.ModeDesc.Height); | |
352 return true; | |
353 } else { | |
354 // Make sure we have correct signal and duplicate the output next time. | |
355 g_container->duplication.Reset(); | |
356 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error " | |
357 << error.ErrorMessage() << ", with code " | |
358 << error.Error(); | |
359 Sleep(kDuplicateOutputWait); | |
360 } | |
361 } | |
362 | |
363 return false; | |
364 } | |
365 | |
366 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx( | |
367 const DesktopCaptureOptions& options) | |
368 : callback_(nullptr), set_thread_execution_state_failed_(false) { | |
369 RTC_DCHECK(g_container && g_container->initialize_result); | |
370 | |
371 // Texture instance won't change forever. | |
372 while (!surfaces_.current()) { | |
373 surfaces_.ReplaceCurrent(new Texture()); | |
374 surfaces_.MoveToNext(); | |
375 } | |
376 | |
377 // Make sure we always have a valid DesktopFrame instance to emit. | |
378 // Not similar as other methods, this class may not always be able to capture | |
Sergey Ulanov
2016/04/19 23:51:07
s/Not similar as other methods/Unlike other captur
Hzj_jie
2016/04/26 23:00:07
Done.
| |
379 // desktop frames, the desktop may not change. So we create a set of default | |
380 // frames to emit, if first several attempts do not capture anything. | |
381 while (!frames_.current_frame()) { | |
382 frames_.ReplaceCurrentFrame(new DxgiDesktopFrame()); | |
Sergey Ulanov
2016/04/19 23:51:07
Why do we need this? We can keep the queue empty u
Hzj_jie
2016/04/26 23:00:07
I doubt this decision, several DesktopFrame::Callb
| |
383 frames_.MoveToNextFrame(); | |
384 } | |
385 } | |
386 | |
387 ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() {} | |
388 | |
389 void ScreenCapturerWinDirectx::Start(Callback* callback) { | |
390 RTC_DCHECK(!callback_); | |
391 RTC_DCHECK(callback); | |
392 | |
393 callback_ = callback; | |
394 } | |
395 | |
396 void ScreenCapturerWinDirectx::SetSharedMemoryFactory( | |
397 rtc::scoped_ptr<SharedMemoryFactory> shared_memory_factory) { | |
398 shared_memory_factory_ = | |
399 rtc::ScopedToUnique(std::move(shared_memory_factory)); | |
400 } | |
401 | |
402 bool ScreenCapturerWinDirectx::DetectUpdatedRegion( | |
403 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | |
404 DesktopFrame* frame) { | |
405 RTC_DCHECK(g_container->duplication); | |
406 RTC_DCHECK(frame); | |
407 DesktopRegion& updated_region = *frame->mutable_updated_region(); | |
408 updated_region.Clear(); | |
409 if (frame_info.TotalMetadataBufferSize == 0) { | |
410 // This should not happen, since frame_info.AccumulatedFrames > 0. | |
411 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " | |
412 "but TotalMetadataBufferSize == 0"; | |
413 return false; | |
414 } | |
415 | |
416 if (g_container->metadata.size() < frame_info.TotalMetadataBufferSize) { | |
417 g_container->metadata.clear(); // Avoid data copy | |
418 g_container->metadata.reserve(frame_info.TotalMetadataBufferSize); | |
Sergey Ulanov
2016/04/19 23:51:07
reserve() doesn't actually change the size of the
Hzj_jie
2016/04/26 23:00:07
resize will fill the following data with a default
| |
419 } | |
420 | |
421 UINT buff_size = 0; | |
422 DXGI_OUTDUPL_MOVE_RECT* move_rects = nullptr; | |
423 size_t move_rects_count = 0; | |
424 RECT* dirty_rects = nullptr; | |
425 size_t dirty_rects_count = 0; | |
426 for (int i = 0; i < 2; i++) { | |
Sergey Ulanov
2016/04/19 23:51:08
I commented on this before: I don't think we want
Hzj_jie
2016/04/26 23:00:07
Done.
| |
427 _com_error error = S_OK; | |
428 if (i == 0) { | |
429 move_rects = reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>( | |
430 g_container->metadata.data()); | |
431 error = _com_error(g_container->duplication->GetFrameMoveRects( | |
432 static_cast<UINT>(g_container->metadata.capacity()), | |
433 move_rects, &buff_size)); | |
434 } else { | |
435 dirty_rects = reinterpret_cast<RECT*>( | |
436 g_container->metadata.data() + buff_size); | |
437 error = _com_error(g_container->duplication->GetFrameDirtyRects( | |
438 static_cast<UINT>(g_container->metadata.capacity()) - buff_size, | |
439 dirty_rects, &buff_size)); | |
440 } | |
441 if (error.Error() != S_OK) { | |
442 if (error.Error() == DXGI_ERROR_ACCESS_LOST) { | |
443 DuplicateOutput(); | |
444 } else { | |
445 LOG(LS_ERROR) << "Failed to get " << (i == 0 ? "move" : "dirty") | |
446 << " rectangles, error " << error.ErrorMessage() | |
447 << ", code " << error.Error(); | |
448 } | |
449 // Send entire desktop as we cannot get dirty or move rectangles. | |
450 return false; | |
451 } | |
452 if (i == 0) { | |
453 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); | |
454 } else { | |
455 dirty_rects_count = buff_size / sizeof(RECT); | |
456 } | |
457 } | |
458 | |
459 while (move_rects_count > 0) { | |
460 updated_region.AddRect(DesktopRect::MakeXYWH( | |
461 move_rects->SourcePoint.x, move_rects->SourcePoint.y, | |
462 move_rects->DestinationRect.right - move_rects->DestinationRect.left, | |
463 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); | |
464 updated_region.AddRect(DesktopRect::MakeLTRB( | |
465 move_rects->DestinationRect.left, move_rects->DestinationRect.top, | |
466 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); | |
467 move_rects++; | |
468 move_rects_count--; | |
469 } | |
470 | |
471 while (dirty_rects_count > 0) { | |
472 updated_region.AddRect( | |
473 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, | |
474 dirty_rects->right, dirty_rects->bottom)); | |
475 dirty_rects++; | |
476 dirty_rects_count--; | |
477 } | |
478 | |
479 return true; | |
480 } | |
481 | |
482 bool ScreenCapturerWinDirectx::ProcessFrame( | |
483 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | |
484 IDXGIResource* resource) { | |
485 RTC_DCHECK(resource); | |
486 RTC_DCHECK(frame_info.AccumulatedFrames > 0); | |
487 // We have something to update, so move to next surface. | |
488 surfaces_.MoveToNext(); | |
489 RTC_DCHECK(surfaces_.current()); | |
490 if (!surfaces_.current()->Capture(frame_info, resource)) { | |
491 return false; | |
492 } | |
493 | |
494 frames_.MoveToNextFrame(); | |
495 if (shared_memory_factory_) { | |
496 // When using shared_memory_factory_, try to avoid reallocate memory. | |
Sergey Ulanov
2016/04/19 23:51:07
Suggest rewording:
// When using shared memory |
Hzj_jie
2016/04/26 23:00:08
Done.
| |
497 SharedMemoryDesktopFrame* frame = | |
498 reinterpret_cast<SharedMemoryDesktopFrame*>(frames_.current_frame()); | |
499 if (!frame || !frame->size().equals(surfaces_.current()->desktop_size()) || | |
500 frame->stride() != surfaces_.current()->pitch()) { | |
501 // Current frame does not have a same size as last captured surface. | |
502 // Note, Windows does not guarantee to return exactly the same buffer size | |
503 // as the width * height * 4. The pitch may be larger than width * 4. | |
504 std::unique_ptr<DesktopFrame> new_frame = | |
505 SharedMemoryDesktopFrame::Create(surfaces_.current()->desktop_size(), | |
506 surfaces_.current()->pitch(), shared_memory_factory_.get()); | |
Sergey Ulanov
2016/04/19 23:51:07
why does the SharedMemoryDesktopFrame need to have
Hzj_jie
2016/04/26 23:00:07
Done.
| |
507 if (!new_frame) { | |
508 LOG(LS_ERROR) << "Failed to allocate a new SharedMemoryDesktopFrame"; | |
509 return false; | |
510 } | |
511 frames_.ReplaceCurrentFrame(new_frame.release()); | |
512 } | |
513 } else { | |
514 frames_.ReplaceCurrentFrame( | |
515 surfaces_.current()->CreateDesktopFrame().release()); | |
516 } | |
517 | |
518 if (!DetectUpdatedRegion(frame_info, frames_.current_frame())) { | |
519 frames_.current_frame()->mutable_updated_region()->Clear(); | |
520 frames_.current_frame()->mutable_updated_region()->AddRect( | |
Sergey Ulanov
2016/04/19 23:51:08
When using SharedDesktopFrame there is no reason t
Hzj_jie
2016/04/26 23:00:08
Done.
| |
521 DesktopRect::MakeSize(surfaces_.current()->desktop_size())); | |
522 } | |
523 | |
524 if (shared_memory_factory_) { | |
525 // There is a shared memory provided, we need to copy data to it. | |
526 std::unique_ptr<DesktopFrame> frame( | |
527 surfaces_.current()->CreateDesktopFrame()); | |
528 for (DesktopRegion::Iterator it(frames_.current_frame()->updated_region()); | |
529 !it.IsAtEnd(); it.Advance()) { | |
530 // VideoEncoderVpx expects to use 8 pixels (for v9) or 3 pixels (for v8) | |
531 // more area to encode final video. So we need to copy a little bit more | |
532 // than the real changed area. | |
533 // In AlignRect function, 1 more pixel may be applied to right and bottom, | |
534 // so we add 9 pixels here. | |
535 DesktopRect rect = it.rect(); | |
536 rect.Expand(9); | |
537 rect.IntersectWith(DesktopRect::MakeSize(frame->size())); | |
538 frames_.current_frame()->CopyPixelsFrom(*frame, rect.top_left(), rect); | |
539 } | |
540 } | |
541 | |
542 return true; | |
543 } | |
544 | |
545 void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) { | |
546 RTC_DCHECK(g_container->duplication); | |
547 RTC_DCHECK(callback_); | |
548 | |
549 if (!g_container->duplication && !DuplicateOutput()) { | |
550 // Receive a capture request when application is shutting down, or between | |
551 // mode change. | |
552 callback_->OnCaptureCompleted(nullptr); | |
553 return; | |
554 } | |
555 | |
556 TickTime capture_start_time = TickTime::Now(); | |
557 | |
558 if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { | |
559 if (!set_thread_execution_state_failed_) { | |
560 set_thread_execution_state_failed_ = true; | |
561 LOG(LS_WARNING) << "Failed to make system & display power assertion: " | |
562 << GetLastError(); | |
563 } | |
564 } | |
565 | |
566 DXGI_OUTDUPL_FRAME_INFO frame_info; | |
567 memset(&frame_info, 0, sizeof(DXGI_OUTDUPL_FRAME_INFO)); | |
568 ComPtr<IDXGIResource> resource; | |
569 CritScope lock(&g_container->acquire_lock); | |
570 _com_error error = g_container->duplication->AcquireNextFrame( | |
571 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); | |
572 if (error.Error() == DXGI_ERROR_ACCESS_LOST) { | |
573 LOG(LS_ERROR) << "Access lost " << error.ErrorMessage(); | |
574 if (DuplicateOutput()) { | |
575 EmitCurrentFrame(); | |
576 } else { | |
577 callback_->OnCaptureCompleted(nullptr); | |
578 } | |
579 return; | |
580 } | |
581 | |
582 if (error.Error() == DXGI_ERROR_WAIT_TIMEOUT) { | |
583 // Nothing changed. | |
584 EmitCurrentFrame(); | |
Sergey Ulanov
2016/04/19 23:51:07
This will emit current frame with the old updated_
Hzj_jie
2016/04/26 23:00:08
Done.
| |
585 return; | |
586 } | |
587 | |
588 if (error.Error() != S_OK) { | |
589 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() | |
590 << ", code " << error.Error(); | |
591 callback_->OnCaptureCompleted(nullptr); | |
592 return; | |
593 } | |
594 | |
595 if (frame_info.AccumulatedFrames == 0) { | |
596 EmitCurrentFrame(); | |
Sergey Ulanov
2016/04/19 23:51:07
Same as above. We don't want to emit the same fram
Hzj_jie
2016/04/26 23:00:08
Done.
| |
597 g_container->duplication->ReleaseFrame(); | |
Sergey Ulanov
2016/04/19 23:51:07
Move this before EmitCurrentFrame(). OnCaptureComp
Hzj_jie
2016/04/26 23:00:08
Done.
| |
598 return; | |
599 } | |
600 | |
601 // Everything looks good so far, build next frame. | |
602 bool result = ProcessFrame(frame_info, resource.Get()); | |
603 // DetectUpdatedRegion may release last g_container->duplication. But | |
604 // DuplicateOutput function will always release last frame, so there is no | |
605 // potential leak. | |
606 if (g_container->duplication) { | |
607 g_container->duplication->ReleaseFrame(); | |
608 } | |
609 if (result) { | |
610 frames_.current_frame()->set_capture_time_ms( | |
611 (TickTime::Now() - capture_start_time).Milliseconds()); | |
612 EmitCurrentFrame(); | |
613 } else { | |
614 callback_->OnCaptureCompleted(nullptr); | |
615 } | |
616 } | |
617 | |
618 bool ScreenCapturerWinDirectx::GetScreenList(ScreenList* screens) { | |
619 return true; | |
620 } | |
621 | |
622 bool ScreenCapturerWinDirectx::SelectScreen(ScreenId id) { | |
623 return id == kFullDesktopScreenId; | |
Sergey Ulanov
2016/04/19 23:51:08
Add a comment that only full desktop capture is su
Hzj_jie
2016/04/26 23:00:07
Done.
| |
624 } | |
625 | |
626 void ScreenCapturerWinDirectx::EmitCurrentFrame() { | |
627 callback_->OnCaptureCompleted(frames_.current_frame()->Share()); | |
628 } | |
629 | |
630 } // namespace webrtc | |
OLD | NEW |