| Index: webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
 | 
| diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..7ce17c66a464e739537fb01202b97e1b92e894b8
 | 
| --- /dev/null
 | 
| +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
 | 
| @@ -0,0 +1,304 @@
 | 
| +/*
 | 
| + *  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/dxgi_output_duplicator.h"
 | 
| +
 | 
| +#include <string.h>
 | 
| +
 | 
| +#include <unknwn.h>
 | 
| +#include <DXGIFormat.h>
 | 
| +#include <Windows.h>
 | 
| +
 | 
| +#include "webrtc/base/checks.h"
 | 
| +#include "webrtc/base/logging.h"
 | 
| +#include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h"
 | 
| +#include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h"
 | 
| +
 | 
| +namespace webrtc {
 | 
| +
 | 
| +using Microsoft::WRL::ComPtr;
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// Timeout for AcquireNextFrame() call.
 | 
| +const int kAcquireTimeoutMs = 10;
 | 
| +
 | 
| +DesktopRect RECTToDesktopRect(const RECT& rect) {
 | 
| +  return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
 | 
| +                                           const ComPtr<IDXGIOutput1>& output,
 | 
| +                                           const DXGI_OUTPUT_DESC& desc)
 | 
| +    : device_(device),
 | 
| +      output_(output),
 | 
| +      desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
 | 
| +  RTC_DCHECK(output_);
 | 
| +  RTC_DCHECK(!desktop_rect_.is_empty());
 | 
| +  RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0);
 | 
| +}
 | 
| +
 | 
| +DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
 | 
| +    default;
 | 
| +
 | 
| +DxgiOutputDuplicator::~DxgiOutputDuplicator() {
 | 
| +  if (duplication_) {
 | 
| +    duplication_->ReleaseFrame();
 | 
| +  }
 | 
| +  texture_.reset();
 | 
| +}
 | 
| +
 | 
| +bool DxgiOutputDuplicator::Initialize() {
 | 
| +  if (DuplicateOutput()) {
 | 
| +    if (desc_.DesktopImageInSystemMemory) {
 | 
| +      texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get()));
 | 
| +    } else {
 | 
| +      texture_.reset(new DxgiTextureStaging(desktop_rect_, device_));
 | 
| +    }
 | 
| +    return true;
 | 
| +  } else {
 | 
| +    duplication_.Reset();
 | 
| +    return false;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +bool DxgiOutputDuplicator::DuplicateOutput() {
 | 
| +  RTC_DCHECK(!duplication_);
 | 
| +  _com_error error =
 | 
| +      output_->DuplicateOutput(static_cast<IUnknown*>(device_.d3d_device()),
 | 
| +                               duplication_.GetAddressOf());
 | 
| +  if (error.Error() != S_OK || !duplication_) {
 | 
| +    LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
 | 
| +                    << error.ErrorMessage() << ", with code " << error.Error();
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  memset(&desc_, 0, sizeof(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;
 | 
| +  }
 | 
| +
 | 
| +  if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
 | 
| +      static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
 | 
| +    LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its "
 | 
| +                     "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
 | 
| +                  << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
 | 
| +                  << ", size returned by IDXGIOutput1 is "
 | 
| +                  << desktop_rect_.width() << " x " << desktop_rect_.height();
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool DxgiOutputDuplicator::ReleaseFrame() {
 | 
| +  RTC_DCHECK(duplication_);
 | 
| +  _com_error error = duplication_->ReleaseFrame();
 | 
| +  if (error.Error() != S_OK) {
 | 
| +    LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, "
 | 
| +                     "error"
 | 
| +                  << error.ErrorMessage() << ", code " << error.Error();
 | 
| +    return false;
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool DxgiOutputDuplicator::Duplicate(Context* context,
 | 
| +                                     const DesktopFrame* last_frame,
 | 
| +                                     const DesktopVector offset,
 | 
| +                                     DesktopFrame* target) {
 | 
| +  RTC_DCHECK(duplication_);
 | 
| +  RTC_DCHECK(texture_);
 | 
| +  RTC_DCHECK(target);
 | 
| +  DXGI_OUTDUPL_FRAME_INFO frame_info;
 | 
| +  memset(&frame_info, 0, sizeof(frame_info));
 | 
| +  ComPtr<IDXGIResource> resource;
 | 
| +  _com_error error = duplication_->AcquireNextFrame(
 | 
| +      kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
 | 
| +  if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
 | 
| +    LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
 | 
| +                  << ", code " << error.Error();
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  // We need to merge updated region with the one from last frame, since current
 | 
| +  // frame contains the content one frame before. Note, this is for double
 | 
| +  // buffering implementation, as what we have in ScreenCapturerWinDirectx. If
 | 
| +  // a consumer uses single buffering, we should clear context->updated_region
 | 
| +  // after it has been merged to updated_region.
 | 
| +  DesktopRegion updated_region = context->updated_region;
 | 
| +  if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) {
 | 
| +    DetectUpdatedRegion(frame_info, offset, &context->updated_region);
 | 
| +    SpreadContextChange(context);
 | 
| +    updated_region.AddRegion(context->updated_region);
 | 
| +    if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +    const DesktopFrame& source = texture_->AsDesktopFrame();
 | 
| +    DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
 | 
| +    for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
 | 
| +         it.Advance()) {
 | 
| +      if (!target_rect.ContainsRect(it.rect())) {
 | 
| +        // target size is not large enough to copy the pixel from texture.
 | 
| +        return false;
 | 
| +      }
 | 
| +      target->CopyPixelsFrom(source, it.rect().top_left().subtract(offset),
 | 
| +                             it.rect());
 | 
| +    }
 | 
| +    target->mutable_updated_region()->AddRegion(updated_region);
 | 
| +    return texture_->Release() && ReleaseFrame();
 | 
| +  }
 | 
| +
 | 
| +  if (last_frame != nullptr) {
 | 
| +    // DxgiOutputDuplicatorContainer::Duplicate() makes sure target size and
 | 
| +    // last frame size are consistent.
 | 
| +    RTC_DCHECK(target->size().equals(last_frame->size()));
 | 
| +    // No change since last frame or AcquireNextFrame() timed out, we will
 | 
| +    // export last frame to the target.
 | 
| +    context->updated_region.Clear();
 | 
| +    for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
 | 
| +         it.Advance()) {
 | 
| +      target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect());
 | 
| +    }
 | 
| +    target->mutable_updated_region()->AddRegion(updated_region);
 | 
| +  }
 | 
| +  // If AcquireNextFrame() failed with timeout error, we do not need to release
 | 
| +  // the frame.
 | 
| +  return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
 | 
| +}
 | 
| +
 | 
| +DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(
 | 
| +    const DesktopVector offset) {
 | 
| +  DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
 | 
| +  result.Translate(offset);
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +void DxgiOutputDuplicator::DetectUpdatedRegion(
 | 
| +    const DXGI_OUTDUPL_FRAME_INFO& frame_info,
 | 
| +    const DesktopVector offset,
 | 
| +    DesktopRegion* updated_region) {
 | 
| +  if (DoDetectUpdatedRegion(frame_info, updated_region)) {
 | 
| +    updated_region->Translate(offset.x(), offset.y());
 | 
| +    // Make sure even a region returned by Windows API is out of the scope of
 | 
| +    // desktop_rect_, we still won't export it to the target DesktopFrame.
 | 
| +    updated_region->IntersectWith(TranslatedDesktopRect(offset));
 | 
| +  } else {
 | 
| +    updated_region->SetRect(TranslatedDesktopRect(offset));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
 | 
| +    const DXGI_OUTDUPL_FRAME_INFO& frame_info,
 | 
| +    DesktopRegion* updated_region) {
 | 
| +  RTC_DCHECK(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.capacity() < frame_info.TotalMetadataBufferSize) {
 | 
| +    metadata.clear();  // Avoid data copy
 | 
| +    metadata.reserve(frame_info.TotalMetadataBufferSize);
 | 
| +  }
 | 
| +
 | 
| +  UINT buff_size = 0;
 | 
| +  DXGI_OUTDUPL_MOVE_RECT* move_rects =
 | 
| +      reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
 | 
| +  size_t move_rects_count = 0;
 | 
| +  _com_error error = _com_error(duplication_->GetFrameMoveRects(
 | 
| +      static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
 | 
| +  if (error.Error() != S_OK) {
 | 
| +    LOG(LS_ERROR) << "Failed to get move rectangles, error "
 | 
| +                  << error.ErrorMessage() << ", code " << error.Error();
 | 
| +    return false;
 | 
| +  }
 | 
| +  move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
 | 
| +
 | 
| +  RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size);
 | 
| +  size_t dirty_rects_count = 0;
 | 
| +  error = _com_error(duplication_->GetFrameDirtyRects(
 | 
| +      static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects,
 | 
| +      &buff_size));
 | 
| +  if (error.Error() != S_OK) {
 | 
| +    LOG(LS_ERROR) << "Failed to get dirty rectangles, error "
 | 
| +                  << error.ErrorMessage() << ", code " << error.Error();
 | 
| +    return false;
 | 
| +  }
 | 
| +  dirty_rects_count = buff_size / sizeof(RECT);
 | 
| +
 | 
| +  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;
 | 
| +}
 | 
| +
 | 
| +void DxgiOutputDuplicator::Setup(Context* context) {
 | 
| +  RTC_DCHECK(context->updated_region.is_empty());
 | 
| +  // Always copy entire monitor during the first Duplicate() function call.
 | 
| +  context->updated_region.AddRect(desktop_rect_);
 | 
| +  for (size_t i = 0; i < contexts_.size(); i++) {
 | 
| +    if (contexts_[i] == nullptr) {
 | 
| +      contexts_[i] = context;
 | 
| +      return;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  contexts_.push_back(context);
 | 
| +}
 | 
| +
 | 
| +void DxgiOutputDuplicator::Unregister(const Context* const context) {
 | 
| +  for (size_t i = 0; i < contexts_.size(); i++) {
 | 
| +    if (contexts_[i] == context) {
 | 
| +      contexts_[i] = nullptr;
 | 
| +      return;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  RTC_NOTREACHED();
 | 
| +}
 | 
| +
 | 
| +void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
 | 
| +  for (Context* dest : contexts_) {
 | 
| +    if (dest != source) {
 | 
| +      dest->updated_region.AddRegion(source->updated_region);
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +}  // namespace webrtc
 | 
| 
 |