tor-browser

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

screen_capturer_win_directx.cc (8265B)


      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 "modules/desktop_capture/win/screen_capturer_win_directx.h"
     12 
     13 #include <wrl/client.h>
     14 
     15 #include <algorithm>
     16 #include <cstddef>
     17 #include <cstdint>
     18 #include <memory>
     19 #include <string>
     20 #include <utility>
     21 #include <vector>
     22 
     23 #include "modules/desktop_capture/desktop_capture_metrics_helper.h"
     24 #include "modules/desktop_capture/desktop_capture_options.h"
     25 #include "modules/desktop_capture/desktop_capture_types.h"
     26 #include "modules/desktop_capture/desktop_capturer.h"
     27 #include "modules/desktop_capture/desktop_frame.h"
     28 #include "modules/desktop_capture/screen_capture_frame_queue.h"
     29 #include "modules/desktop_capture/shared_memory.h"
     30 #include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
     31 #include "modules/desktop_capture/win/dxgi_frame.h"
     32 #include "modules/desktop_capture/win/screen_capture_utils.h"
     33 #include "rtc_base/checks.h"
     34 #include "rtc_base/logging.h"
     35 #include "rtc_base/time_utils.h"
     36 #include "rtc_base/trace_event.h"
     37 #include "system_wrappers/include/metrics.h"
     38 
     39 namespace webrtc {
     40 
     41 using Microsoft::WRL::ComPtr;
     42 
     43 // static
     44 bool ScreenCapturerWinDirectx::IsSupported() {
     45  // Forwards IsSupported() function call to DxgiDuplicatorController.
     46  return DxgiDuplicatorController::Instance()->IsSupported();
     47 }
     48 
     49 // static
     50 bool ScreenCapturerWinDirectx::RetrieveD3dInfo(D3dInfo* info) {
     51  // Forwards SupportedFeatureLevels() function call to
     52  // DxgiDuplicatorController.
     53  return DxgiDuplicatorController::Instance()->RetrieveD3dInfo(info);
     54 }
     55 
     56 // static
     57 bool ScreenCapturerWinDirectx::IsCurrentSessionSupported() {
     58  return DxgiDuplicatorController::IsCurrentSessionSupported();
     59 }
     60 
     61 // static
     62 bool ScreenCapturerWinDirectx::GetScreenListFromDeviceNames(
     63    const std::vector<std::string>& device_names,
     64    DesktopCapturer::SourceList* screens) {
     65  RTC_DCHECK(screens->empty());
     66 
     67  DesktopCapturer::SourceList gdi_screens;
     68  std::vector<std::string> gdi_names;
     69  if (!GetScreenList(&gdi_screens, &gdi_names)) {
     70    return false;
     71  }
     72 
     73  RTC_DCHECK_EQ(gdi_screens.size(), gdi_names.size());
     74 
     75  ScreenId max_screen_id = -1;
     76  for (const DesktopCapturer::Source& screen : gdi_screens) {
     77    max_screen_id = std::max(max_screen_id, screen.id);
     78  }
     79 
     80  for (const auto& device_name : device_names) {
     81    const auto it = std::find(gdi_names.begin(), gdi_names.end(), device_name);
     82    if (it == gdi_names.end()) {
     83      // devices_names[i] has not been found in gdi_names, so use max_screen_id.
     84      max_screen_id++;
     85      screens->push_back({max_screen_id});
     86    } else {
     87      screens->push_back({gdi_screens[it - gdi_names.begin()]});
     88    }
     89  }
     90 
     91  return true;
     92 }
     93 
     94 // static
     95 int ScreenCapturerWinDirectx::GetIndexFromScreenId(
     96    ScreenId id,
     97    const std::vector<std::string>& device_names) {
     98  DesktopCapturer::SourceList screens;
     99  if (!GetScreenListFromDeviceNames(device_names, &screens)) {
    100    return -1;
    101  }
    102 
    103  RTC_DCHECK_EQ(device_names.size(), screens.size());
    104 
    105  for (size_t i = 0; i < screens.size(); i++) {
    106    if (screens[i].id == id) {
    107      return static_cast<int>(i);
    108    }
    109  }
    110 
    111  return -1;
    112 }
    113 
    114 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx()
    115    : controller_(DxgiDuplicatorController::Instance()) {}
    116 
    117 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
    118    const DesktopCaptureOptions& options)
    119    : ScreenCapturerWinDirectx() {
    120  options_ = options;
    121 }
    122 
    123 ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() = default;
    124 
    125 void ScreenCapturerWinDirectx::Start(Callback* callback) {
    126  RTC_DCHECK(!callback_);
    127  RTC_DCHECK(callback);
    128  RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinDirectx);
    129 
    130  callback_ = callback;
    131 }
    132 
    133 void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
    134    std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
    135  shared_memory_factory_ = std::move(shared_memory_factory);
    136 }
    137 
    138 void ScreenCapturerWinDirectx::CaptureFrame() {
    139  RTC_DCHECK(callback_);
    140  TRACE_EVENT0("webrtc", "ScreenCapturerWinDirectx::CaptureFrame");
    141 
    142  int64_t capture_start_time_nanos = TimeNanos();
    143 
    144  // Note that the [] operator will create the ScreenCaptureFrameQueue if it
    145  // doesn't exist, so this is safe.
    146  ScreenCaptureFrameQueue<DxgiFrame>& frames =
    147      frame_queue_map_[current_screen_id_];
    148 
    149  frames.MoveToNextFrame();
    150 
    151  if (!frames.current_frame()) {
    152    frames.ReplaceCurrentFrame(
    153        std::make_unique<DxgiFrame>(shared_memory_factory_.get()));
    154  }
    155 
    156  DxgiDuplicatorController::Result result;
    157  if (current_screen_id_ == kFullDesktopScreenId) {
    158    result = controller_->Duplicate(frames.current_frame());
    159  } else {
    160    result = controller_->DuplicateMonitor(frames.current_frame(),
    161                                           current_screen_id_);
    162  }
    163 
    164  using DuplicateResult = DxgiDuplicatorController::Result;
    165  if (result != DuplicateResult::SUCCEEDED) {
    166    RTC_LOG(LS_ERROR) << "DxgiDuplicatorController failed to capture desktop, "
    167                         "error code "
    168                      << DxgiDuplicatorController::ResultName(result);
    169  }
    170  RTC_HISTOGRAM_ENUMERATION(
    171      "WebRTC.DesktopCapture.Win.DirectXCapturerResult",
    172      static_cast<int>(result),
    173      static_cast<int>(DxgiDuplicatorController::Result::MAX_VALUE));
    174  switch (result) {
    175    case DuplicateResult::UNSUPPORTED_SESSION: {
    176      RTC_LOG(LS_ERROR)
    177          << "Current binary is running on a session not supported "
    178             "by DirectX screen capturer.";
    179      callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    180      break;
    181    }
    182    case DuplicateResult::FRAME_PREPARE_FAILED: {
    183      RTC_LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame.";
    184      // This usually means we do not have enough memory or SharedMemoryFactory
    185      // cannot work correctly.
    186      callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    187      break;
    188    }
    189    case DuplicateResult::INVALID_MONITOR_ID: {
    190      RTC_LOG(LS_ERROR) << "Invalid monitor id " << current_screen_id_;
    191      callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    192      break;
    193    }
    194    case DuplicateResult::INITIALIZATION_FAILED:
    195    case DuplicateResult::DUPLICATION_FAILED: {
    196      callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    197      break;
    198    }
    199    case DuplicateResult::SUCCEEDED: {
    200      std::unique_ptr<DesktopFrame> frame =
    201          frames.current_frame()->frame()->Share();
    202 
    203      int capture_time_ms =
    204          (TimeNanos() - capture_start_time_nanos) / kNumNanosecsPerMillisec;
    205      RTC_HISTOGRAM_COUNTS_1000(
    206          "WebRTC.DesktopCapture.Win.DirectXCapturerFrameTime",
    207          capture_time_ms);
    208      frame->set_capture_time_ms(capture_time_ms);
    209      frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx);
    210      // The DXGI Output Duplicator supports embedding the cursor but it is
    211      // only supported on very few display adapters. This switch allows us
    212      // to exclude an integrated cursor for all captured frames.
    213      if (!options_.prefer_cursor_embedded()) {
    214        frame->set_may_contain_cursor(false);
    215      }
    216 
    217      // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
    218      // the frame, see WindowCapturerMac::CaptureFrame.
    219 
    220      callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    221      break;
    222    }
    223  }
    224 }
    225 
    226 bool ScreenCapturerWinDirectx::GetSourceList(SourceList* sources) {
    227  std::vector<std::string> device_names;
    228  if (!controller_->GetDeviceNames(&device_names)) {
    229    return false;
    230  }
    231 
    232  return GetScreenListFromDeviceNames(device_names, sources);
    233 }
    234 
    235 bool ScreenCapturerWinDirectx::SelectSource(SourceId id) {
    236  if (id == kFullDesktopScreenId) {
    237    current_screen_id_ = id;
    238    return true;
    239  }
    240 
    241  std::vector<std::string> device_names;
    242  if (!controller_->GetDeviceNames(&device_names)) {
    243    return false;
    244  }
    245 
    246  int index;
    247  index = GetIndexFromScreenId(id, device_names);
    248  if (index == -1) {
    249    return false;
    250  }
    251 
    252  current_screen_id_ = index;
    253  return true;
    254 }
    255 
    256 }  // namespace webrtc