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