| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h" | 11 #include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h" |
| 12 | 12 |
| 13 #include <string.h> | 13 #include <string.h> |
| 14 | 14 |
| 15 #include <unknwn.h> | 15 #include <unknwn.h> |
| 16 #include <DXGI.h> |
| 16 #include <DXGIFormat.h> | 17 #include <DXGIFormat.h> |
| 17 #include <Windows.h> | 18 #include <Windows.h> |
| 18 | 19 |
| 19 #include <algorithm> | 20 #include <algorithm> |
| 20 | 21 |
| 21 #include "webrtc/base/checks.h" | 22 #include "webrtc/base/checks.h" |
| 22 #include "webrtc/base/logging.h" | 23 #include "webrtc/base/logging.h" |
| 23 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h" | 24 #include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h" |
| 24 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h" | 25 #include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h" |
| 25 | 26 |
| 26 namespace webrtc { | 27 namespace webrtc { |
| 27 | 28 |
| 28 using Microsoft::WRL::ComPtr; | 29 using Microsoft::WRL::ComPtr; |
| 29 | 30 |
| 30 namespace { | 31 namespace { |
| 31 | 32 |
| 32 // Timeout for AcquireNextFrame() call. | 33 // Timeout for AcquireNextFrame() call. |
| 33 const int kAcquireTimeoutMs = 10; | 34 const int kAcquireTimeoutMs = 10; |
| 34 | 35 |
| 35 DesktopRect RECTToDesktopRect(const RECT& rect) { | 36 DesktopRect RECTToDesktopRect(const RECT& rect) { |
| 36 return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); | 37 return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); |
| 37 } | 38 } |
| 38 | 39 |
| 40 Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) { |
| 41 switch (rotation) { |
| 42 case DXGI_MODE_ROTATION_IDENTITY: |
| 43 case DXGI_MODE_ROTATION_UNSPECIFIED: |
| 44 return Rotation::CLOCK_WISE_0; |
| 45 case DXGI_MODE_ROTATION_ROTATE90: |
| 46 return Rotation::CLOCK_WISE_90; |
| 47 case DXGI_MODE_ROTATION_ROTATE180: |
| 48 return Rotation::CLOCK_WISE_180; |
| 49 case DXGI_MODE_ROTATION_ROTATE270: |
| 50 return Rotation::CLOCK_WISE_270; |
| 51 } |
| 52 |
| 53 RTC_NOTREACHED(); |
| 54 return Rotation::CLOCK_WISE_0; |
| 55 } |
| 56 |
| 57 // Translates |rect| with the reverse of |offset| |
| 58 DesktopRect ReverseTranslate(DesktopRect rect, DesktopVector offset) { |
| 59 rect.Translate(-offset.x(), -offset.y()); |
| 60 return rect; |
| 61 } |
| 62 |
| 39 } // namespace | 63 } // namespace |
| 40 | 64 |
| 41 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, | 65 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, |
| 42 const ComPtr<IDXGIOutput1>& output, | 66 const ComPtr<IDXGIOutput1>& output, |
| 43 const DXGI_OUTPUT_DESC& desc) | 67 const DXGI_OUTPUT_DESC& desc) |
| 44 : device_(device), | 68 : device_(device), |
| 45 output_(output), | 69 output_(output), |
| 46 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { | 70 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { |
| 47 RTC_DCHECK(output_); | 71 RTC_DCHECK(output_); |
| 48 RTC_DCHECK(!desktop_rect_.is_empty()); | 72 RTC_DCHECK(!desktop_rect_.is_empty()); |
| 49 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0); | 73 RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0); |
| 50 } | 74 } |
| 51 | 75 |
| 52 DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) = | 76 DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) = |
| 53 default; | 77 default; |
| 54 | 78 |
| 55 DxgiOutputDuplicator::~DxgiOutputDuplicator() { | 79 DxgiOutputDuplicator::~DxgiOutputDuplicator() { |
| 56 if (duplication_) { | 80 if (duplication_) { |
| 57 duplication_->ReleaseFrame(); | 81 duplication_->ReleaseFrame(); |
| 58 } | 82 } |
| 59 texture_.reset(); | 83 texture_.reset(); |
| 60 } | 84 } |
| 61 | 85 |
| 62 bool DxgiOutputDuplicator::Initialize() { | 86 bool DxgiOutputDuplicator::Initialize() { |
| 63 if (DuplicateOutput()) { | 87 if (DuplicateOutput()) { |
| 88 DesktopSize unrotated_size = |
| 89 RotateSize(desktop_rect().size(), ReverseRotation(rotation_)); |
| 64 if (desc_.DesktopImageInSystemMemory) { | 90 if (desc_.DesktopImageInSystemMemory) { |
| 65 texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get())); | 91 texture_.reset( |
| 92 new DxgiTextureMapping(unrotated_size, duplication_.Get())); |
| 66 } else { | 93 } else { |
| 67 texture_.reset(new DxgiTextureStaging(desktop_rect_, device_)); | 94 texture_.reset(new DxgiTextureStaging(unrotated_size, device_)); |
| 68 } | 95 } |
| 69 return true; | 96 return true; |
| 70 } else { | 97 } else { |
| 71 duplication_.Reset(); | 98 duplication_.Reset(); |
| 72 return false; | 99 return false; |
| 73 } | 100 } |
| 74 } | 101 } |
| 75 | 102 |
| 76 bool DxgiOutputDuplicator::DuplicateOutput() { | 103 bool DxgiOutputDuplicator::DuplicateOutput() { |
| 77 RTC_DCHECK(!duplication_); | 104 RTC_DCHECK(!duplication_); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 97 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() || | 124 if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() || |
| 98 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { | 125 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { |
| 99 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " | 126 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " |
| 100 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " | 127 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " |
| 101 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height | 128 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height |
| 102 << ", size returned by IDXGIOutput1 is " | 129 << ", size returned by IDXGIOutput1 is " |
| 103 << desktop_rect_.width() << " x " << desktop_rect_.height(); | 130 << desktop_rect_.width() << " x " << desktop_rect_.height(); |
| 104 return false; | 131 return false; |
| 105 } | 132 } |
| 106 | 133 |
| 134 rotation_ = DxgiRotationToRotation(desc_.Rotation); |
| 135 unrotated_size_ = |
| 136 RotateSize(desktop_rect_.size(), ReverseRotation(rotation_)); |
| 137 |
| 107 return true; | 138 return true; |
| 108 } | 139 } |
| 109 | 140 |
| 110 bool DxgiOutputDuplicator::ReleaseFrame() { | 141 bool DxgiOutputDuplicator::ReleaseFrame() { |
| 111 RTC_DCHECK(duplication_); | 142 RTC_DCHECK(duplication_); |
| 112 _com_error error = duplication_->ReleaseFrame(); | 143 _com_error error = duplication_->ReleaseFrame(); |
| 113 if (error.Error() != S_OK) { | 144 if (error.Error() != S_OK) { |
| 114 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " | 145 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " |
| 115 "error" | 146 "error" |
| 116 << error.ErrorMessage() << ", code " << error.Error(); | 147 << error.ErrorMessage() << ", code " << error.Error(); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 135 memset(&frame_info, 0, sizeof(frame_info)); | 166 memset(&frame_info, 0, sizeof(frame_info)); |
| 136 ComPtr<IDXGIResource> resource; | 167 ComPtr<IDXGIResource> resource; |
| 137 _com_error error = duplication_->AcquireNextFrame( | 168 _com_error error = duplication_->AcquireNextFrame( |
| 138 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); | 169 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); |
| 139 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { | 170 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { |
| 140 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() | 171 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() |
| 141 << ", code " << error.Error(); | 172 << ", code " << error.Error(); |
| 142 return false; | 173 return false; |
| 143 } | 174 } |
| 144 | 175 |
| 145 // We need to merge updated region with the one from last frame, since current | 176 // We need to merge updated region with the one from context, but only spread |
| 146 // frame contains the content one frame before. Note, this is for double | 177 // updated region from current frame. So keeps a copy of updated region from |
| 147 // buffering implementation, as what we have in ScreenCapturerWinDirectx. If | 178 // context here. |
| 148 // a consumer uses single buffering, we should clear context->updated_region | |
| 149 // after it has been merged to updated_region. | |
| 150 DesktopRegion updated_region; | 179 DesktopRegion updated_region; |
| 151 updated_region.Swap(&context->updated_region); | 180 updated_region.Swap(&context->updated_region); |
| 152 if (error.Error() == S_OK && | 181 if (error.Error() == S_OK && |
| 153 frame_info.AccumulatedFrames > 0 && | 182 frame_info.AccumulatedFrames > 0 && |
| 154 resource) { | 183 resource) { |
| 155 DetectUpdatedRegion(frame_info, offset, &context->updated_region); | 184 DetectUpdatedRegion(frame_info, offset, &context->updated_region); |
| 156 if (!texture_->CopyFrom(frame_info, resource.Get(), | 185 if (!texture_->CopyFrom(frame_info, resource.Get())) { |
| 157 context->updated_region)) { | |
| 158 return false; | 186 return false; |
| 159 } | 187 } |
| 160 SpreadContextChange(context); | 188 SpreadContextChange(context); |
| 161 updated_region.AddRegion(context->updated_region); | 189 updated_region.AddRegion(context->updated_region); |
| 162 | 190 |
| 163 const DesktopFrame& source = texture_->AsDesktopFrame(); | 191 const DesktopFrame& source = texture_->AsDesktopFrame(); |
| 164 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | 192 if (rotation_ != Rotation::CLOCK_WISE_0) { |
| 165 it.Advance()) { | 193 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 166 target->CopyPixelsFrom( | 194 it.Advance()) { |
| 167 source, SourceRect(it.rect()).top_left(), it.rect()); | 195 const DesktopRect source_rect = |
| 196 RotateRect(ReverseTranslate(it.rect(), offset), |
| 197 desktop_rect().size(), ReverseRotation(rotation_)); |
| 198 RotateDesktopFrame(source, source_rect, rotation_, offset, target); |
| 199 } |
| 200 } else { |
| 201 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 202 it.Advance()) { |
| 203 target->CopyPixelsFrom( |
| 204 source, ReverseTranslate(it.rect(), offset).top_left(), it.rect()); |
| 205 } |
| 168 } | 206 } |
| 169 last_frame_ = target->Share(); | 207 last_frame_ = target->Share(); |
| 170 last_frame_offset_ = offset; | 208 last_frame_offset_ = offset; |
| 171 target->mutable_updated_region()->AddRegion(updated_region); | 209 target->mutable_updated_region()->AddRegion(updated_region); |
| 172 return texture_->Release() && ReleaseFrame(); | 210 return texture_->Release() && ReleaseFrame(); |
| 173 } | 211 } |
| 174 | 212 |
| 175 if (last_frame_) { | 213 if (last_frame_) { |
| 176 // No change since last frame or AcquireNextFrame() timed out, we will | 214 // No change since last frame or AcquireNextFrame() timed out, we will |
| 177 // export last frame to the target. | 215 // export last frame to the target. |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 DesktopRegion* updated_region) { | 249 DesktopRegion* updated_region) { |
| 212 RTC_DCHECK(updated_region); | 250 RTC_DCHECK(updated_region); |
| 213 updated_region->Clear(); | 251 updated_region->Clear(); |
| 214 if (frame_info.TotalMetadataBufferSize == 0) { | 252 if (frame_info.TotalMetadataBufferSize == 0) { |
| 215 // This should not happen, since frame_info.AccumulatedFrames > 0. | 253 // This should not happen, since frame_info.AccumulatedFrames > 0. |
| 216 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " | 254 LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, " |
| 217 "but TotalMetadataBufferSize == 0"; | 255 "but TotalMetadataBufferSize == 0"; |
| 218 return false; | 256 return false; |
| 219 } | 257 } |
| 220 | 258 |
| 221 if (metadata.capacity() < frame_info.TotalMetadataBufferSize) { | 259 if (metadata_.capacity() < frame_info.TotalMetadataBufferSize) { |
| 222 metadata.clear(); // Avoid data copy | 260 metadata_.clear(); // Avoid data copy |
| 223 metadata.reserve(frame_info.TotalMetadataBufferSize); | 261 metadata_.reserve(frame_info.TotalMetadataBufferSize); |
| 224 } | 262 } |
| 225 | 263 |
| 226 UINT buff_size = 0; | 264 UINT buff_size = 0; |
| 227 DXGI_OUTDUPL_MOVE_RECT* move_rects = | 265 DXGI_OUTDUPL_MOVE_RECT* move_rects = |
| 228 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data()); | 266 reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data()); |
| 229 size_t move_rects_count = 0; | 267 size_t move_rects_count = 0; |
| 230 _com_error error = duplication_->GetFrameMoveRects( | 268 _com_error error = duplication_->GetFrameMoveRects( |
| 231 static_cast<UINT>(metadata.capacity()), move_rects, &buff_size); | 269 static_cast<UINT>(metadata_.capacity()), move_rects, &buff_size); |
| 232 if (error.Error() != S_OK) { | 270 if (error.Error() != S_OK) { |
| 233 LOG(LS_ERROR) << "Failed to get move rectangles, error " | 271 LOG(LS_ERROR) << "Failed to get move rectangles, error " |
| 234 << error.ErrorMessage() << ", code " << error.Error(); | 272 << error.ErrorMessage() << ", code " << error.Error(); |
| 235 return false; | 273 return false; |
| 236 } | 274 } |
| 237 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); | 275 move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); |
| 238 | 276 |
| 239 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size); | 277 RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size); |
| 240 size_t dirty_rects_count = 0; | 278 size_t dirty_rects_count = 0; |
| 241 error = duplication_->GetFrameDirtyRects( | 279 error = duplication_->GetFrameDirtyRects( |
| 242 static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects, | 280 static_cast<UINT>(metadata_.capacity()) - buff_size, dirty_rects, |
| 243 &buff_size); | 281 &buff_size); |
| 244 if (error.Error() != S_OK) { | 282 if (error.Error() != S_OK) { |
| 245 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " | 283 LOG(LS_ERROR) << "Failed to get dirty rectangles, error " |
| 246 << error.ErrorMessage() << ", code " << error.Error(); | 284 << error.ErrorMessage() << ", code " << error.Error(); |
| 247 return false; | 285 return false; |
| 248 } | 286 } |
| 249 dirty_rects_count = buff_size / sizeof(RECT); | 287 dirty_rects_count = buff_size / sizeof(RECT); |
| 250 | 288 |
| 251 while (move_rects_count > 0) { | 289 while (move_rects_count > 0) { |
| 252 updated_region->AddRect(DesktopRect::MakeXYWH( | 290 updated_region->AddRect( |
| 253 move_rects->SourcePoint.x, move_rects->SourcePoint.y, | 291 RotateRect(DesktopRect::MakeXYWH(move_rects->SourcePoint.x, |
| 254 move_rects->DestinationRect.right - move_rects->DestinationRect.left, | 292 move_rects->SourcePoint.y, |
| 255 move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); | 293 move_rects->DestinationRect.right - |
| 256 updated_region->AddRect(DesktopRect::MakeLTRB( | 294 move_rects->DestinationRect.left, |
| 257 move_rects->DestinationRect.left, move_rects->DestinationRect.top, | 295 move_rects->DestinationRect.bottom - |
| 258 move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); | 296 move_rects->DestinationRect.top), |
| 297 unrotated_size_, rotation_)); |
| 298 updated_region->AddRect( |
| 299 RotateRect(DesktopRect::MakeLTRB(move_rects->DestinationRect.left, |
| 300 move_rects->DestinationRect.top, |
| 301 move_rects->DestinationRect.right, |
| 302 move_rects->DestinationRect.bottom), |
| 303 unrotated_size_, rotation_)); |
| 259 move_rects++; | 304 move_rects++; |
| 260 move_rects_count--; | 305 move_rects_count--; |
| 261 } | 306 } |
| 262 | 307 |
| 263 while (dirty_rects_count > 0) { | 308 while (dirty_rects_count > 0) { |
| 264 updated_region->AddRect( | 309 updated_region->AddRect(RotateRect( |
| 265 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, | 310 DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, |
| 266 dirty_rects->right, dirty_rects->bottom)); | 311 dirty_rects->right, dirty_rects->bottom), |
| 312 unrotated_size_, rotation_)); |
| 267 dirty_rects++; | 313 dirty_rects++; |
| 268 dirty_rects_count--; | 314 dirty_rects_count--; |
| 269 } | 315 } |
| 270 | 316 |
| 271 return true; | 317 return true; |
| 272 } | 318 } |
| 273 | 319 |
| 274 void DxgiOutputDuplicator::Setup(Context* context) { | 320 void DxgiOutputDuplicator::Setup(Context* context) { |
| 275 RTC_DCHECK(context->updated_region.is_empty()); | 321 RTC_DCHECK(context->updated_region.is_empty()); |
| 276 // Always copy entire monitor during the first Duplicate() function call. | 322 // Always copy entire monitor during the first Duplicate() function call. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 288 | 334 |
| 289 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { | 335 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { |
| 290 for (Context* dest : contexts_) { | 336 for (Context* dest : contexts_) { |
| 291 RTC_DCHECK(dest); | 337 RTC_DCHECK(dest); |
| 292 if (dest != source) { | 338 if (dest != source) { |
| 293 dest->updated_region.AddRegion(source->updated_region); | 339 dest->updated_region.AddRegion(source->updated_region); |
| 294 } | 340 } |
| 295 } | 341 } |
| 296 } | 342 } |
| 297 | 343 |
| 298 DesktopRect DxgiOutputDuplicator::SourceRect(DesktopRect rect) { | |
| 299 // |texture_|->AsDesktopFrame() starts from (0, 0). | |
| 300 rect.Translate(-desktop_rect_.left(), -desktop_rect_.top()); | |
| 301 return rect; | |
| 302 } | |
| 303 | |
| 304 } // namespace webrtc | 344 } // namespace webrtc |
| OLD | NEW |