| 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 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 case DXGI_MODE_ROTATION_ROTATE180: | 50 case DXGI_MODE_ROTATION_ROTATE180: |
| 51 return Rotation::CLOCK_WISE_180; | 51 return Rotation::CLOCK_WISE_180; |
| 52 case DXGI_MODE_ROTATION_ROTATE270: | 52 case DXGI_MODE_ROTATION_ROTATE270: |
| 53 return Rotation::CLOCK_WISE_270; | 53 return Rotation::CLOCK_WISE_270; |
| 54 } | 54 } |
| 55 | 55 |
| 56 RTC_NOTREACHED(); | 56 RTC_NOTREACHED(); |
| 57 return Rotation::CLOCK_WISE_0; | 57 return Rotation::CLOCK_WISE_0; |
| 58 } | 58 } |
| 59 | 59 |
| 60 // Translates |rect| with the reverse of |offset| | |
| 61 DesktopRect ReverseTranslate(DesktopRect rect, DesktopVector offset) { | |
| 62 rect.Translate(-offset.x(), -offset.y()); | |
| 63 return rect; | |
| 64 } | |
| 65 | |
| 66 } // namespace | 60 } // namespace |
| 67 | 61 |
| 68 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, | 62 DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, |
| 69 const ComPtr<IDXGIOutput1>& output, | 63 const ComPtr<IDXGIOutput1>& output, |
| 70 const DXGI_OUTPUT_DESC& desc) | 64 const DXGI_OUTPUT_DESC& desc) |
| 71 : device_(device), | 65 : device_(device), |
| 72 output_(output), | 66 output_(output), |
| 73 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { | 67 desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) { |
| 74 RTC_DCHECK(output_); | 68 RTC_DCHECK(output_); |
| 75 RTC_DCHECK(!desktop_rect_.is_empty()); | 69 RTC_DCHECK(!desktop_rect_.is_empty()); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { | 119 static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) { |
| 126 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " | 120 LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its " |
| 127 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " | 121 "IDXGIOutput1, size returned by IDXGIDuplicateOutput is " |
| 128 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height | 122 << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height |
| 129 << ", size returned by IDXGIOutput1 is " | 123 << ", size returned by IDXGIOutput1 is " |
| 130 << desktop_rect_.width() << " x " << desktop_rect_.height(); | 124 << desktop_rect_.width() << " x " << desktop_rect_.height(); |
| 131 return false; | 125 return false; |
| 132 } | 126 } |
| 133 | 127 |
| 134 rotation_ = DxgiRotationToRotation(desc_.Rotation); | 128 rotation_ = DxgiRotationToRotation(desc_.Rotation); |
| 135 unrotated_size_ = | 129 unrotated_size_ = RotateSize(desktop_size(), ReverseRotation(rotation_)); |
| 136 RotateSize(desktop_rect_.size(), ReverseRotation(rotation_)); | |
| 137 | 130 |
| 138 return true; | 131 return true; |
| 139 } | 132 } |
| 140 | 133 |
| 141 bool DxgiOutputDuplicator::ReleaseFrame() { | 134 bool DxgiOutputDuplicator::ReleaseFrame() { |
| 142 RTC_DCHECK(duplication_); | 135 RTC_DCHECK(duplication_); |
| 143 _com_error error = duplication_->ReleaseFrame(); | 136 _com_error error = duplication_->ReleaseFrame(); |
| 144 if (error.Error() != S_OK) { | 137 if (error.Error() != S_OK) { |
| 145 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " | 138 LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, " |
| 146 "error" | 139 "error" |
| 147 << error.ErrorMessage() << ", code " << error.Error(); | 140 << error.ErrorMessage() << ", code " << error.Error(); |
| 148 return false; | 141 return false; |
| 149 } | 142 } |
| 150 return true; | 143 return true; |
| 151 } | 144 } |
| 152 | 145 |
| 153 bool DxgiOutputDuplicator::Duplicate(Context* context, | 146 bool DxgiOutputDuplicator::Duplicate(Context* context, |
| 154 DesktopVector offset, | 147 DesktopVector offset, |
| 155 SharedDesktopFrame* target) { | 148 SharedDesktopFrame* target) { |
| 156 RTC_DCHECK(duplication_); | 149 RTC_DCHECK(duplication_); |
| 157 RTC_DCHECK(texture_); | 150 RTC_DCHECK(texture_); |
| 158 RTC_DCHECK(target); | 151 RTC_DCHECK(target); |
| 159 if (!DesktopRect::MakeSize(target->size()) | 152 if (!DesktopRect::MakeSize(target->size()) |
| 160 .ContainsRect(TranslatedDesktopRect(offset))) { | 153 .ContainsRect(GetTranslatedDesktopRect(offset))) { |
| 161 // target size is not large enough to cover current output region. | 154 // target size is not large enough to cover current output region. |
| 162 return false; | 155 return false; |
| 163 } | 156 } |
| 164 | 157 |
| 165 DXGI_OUTDUPL_FRAME_INFO frame_info; | 158 DXGI_OUTDUPL_FRAME_INFO frame_info; |
| 166 memset(&frame_info, 0, sizeof(frame_info)); | 159 memset(&frame_info, 0, sizeof(frame_info)); |
| 167 ComPtr<IDXGIResource> resource; | 160 ComPtr<IDXGIResource> resource; |
| 168 _com_error error = duplication_->AcquireNextFrame( | 161 _com_error error = duplication_->AcquireNextFrame( |
| 169 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); | 162 kAcquireTimeoutMs, &frame_info, resource.GetAddressOf()); |
| 170 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { | 163 if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) { |
| 171 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() | 164 LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage() |
| 172 << ", code " << error.Error(); | 165 << ", code " << error.Error(); |
| 173 return false; | 166 return false; |
| 174 } | 167 } |
| 175 | 168 |
| 176 // We need to merge updated region with the one from context, but only spread | 169 // We need to merge updated region with the one from context, but only spread |
| 177 // updated region from current frame. So keeps a copy of updated region from | 170 // updated region from current frame. So keeps a copy of updated region from |
| 178 // context here. | 171 // context here. The |updated_region| always starts from (0, 0). |
| 179 DesktopRegion updated_region; | 172 DesktopRegion updated_region; |
| 180 updated_region.Swap(&context->updated_region); | 173 updated_region.Swap(&context->updated_region); |
| 181 if (error.Error() == S_OK && | 174 if (error.Error() == S_OK && |
| 182 frame_info.AccumulatedFrames > 0 && | 175 frame_info.AccumulatedFrames > 0 && |
| 183 resource) { | 176 resource) { |
| 184 DetectUpdatedRegion(frame_info, offset, &context->updated_region); | 177 DetectUpdatedRegion(frame_info, &context->updated_region); |
| 178 SpreadContextChange(context); |
| 185 if (!texture_->CopyFrom(frame_info, resource.Get())) { | 179 if (!texture_->CopyFrom(frame_info, resource.Get())) { |
| 186 return false; | 180 return false; |
| 187 } | 181 } |
| 188 SpreadContextChange(context); | |
| 189 updated_region.AddRegion(context->updated_region); | 182 updated_region.AddRegion(context->updated_region); |
| 183 // TODO(zijiehe): Figure out why clearing context->updated_region() here |
| 184 // triggers screen flickering? |
| 190 | 185 |
| 191 const DesktopFrame& source = texture_->AsDesktopFrame(); | 186 const DesktopFrame& source = texture_->AsDesktopFrame(); |
| 192 if (rotation_ != Rotation::CLOCK_WISE_0) { | 187 if (rotation_ != Rotation::CLOCK_WISE_0) { |
| 193 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | 188 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 194 it.Advance()) { | 189 it.Advance()) { |
| 195 const DesktopRect source_rect = | 190 // The |updated_region| returned by Windows is rotated, but the |source| |
| 196 RotateRect(ReverseTranslate(it.rect(), offset), | 191 // frame is not. So we need to rotate it reversely. |
| 197 desktop_rect().size(), ReverseRotation(rotation_)); | 192 const DesktopRect source_rect = RotateRect( |
| 193 it.rect(), desktop_size(), ReverseRotation(rotation_)); |
| 198 RotateDesktopFrame(source, source_rect, rotation_, offset, target); | 194 RotateDesktopFrame(source, source_rect, rotation_, offset, target); |
| 199 } | 195 } |
| 200 } else { | 196 } else { |
| 201 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | 197 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 202 it.Advance()) { | 198 it.Advance()) { |
| 203 target->CopyPixelsFrom( | 199 // The DesktopRect in |target|, starts from offset. |
| 204 source, ReverseTranslate(it.rect(), offset).top_left(), it.rect()); | 200 DesktopRect dest_rect = it.rect(); |
| 201 dest_rect.Translate(offset); |
| 202 target->CopyPixelsFrom(source, it.rect().top_left(), dest_rect); |
| 205 } | 203 } |
| 206 } | 204 } |
| 207 last_frame_ = target->Share(); | 205 last_frame_ = target->Share(); |
| 208 last_frame_offset_ = offset; | 206 last_frame_offset_ = offset; |
| 207 updated_region.Translate(offset.x(), offset.y()); |
| 209 target->mutable_updated_region()->AddRegion(updated_region); | 208 target->mutable_updated_region()->AddRegion(updated_region); |
| 210 num_frames_captured_++; | 209 num_frames_captured_++; |
| 211 return texture_->Release() && ReleaseFrame(); | 210 return texture_->Release() && ReleaseFrame(); |
| 212 } | 211 } |
| 213 | 212 |
| 214 if (last_frame_) { | 213 if (last_frame_) { |
| 215 // No change since last frame or AcquireNextFrame() timed out, we will | 214 // No change since last frame or AcquireNextFrame() timed out, we will |
| 216 // export last frame to the target. | 215 // export last frame to the target. |
| 217 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); | 216 for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); |
| 218 it.Advance()) { | 217 it.Advance()) { |
| 219 target->CopyPixelsFrom(*last_frame_, it.rect().top_left(), it.rect()); | 218 // The DesktopRect in |source|, starts from last_frame_offset_. |
| 219 DesktopRect source_rect = it.rect(); |
| 220 // The DesktopRect in |target|, starts from offset. |
| 221 DesktopRect target_rect = source_rect; |
| 222 source_rect.Translate(last_frame_offset_); |
| 223 target_rect.Translate(offset); |
| 224 target->CopyPixelsFrom(*last_frame_, source_rect.top_left(), target_rect); |
| 220 } | 225 } |
| 226 updated_region.Translate(offset.x(), offset.y()); |
| 221 target->mutable_updated_region()->AddRegion(updated_region); | 227 target->mutable_updated_region()->AddRegion(updated_region); |
| 222 } else { | 228 } else { |
| 223 // If we were at the very first frame, and capturing failed, the | 229 // If we were at the very first frame, and capturing failed, the |
| 224 // context->updated_region should be kept unchanged for next attempt. | 230 // context->updated_region should be kept unchanged for next attempt. |
| 225 context->updated_region.Swap(&updated_region); | 231 context->updated_region.Swap(&updated_region); |
| 226 } | 232 } |
| 227 // If AcquireNextFrame() failed with timeout error, we do not need to release | 233 // If AcquireNextFrame() failed with timeout error, we do not need to release |
| 228 // the frame. | 234 // the frame. |
| 229 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame(); | 235 return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame(); |
| 230 } | 236 } |
| 231 | 237 |
| 232 DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(DesktopVector offset) { | 238 DesktopRect DxgiOutputDuplicator::GetTranslatedDesktopRect( |
| 233 DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size())); | 239 DesktopVector offset) const { |
| 240 DesktopRect result(DesktopRect::MakeSize(desktop_size())); |
| 234 result.Translate(offset); | 241 result.Translate(offset); |
| 235 return result; | 242 return result; |
| 236 } | 243 } |
| 237 | 244 |
| 245 DesktopRect DxgiOutputDuplicator::GetUntranslatedDesktopRect() const { |
| 246 return DesktopRect::MakeSize(desktop_size()); |
| 247 } |
| 248 |
| 238 void DxgiOutputDuplicator::DetectUpdatedRegion( | 249 void DxgiOutputDuplicator::DetectUpdatedRegion( |
| 239 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | 250 const DXGI_OUTDUPL_FRAME_INFO& frame_info, |
| 240 DesktopVector offset, | |
| 241 DesktopRegion* updated_region) { | 251 DesktopRegion* updated_region) { |
| 242 if (DoDetectUpdatedRegion(frame_info, updated_region)) { | 252 if (DoDetectUpdatedRegion(frame_info, updated_region)) { |
| 243 updated_region->Translate(offset.x(), offset.y()); | |
| 244 // Make sure even a region returned by Windows API is out of the scope of | 253 // Make sure even a region returned by Windows API is out of the scope of |
| 245 // desktop_rect_, we still won't export it to the target DesktopFrame. | 254 // desktop_rect_, we still won't export it to the target DesktopFrame. |
| 246 updated_region->IntersectWith(TranslatedDesktopRect(offset)); | 255 updated_region->IntersectWith(GetUntranslatedDesktopRect()); |
| 247 } else { | 256 } else { |
| 248 updated_region->SetRect(TranslatedDesktopRect(offset)); | 257 updated_region->SetRect(GetUntranslatedDesktopRect()); |
| 249 } | 258 } |
| 250 } | 259 } |
| 251 | 260 |
| 252 bool DxgiOutputDuplicator::DoDetectUpdatedRegion( | 261 bool DxgiOutputDuplicator::DoDetectUpdatedRegion( |
| 253 const DXGI_OUTDUPL_FRAME_INFO& frame_info, | 262 const DXGI_OUTDUPL_FRAME_INFO& frame_info, |
| 254 DesktopRegion* updated_region) { | 263 DesktopRegion* updated_region) { |
| 255 RTC_DCHECK(updated_region); | 264 RTC_DCHECK(updated_region); |
| 256 updated_region->Clear(); | 265 updated_region->Clear(); |
| 257 if (frame_info.TotalMetadataBufferSize == 0) { | 266 if (frame_info.TotalMetadataBufferSize == 0) { |
| 258 // This should not happen, since frame_info.AccumulatedFrames > 0. | 267 // This should not happen, since frame_info.AccumulatedFrames > 0. |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 dirty_rects++; | 342 dirty_rects++; |
| 334 dirty_rects_count--; | 343 dirty_rects_count--; |
| 335 } | 344 } |
| 336 | 345 |
| 337 return true; | 346 return true; |
| 338 } | 347 } |
| 339 | 348 |
| 340 void DxgiOutputDuplicator::Setup(Context* context) { | 349 void DxgiOutputDuplicator::Setup(Context* context) { |
| 341 RTC_DCHECK(context->updated_region.is_empty()); | 350 RTC_DCHECK(context->updated_region.is_empty()); |
| 342 // Always copy entire monitor during the first Duplicate() function call. | 351 // Always copy entire monitor during the first Duplicate() function call. |
| 343 context->updated_region.AddRect(desktop_rect_); | 352 context->updated_region.AddRect(GetUntranslatedDesktopRect()); |
| 344 RTC_DCHECK(std::find(contexts_.begin(), contexts_.end(), context) == | 353 RTC_DCHECK(std::find(contexts_.begin(), contexts_.end(), context) == |
| 345 contexts_.end()); | 354 contexts_.end()); |
| 346 contexts_.push_back(context); | 355 contexts_.push_back(context); |
| 347 } | 356 } |
| 348 | 357 |
| 349 void DxgiOutputDuplicator::Unregister(const Context* const context) { | 358 void DxgiOutputDuplicator::Unregister(const Context* const context) { |
| 350 auto it = std::find(contexts_.begin(), contexts_.end(), context); | 359 auto it = std::find(contexts_.begin(), contexts_.end(), context); |
| 351 RTC_DCHECK(it != contexts_.end()); | 360 RTC_DCHECK(it != contexts_.end()); |
| 352 contexts_.erase(it); | 361 contexts_.erase(it); |
| 353 } | 362 } |
| 354 | 363 |
| 355 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { | 364 void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { |
| 356 for (Context* dest : contexts_) { | 365 for (Context* dest : contexts_) { |
| 357 RTC_DCHECK(dest); | 366 RTC_DCHECK(dest); |
| 358 if (dest != source) { | 367 if (dest != source) { |
| 359 dest->updated_region.AddRegion(source->updated_region); | 368 dest->updated_region.AddRegion(source->updated_region); |
| 360 } | 369 } |
| 361 } | 370 } |
| 362 } | 371 } |
| 363 | 372 |
| 373 DesktopSize DxgiOutputDuplicator::desktop_size() const { |
| 374 return desktop_rect_.size(); |
| 375 } |
| 376 |
| 364 int64_t DxgiOutputDuplicator::num_frames_captured() const { | 377 int64_t DxgiOutputDuplicator::num_frames_captured() const { |
| 365 #if !defined(NDEBUG) | 378 #if !defined(NDEBUG) |
| 366 RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0); | 379 RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0); |
| 367 #endif | 380 #endif |
| 368 return num_frames_captured_; | 381 return num_frames_captured_; |
| 369 } | 382 } |
| 370 | 383 |
| 371 } // namespace webrtc | 384 } // namespace webrtc |
| OLD | NEW |