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

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

Powered by Google App Engine
This is Rietveld 408576698