Chromium Code Reviews| OLD | NEW |
|---|---|
| (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/dxgi_output_duplicator.h" | |
| 12 | |
| 13 #include <string.h> | |
| 14 | |
| 15 #include <unknwn.h> | |
| 16 #include <DXGIFormat.h> | |
| 17 #include <Windows.h> | |
| 18 | |
| 19 #include "webrtc/base/checks.h" | |
| 20 #include "webrtc/base/logging.h" | |
| 21 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h" | |
| 22 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h" | |
| 23 | |
| 24 namespace webrtc { | |
| 25 | |
| 26 using Microsoft::WRL::ComPtr; | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Timeout for AcquireNextFrame() call. | |
| 31 const int kAcquireTimeoutMs = 10; | |
| 32 | |
| 33 DesktopRect RECTToDesktopRect(const RECT& rect) { | |
| 34 return DesktopRect::MakeLTRB( | |
| 35 rect.left, rect.top, rect.right, rect.bottom); | |
| 36 } | |
| 37 | |
| 38 } // namespace | |
| 39 | |
| 40 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, | |
| 41 const ComPtr<IDXGIOutput1>& output, | |
|
Sergey Ulanov
2016/07/26 00:48:52
indentation
Hzj_jie
2016/07/26 02:13:02
Done.
| |
| 42 const DXGI_OUTPUT_DESC& desc) | |
| 43 : device_(device), | |
| 44 output_(output), | |
| 45 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { | |
| 46 RTC_DCHECK(output_); | |
| 47 RTC_DCHECK(!desktop_rect_.is_empty()); | |
| 48 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0); | |
| 49 } | |
| 50 | |
| 51 DxgiOutputDuplicator::DxgiOutputDuplicator( | |
| 52 DxgiOutputDuplicator&& other) = default; | |
| 53 | |
| 54 DxgiOutputDuplicator::~DxgiOutputDuplicator() { | |
| 55 if (duplication_) { | |
| 56 duplication_->ReleaseFrame(); | |
| 57 } | |
| 58 texture_.reset(); | |
| 59 } | |
| 60 | |
| 61 bool DxgiOutputDuplicator::Initialize() { | |
| 62 if (DuplicateOutput()) { | |
| 63 if (desc_.DesktopImageInSystemMemory) { | |
| 64 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get())); | |
| 65 } else { | |
| 66 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_)); | |
| 67 } | |
| 68 return true; | |
| 69 } else { | |
| 70 duplication_.Reset(); | |
| 71 return false; | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 bool DxgiOutputDuplicator::DuplicateOutput() { | |
| 76 RTC_DCHECK(!duplication_); | |
| 77 _com_error error = output_->DuplicateOutput( | |
| 78 static_cast<IUnknown*>(device_.d3d_device()), | |
| 79 duplication_.GetAddressOf()); | |
| 80 if (error.Error() != S_OK || !duplication_) { | |
| 81 LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error " | |
| 82 << error.ErrorMessage() << ", with code " << error.Error(); | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 memset(&desc_, 0, sizeof(desc_)); | |
| 87 duplication_->GetDesc(&desc_); | |
| 88 if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) { | |
| 89 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) " | |
| 90 "format, which is required by downstream components, " | |
| 91 "format is " | |
| 92 << desc_.ModeDesc.Format; | |
| 93 return false; | |
| 94 } | |
| 95 | |
| 96 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() || | |
| 97 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { | |
| 98 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " | |
| 99 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " | |
| 100 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height | |
| 101 << ", size returned by IDXGIOutput1 is " | |
| 102 << desktop_rect_.width() << " x " << desktop_rect_.height(); | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 return true; | |
| 107 } | |
| 108 | |
| 109 bool DxgiOutputDuplicator::ReleaseFrame() { | |
| 110 RTC_DCHECK(duplication_); | |
| 111 _com_error error = duplication_->ReleaseFrame(); | |
| 112 if (error.Error() != S_OK) { | |
| 113 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " | |
| 114 "error" << error.ErrorMessage() << ", code " | |
| 115 << error.Error(); | |
| 116 return false; | |
| 117 } | |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 bool DxgiOutputDuplicator::Duplicate(DxgiContext* context, | |
| 122 DesktopFrame* target, | |
| 123 const DesktopFrame* last_frame, | |
| 124 const DesktopVector offset) { | |
| 125 RTC_DCHECK(duplication_); | |
| 126 RTC_DCHECK(texture_); | |
| 127 RTC_DCHECK(target); | |
| 128 DXGI_OUTDUPL_FRAME_INFO frame_info; | |
| 129 memset(&frame_info, 0, sizeof(frame_info)); | |
| 130 ComPtr<IDXGIResource> resource; | |
|
Sergey Ulanov
2016/07/26 00:48:51
Move this just before AcquireNextFrame() call.
Hzj_jie
2016/07/26 02:13:02
I should move updated_region and the following lin
| |
| 131 // We need to merge updated region with the one from last frame, since current | |
| 132 // frame contains the content one frame before. | |
| 133 DesktopRegion updated_region; | |
| 134 updated_region.AddRegion(context->updated_region_); | |
|
Sergey Ulanov
2016/07/26 00:48:51
Add an empty line after this one as the comment ab
Sergey Ulanov
2016/07/26 00:48:51
updated_region = context->updated_region_;
No need
Hzj_jie
2016/07/26 02:13:02
Acknowledged.
Hzj_jie
2016/07/26 02:13:02
Done.
| |
| 135 _com_error error = duplication_->AcquireNextFrame( | |
| 136 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); | |
| 137 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { | |
| 138 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() | |
| 139 << ", code " << error.Error(); | |
| 140 return false; | |
| 141 } | |
| 142 | |
| 143 if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) { | |
| 144 DetectUpdatedRegion(frame_info, offset, &context->updated_region_); | |
|
Sergey Ulanov
2016/07/26 00:48:51
I do not understand how this will work with multip
Hzj_jie
2016/07/26 02:13:02
Two capturers will have two DxgiContext instances.
Hzj_jie
2016/07/26 03:45:35
Sorry, this is not correct. Instantiating several
| |
| 145 updated_region.AddRegion(context->updated_region_); | |
| 146 if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) { | |
| 147 return false; | |
| 148 } | |
| 149 | |
| 150 const DesktopFrame& source = texture_->AsDesktopFrame(); | |
| 151 DesktopRect target_rect(DesktopRect::MakeSize(target->size())); | |
| 152 for (DesktopRegion::Iterator it(updated_region); | |
| 153 !it.IsAtEnd(); | |
| 154 it.Advance()) { | |
| 155 if (!target_rect.ContainsRect(it.rect())) { | |
| 156 // target size is not large enough to copy the pixel from texture. | |
| 157 return false; | |
| 158 } | |
| 159 target->CopyPixelsFrom(source, | |
| 160 it.rect().top_left().subtract(offset), | |
| 161 it.rect()); | |
| 162 } | |
| 163 target->mutable_updated_region()->AddRegion(updated_region); | |
| 164 return texture_->Release() && ReleaseFrame(); | |
| 165 } | |
| 166 | |
| 167 if (last_frame != nullptr) { | |
| 168 // DxgiOutputDuplicatorContainer::Duplicate makes sure target size and last | |
|
Sergey Ulanov
2016/07/26 00:48:52
s/Duplicate/Duplicate()/
Same for other comments.
Hzj_jie
2016/07/26 02:13:02
Done.
| |
| 169 // frame size are consistent. | |
| 170 RTC_DCHECK(target->size().equals(last_frame->size())); | |
| 171 // No change since last frame or AcquireNextFrame timed out, we export last | |
| 172 // frame to the target. | |
| 173 context->updated_region_.Clear(); | |
| 174 for (DesktopRegion::Iterator it(updated_region); | |
| 175 !it.IsAtEnd(); | |
| 176 it.Advance()) { | |
| 177 target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect()); | |
| 178 } | |
| 179 target->mutable_updated_region()->AddRegion(updated_region); | |
| 180 } | |
| 181 // If AcquireNextFrame failed with timeout error, we do not need to release | |
| 182 // the frame. | |
| 183 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame(); | |
| 184 } | |
| 185 | |
| 186 DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect( | |
| 187 const DesktopVector offset) { | |
| 188 DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size())); | |
| 189 result.Translate(offset); | |
| 190 return result; | |
| 191 } | |
| 192 | |
| 193 void DxgiOutputDuplicator::DetectUpdatedRegion( | |
| 194 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | |
| 195 const DesktopVector offset, | |
| 196 DesktopRegion* updated_region) { | |
| 197 if (DoDetectUpdatedRegion(frame_info, updated_region)) { | |
| 198 updated_region->Translate(offset.x(), offset.y()); | |
| 199 // Make sure even a region returned by Windows API is out of the scope of | |
| 200 // desktop_rect_, we still won't export it to the target DesktopFrame. | |
| 201 updated_region->IntersectWith(TranslatedDesktopRect(offset)); | |
| 202 } else { | |
| 203 updated_region->SetRect(TranslatedDesktopRect(offset)); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 bool DxgiOutputDuplicator::DoDetectUpdatedRegion( | |
| 208 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | |
| 209 DesktopRegion* updated_region) { | |
| 210 RTC_DCHECK(updated_region); | |
| 211 updated_region->Clear(); | |
| 212 if (frame_info.TotalMetadataBufferSize == 0) { | |
| 213 // This should not happen, since frame_info.AccumulatedFrames > 0. | |
|
Sergey Ulanov
2016/07/26 00:48:52
Should this be a DCHECK if it's not supposed to ha
Hzj_jie
2016/07/26 02:13:02
I have not seen a clear comment in MSDN this won't
| |
| 214 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " | |
| 215 "but TotalMetadataBufferSize == 0"; | |
| 216 return false; | |
| 217 } | |
| 218 | |
| 219 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) { | |
| 220 metadata.clear(); // Avoid data copy | |
| 221 metadata.reserve(frame_info.TotalMetadataBufferSize); | |
| 222 } | |
| 223 | |
| 224 UINT buff_size = 0; | |
| 225 DXGI_OUTDUPL_MOVE_RECT* move_rects = | |
| 226 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data()); | |
| 227 size_t move_rects_count = 0; | |
| 228 _com_error error = _com_error(duplication_->GetFrameMoveRects( | |
| 229 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size)); | |
| 230 if (error.Error() != S_OK) { | |
| 231 LOG(LS_ERROR) << "Failed to get move rectangles, error " | |
| 232 << error.ErrorMessage() << ", code " << error.Error(); | |
| 233 return false; | |
| 234 } | |
| 235 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); | |
| 236 | |
| 237 RECT* dirty_rects = | |
| 238 reinterpret_cast<RECT*>(metadata.data() + buff_size); | |
| 239 size_t dirty_rects_count = 0; | |
| 240 error = _com_error(duplication_->GetFrameDirtyRects( | |
| 241 static_cast<UINT>(metadata.capacity()) - buff_size, | |
| 242 dirty_rects, &buff_size)); | |
| 243 if (error.Error() != S_OK) { | |
| 244 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " | |
| 245 << error.ErrorMessage() << ", code " << error.Error(); | |
| 246 return false; | |
| 247 } | |
| 248 dirty_rects_count = buff_size / sizeof(RECT); | |
| 249 | |
| 250 while (move_rects_count > 0) { | |
| 251 updated_region->AddRect(DesktopRect::MakeXYWH( | |
| 252 move_rects->SourcePoint.x, move_rects->SourcePoint.y, | |
| 253 move_rects->DestinationRect.right - move_rects->DestinationRect.left, | |
| 254 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); | |
| 255 updated_region->AddRect(DesktopRect::MakeLTRB( | |
| 256 move_rects->DestinationRect.left, move_rects->DestinationRect.top, | |
| 257 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); | |
| 258 move_rects++; | |
| 259 move_rects_count--; | |
| 260 } | |
| 261 | |
| 262 while (dirty_rects_count > 0) { | |
| 263 updated_region->AddRect(DesktopRect::MakeLTRB( | |
| 264 dirty_rects->left, dirty_rects->top, | |
| 265 dirty_rects->right, dirty_rects->bottom)); | |
| 266 dirty_rects++; | |
| 267 dirty_rects_count--; | |
| 268 } | |
| 269 | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 void DxgiOutputDuplicator::Setup(DxgiContext* context) { | |
| 274 RTC_DCHECK(context->contexts_.empty()); | |
| 275 RTC_DCHECK(context->updated_region_.is_empty()); | |
| 276 } | |
| 277 | |
| 278 } // namespace webrtc | |
| OLD | NEW |