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