Chromium Code Reviews| Index: webrtc/modules/desktop_capture/win/desktop_frame_win_dxgi.cc |
| diff --git a/webrtc/modules/desktop_capture/win/desktop_frame_win_dxgi.cc b/webrtc/modules/desktop_capture/win/desktop_frame_win_dxgi.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7d55934d91874261a097f13b79bc9acd17846b04 |
| --- /dev/null |
| +++ b/webrtc/modules/desktop_capture/win/desktop_frame_win_dxgi.cc |
| @@ -0,0 +1,199 @@ |
| +/* |
| + * 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/desktop_frame_win_dxgi.h" |
| + |
| +#include <string.h> |
| + |
| +#include <comdef.h> |
| +#include <D3D11.h> |
| + |
| +#include "webrtc/base/checks.h" |
| +#include "webrtc/base/scoped_ptr.h" |
| +#include "webrtc/system_wrappers/include/logging.h" |
| + |
| +namespace webrtc { |
| + |
| +using std::unique_ptr; |
| +using Microsoft::WRL::ComPtr; |
| + |
| +namespace { |
| + |
| +ID3D11Device* device = nullptr; |
| +ID3D11DeviceContext* context = nullptr; |
| + |
| +} // namespace |
| + |
| +void DesktopFrameWinDxgi::Initialize(ID3D11Device* dev, |
| + ID3D11DeviceContext* ctx) { |
| + RTC_DCHECK(dev != nullptr); |
| + RTC_DCHECK(ctx != nullptr); |
| + RTC_DCHECK(device == nullptr); |
| + RTC_DCHECK(context == nullptr); |
| + |
| + device = dev; |
| + context = ctx; |
| +} |
| + |
| +DesktopFrameWinDxgi::DesktopFrameWinDxgi(DesktopSize size, |
| + int stride, |
| + SharedMemory* shared_memory) |
| + : DesktopFrame(size, |
| + stride, |
| + nullptr, // Data will always be returned from rect_.pBits. |
| + shared_memory) { |
| + RTC_DCHECK(device != nullptr); |
| + RTC_DCHECK(context != nullptr); |
| +} |
| + |
| +DesktopFrameWinDxgi::~DesktopFrameWinDxgi() {} |
| + |
| +unique_ptr<DesktopFrameWinDxgi> DesktopFrameWinDxgi::Create( |
| + DesktopSize size, |
| + SharedMemoryFactory* shared_memory_factory) { |
| + int stride = size.width() * DesktopFrame::kBytesPerPixel; |
| + int buffer_size = stride * size.height(); |
| + rtc::scoped_ptr<SharedMemory> shared_memory; |
| + if (shared_memory_factory != nullptr) { |
| + shared_memory = shared_memory_factory->CreateSharedMemory(buffer_size); |
| + if (!shared_memory) { |
| + LOG(LS_ERROR) << "Failed to create shared memory with size " |
| + << buffer_size; |
| + // Well, we are still able to work without shared_memory. |
| + } |
| + } else { |
| + LOG(LS_WARNING) << "No SharedMemoryFactory provided"; |
| + } |
| + |
| + return unique_ptr<DesktopFrameWinDxgi>( |
| + new DesktopFrameWinDxgi(size, stride, shared_memory.release())); |
| +} |
| + |
| +bool DesktopFrameWinDxgi::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( |
| + __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; |
| + } |
| + |
| + 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; |
| + } |
| + |
| + if (rect_.Pitch != stride()) { |
| + // Unfortunately we cannot check whether the buffer size is also consistent. |
| + LOG(LS_ERROR) << "System returns a different size of bitmap, with pitch " |
| + << rect_.Pitch; |
| + return false; |
| + } |
| + |
| + if (shared_memory() != nullptr) { |
|
Sergey Ulanov
2016/04/08 21:22:26
There are two cases:
1. SharedMemoryFactory is se
Hzj_jie
2016/04/11 22:19:16
There are two reasons I used current solution.
1.
Sergey Ulanov
2016/04/12 03:00:46
ScreenCaptureFrameQueue is not the only place wher
Hzj_jie
2016/04/12 17:40:40
For the reason we make a copy to stage_, it's a Wi
|
| + // If there is a shared_memory, we need to copy the data to it, so other |
| + // processes can use it. |
| + memcpy(shared_memory()->data(), rect_.pBits, shared_memory()->size()); |
|
Sergey Ulanov
2016/04/08 21:22:26
This copies the whole frame every time. It should
Hzj_jie
2016/04/11 22:19:16
What's my initial plan is to move all variables fr
Sergey Ulanov
2016/04/12 03:00:47
IMO ScreenCapturerFrameQueue would a better place
|
| + } |
| + // surface_->Unmap will be called next time we capture an image to avoid |
| + // memory copy without shared_memory. |
| + return true; |
| +} |
| + |
| +bool DesktopFrameWinDxgi::CreateTexture(ID3D11Texture2D* texture) { |
| + RTC_DCHECK(texture != nullptr); |
| + D3D11_TEXTURE2D_DESC desc; |
|
Sergey Ulanov
2016/04/08 21:22:26
memset the struct to 0?
Hzj_jie
2016/04/11 22:19:16
Done.
|
| + texture->GetDesc(&desc); |
| + // TODO(zijiehe): Debug |
| + // desc.ArraySize = 1; // Capture only one frame. |
| + desc.BindFlags = 0; |
| + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; |
| + // desc.MipLevels = 1; |
| + desc.MiscFlags = 0; |
| + // desc.SampleDesc.Count = 1; |
| + // desc.SampleDesc.Quality = 0; |
|
Sergey Ulanov
2016/04/08 21:22:26
remove commented code?
Hzj_jie
2016/04/11 22:19:16
Yes, should be, sorry.
|
| + 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( |
| + 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; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace webrtc |