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