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

Side by Side Diff: webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc

Issue 1845113002: DirectX based screen capturer logic (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Resolve a lint error Created 4 years, 7 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/screen_capturer_win_directx.h"
12
13 #include <string.h>
14
15 #include <comdef.h>
16 #include <wincodec.h>
17 #include <wingdi.h>
18 #include <DXGI.h>
19 #if (WINVER >= 0x0602) // Windows 8.1 or upper
Sergey Ulanov 2016/05/19 18:12:09 I think we should have this check in runtime, not
Hzj_jie 2016/05/19 21:20:28 Removed.
20 #define USE_GET_DPI_FOR_MONITOR
21 #endif
22 #ifdef USE_GET_DPI_FOR_MONITOR
23 #include <ShellScalingAPI.h>
24 #endif
25
26 #include "webrtc/base/checks.h"
27 #include "webrtc/base/criticalsection.h"
28 #include "webrtc/base/scoped_ref_ptr.h"
29 #include "webrtc/base/timeutils.h"
30 #include "webrtc/modules/desktop_capture/desktop_frame.h"
31 #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
32 #include "webrtc/system_wrappers/include/atomic32.h"
33 #include "webrtc/system_wrappers/include/logging.h"
34
35 namespace webrtc {
36
37 using Microsoft::WRL::ComPtr;
38
39 namespace {
40
41 // Timeout for AcquireNextFrame() call.
42 const int kAcquireTimeoutMs = 10;
43
44 // Wait time between two DuplicateOutput operations, DuplicateOutput may fail if
45 // display mode is changing.
46 const int kDuplicateOutputWaitMs = 50;
47
48 // How many times we attempt to DuplicateOutput before returning an error to
49 // upstream components.
50 const int kDuplicateOutputAttempts = 10;
51
52 rtc::GlobalLockPod g_initialize_lock;
53
54 // A container of all the objects we need to call Windows API. Note, one
55 // application can only have one IDXGIOutputDuplication instance, that's the
56 // reason the container is singleton.
57 struct DxgiContainer {
58 rtc::CriticalSection duplication_lock;
59 rtc::CriticalSection acquire_lock;
60 bool initialize_result GUARDED_BY(g_initialize_lock) = false;
61 ID3D11Device* device GUARDED_BY(g_initialize_lock) = nullptr;
62 ID3D11DeviceContext* context GUARDED_BY(g_initialize_lock) = nullptr;
63 IDXGIOutput1* output1 GUARDED_BY(g_initialize_lock) = nullptr;
64 ComPtr<IDXGIOutputDuplication> duplication
65 GUARDED_BY(duplication_lock);
66 DXGI_OUTDUPL_DESC duplication_desc;
67 std::vector<uint8_t> metadata GUARDED_BY(acquire_lock);
68 DesktopVector dpi;
69 // A flag to indicate whether the dpi is coming from GetDpiForMinotor, if not
70 // we should update it each time in DuplicateOutput.
71 bool fixed_dpi;
72 };
73
74 DxgiContainer* g_container GUARDED_BY(g_initialize_lock);
75
76 } // namespace
77
78 // A pair of an ID3D11Texture2D and an IDXGISurface. We need an
79 // ID3D11Texture2D instance to copy GPU texture to RAM, but an IDXGISurface to
80 // map the texture into a bitmap buffer. These two instances are always
81 // pointing to a same object.
82 // This class also has two DesktopRegions, one is the updated region from
83 // returned from Windows API, the other is the region intersects with the
84 // updated region of last frame.
85 //
86 // This class is not thread safe.
87 class ScreenCapturerWinDirectx::Texture {
88 public:
89 // Copy a frame represented by frame_info and resource. Returns false if
90 // anything wrong.
91 bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
92 IDXGIResource* resource,
93 const DesktopRegion& last_updated_region) {
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 (!InitializeStage(texture.Get())) {
113 return false;
114 }
115
116 updated_region_.Clear();
117 if (!DetectUpdatedRegion(frame_info, &updated_region_)) {
118 updated_region_.SetRect(DesktopRect::MakeSize(size()));
119 }
120 // We need to copy changed area in both this frame and last frame, since
121 // currently this frame stores the bitmap of the one before last frame.
122 copied_region_.Clear();
123 copied_region_.AddRegion(updated_region_);
124 copied_region_.AddRegion(last_updated_region);
125 copied_region_.IntersectWith(DesktopRect::MakeSize(size()));
126
127 for (DesktopRegion::Iterator it(copied_region_);
128 !it.IsAtEnd();
129 it.Advance()) {
130 D3D11_BOX box;
131 box.left = it.rect().left();
132 box.top = it.rect().top();
133 box.right = it.rect().right();
134 box.bottom = it.rect().bottom();
135 box.front = 0;
136 box.back = 1;
137 g_container->context->CopySubresourceRegion(
138 static_cast<ID3D11Resource*>(stage_.Get()),
139 0, it.rect().left(), it.rect().top(), 0,
140 static_cast<ID3D11Resource*>(texture.Get()),
141 0, &box);
142 }
143
144 rect_ = {0};
145 error = _com_error(surface_->Map(&rect_, DXGI_MAP_READ));
146 if (error.Error() != S_OK) {
147 LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap, error "
148 << error.ErrorMessage() << ", code " << error.Error();
149 return false;
150 }
151
152 // surface_->Unmap() will be called next time we capture an image to avoid
153 // memory copy without shared_memory.
154 return true;
155 }
156
157 uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
158 int pitch() const { return static_cast<int>(rect_.Pitch); }
159 const DesktopSize& size() const { return size_; }
160
161 int32_t AddRef() {
162 return ++ref_count_;
163 }
164
165 int32_t Release() {
166 int32_t ref_count;
167 ref_count = --ref_count_;
168 if (ref_count == 0) {
169 delete this;
170 }
171 return ref_count;
172 }
173
174 const DesktopRegion& updated_region() {
175 return updated_region_;
176 }
177
178 const DesktopRegion& copied_region() {
179 return copied_region_;
180 }
181
182 private:
183 // Texture should only be deleted by Release function.
184 ~Texture() = default;
185
186 // Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false
187 // if it fails to execute windows api.
188 bool InitializeStage(ID3D11Texture2D* texture) {
189 RTC_DCHECK(texture);
190 D3D11_TEXTURE2D_DESC desc = {0};
191 texture->GetDesc(&desc);
192 desc.BindFlags = 0;
193 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
194 desc.MiscFlags = 0;
195 desc.Usage = D3D11_USAGE_STAGING;
196 if (stage_) {
197 // Make sure stage_ and surface_ are always pointing to a same object.
198 // We need an ID3D11Texture2D instance for
199 // ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
200 // IDXGISurface::Map.
201 {
202 ComPtr<IUnknown> left;
203 ComPtr<IUnknown> right;
204 bool left_result = SUCCEEDED(stage_.As(&left));
205 bool right_result = SUCCEEDED(surface_.As(&right));
206 RTC_DCHECK(left_result);
207 RTC_DCHECK(right_result);
208 RTC_DCHECK(left.Get() == right.Get());
209 }
210
211 // This buffer should be used already.
212 _com_error error = _com_error(surface_->Unmap());
213 if (error.Error() == S_OK) {
214 D3D11_TEXTURE2D_DESC current_desc;
215 stage_->GetDesc(&current_desc);
216 if (memcmp(&desc, &current_desc, sizeof(D3D11_TEXTURE2D_DESC)) == 0) {
217 return true;
218 }
219 } else {
220 // Let's recreate stage_ and surface_ later.
221 LOG(LS_ERROR) << "Failed to unmap surface, error "
222 << error.ErrorMessage() << ", code " << error.Error();
223 }
224
225 stage_.Reset();
226 surface_.Reset();
227 } else {
228 RTC_DCHECK(!surface_);
229 }
230
231 _com_error error = _com_error(g_container->device->CreateTexture2D(
232 &desc, nullptr, stage_.GetAddressOf()));
233 if (error.Error() != S_OK || !stage_) {
234 LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage, "
235 "error "
236 << error.ErrorMessage() << ", code " << error.Error();
237 return false;
238 }
239
240 error = _com_error(stage_.As(&surface_));
241 if (error.Error() != S_OK || !surface_) {
242 LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface, "
243 "error "
244 << error.ErrorMessage() << ", code " << error.Error();
245 return false;
246 }
247
248 size_.set(desc.Width, desc.Height);
249 return true;
250 }
251
252 ComPtr<ID3D11Texture2D> stage_;
253 ComPtr<IDXGISurface> surface_;
254 DXGI_MAPPED_RECT rect_;
255 DesktopSize size_;
256 Atomic32 ref_count_;
257 // The updated region from Windows API.
258 DesktopRegion updated_region_;
259 // Combination of updated regions from both current frame and previous frame.
260 DesktopRegion copied_region_;
261 };
262
263 // A DesktopFrame which does not own the data buffer, and also does not have
264 // shared memory. This uses in IT2ME scenario only.
265 class ScreenCapturerWinDirectx::DxgiDesktopFrame : public DesktopFrame {
266 public:
267 DxgiDesktopFrame(
268 const rtc::scoped_refptr<ScreenCapturerWinDirectx::Texture>& texture)
269 : DesktopFrame(texture.get()->size(),
270 texture.get()->pitch(),
271 texture.get()->bits(),
272 nullptr),
273 texture_(texture) {
274 }
275
276 virtual ~DxgiDesktopFrame() {}
277
278 private:
279 // Keep a reference to the Texture instance to make sure we can still access
280 // its bytes array.
281 rtc::scoped_refptr<ScreenCapturerWinDirectx::Texture> texture_;
282 };
283
284 bool ScreenCapturerWinDirectx::Initialize() {
285 if (!g_container) {
286 rtc::GlobalLockScope lock(&g_initialize_lock);
287 if (!g_container) {
288 g_container = new DxgiContainer();
289 g_container->initialize_result = DoInitialize();
290 if (g_container->initialize_result) {
291 return true;
292 }
293
294 // Clean up if DirectX cannot work on the system.
295 if (g_container->duplication) {
296 g_container->duplication.Reset();
297 }
298
299 if (g_container->output1) {
300 g_container->output1->Release();
301 g_container->output1 = nullptr;
302 }
303
304 if (g_container->context) {
305 g_container->context->Release();
306 g_container->context = nullptr;
307 }
308
309 if (g_container->device) {
310 g_container->device->Release();
311 g_container->device = nullptr;
312 }
313
314 return false;
315 }
316 }
317
318 return g_container->initialize_result;
319 }
320
321 bool ScreenCapturerWinDirectx::DoInitialize() {
322 D3D_FEATURE_LEVEL feature_level;
323 _com_error error = D3D11CreateDevice(
324 nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
325 D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
326 nullptr, 0, D3D11_SDK_VERSION, &g_container->device, &feature_level,
327 &g_container->context);
328 if (error.Error() != S_OK || !g_container->device || !g_container->context) {
329 LOG(LS_WARNING) << "D3D11CreateDeivce returns error "
330 << error.ErrorMessage() << " with code " << error.Error();
331 return false;
332 }
333
334 if (feature_level < D3D_FEATURE_LEVEL_11_0) {
335 LOG(LS_WARNING) << "D3D11CreateDevice returns an instance without DirectX "
336 "11 support, level "
337 << feature_level;
338 return false;
339 }
340
341 ComPtr<IDXGIDevice> device;
342 error = _com_error(g_container->device->QueryInterface(
343 __uuidof(IDXGIDevice), reinterpret_cast<void**>(device.GetAddressOf())));
344 if (error.Error() != S_OK || !device) {
345 LOG(LS_WARNING) << "ID3D11Device is not an implementation of IDXGIDevice, "
346 "this usually means the system does not support DirectX "
347 "11";
348 return false;
349 }
350
351 ComPtr<IDXGIAdapter> adapter;
352 error = _com_error(device->GetAdapter(adapter.GetAddressOf()));
353 if (error.Error() != S_OK || !adapter) {
354 LOG(LS_WARNING) << "Failed to get an IDXGIAdapter implementation from "
355 "IDXGIDevice.";
356 return false;
357 }
358
359 ComPtr<IDXGIOutput> output;
360 for (int i = 0;; i++) {
361 error = _com_error(adapter->EnumOutputs(i, output.GetAddressOf()));
362 if (error.Error() == DXGI_ERROR_NOT_FOUND) {
363 LOG(LS_WARNING) << "No output detected.";
364 return false;
365 }
366 if (error.Error() == S_OK && output) {
367 DXGI_OUTPUT_DESC desc;
368 error = _com_error(output->GetDesc(&desc));
369 if (error.Error() == S_OK) {
370 if (desc.AttachedToDesktop) {
371 // Current output instance is the device attached to desktop.
372 #ifdef USE_GET_DPI_FOR_MONITOR
373 UINT dpi_x, dpi_y;
374 error = _com_error(GetDpiForMonitor(
Sergey Ulanov 2016/05/19 18:12:09 GetDpiForMonitor is available only on Win8.1, so t
Hzj_jie 2016/05/19 21:20:28 Removed.
375 desc.Monitor, MDT_DEFAULT, &dpi_x, &dpi_y));
376 if (error.Error() == S_OK) {
377 g_container->dpi.set(dpi_x, dpi_y);
378 g_container->fixed_dpi = true;
379 } else {
380 LOG(LS_WARNING) << "Failed to get DPI from device description, "
381 "error " << error.ErrorMessage()
382 << ", with code " << error.Error();
383 g_container->fixed_dpi = false;
384 }
385 #else
386 g_container->fixed_dpi = false;
387 #endif
388 break;
389 }
390 } else {
391 LOG(LS_WARNING) << "Failed to get output description of device " << i
392 << ", ignore.";
393 }
394 }
395 }
396
397 RTC_DCHECK(output);
398 error = _com_error(output.CopyTo(
399 __uuidof(IDXGIOutput1), reinterpret_cast<void**>(&g_container->output1)));
400 if (error.Error() != S_OK || !g_container->output1) {
401 LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, this "
402 "usually means the system does not support DirectX 11";
403 return false;
404 }
405
406 return DuplicateOutput();
407 }
408
409 bool ScreenCapturerWinDirectx::DuplicateOutput() {
410 // We are updating the instance.
411 rtc::CritScope lock(&g_container->duplication_lock);
412 // Make sure nobody is using current instance.
413 rtc::CritScope lock2(&g_container->acquire_lock);
414 if (g_container->duplication) {
415 g_container->duplication->ReleaseFrame();
416 g_container->duplication.Reset();
417 }
418
419 for (int i = 0; i < kDuplicateOutputAttempts; i++) {
420 _com_error error = g_container->output1->DuplicateOutput(
421 static_cast<IUnknown*>(g_container->device),
422 g_container->duplication.GetAddressOf());
423 if (error.Error() == S_OK && g_container->duplication) {
424 memset(&g_container->duplication_desc, 0, sizeof(DXGI_OUTDUPL_DESC));
425 g_container->duplication->GetDesc(&g_container->duplication_desc);
426 if (g_container->duplication_desc.ModeDesc.Format !=
427 DXGI_FORMAT_B8G8R8A8_UNORM) {
428 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
429 "format, which is required by downstream components, "
430 "format is "
431 << g_container->duplication_desc.ModeDesc.Format;
432 return false;
433 }
434
435 // When we are building without Windows 8.1 SDK or running on Windows 8 or
436 // earlier, fall back to use GetDeviceCaps.
437 if (!g_container->fixed_dpi) {
438 HDC hdc = GetDC(nullptr);
439 if (hdc == nullptr) {
440 g_container->dpi.set(0, 0);
441 } else {
442 g_container->dpi.set(GetDeviceCaps(hdc, LOGPIXELSX),
443 GetDeviceCaps(hdc, LOGPIXELSY));
444 }
445 }
446
447 return true;
448 } else {
449 // Make sure we have correct signal and duplicate the output next time.
450 g_container->duplication.Reset();
451 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
452 << error.ErrorMessage() << ", with code "
453 << error.Error();
454 Sleep(kDuplicateOutputWaitMs);
455 }
456 }
457
458 return false;
459 }
460
461 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
462 const DesktopCaptureOptions& options)
463 : callback_(nullptr), set_thread_execution_state_failed_(false) {
464 RTC_DCHECK(g_container && g_container->initialize_result);
465
466 // Texture instance won't change forever.
467 while (!surfaces_.current_frame()) {
468 surfaces_.ReplaceCurrentFrame(
469 new rtc::scoped_refptr<Texture>(new Texture()));
470 surfaces_.MoveToNextFrame();
471 }
472 }
473
474 ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() {}
475
476 void ScreenCapturerWinDirectx::Start(Callback* callback) {
477 RTC_DCHECK(!callback_);
478 RTC_DCHECK(callback);
479
480 callback_ = callback;
481 }
482
483 void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
484 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
485 shared_memory_factory_ = std::move(shared_memory_factory);
486 }
487
488 bool ScreenCapturerWinDirectx::HandleDetectUpdatedRegionError(
489 const _com_error& error,
490 const char* stage) {
491 if (error.Error() != S_OK) {
492 if (error.Error() == DXGI_ERROR_ACCESS_LOST) {
493 DuplicateOutput();
494 } else {
495 LOG(LS_ERROR) << "Failed to get " << stage << " rectangles, error "
496 << error.ErrorMessage() << ", code " << error.Error();
497 }
498 // Send entire desktop as we cannot get dirty or move rectangles.
499 return false;
500 }
501
502 return true;
503 }
504
505 bool ScreenCapturerWinDirectx::DetectUpdatedRegion(
506 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
507 DesktopRegion* updated_region) {
508 RTC_DCHECK(g_container->duplication);
509 RTC_DCHECK(updated_region);
510 updated_region->Clear();
511 if (frame_info.TotalMetadataBufferSize == 0) {
512 // This should not happen, since frame_info.AccumulatedFrames > 0.
513 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
514 "but TotalMetadataBufferSize == 0";
515 return false;
516 }
517
518 if (g_container->metadata.capacity() < frame_info.TotalMetadataBufferSize) {
519 g_container->metadata.clear(); // Avoid data copy
520 g_container->metadata.reserve(frame_info.TotalMetadataBufferSize);
521 }
522
523 UINT buff_size = 0;
524 DXGI_OUTDUPL_MOVE_RECT* move_rects =
525 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(g_container->metadata.data());
526 size_t move_rects_count = 0;
527 _com_error error = _com_error(g_container->duplication->GetFrameMoveRects(
528 static_cast<UINT>(g_container->metadata.capacity()),
529 move_rects, &buff_size));
530 if (!HandleDetectUpdatedRegionError(error, "move")) {
531 return false;
532 }
533 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
534
535 RECT* dirty_rects =
536 reinterpret_cast<RECT*>(g_container->metadata.data() + buff_size);
537 size_t dirty_rects_count = 0;
538 error = _com_error(g_container->duplication->GetFrameDirtyRects(
539 static_cast<UINT>(g_container->metadata.capacity()) - buff_size,
540 dirty_rects, &buff_size));
541 if (!HandleDetectUpdatedRegionError(error, "dirty")) {
542 return false;
543 }
544 dirty_rects_count = buff_size / sizeof(RECT);
545
546 while (move_rects_count > 0) {
547 updated_region->AddRect(DesktopRect::MakeXYWH(
548 move_rects->SourcePoint.x, move_rects->SourcePoint.y,
549 move_rects->DestinationRect.right - move_rects->DestinationRect.left,
550 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
551 updated_region->AddRect(DesktopRect::MakeLTRB(
552 move_rects->DestinationRect.left, move_rects->DestinationRect.top,
553 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
554 move_rects++;
555 move_rects_count--;
556 }
557
558 while (dirty_rects_count > 0) {
559 updated_region->AddRect(DesktopRect::MakeLTRB(
560 dirty_rects->left, dirty_rects->top,
561 dirty_rects->right, dirty_rects->bottom));
562 dirty_rects++;
563 dirty_rects_count--;
564 }
565
566 return true;
567 }
568
569 std::unique_ptr<DesktopFrame> ScreenCapturerWinDirectx::ProcessFrame(
570 const DXGI_OUTDUPL_FRAME_INFO& frame_info,
571 IDXGIResource* resource) {
572 RTC_DCHECK(resource);
573 RTC_DCHECK(frame_info.AccumulatedFrames > 0);
574 // We have something to update, so move to next surface.
575 surfaces_.MoveToNextFrame();
576 RTC_DCHECK(surfaces_.current_frame());
577 if (!surfaces_.current_frame()->get()->CopyFrom(frame_info, resource,
578 surfaces_.previous_frame()->get()->updated_region())) {
579 return std::unique_ptr<DesktopFrame>();
580 }
581
582 std::unique_ptr<DesktopFrame> result;
583 if (shared_memory_factory_) {
584 // When using shared memory, |frames_| is used to store a queue of
585 // SharedMemoryDesktopFrame's.
586 if (!frames_.current_frame() ||
587 !frames_.current_frame()->size().equals(
588 surfaces_.current_frame()->get()->size())) {
589 // Current frame does not have a same size as last captured surface.
590 std::unique_ptr<DesktopFrame> new_frame =
591 SharedMemoryDesktopFrame::Create(
592 surfaces_.current_frame()->get()->size(),
593 shared_memory_factory_.get());
594 if (!new_frame) {
595 LOG(LS_ERROR) << "Failed to allocate a new SharedMemoryDesktopFrame";
596 return std::unique_ptr<DesktopFrame>();
597 }
598 frames_.ReplaceCurrentFrame(
599 SharedDesktopFrame::Wrap(new_frame.release()));
600 }
601 result.reset(frames_.current_frame()->Share());
602
603 std::unique_ptr<DesktopFrame> frame(
604 new DxgiDesktopFrame(*surfaces_.current_frame()));
605 // Copy data into SharedMemory.
606 for (DesktopRegion::Iterator it(
607 surfaces_.current_frame()->get()->copied_region());
608 !it.IsAtEnd();
609 it.Advance()) {
610 result->CopyPixelsFrom(*frame, it.rect().top_left(), it.rect());
611 }
612 } else {
613 result.reset(new DxgiDesktopFrame(*surfaces_.current_frame()));
614 }
615 RTC_DCHECK(result);
616 *result->mutable_updated_region() =
617 surfaces_.current_frame()->get()->updated_region();
618 if (!g_container->dpi.is_zero()) {
619 result->set_dpi(g_container->dpi);
620 }
621 return result;
622 }
623
624 void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) {
625 RTC_DCHECK(g_container->duplication);
626 RTC_DCHECK(callback_);
627
628 if (!g_container->duplication && !DuplicateOutput()) {
629 // Receive a capture request when application is shutting down, or between
630 // mode change.
631 callback_->OnCaptureCompleted(nullptr);
632 return;
633 }
634
635 int64_t capture_start_time_nanos = rtc::TimeNanos();
636
637 if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
638 if (!set_thread_execution_state_failed_) {
639 set_thread_execution_state_failed_ = true;
640 LOG(LS_WARNING) << "Failed to make system & display power assertion: "
641 << GetLastError();
642 }
643 }
644
645 DXGI_OUTDUPL_FRAME_INFO frame_info;
646 memset(&frame_info, 0, sizeof(DXGI_OUTDUPL_FRAME_INFO));
647 ComPtr<IDXGIResource> resource;
648 rtc::CritScope lock(&g_container->acquire_lock);
649 _com_error error = g_container->duplication->AcquireNextFrame(
650 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
651 if (error.Error() == DXGI_ERROR_ACCESS_LOST) {
652 LOG(LS_ERROR) << "Access lost " << error.ErrorMessage();
653 if (DuplicateOutput()) {
654 EmitCurrentFrame();
655 } else {
656 callback_->OnCaptureCompleted(nullptr);
657 }
658 return;
659 }
660
661 if (error.Error() == DXGI_ERROR_WAIT_TIMEOUT) {
662 // Nothing changed.
663 EmitCurrentFrame();
664 return;
665 }
666
667 if (error.Error() != S_OK) {
668 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
669 << ", code " << error.Error();
670 callback_->OnCaptureCompleted(nullptr);
671 return;
672 }
673
674 if (frame_info.AccumulatedFrames == 0) {
675 g_container->duplication->ReleaseFrame();
676 EmitCurrentFrame();
677 return;
678 }
679
680 // Everything looks good so far, build next frame.
681 std::unique_ptr<DesktopFrame> result =
682 ProcessFrame(frame_info, resource.Get());
683 // DetectUpdatedRegion may release last g_container->duplication. But
684 // DuplicateOutput function will always release last frame, so there is no
685 // potential leak.
686 if (g_container->duplication) {
687 g_container->duplication->ReleaseFrame();
688 }
689 if (!result) {
690 callback_->OnCaptureCompleted(nullptr);
691 return;
692 }
693
694 result->set_capture_time_ms(
695 (rtc::TimeNanos() - capture_start_time_nanos) /
696 rtc::kNumNanosecsPerMillisec);
697 callback_->OnCaptureCompleted(result.release());
698 }
699
700 bool ScreenCapturerWinDirectx::GetScreenList(ScreenList* screens) {
701 return true;
702 }
703
704 bool ScreenCapturerWinDirectx::SelectScreen(ScreenId id) {
705 // Only full desktop capture is supported.
706 return id == kFullDesktopScreenId;
707 }
708
709 void ScreenCapturerWinDirectx::EmitCurrentFrame() {
710 static const DesktopSize dummy_frame_size(100, 100);
711 if (frames_.current_frame()) {
712 std::unique_ptr<SharedDesktopFrame> frame(frames_.current_frame()->Share());
713 frame->mutable_updated_region()->Clear();
714 callback_->OnCaptureCompleted(frame.release());
715 } else if (shared_memory_factory_) {
716 // If shared memory is used, upstream components always expect a valid frame
717 // with shared memory are returned. Otherwise the service will crash.
718 // TODO(zijiehe): Fix crash when calling OnCaptureCompleted(nullptr).
Sergey Ulanov 2016/05/19 18:12:09 Please open a bug to fix this issue in DesktopSess
719 std::unique_ptr<DesktopFrame> dummy(
720 SharedMemoryDesktopFrame::Create(
721 dummy_frame_size, shared_memory_factory_.get()));
722 RTC_DCHECK(dummy);
723 callback_->OnCaptureCompleted(dummy.release());
724 } else {
725 std::unique_ptr<BasicDesktopFrame> dummy(
726 new BasicDesktopFrame(dummy_frame_size));
727 callback_->OnCaptureCompleted(dummy.release());
Sergey Ulanov 2016/05/19 18:12:09 I still think we shouldn't to return dummy frame e
Hzj_jie 2016/05/19 21:20:28 Done.
728 }
729 }
730
731 #ifdef USE_GET_DPI_FOR_MONITOR
732 #undef USE_GET_DPI_FOR_MONITOR
733 #endif
Sergey Ulanov 2016/05/19 18:12:09 Don't need to undef anything - the is no code belo
Hzj_jie 2016/05/19 21:20:28 Done.
734
735 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698