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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0281a20b82e2376d2df358da8a4f038cb2971d73
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h"
+
+#include <string.h>
+
+#include <comdef.h>
+#include <wincodec.h>
+#include <DXGI.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+using rtc::CriticalSection;
+using rtc::CritScope;
+
+namespace {
+
+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
+CriticalSection* duplication_lock = nullptr;
+CriticalSection* acquire_lock = nullptr;
+
+bool initialized GUARDED_BY(initialize_lock) = false;
+bool initialize_result GUARDED_BY(initialize_lock) = false;
+ID3D11Device* d3d11_device GUARDED_BY(initialize_lock) = nullptr;
+ID3D11DeviceContext* d3d11_context GUARDED_BY(initialize_lock) = nullptr;
+IDXGIOutput1* dxgi_output1 GUARDED_BY(initialize_lock) = nullptr;
+ComPtr<IDXGIOutputDuplication>* dxgi_output_duplication
+ GUARDED_BY(duplication_lock) = nullptr;
+std::vector<BYTE>* metadata GUARDED_BY(acquire_lock) = nullptr;
+
+} // namespace
+
+// A pair of an ID3D11Texture2D and an IDXGISurface. We need an
+// ID3D11Texture2D instance to copy GPU texture to RAM, but an IDXGISurface to
+// map the texture into a bitmap buffer. These two instances are always
+// pointing to a same object.
+//
+// This class is not thread safe.
+class ScreenCapturerWinDirectx::Texture {
+ public:
+ // Captures a frame represented by frame_info and resource. Returns false if
+ // anything wrong.
+ // Before the first Capture is called, this instance is still workable, but
+ // the data in underlying buffer (SharedMemory) is undefined.
+ bool Capture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource) {
+ if (resource == nullptr || frame_info.AccumulatedFrames == 0) {
+ // Nothing updated, but current data is still valid.
+ return false;
+ }
+
+ ComPtr<ID3D11Texture2D> texture;
+ _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
+ __uuidof(ID3D11Texture2D),
+ reinterpret_cast<void**>(texture.GetAddressOf())));
+ if (err.Error() != S_OK || !texture) {
+ LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D, "
+ "error "
+ << err.ErrorMessage() << ", code " << err.Error();
+ return false;
+ }
+
+ // AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to
+ // make a copy.
+ if (!CreateTexture(texture.Get())) {
+ return false;
+ }
+
+ d3d11_context->CopyResource(static_cast<ID3D11Resource*>(stage_.Get()),
+ static_cast<ID3D11Resource*>(texture.Get()));
+
+ rect_ = {0};
+ err = _com_error(surface_->Map(&rect_, DXGI_MAP_READ));
+ if (err.Error() != S_OK) {
+ LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap, error "
+ << err.ErrorMessage() << ", code " << err.Error();
+ return false;
+ }
+
+ // 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.
+ // memory copy without shared_memory.
+ return true;
+ }
+
+ // Create a DesktopFrame from current instance.
+ 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.
+ 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.
+ }
+
+ // Returns the bytes array of last captured screen.
+ uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
+ // Returns the pitch of last captured screen.
+ int pitch() const { return static_cast<int>(rect_.Pitch); }
+ // Returns the width of last captured screen.
+ int width() const { return width_; }
+ // 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.
+ int height() const { return height_; }
+ // Returns the DesktopSize of last captured screen.
+ 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.
+
+ private:
+ // Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false
+ // if it fails to execute windows api.
+ bool CreateTexture(ID3D11Texture2D* texture) {
+ RTC_DCHECK(texture != nullptr);
+ D3D11_TEXTURE2D_DESC desc = {0};
+ texture->GetDesc(&desc);
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MiscFlags = 0;
+ desc.Usage = D3D11_USAGE_STAGING;
+ if (stage_) {
+ // Make sure stage_ and surface_ are always pointing to a same object.
+ // We need an ID3D11Texture2D instance for
+ // ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
+ // IDXGISurface::Map.
+ {
+ ComPtr<IUnknown> left;
+ ComPtr<IUnknown> right;
+ bool left_result = SUCCEEDED(stage_.As(&left));
+ bool right_result = SUCCEEDED(surface_.As(&right));
+ RTC_DCHECK(left_result);
+ RTC_DCHECK(right_result);
+ RTC_DCHECK(left.Get() == right.Get());
+ }
+
+ // This buffer should be used already.
+ _com_error err = _com_error(surface_->Unmap());
+ if (err.Error() == S_OK) {
+ D3D11_TEXTURE2D_DESC orgi_desc;
+ stage_->GetDesc(&orgi_desc);
+ if (memcmp(&desc, &orgi_desc, sizeof(D3D11_TEXTURE2D_DESC)) == 0) {
+ return true;
+ }
+ } else {
+ // Let's recreate stage_ and surface_ later.
+ LOG(LS_ERROR) << "Failed to unmap surface, error " << err.ErrorMessage()
+ << ", code " << err.Error();
+ }
+
+ stage_.Reset();
+ surface_.Reset();
+ } else {
+ RTC_DCHECK(!surface_);
+ }
+
+ _com_error err = _com_error(
+ d3d11_device->CreateTexture2D(&desc, nullptr, stage_.GetAddressOf()));
+ if (err.Error() != S_OK || !stage_) {
+ LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage, "
+ "error "
+ << err.ErrorMessage() << ", code " << err.Error();
+ return false;
+ }
+
+ err = _com_error(stage_.As(&surface_));
+ if (err.Error() != S_OK || !surface_) {
+ LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface, "
+ "error "
+ << err.ErrorMessage() << ", code " << err.Error();
+ return false;
+ }
+
+ width_ = static_cast<int>(desc.Width);
+ height_ = static_cast<int>(desc.Height);
+ desktop_size_.set(width_, height_);
+
+ return true;
+ }
+
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> stage_;
+ Microsoft::WRL::ComPtr<IDXGISurface> surface_;
+ DXGI_MAPPED_RECT rect_;
+ int width_;
+ int height_;
+ DesktopSize desktop_size_;
+};
+
+bool ScreenCapturerWinDirectx::Initialize() {
+ if (!initialized) {
+ rtc::GlobalLockScope lock(&initialize_lock);
+ if (!initialized) {
+ initialize_result = DoInitialize();
+ initialized = true;
+ if (initialize_result) {
+ return true;
+ }
+
+ // Clean up if DirectX cannot work on the system.
+ if (dxgi_output_duplication != nullptr && *dxgi_output_duplication) {
+ (*dxgi_output_duplication).Reset();
+ }
+
+ if (dxgi_output1 != nullptr) {
+ dxgi_output1->Release();
+ dxgi_output1 = nullptr;
+ }
+
+ if (d3d11_context != nullptr) {
+ d3d11_context->Release();
+ d3d11_context = nullptr;
+ }
+
+ if (d3d11_device != nullptr) {
+ d3d11_device->Release();
+ d3d11_device = nullptr;
+ }
+
+ return false;
+ }
+ }
+
+ return initialize_result;
+}
+
+bool ScreenCapturerWinDirectx::DoInitialize() {
+ D3D_FEATURE_LEVEL feature_level;
+ _com_error err(D3D11CreateDevice(
+ nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
+ nullptr, 0, D3D11_SDK_VERSION, &d3d11_device, &feature_level,
+ &d3d11_context));
+ if (err.Error() != S_OK || d3d11_device == nullptr ||
+ d3d11_context == nullptr) {
+ LOG(LS_WARNING) << "D3D11CreateDeivce returns error " << err.ErrorMessage()
+ << " with code " << err.Error();
+ return false;
+ }
+
+ if (feature_level < D3D_FEATURE_LEVEL_11_0) {
+ LOG(LS_WARNING) << "D3D11CreateDevice returns an instance without DirectX "
+ "11 support, level "
+ << feature_level;
+ return false;
+ }
+
+ ComPtr<IDXGIDevice> device;
+ err = _com_error(d3d11_device->QueryInterface(
+ __uuidof(IDXGIDevice), reinterpret_cast<void**>(device.GetAddressOf())));
+ if (err.Error() != S_OK || !device) {
+ LOG(LS_WARNING) << "ID3D11Device is not an implementation of IDXGIDevice, "
+ "this usually means the system does not support DirectX "
+ "11";
+ return false;
+ }
+
+ ComPtr<IDXGIAdapter> adapter;
+ err = _com_error(device->GetAdapter(adapter.GetAddressOf()));
+ if (err.Error() != S_OK || !adapter) {
+ LOG(LS_WARNING) << "Failed to get an IDXGIAdapter implementation from "
+ "IDXGIDevice.";
+ return false;
+ }
+
+ ComPtr<IDXGIOutput> output;
+ for (int i = 0;; i++) {
+ err = _com_error(adapter->EnumOutputs(i, output.GetAddressOf()));
+ if (err.Error() == DXGI_ERROR_NOT_FOUND) {
+ LOG(LS_WARNING) << "No output detected.";
+ return false;
+ }
+ if (err.Error() == S_OK && output) {
+ DXGI_OUTPUT_DESC desc;
+ err = _com_error(output->GetDesc(&desc));
+ if (err.Error() == S_OK) {
+ if (desc.AttachedToDesktop) {
+ // Current output instance is the device attached to desktop.
+ break;
+ }
+ } else {
+ LOG(LS_WARNING) << "Failed to get output description of device " << i
+ << ", ignore.";
+ }
+ }
+ }
+
+ RTC_DCHECK(output);
+ err = _com_error(output.CopyTo(__uuidof(IDXGIOutput1),
+ reinterpret_cast<void**>(&dxgi_output1)));
+ if (err.Error() != S_OK || dxgi_output1 == nullptr) {
+ LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, this "
+ "usually means the system does not support DirectX 11";
+ return false;
+ }
+
+ duplication_lock = new CriticalSection();
+ acquire_lock = new CriticalSection();
+ dxgi_output_duplication = new ComPtr<IDXGIOutputDuplication>();
+ 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
+ return DuplicateOutput();
+}
+
+bool ScreenCapturerWinDirectx::DuplicateOutput() {
+ RTC_DCHECK(dxgi_output_duplication != nullptr);
+ RTC_DCHECK(dxgi_output1 != nullptr);
+ ComPtr<IDXGIOutputDuplication>& duplication = *dxgi_output_duplication;
+ // We are updating the instance.
+ CritScope lock(duplication_lock);
+ // Make sure nobody is using current instance.
+ CritScope lock2(acquire_lock);
+ if (duplication) {
+ duplication->ReleaseFrame();
+ duplication.Reset();
+ }
+
+ for (int i = 0; i < kDuplicateOutputAttempts; i++) {
+ _com_error err(
+ dxgi_output1->DuplicateOutput(static_cast<IUnknown*>(d3d11_device),
+ duplication.GetAddressOf()));
+ if (err.Error() == S_OK && duplication) {
+ DXGI_OUTDUPL_DESC desc;
+ duplication->GetDesc(&desc);
+ if (desc.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
+ LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
+ "format, which is required by downstream components, "
+ "format is "
+ << desc.ModeDesc.Format;
+ return false;
+ }
+
+ return true;
+ } else {
+ // Make sure we have correct signal and duplicate the output next time.
+ duplication.Reset();
+ LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
+ << err.ErrorMessage() << ", with code " << err.Error();
+ Sleep(kDuplicateOutputWait);
+ }
+ }
+
+ return false;
+}
+
+ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
+ const DesktopCaptureOptions& options)
+ : callback_(nullptr), set_thread_execution_state_failed_(false) {
+ RTC_DCHECK(initialized && initialize_result);
+
+ // Texture instance won't change forever.
+ while (surfaces_.current() == nullptr) {
+ surfaces_.ReplaceCurrent(new Texture());
+ surfaces_.MoveToNext();
+ }
+
+ // Make sure we always have a valid DesktopFrame instance to emit.
+ // Not similar as other methods, this class may not always be able to capture
+ // desktop frames, the desktop may not change. So we create a set of default
+ // frames to emit, if first several attempts do not capture anything.
+ while (frames_.current_frame() == nullptr) {
+ frames_.ReplaceCurrentFrame(
+ new DesktopFrame(DesktopSize(100, 100), 0, nullptr, nullptr));
+ frames_.MoveToNextFrame();
+ }
+}
+
+ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() {}
+
+void ScreenCapturerWinDirectx::Start(Callback* callback) {
+ RTC_DCHECK(callback_ == nullptr);
+ RTC_DCHECK(callback != nullptr);
+
+ callback_ = callback;
+}
+
+void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
+ rtc::scoped_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ =
+ rtc::ScopedToUnique(std::move(shared_memory_factory));
+}
+
+bool ScreenCapturerWinDirectx::DetectUpdatedRegion(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopFrame* frame) {
+ RTC_DCHECK(dxgi_output_duplication != nullptr);
+ RTC_DCHECK(*dxgi_output_duplication);
+ RTC_DCHECK(metadata != nullptr);
+ RTC_DCHECK(frame != nullptr);
+ DesktopRegion& updated_region = *frame->mutable_updated_region();
+ updated_region.Clear();
+ if (frame_info.TotalMetadataBufferSize == 0) {
+ // This should not happen, since frame_info.AccumulatedFrames > 0.
+ LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
+ "but TotalMetadataBufferSize == 0";
+ return false;
+ }
+
+ if (metadata->size() < frame_info.TotalMetadataBufferSize) {
+ metadata->clear(); // Avoid data copy
+ metadata->reserve(frame_info.TotalMetadataBufferSize);
+ }
+
+ UINT buff_size = 0;
+ DXGI_OUTDUPL_MOVE_RECT* move_rects = nullptr;
+ size_t move_rects_count = 0;
+ RECT* dirty_rects = nullptr;
+ size_t dirty_rects_count = 0;
+ for (int i = 0; i < 2; i++) {
+ _com_error err(S_OK);
+ if (i == 0) {
+ move_rects = reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata->data());
+ err = _com_error((*dxgi_output_duplication)->GetFrameMoveRects(
+ static_cast<UINT>(metadata->capacity()), move_rects, &buff_size));
+ } else {
+ dirty_rects = reinterpret_cast<RECT*>(metadata->data() + buff_size);
+ err = _com_error((*dxgi_output_duplication)->GetFrameDirtyRects(
+ static_cast<UINT>(metadata->capacity()) - buff_size, dirty_rects,
+ &buff_size));
+ }
+ if (err.Error() != S_OK) {
+ if (err.Error() == DXGI_ERROR_ACCESS_LOST) {
+ DuplicateOutput();
+ } else {
+ LOG(LS_ERROR) << "Failed to get " << (i == 0 ? "move" : "dirty")
+ << " rectangles, error " << err.ErrorMessage()
+ << ", code " << err.Error();
+ }
+ // Send entire desktop as we cannot get dirty or move rectangles.
+ return false;
+ }
+ if (i == 0) {
+ move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
+ } else {
+ dirty_rects_count = buff_size / sizeof(RECT);
+ }
+ }
+
+ LOG(LS_INFO) << "Found " << move_rects_count << " moved rectangles and "
+ << dirty_rects_count << " dirty rectangles";
+ while (move_rects_count > 0) {
+ updated_region.AddRect(DesktopRect::MakeXYWH(
+ move_rects->SourcePoint.x, move_rects->SourcePoint.y,
+ move_rects->DestinationRect.right - move_rects->DestinationRect.left,
+ move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
+ updated_region.AddRect(DesktopRect::MakeLTRB(
+ move_rects->DestinationRect.left, move_rects->DestinationRect.top,
+ move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
+ move_rects++;
+ move_rects_count--;
+ }
+
+ while (dirty_rects_count > 0) {
+ updated_region.AddRect(
+ DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top,
+ dirty_rects->right, dirty_rects->bottom));
+ dirty_rects++;
+ dirty_rects_count--;
+ }
+
+ return true;
+}
+
+bool ScreenCapturerWinDirectx::ProcessFrame(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource) {
+ RTC_DCHECK(resource != nullptr);
+ RTC_DCHECK(frame_info.AccumulatedFrames > 0);
+ // We have something to update, so move to next surface.
+ surfaces_.MoveToNext();
+ RTC_DCHECK(surfaces_.current() != nullptr);
+ if (!surfaces_.current()->Capture(frame_info, resource)) {
+ return false;
+ }
+
+ frames_.MoveToNextFrame();
+ if (shared_memory_factory_) {
+ // When using shared_memory_factory_, try to avoid reallocate memory.
+ SharedMemoryDesktopFrame* frame =
+ reinterpret_cast<SharedMemoryDesktopFrame*>(frames_.current_frame());
+ if (frame == nullptr ||
+ frame->size().width() != surfaces_.current()->width() ||
+ frame->size().height() != surfaces_.current()->height() ||
+ frame->stride() != surfaces_.current()->pitch()) {
+ // Current frame does not have a same size as last captured surface.
+ // Note, Windows does not guarantee to return exactly the same buffer size
+ // as the width * height * 4. The pitch may be larger than width * 4.
+ std::unique_ptr<DesktopFrame> new_frame =
+ SharedMemoryDesktopFrame::Create(surfaces_.current()->desktop_size(),
+ surfaces_.current()->pitch(), shared_memory_factory_.get());
+ if (!new_frame) {
+ LOG(LS_ERROR) << "Failed to allocate a new SharedMemoryDesktopFrame";
+ return false;
+ }
+ frames_.ReplaceCurrentFrame(new_frame.release());
+ }
+ } else {
+ frames_.ReplaceCurrentFrame(surfaces_.current()->desktop_frame());
+ }
+
+ if (!DetectUpdatedRegion(frame_info, frames_.current_frame())) {
+ frames_.current_frame()->mutable_updated_region()->Clear();
+ frames_.current_frame()->mutable_updated_region()->AddRect(
+ DesktopRect::MakeSize(surfaces_.current()->desktop_size()));
+ }
+
+ if (shared_memory_factory_) {
+ // There is a shared memory provided, we need to copy data to it.
+ std::unique_ptr<DesktopFrame> frame(surfaces_.current()->desktop_frame());
+ for (DesktopRegion::Iterator it(frames_.current_frame()->updated_region());
+ !it.IsAtEnd(); it.Advance()) {
+ // VideoEncoderVpx expects to use 8 pixels (for v9) or 3 pixels (for v8)
+ // more area to encode final video. So we need to copy a little bit more
+ // than the real changed area.
+ // In AlignRect function, 1 more pixel may be applied to right and bottom,
+ // so we add 9 pixels here.
+ DesktopRect rect = it.rect().Expand(9, frame->size());
+ frames_.current_frame()->CopyPixelsFrom(*frame, rect.top_left(), rect);
+ }
+ }
+
+ return true;
+}
+
+void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) {
+ RTC_DCHECK(dxgi_output_duplication != nullptr);
+ RTC_DCHECK(callback_ != nullptr);
Sergey Ulanov 2016/04/14 23:10:43 RTC_DCHECK(callback_)
Hzj_jie 2016/04/15 19:42:18 Done.
+
+ if (!(*dxgi_output_duplication) && !DuplicateOutput()) {
+ // Receive a capture request when application is shutting down, or between
+ // mode change.
+ callback_->OnCaptureCompleted(nullptr);
+ return;
+ }
+
+ TickTime capture_start_time = TickTime::Now();
+
+ if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
+ if (!set_thread_execution_state_failed_) {
+ set_thread_execution_state_failed_ = true;
+ LOG(LS_WARNING) << "Failed to make system & display power assertion: "
+ << GetLastError();
+ }
+ }
+
+ DXGI_OUTDUPL_FRAME_INFO frame_info;
+ memset(&frame_info, 0, sizeof(DXGI_OUTDUPL_FRAME_INFO));
+ ComPtr<IDXGIResource> resource;
+ CritScope lock(acquire_lock);
+ _com_error err((*dxgi_output_duplication)->AcquireNextFrame(
+ kAcquireTimeout, &frame_info, resource.GetAddressOf()));
+ if (err.Error() == DXGI_ERROR_ACCESS_LOST) {
+ LOG(LS_ERROR) << "Access lost " << err.ErrorMessage();
+ if (DuplicateOutput()) {
+ EmitCurrentFrame();
+ } else {
+ callback_->OnCaptureCompleted(nullptr);
+ }
+ return;
+ }
+
+ if (err.Error() == DXGI_ERROR_WAIT_TIMEOUT) {
+ LOG(LS_INFO) << "Acquire timeout " << err.ErrorMessage();
+ // Nothing changed.
+ EmitCurrentFrame();
+ return;
+ }
+
+ if (err.Error() != S_OK) {
+ LOG(LS_ERROR) << "Failed to capture frame, error " << err.ErrorMessage()
+ << ", code " << err.Error();
+ callback_->OnCaptureCompleted(nullptr);
+ return;
+ }
+
+ if (frame_info.AccumulatedFrames == 0) {
+ LOG(LS_INFO) << "Nothing captured";
+ EmitCurrentFrame();
+ (*dxgi_output_duplication)->ReleaseFrame();
+ return;
+ }
+
+ 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
+ // Everything looks good so far, build next frame.
+ bool result = ProcessFrame(frame_info, resource.Get());
+ // DetectUpdatedRegion may release last dxgi_output_duplication. But
+ // DuplicateOutput function will always release last frame, so there is no
+ // potential leak.
+ if (*dxgi_output_duplication) {
+ (*dxgi_output_duplication)->ReleaseFrame();
+ }
+ if (result) {
+ frames_.current_frame()->set_capture_time_ms(
+ (TickTime::Now() - capture_start_time).Milliseconds());
+ EmitCurrentFrame();
+ } else {
+ callback_->OnCaptureCompleted(nullptr);
+ }
+}
+
+bool ScreenCapturerWinDirectx::GetScreenList(ScreenList* screens) {
+ 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.
+ RTC_DCHECK(screens->size() == 0);
Sergey Ulanov 2016/04/14 23:10:43 screens->empty()
Hzj_jie 2016/04/15 19:42:18 Done.
+ screens->push_back(Screen());
+ return true;
+}
+
+bool ScreenCapturerWinDirectx::SelectScreen(ScreenId id) {
+ 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
+}
+
+void ScreenCapturerWinDirectx::EmitCurrentFrame() {
+ callback_->OnCaptureCompleted(frames_.current_frame()->Share());
+}
+
+} // namespace webrtc

Powered by Google App Engine
This is Rietveld 408576698