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

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

Powered by Google App Engine
This is Rietveld 408576698