/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "desktop_device_info.h" #include "VideoEngine.h" #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/SyncRunnable.h" #include "nsIBrowserWindowTracker.h" #include "nsImportModule.h" #include #include #include #include #include #include using mozilla::camera::CaptureDeviceType; namespace webrtc { void DesktopSource::setScreenId(ScreenId aId) { mScreenId = aId; } void DesktopSource::setName(nsCString&& aName) { mName = std::move(aName); } void DesktopSource::setUniqueId(nsCString&& aId) { mUniqueId = std::move(aId); } void DesktopSource::setPid(const int aPid) { mPid = aPid; } ScreenId DesktopSource::getScreenId() const { return mScreenId; } const nsCString& DesktopSource::getName() const { return mName; } const nsCString& DesktopSource::getUniqueId() const { return mUniqueId; } pid_t DesktopSource::getPid() const { return mPid; } void TabSource::setBrowserId(uint64_t aId) { mBrowserId = aId; } void TabSource::setUniqueId(nsCString&& aId) { mUniqueId = std::move(aId); } void TabSource::setName(nsCString&& aName) { mName = std::move(aName); } uint64_t TabSource::getBrowserId() const { return mBrowserId; } const nsCString& TabSource::getName() const { return mName; } const nsCString& TabSource::getUniqueId() const { return mUniqueId; } template class DesktopDeviceInfoImpl : public CaptureInfo { public: explicit DesktopDeviceInfoImpl(const DesktopCaptureOptions& aOptions); void Refresh() override; size_t getSourceCount() const override; const Device* getSource(size_t aIndex) const override; protected: const DesktopCaptureOptions mOptions; std::map mDeviceList; }; template DesktopDeviceInfoImpl::DesktopDeviceInfoImpl( const DesktopCaptureOptions& aOptions) : mOptions(aOptions) {} template size_t DesktopDeviceInfoImpl::getSourceCount() const { return mDeviceList.size(); } template const Device* DesktopDeviceInfoImpl::getSource( size_t aIndex) const { if (aIndex >= mDeviceList.size()) { return nullptr; } auto it = mDeviceList.begin(); std::advance(it, aIndex); return &std::get(*it); } static std::map InitializeTabList() { std::map tabList; if (!mozilla::StaticPrefs::media_getusermedia_browser_enabled()) { return tabList; } // This is a sync dispatch to main thread, which is unfortunate. To // call JavaScript we have to be on main thread, but the remaining // DesktopCapturer very much wants to be off main thread. This might // be solvable by calling this method earlier on while we're still on // main thread and plumbing the information down to here. nsCOMPtr runnable = NS_NewRunnableFunction(__func__, [&] { nsresult rv; nsCOMPtr bwt = do_ImportESModule("resource:///modules/BrowserWindowTracker.sys.mjs", "BrowserWindowTracker", &rv); if (NS_FAILED(rv)) { return; } nsTArray> tabArray; rv = bwt->GetAllVisibleTabs(tabArray); if (NS_FAILED(rv)) { return; } for (const auto& browserTab : tabArray) { nsString contentTitle; browserTab->GetContentTitle(contentTitle); int64_t browserId; browserTab->GetBrowserId(&browserId); auto result = tabList.try_emplace(mozilla::AssertedCast(browserId)); auto& [iter, inserted] = result; if (!inserted) { MOZ_ASSERT_UNREACHABLE("Duplicate browser ids"); continue; } auto& [key, desktopTab] = *iter; desktopTab.setBrowserId(browserId); desktopTab.setName(NS_ConvertUTF16toUTF8(contentTitle)); desktopTab.setUniqueId(nsPrintfCString("%" PRId64, browserId)); } }); mozilla::SyncRunnable::DispatchToThread( mozilla::GetMainThreadSerialEventTarget(), runnable); return tabList; } template void DesktopDeviceInfoImpl::Refresh() { if constexpr (Type == CaptureDeviceType::Browser) { mDeviceList = InitializeTabList(); return; } mDeviceList.clear(); std::unique_ptr cap; if constexpr (Type == CaptureDeviceType::Screen || Type == CaptureDeviceType::Window) { cap = DesktopCapturer::CreateGenericCapturer(mOptions); if constexpr (Type == CaptureDeviceType::Screen) { if (!cap) { cap = DesktopCapturer::CreateScreenCapturer(mOptions); } } else if constexpr (Type == CaptureDeviceType::Window) { if (cap) { // We only use the screen side of a generic capturer for enumeration. return; } cap = DesktopCapturer::CreateWindowCapturer(mOptions); } if (!cap) { return; } DesktopCapturer::SourceList list; if (!cap->GetSourceList(&list)) { return; } for (const auto& elem : list) { auto result = mDeviceList.try_emplace(elem.id); auto& [iter, inserted] = result; if (!inserted) { MOZ_ASSERT_UNREACHABLE("Duplicate screen id"); continue; } auto& [key, device] = *iter; device.setScreenId(elem.id); device.setUniqueId(nsPrintfCString("%" PRIdPTR, elem.id)); if (Type == CaptureDeviceType::Screen && list.size() == 1) { device.setName(nsCString("Primary Monitor")); } else { device.setName(nsCString(elem.title.c_str())); } device.setPid(elem.pid); } } } std::unique_ptr CreateScreenCaptureInfo( const DesktopCaptureOptions& aOptions) { std::unique_ptr info( new DesktopDeviceInfoImpl( aOptions)); info->Refresh(); return info; } std::unique_ptr CreateWindowCaptureInfo( const DesktopCaptureOptions& aOptions) { std::unique_ptr info( new DesktopDeviceInfoImpl( aOptions)); info->Refresh(); return info; } std::unique_ptr CreateTabCaptureInfo() { std::unique_ptr info( new DesktopDeviceInfoImpl( DesktopCaptureOptions())); info->Refresh(); return info; } // simulate deviceInfo interface for video engine, bridge screen/application and // real screen/application device info template class DesktopCaptureDeviceInfo final : public VideoCaptureModule::DeviceInfo { public: DesktopCaptureDeviceInfo(int32_t aId, std::unique_ptr>&& aSourceInfo); int32_t Refresh() override; uint32_t NumberOfDevices() override; int32_t GetDeviceName(uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameUTF8Size, char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Size, char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, bool* aDeviceIsPlaceholder = nullptr) override; int32_t DisplayCaptureSettingsDialogBox(const char* aDeviceUniqueIdUTF8, const char* aDialogTitleUTF8, void* aParentWindow, uint32_t aPositionX, uint32_t aPositionY) override; int32_t NumberOfCapabilities(const char* aDeviceUniqueIdUTF8) override; int32_t GetCapability(const char* aDeviceUniqueIdUTF8, uint32_t aDeviceCapabilityNumber, VideoCaptureCapability& aCapability) override; int32_t GetBestMatchedCapability(const char* aDeviceUniqueIdUTF8, const VideoCaptureCapability& aRequested, VideoCaptureCapability& aResulting) override; int32_t GetOrientation(const char* aDeviceUniqueIdUTF8, VideoRotation& aOrientation) override; protected: int32_t mId; std::unique_ptr> mDeviceInfo; }; using DesktopDeviceInfo = DesktopCaptureDeviceInfo; using TabDeviceInfo = DesktopCaptureDeviceInfo; template DesktopCaptureDeviceInfo::DesktopCaptureDeviceInfo( int32_t aId, std::unique_ptr>&& aSourceInfo) : mId(aId), mDeviceInfo(std::move(aSourceInfo)) {} template int32_t DesktopCaptureDeviceInfo::Refresh() { mDeviceInfo->Refresh(); return 0; } template uint32_t DesktopCaptureDeviceInfo::NumberOfDevices() { return mDeviceInfo->getSourceCount(); } template <> int32_t DesktopCaptureDeviceInfo::GetDeviceName( uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameUTF8Size, char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Size, char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, bool* aDeviceIsPlaceholder) { // always initialize output if (aDeviceNameUTF8 && aDeviceNameUTF8Size > 0) { memset(aDeviceNameUTF8, 0, aDeviceNameUTF8Size); } if (aDeviceUniqueIdUTF8 && aDeviceUniqueIdUTF8Size > 0) { memset(aDeviceUniqueIdUTF8, 0, aDeviceUniqueIdUTF8Size); } if (aProductUniqueIdUTF8 && aProductUniqueIdUTF8Size > 0) { memset(aProductUniqueIdUTF8, 0, aProductUniqueIdUTF8Size); } const DesktopSource* source = mDeviceInfo->getSource(aDeviceNumber); if (!source) { return 0; } const nsCString& deviceName = source->getName(); size_t len = deviceName.Length(); if (len && aDeviceNameUTF8 && len < aDeviceNameUTF8Size) { memcpy(aDeviceNameUTF8, deviceName.Data(), len); } const nsCString& deviceUniqueId = source->getUniqueId(); len = deviceUniqueId.Length(); if (len && aDeviceUniqueIdUTF8 && len < aDeviceUniqueIdUTF8Size) { memcpy(aDeviceUniqueIdUTF8, deviceUniqueId.Data(), len); } if (aPid) { *aPid = source->getPid(); } return 0; } template <> int32_t DesktopCaptureDeviceInfo::GetDeviceName( uint32_t aDeviceNumber, char* aDeviceNameUTF8, uint32_t aDeviceNameUTF8Size, char* aDeviceUniqueIdUTF8, uint32_t aDeviceUniqueIdUTF8Size, char* aProductUniqueIdUTF8, uint32_t aProductUniqueIdUTF8Size, pid_t* aPid, bool* aDeviceIsPlaceholder) { // always initialize output if (aDeviceNameUTF8 && aDeviceNameUTF8Size > 0) { memset(aDeviceNameUTF8, 0, aDeviceNameUTF8Size); } if (aDeviceUniqueIdUTF8 && aDeviceUniqueIdUTF8Size > 0) { memset(aDeviceUniqueIdUTF8, 0, aDeviceUniqueIdUTF8Size); } if (aProductUniqueIdUTF8 && aProductUniqueIdUTF8Size > 0) { memset(aProductUniqueIdUTF8, 0, aProductUniqueIdUTF8Size); } const TabSource* source = mDeviceInfo->getSource(aDeviceNumber); if (!source) { return 0; } const nsCString& deviceName = source->getName(); size_t len = deviceName.Length(); if (len && aDeviceNameUTF8 && len < aDeviceNameUTF8Size) { memcpy(aDeviceNameUTF8, deviceName.Data(), len); } const nsCString& deviceUniqueId = source->getUniqueId(); len = deviceUniqueId.Length(); if (len && aDeviceUniqueIdUTF8 && len < aDeviceUniqueIdUTF8Size) { memcpy(aDeviceUniqueIdUTF8, deviceUniqueId.Data(), len); } return 0; } template int32_t DesktopCaptureDeviceInfo::DisplayCaptureSettingsDialogBox( const char* aDeviceUniqueIdUTF8, const char* aDialogTitleUTF8, void* aParentWindow, uint32_t aPositionX, uint32_t aPositionY) { // no device properties to change return 0; } template int32_t DesktopCaptureDeviceInfo::NumberOfCapabilities( const char* aDeviceUniqueIdUTF8) { return 0; } template int32_t DesktopCaptureDeviceInfo::GetCapability( const char* aDeviceUniqueIdUTF8, uint32_t aDeviceCapabilityNumber, VideoCaptureCapability& aCapability) { return 0; } template int32_t DesktopCaptureDeviceInfo::GetBestMatchedCapability( const char* aDeviceUniqueIdUTF8, const VideoCaptureCapability& aRequested, VideoCaptureCapability& aResulting) { return 0; } template int32_t DesktopCaptureDeviceInfo::GetOrientation( const char* aDeviceUniqueIdUTF8, VideoRotation& aOrientation) { return 0; } std::shared_ptr CreateDesktopDeviceInfo( int32_t aId, std::unique_ptr&& aInfo) { return std::make_shared(aId, std::move(aInfo)); } std::shared_ptr CreateTabDeviceInfo( int32_t aId, std::unique_ptr&& aInfo) { return std::make_shared(aId, std::move(aInfo)); } } // namespace webrtc