desktop_device_info.cc (13411B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "desktop_device_info.h" 6 7 #include <cstddef> 8 #include <cstring> 9 #include <map> 10 #include <memory> 11 12 #include "VideoEngine.h" 13 #include "modules/desktop_capture/desktop_capture_options.h" 14 #include "modules/desktop_capture/desktop_capturer.h" 15 #include "mozilla/StaticPrefs_media.h" 16 #include "mozilla/SyncRunnable.h" 17 #include "nsIBrowserWindowTracker.h" 18 #include "nsImportModule.h" 19 #include "nsPrintfCString.h" 20 21 using mozilla::camera::CaptureDeviceType; 22 23 namespace webrtc { 24 25 void DesktopSource::setScreenId(ScreenId aId) { mScreenId = aId; } 26 void DesktopSource::setName(nsCString&& aName) { mName = std::move(aName); } 27 void DesktopSource::setUniqueId(nsCString&& aId) { mUniqueId = std::move(aId); } 28 void DesktopSource::setPid(const pid_t aPid) { mPid = aPid; } 29 30 ScreenId DesktopSource::getScreenId() const { return mScreenId; } 31 const nsCString& DesktopSource::getName() const { return mName; } 32 const nsCString& DesktopSource::getUniqueId() const { return mUniqueId; } 33 pid_t DesktopSource::getPid() const { return mPid; } 34 35 void TabSource::setBrowserId(uint64_t aId) { mBrowserId = aId; } 36 void TabSource::setUniqueId(nsCString&& aId) { mUniqueId = std::move(aId); } 37 void TabSource::setName(nsCString&& aName) { mName = std::move(aName); } 38 39 uint64_t TabSource::getBrowserId() const { return mBrowserId; } 40 const nsCString& TabSource::getName() const { return mName; } 41 const nsCString& TabSource::getUniqueId() const { return mUniqueId; } 42 43 template <CaptureDeviceType Type, typename Device> 44 class DesktopDeviceInfoImpl : public CaptureInfo<Device> { 45 public: 46 explicit DesktopDeviceInfoImpl(const DesktopCaptureOptions& aOptions); 47 48 void Refresh() override; 49 size_t getSourceCount() const override; 50 const Device* getSource(size_t aIndex) const override; 51 52 protected: 53 const DesktopCaptureOptions mOptions; 54 std::map<intptr_t, Device> mDeviceList; 55 }; 56 57 template <CaptureDeviceType Type, typename Device> 58 DesktopDeviceInfoImpl<Type, Device>::DesktopDeviceInfoImpl( 59 const DesktopCaptureOptions& aOptions) 60 : mOptions(aOptions) {} 61 62 template <CaptureDeviceType Type, typename Device> 63 size_t DesktopDeviceInfoImpl<Type, Device>::getSourceCount() const { 64 return mDeviceList.size(); 65 } 66 67 template <CaptureDeviceType Type, typename Device> 68 const Device* DesktopDeviceInfoImpl<Type, Device>::getSource( 69 size_t aIndex) const { 70 if (aIndex >= mDeviceList.size()) { 71 return nullptr; 72 } 73 auto it = mDeviceList.begin(); 74 std::advance(it, aIndex); 75 return &std::get<Device>(*it); 76 } 77 78 static std::map<intptr_t, TabSource> InitializeTabList() { 79 std::map<intptr_t, TabSource> tabList; 80 if (!mozilla::StaticPrefs::media_getusermedia_browser_enabled()) { 81 return tabList; 82 } 83 84 // This is a sync dispatch to main thread, which is unfortunate. To 85 // call JavaScript we have to be on main thread, but the remaining 86 // DesktopCapturer very much wants to be off main thread. This might 87 // be solvable by calling this method earlier on while we're still on 88 // main thread and plumbing the information down to here. 89 nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(__func__, [&] { 90 nsresult rv; 91 nsCOMPtr<nsIBrowserWindowTracker> bwt = 92 do_ImportESModule("resource:///modules/BrowserWindowTracker.sys.mjs", 93 "BrowserWindowTracker", &rv); 94 if (NS_FAILED(rv)) { 95 return; 96 } 97 98 nsTArray<RefPtr<nsIVisibleTab>> tabArray; 99 rv = bwt->GetAllVisibleTabs(tabArray); 100 if (NS_FAILED(rv)) { 101 return; 102 } 103 104 for (const auto& browserTab : tabArray) { 105 nsString contentTitle; 106 browserTab->GetContentTitle(contentTitle); 107 int64_t browserId; 108 browserTab->GetBrowserId(&browserId); 109 110 auto result = 111 tabList.try_emplace(mozilla::AssertedCast<intptr_t>(browserId)); 112 auto& [iter, inserted] = result; 113 if (!inserted) { 114 MOZ_ASSERT_UNREACHABLE("Duplicate browser ids"); 115 continue; 116 } 117 auto& [key, desktopTab] = *iter; 118 desktopTab.setBrowserId(browserId); 119 desktopTab.setName(NS_ConvertUTF16toUTF8(contentTitle)); 120 desktopTab.setUniqueId(nsPrintfCString("%" PRId64, browserId)); 121 } 122 }); 123 mozilla::SyncRunnable::DispatchToThread( 124 mozilla::GetMainThreadSerialEventTarget(), runnable); 125 return tabList; 126 } 127 128 template <CaptureDeviceType Type, typename Device> 129 void DesktopDeviceInfoImpl<Type, Device>::Refresh() { 130 if constexpr (Type == CaptureDeviceType::Browser) { 131 mDeviceList = InitializeTabList(); 132 return; 133 } 134 135 mDeviceList.clear(); 136 137 std::unique_ptr<DesktopCapturer> cap; 138 if constexpr (Type == CaptureDeviceType::Screen || 139 Type == CaptureDeviceType::Window) { 140 cap = DesktopCapturer::CreateGenericCapturer(mOptions); 141 if constexpr (Type == CaptureDeviceType::Screen) { 142 if (!cap) { 143 cap = DesktopCapturer::CreateScreenCapturer(mOptions); 144 } 145 } else if constexpr (Type == CaptureDeviceType::Window) { 146 if (cap) { 147 // We only use the screen side of a generic capturer for enumeration. 148 return; 149 } 150 cap = DesktopCapturer::CreateWindowCapturer(mOptions); 151 } 152 153 if (!cap) { 154 return; 155 } 156 157 DesktopCapturer::SourceList list; 158 if (!cap->GetSourceList(&list)) { 159 return; 160 } 161 162 for (const auto& elem : list) { 163 auto result = mDeviceList.try_emplace(elem.id); 164 auto& [iter, inserted] = result; 165 if (!inserted) { 166 MOZ_ASSERT_UNREACHABLE("Duplicate screen id"); 167 continue; 168 } 169 auto& [key, device] = *iter; 170 device.setScreenId(elem.id); 171 device.setUniqueId(nsPrintfCString("%" PRIdPTR, elem.id)); 172 if (Type == CaptureDeviceType::Screen && list.size() == 1) { 173 device.setName(nsCString("Primary Monitor")); 174 } else { 175 device.setName(nsCString(elem.title.c_str())); 176 } 177 device.setPid(elem.pid); 178 } 179 } 180 } 181 182 std::unique_ptr<DesktopCaptureInfo> CreateScreenCaptureInfo( 183 const DesktopCaptureOptions& aOptions) { 184 std::unique_ptr<DesktopCaptureInfo> info( 185 new DesktopDeviceInfoImpl<CaptureDeviceType::Screen, DesktopSource>( 186 aOptions)); 187 info->Refresh(); 188 return info; 189 } 190 191 std::unique_ptr<DesktopCaptureInfo> CreateWindowCaptureInfo( 192 const DesktopCaptureOptions& aOptions) { 193 std::unique_ptr<DesktopCaptureInfo> info( 194 new DesktopDeviceInfoImpl<CaptureDeviceType::Window, DesktopSource>( 195 aOptions)); 196 info->Refresh(); 197 return info; 198 } 199 200 std::unique_ptr<TabCaptureInfo> CreateTabCaptureInfo() { 201 std::unique_ptr<TabCaptureInfo> info( 202 new DesktopDeviceInfoImpl<CaptureDeviceType::Browser, TabSource>( 203 DesktopCaptureOptions())); 204 info->Refresh(); 205 return info; 206 } 207 208 // simulate deviceInfo interface for video engine, bridge screen/application and 209 // real screen/application device info 210 template <typename Source> 211 class DesktopCaptureDeviceInfo final : public VideoCaptureModule::DeviceInfo { 212 public: 213 DesktopCaptureDeviceInfo(int32_t aId, 214 std::unique_ptr<CaptureInfo<Source>>&& aSourceInfo); 215 216 int32_t Refresh() override; 217 218 uint32_t NumberOfDevices() override; 219 int32_t GetDeviceName(uint32_t aDeviceNumber, char* aDeviceNameUTF8, 220 uint32_t aDeviceNameUTF8Size, char* aDeviceUniqueIdUTF8, 221 uint32_t aDeviceUniqueIdUTF8Size, 222 char* aProductUniqueIdUTF8, 223 uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, 224 bool* aDeviceIsPlaceholder = nullptr) override; 225 226 int32_t DisplayCaptureSettingsDialogBox(const char* aDeviceUniqueIdUTF8, 227 const char* aDialogTitleUTF8, 228 void* aParentWindow, 229 uint32_t aPositionX, 230 uint32_t aPositionY) override; 231 int32_t NumberOfCapabilities(const char* aDeviceUniqueIdUTF8) override; 232 int32_t GetCapability(const char* aDeviceUniqueIdUTF8, 233 uint32_t aDeviceCapabilityNumber, 234 VideoCaptureCapability& aCapability) override; 235 236 int32_t GetBestMatchedCapability(const char* aDeviceUniqueIdUTF8, 237 const VideoCaptureCapability& aRequested, 238 VideoCaptureCapability& aResulting) override; 239 int32_t GetOrientation(const char* aDeviceUniqueIdUTF8, 240 VideoRotation& aOrientation) override; 241 242 protected: 243 int32_t mId; 244 std::unique_ptr<CaptureInfo<Source>> mDeviceInfo; 245 }; 246 247 using DesktopDeviceInfo = DesktopCaptureDeviceInfo<DesktopSource>; 248 using TabDeviceInfo = DesktopCaptureDeviceInfo<TabSource>; 249 250 template <typename Source> 251 DesktopCaptureDeviceInfo<Source>::DesktopCaptureDeviceInfo( 252 int32_t aId, std::unique_ptr<CaptureInfo<Source>>&& aSourceInfo) 253 : mId(aId), mDeviceInfo(std::move(aSourceInfo)) {} 254 255 template <typename Source> 256 int32_t DesktopCaptureDeviceInfo<Source>::Refresh() { 257 mDeviceInfo->Refresh(); 258 return 0; 259 } 260 261 template <typename Source> 262 uint32_t DesktopCaptureDeviceInfo<Source>::NumberOfDevices() { 263 return mDeviceInfo->getSourceCount(); 264 } 265 266 template <> 267 int32_t DesktopCaptureDeviceInfo<DesktopSource>::GetDeviceName( 268 uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameUTF8Size, 269 char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Size, 270 char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, 271 bool* aDeviceIsPlaceholder) { 272 // always initialize output 273 if (aDeviceNameUTF8 && aDeviceNameUTF8Size > 0) { 274 memset(aDeviceNameUTF8, 0, aDeviceNameUTF8Size); 275 } 276 if (aDeviceUniqueIdUTF8 && aDeviceUniqueIdUTF8Size > 0) { 277 memset(aDeviceUniqueIdUTF8, 0, aDeviceUniqueIdUTF8Size); 278 } 279 if (aProductUniqueIdUTF8 && aProductUniqueIdUTF8Size > 0) { 280 memset(aProductUniqueIdUTF8, 0, aProductUniqueIdUTF8Size); 281 } 282 283 const DesktopSource* source = mDeviceInfo->getSource(aDeviceNumber); 284 if (!source) { 285 return 0; 286 } 287 288 const nsCString& deviceName = source->getName(); 289 size_t len = deviceName.Length(); 290 if (len && aDeviceNameUTF8 && len < aDeviceNameUTF8Size) { 291 memcpy(aDeviceNameUTF8, deviceName.Data(), len); 292 } 293 294 const nsCString& deviceUniqueId = source->getUniqueId(); 295 len = deviceUniqueId.Length(); 296 if (len && aDeviceUniqueIdUTF8 && len < aDeviceUniqueIdUTF8Size) { 297 memcpy(aDeviceUniqueIdUTF8, deviceUniqueId.Data(), len); 298 } 299 300 if (aPid) { 301 *aPid = source->getPid(); 302 } 303 304 return 0; 305 } 306 307 template <> 308 int32_t DesktopCaptureDeviceInfo<TabSource>::GetDeviceName( 309 uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameUTF8Size, 310 char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Size, 311 char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, 312 bool* aDeviceIsPlaceholder) { 313 // always initialize output 314 if (aDeviceNameUTF8 && aDeviceNameUTF8Size > 0) { 315 memset(aDeviceNameUTF8, 0, aDeviceNameUTF8Size); 316 } 317 if (aDeviceUniqueIdUTF8 && aDeviceUniqueIdUTF8Size > 0) { 318 memset(aDeviceUniqueIdUTF8, 0, aDeviceUniqueIdUTF8Size); 319 } 320 if (aProductUniqueIdUTF8 && aProductUniqueIdUTF8Size > 0) { 321 memset(aProductUniqueIdUTF8, 0, aProductUniqueIdUTF8Size); 322 } 323 324 const TabSource* source = mDeviceInfo->getSource(aDeviceNumber); 325 if (!source) { 326 return 0; 327 } 328 329 const nsCString& deviceName = source->getName(); 330 size_t len = deviceName.Length(); 331 if (len && aDeviceNameUTF8 && len < aDeviceNameUTF8Size) { 332 memcpy(aDeviceNameUTF8, deviceName.Data(), len); 333 } 334 335 const nsCString& deviceUniqueId = source->getUniqueId(); 336 len = deviceUniqueId.Length(); 337 if (len && aDeviceUniqueIdUTF8 && len < aDeviceUniqueIdUTF8Size) { 338 memcpy(aDeviceUniqueIdUTF8, deviceUniqueId.Data(), len); 339 } 340 341 return 0; 342 } 343 344 template <typename Source> 345 int32_t DesktopCaptureDeviceInfo<Source>::DisplayCaptureSettingsDialogBox( 346 const char* aDeviceUniqueIdUTF8, const char* aDialogTitleUTF8, 347 void* aParentWindow, uint32_t aPositionX, uint32_t aPositionY) { 348 // no device properties to change 349 return 0; 350 } 351 352 template <typename Source> 353 int32_t DesktopCaptureDeviceInfo<Source>::NumberOfCapabilities( 354 const char* aDeviceUniqueIdUTF8) { 355 return 0; 356 } 357 358 template <typename Source> 359 int32_t DesktopCaptureDeviceInfo<Source>::GetCapability( 360 const char* aDeviceUniqueIdUTF8, uint32_t aDeviceCapabilityNumber, 361 VideoCaptureCapability& aCapability) { 362 return 0; 363 } 364 365 template <typename Source> 366 int32_t DesktopCaptureDeviceInfo<Source>::GetBestMatchedCapability( 367 const char* aDeviceUniqueIdUTF8, const VideoCaptureCapability& aRequested, 368 VideoCaptureCapability& aResulting) { 369 return 0; 370 } 371 372 template <typename Source> 373 int32_t DesktopCaptureDeviceInfo<Source>::GetOrientation( 374 const char* aDeviceUniqueIdUTF8, VideoRotation& aOrientation) { 375 return 0; 376 } 377 378 std::shared_ptr<VideoCaptureModule::DeviceInfo> CreateDesktopDeviceInfo( 379 int32_t aId, std::unique_ptr<DesktopCaptureInfo>&& aInfo) { 380 return std::make_shared<DesktopDeviceInfo>(aId, std::move(aInfo)); 381 } 382 383 std::shared_ptr<VideoCaptureModule::DeviceInfo> CreateTabDeviceInfo( 384 int32_t aId, std::unique_ptr<TabCaptureInfo>&& aInfo) { 385 return std::make_shared<TabDeviceInfo>(aId, std::move(aInfo)); 386 } 387 } // namespace webrtc