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

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: Several lint formatting 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 // 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698