tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

screen_capturer_win_gdi.cc (8402B)


      1 /*
      2 *  Copyright (c) 2014 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 "modules/desktop_capture/win/screen_capturer_win_gdi.h"
     12 
     13 #include <cstddef>
     14 #include <cstdint>
     15 #include <memory>
     16 #include <optional>
     17 #include <string>
     18 #include <utility>
     19 
     20 #include "modules/desktop_capture/desktop_capture_metrics_helper.h"
     21 #include "modules/desktop_capture/desktop_capture_options.h"
     22 #include "modules/desktop_capture/desktop_capture_types.h"
     23 #include "modules/desktop_capture/desktop_frame.h"
     24 #include "modules/desktop_capture/desktop_frame_win.h"
     25 #include "modules/desktop_capture/desktop_geometry.h"
     26 #include "modules/desktop_capture/desktop_region.h"
     27 #include "modules/desktop_capture/shared_desktop_frame.h"
     28 #include "modules/desktop_capture/shared_memory.h"
     29 #include "modules/desktop_capture/win/desktop.h"
     30 #include "modules/desktop_capture/win/screen_capture_utils.h"
     31 #include "rtc_base/checks.h"
     32 #include "rtc_base/logging.h"
     33 #include "rtc_base/time_utils.h"
     34 #include "rtc_base/trace_event.h"
     35 #include "system_wrappers/include/metrics.h"
     36 
     37 namespace webrtc {
     38 
     39 namespace {
     40 
     41 // Constants from dwmapi.h.
     42 const UINT DWM_EC_DISABLECOMPOSITION = 0;
     43 const UINT DWM_EC_ENABLECOMPOSITION = 1;
     44 
     45 const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
     46 
     47 }  // namespace
     48 
     49 ScreenCapturerWinGdi::ScreenCapturerWinGdi(
     50    const DesktopCaptureOptions& options) {
     51  if (options.disable_effects()) {
     52    // Load dwmapi.dll dynamically since it is not available on XP.
     53    if (!dwmapi_library_)
     54      dwmapi_library_ = LoadLibraryW(kDwmapiLibraryName);
     55 
     56    if (dwmapi_library_) {
     57      composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
     58          GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
     59    }
     60  }
     61 }
     62 
     63 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
     64  if (desktop_dc_)
     65    ReleaseDC(NULL, desktop_dc_);
     66  if (memory_dc_)
     67    DeleteDC(memory_dc_);
     68 
     69  // Restore Aero.
     70  if (composition_func_)
     71    (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
     72 
     73  if (dwmapi_library_)
     74    FreeLibrary(dwmapi_library_);
     75 }
     76 
     77 void ScreenCapturerWinGdi::SetSharedMemoryFactory(
     78    std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
     79  shared_memory_factory_ = std::move(shared_memory_factory);
     80 }
     81 
     82 void ScreenCapturerWinGdi::CaptureFrame() {
     83  TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
     84  int64_t capture_start_time_nanos = TimeNanos();
     85 
     86  queue_.MoveToNextFrame();
     87  if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
     88    RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
     89  }
     90 
     91  // Make sure the GDI capture resources are up-to-date.
     92  PrepareCaptureResources();
     93 
     94  if (!CaptureImage()) {
     95    RTC_LOG(LS_WARNING) << "Failed to capture screen by GDI.";
     96    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
     97    return;
     98  }
     99 
    100  // Emit the current frame.
    101  std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
    102  frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
    103                               GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
    104  frame->mutable_updated_region()->SetRect(
    105      DesktopRect::MakeSize(frame->size()));
    106 
    107  int capture_time_ms =
    108      (TimeNanos() - capture_start_time_nanos) / kNumNanosecsPerMillisec;
    109  RTC_HISTOGRAM_COUNTS_1000(
    110      "WebRTC.DesktopCapture.Win.ScreenGdiCapturerFrameTime", capture_time_ms);
    111  frame->set_capture_time_ms(capture_time_ms);
    112  frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
    113  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    114 }
    115 
    116 bool ScreenCapturerWinGdi::GetSourceList(SourceList* sources) {
    117  return GetScreenList(sources);
    118 }
    119 
    120 bool ScreenCapturerWinGdi::SelectSource(SourceId id) {
    121  std::wstring device_key;
    122  bool valid = IsScreenValid(id, &device_key);
    123  if (valid) {
    124    current_screen_id_ = id;
    125    current_device_key_ = device_key;
    126  } else {
    127    current_screen_id_ = kFullDesktopScreenId;
    128    current_device_key_ = std::nullopt;
    129  }
    130  return valid;
    131 }
    132 
    133 void ScreenCapturerWinGdi::Start(Callback* callback) {
    134  RTC_DCHECK(!callback_);
    135  RTC_DCHECK(callback);
    136  RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinGdi);
    137 
    138  callback_ = callback;
    139 
    140  // Vote to disable Aero composited desktop effects while capturing. Windows
    141  // will restore Aero automatically if the process exits. This has no effect
    142  // under Windows 8 or higher.  See crbug.com/124018.
    143  if (composition_func_)
    144    (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
    145 }
    146 
    147 void ScreenCapturerWinGdi::PrepareCaptureResources() {
    148  // Switch to the desktop receiving user input if different from the current
    149  // one.
    150  std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
    151  if (input_desktop && !desktop_.IsSame(*input_desktop)) {
    152    // Release GDI resources otherwise SetThreadDesktop will fail.
    153    if (desktop_dc_) {
    154      ReleaseDC(NULL, desktop_dc_);
    155      desktop_dc_ = nullptr;
    156    }
    157 
    158    if (memory_dc_) {
    159      DeleteDC(memory_dc_);
    160      memory_dc_ = nullptr;
    161    }
    162 
    163    // If SetThreadDesktop() fails, the thread is still assigned a desktop.
    164    // So we can continue capture screen bits, just from the wrong desktop.
    165    desktop_.SetThreadDesktop(input_desktop.release());
    166 
    167    // Re-assert our vote to disable Aero.
    168    // See crbug.com/124018 and crbug.com/129906.
    169    if (composition_func_) {
    170      (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
    171    }
    172  }
    173 
    174  // If the display configurations have changed then recreate GDI resources.
    175  if (display_configuration_monitor_.IsChanged(kFullDesktopScreenId)) {
    176    if (desktop_dc_) {
    177      ReleaseDC(NULL, desktop_dc_);
    178      desktop_dc_ = nullptr;
    179    }
    180    if (memory_dc_) {
    181      DeleteDC(memory_dc_);
    182      memory_dc_ = nullptr;
    183    }
    184  }
    185 
    186  if (!desktop_dc_) {
    187    RTC_DCHECK(!memory_dc_);
    188 
    189    // Create GDI device contexts to capture from the desktop into memory.
    190    desktop_dc_ = GetDC(nullptr);
    191    RTC_CHECK(desktop_dc_);
    192    memory_dc_ = CreateCompatibleDC(desktop_dc_);
    193    RTC_CHECK(memory_dc_);
    194 
    195    // Make sure the frame buffers will be reallocated.
    196    queue_.Reset();
    197  }
    198 }
    199 
    200 bool ScreenCapturerWinGdi::CaptureImage() {
    201  DesktopRect screen_rect =
    202      GetScreenRect(current_screen_id_, current_device_key_);
    203  if (screen_rect.is_empty()) {
    204    RTC_LOG(LS_WARNING) << "Failed to get screen rect.";
    205    return false;
    206  }
    207 
    208  DesktopSize size = screen_rect.size();
    209  // If the current buffer is from an older generation then allocate a new one.
    210  // Note that we can't reallocate other buffers at this point, since the caller
    211  // may still be reading from them.
    212  if (!queue_.current_frame() ||
    213      !queue_.current_frame()->size().equals(screen_rect.size())) {
    214    RTC_DCHECK(desktop_dc_);
    215    RTC_DCHECK(memory_dc_);
    216 
    217    std::unique_ptr<DesktopFrame> buffer = DesktopFrameWin::Create(
    218        size, shared_memory_factory_.get(), desktop_dc_);
    219    if (!buffer) {
    220      RTC_LOG(LS_WARNING) << "Failed to create frame buffer.";
    221      return false;
    222    }
    223    queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer)));
    224  }
    225  queue_.current_frame()->set_top_left(
    226      screen_rect.top_left().subtract(GetFullscreenRect().top_left()));
    227 
    228  // Select the target bitmap into the memory dc and copy the rect from desktop
    229  // to memory.
    230  DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
    231      queue_.current_frame()->GetUnderlyingFrame());
    232  HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
    233  if (!previous_object || previous_object == HGDI_ERROR) {
    234    RTC_LOG(LS_WARNING) << "Failed to select current bitmap into memery dc.";
    235    return false;
    236  }
    237 
    238  bool result = (BitBlt(memory_dc_, 0, 0, screen_rect.width(),
    239                        screen_rect.height(), desktop_dc_, screen_rect.left(),
    240                        screen_rect.top(), SRCCOPY | CAPTUREBLT) != FALSE);
    241  if (!result) {
    242    RTC_LOG_GLE(LS_WARNING) << "BitBlt failed";
    243  }
    244 
    245  // Select back the previously selected object to that the device contect
    246  // could be destroyed independently of the bitmap if needed.
    247  SelectObject(memory_dc_, previous_object);
    248 
    249  return result;
    250 }
    251 
    252 }  // namespace webrtc