summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/video_engine/desktop_device_info.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/video_engine/desktop_device_info.cc')
-rw-r--r--dom/media/systemservices/video_engine/desktop_device_info.cc488
1 files changed, 488 insertions, 0 deletions
diff --git a/dom/media/systemservices/video_engine/desktop_device_info.cc b/dom/media/systemservices/video_engine/desktop_device_info.cc
new file mode 100644
index 0000000000..b3632a509f
--- /dev/null
+++ b/dom/media/systemservices/video_engine/desktop_device_info.cc
@@ -0,0 +1,488 @@
+/* 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 "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIBrowserWindowTracker.h"
+#include "nsImportModule.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+
+namespace webrtc {
+
+static inline void SetStringMember(char** aMember, const char* aValue) {
+ if (!aValue) {
+ return;
+ }
+
+ if (*aMember) {
+ delete[] * aMember;
+ *aMember = nullptr;
+ }
+
+ size_t nBufLen = strlen(aValue) + 1;
+ char* buffer = new char[nBufLen];
+ memcpy(buffer, aValue, nBufLen - 1);
+ buffer[nBufLen - 1] = '\0';
+ *aMember = buffer;
+}
+
+DesktopDisplayDevice::DesktopDisplayDevice() {
+ mScreenId = kInvalidScreenId;
+ mDeviceUniqueIdUTF8 = nullptr;
+ mDeviceNameUTF8 = nullptr;
+ mPid = 0;
+}
+
+DesktopDisplayDevice::~DesktopDisplayDevice() {
+ mScreenId = kInvalidScreenId;
+
+ delete[] mDeviceUniqueIdUTF8;
+ delete[] mDeviceNameUTF8;
+
+ mDeviceUniqueIdUTF8 = nullptr;
+ mDeviceNameUTF8 = nullptr;
+}
+
+void DesktopDisplayDevice::setScreenId(const ScreenId aScreenId) {
+ mScreenId = aScreenId;
+}
+
+void DesktopDisplayDevice::setDeviceName(const char* aDeviceNameUTF8) {
+ SetStringMember(&mDeviceNameUTF8, aDeviceNameUTF8);
+}
+
+void DesktopDisplayDevice::setUniqueIdName(const char* aDeviceUniqueIdUTF8) {
+ SetStringMember(&mDeviceUniqueIdUTF8, aDeviceUniqueIdUTF8);
+}
+
+void DesktopDisplayDevice::setPid(const int aPid) { mPid = aPid; }
+
+ScreenId DesktopDisplayDevice::getScreenId() { return mScreenId; }
+
+const char* DesktopDisplayDevice::getDeviceName() { return mDeviceNameUTF8; }
+
+const char* DesktopDisplayDevice::getUniqueIdName() {
+ return mDeviceUniqueIdUTF8;
+}
+
+pid_t DesktopDisplayDevice::getPid() { return mPid; }
+
+DesktopDisplayDevice& DesktopDisplayDevice::operator=(
+ DesktopDisplayDevice& aOther) {
+ if (&aOther == this) {
+ return *this;
+ }
+ mScreenId = aOther.getScreenId();
+ setUniqueIdName(aOther.getUniqueIdName());
+ setDeviceName(aOther.getDeviceName());
+ mPid = aOther.getPid();
+
+ return *this;
+}
+
+DesktopTab::DesktopTab() {
+ mTabBrowserId = 0;
+ mTabNameUTF8 = nullptr;
+ mTabUniqueIdUTF8 = nullptr;
+ mTabCount = 0;
+}
+
+DesktopTab::~DesktopTab() {
+ delete[] mTabNameUTF8;
+ delete[] mTabUniqueIdUTF8;
+
+ mTabNameUTF8 = nullptr;
+ mTabUniqueIdUTF8 = nullptr;
+}
+
+void DesktopTab::setTabBrowserId(uint64_t aTabBrowserId) {
+ mTabBrowserId = aTabBrowserId;
+}
+
+void DesktopTab::setUniqueIdName(const char* aTabUniqueIdUTF8) {
+ SetStringMember(&mTabUniqueIdUTF8, aTabUniqueIdUTF8);
+}
+
+void DesktopTab::setTabName(const char* aTabNameUTF8) {
+ SetStringMember(&mTabNameUTF8, aTabNameUTF8);
+}
+
+void DesktopTab::setTabCount(const uint32_t aCount) { mTabCount = aCount; }
+
+uint64_t DesktopTab::getTabBrowserId() { return mTabBrowserId; }
+
+const char* DesktopTab::getUniqueIdName() { return mTabUniqueIdUTF8; }
+
+const char* DesktopTab::getTabName() { return mTabNameUTF8; }
+
+uint32_t DesktopTab::getTabCount() { return mTabCount; }
+
+DesktopTab& DesktopTab::operator=(DesktopTab& aOther) {
+ mTabBrowserId = aOther.getTabBrowserId();
+ setUniqueIdName(aOther.getUniqueIdName());
+ setTabName(aOther.getTabName());
+
+ return *this;
+}
+
+class DesktopDeviceInfoImpl : public DesktopDeviceInfo {
+ public:
+ DesktopDeviceInfoImpl();
+ ~DesktopDeviceInfoImpl();
+
+ int32_t Init() override;
+ int32_t Refresh() override;
+ int32_t getDisplayDeviceCount() override;
+ int32_t getDesktopDisplayDeviceInfo(
+ uint32_t aIndex, DesktopDisplayDevice& aDesktopDisplayDevice) override;
+ int32_t getWindowCount() override;
+ int32_t getWindowInfo(uint32_t aIndex,
+ DesktopDisplayDevice& aWindowDevice) override;
+ uint32_t getTabCount() override;
+ int32_t getTabInfo(uint32_t aIndex, DesktopTab& aDesktopTab) override;
+
+ protected:
+ DesktopDisplayDeviceList mDesktopDisplayList;
+ DesktopDisplayDeviceList mDesktopWindowList;
+ DesktopTabList mDesktopTabList;
+
+ void CleanUp();
+ void CleanUpWindowList();
+ void CleanUpTabList();
+ void CleanUpScreenList();
+
+ void InitializeWindowList();
+ virtual void InitializeTabList();
+ void InitializeScreenList();
+
+ void RefreshWindowList();
+ void RefreshTabList();
+ void RefreshScreenList();
+
+ void DummyTabList(DesktopTabList& aList);
+};
+
+DesktopDeviceInfoImpl::DesktopDeviceInfoImpl() = default;
+
+DesktopDeviceInfoImpl::~DesktopDeviceInfoImpl() { CleanUp(); }
+
+int32_t DesktopDeviceInfoImpl::getDisplayDeviceCount() {
+ return static_cast<int32_t>(mDesktopDisplayList.size());
+}
+
+int32_t DesktopDeviceInfoImpl::getDesktopDisplayDeviceInfo(
+ uint32_t aIndex, DesktopDisplayDevice& aDesktopDisplayDevice) {
+ if (aIndex >= mDesktopDisplayList.size()) {
+ return -1;
+ }
+
+ std::map<intptr_t, DesktopDisplayDevice*>::iterator iter =
+ mDesktopDisplayList.begin();
+ std::advance(iter, aIndex);
+ DesktopDisplayDevice* desktopDisplayDevice = iter->second;
+ if (desktopDisplayDevice) {
+ aDesktopDisplayDevice = (*desktopDisplayDevice);
+ }
+
+ return 0;
+}
+
+int32_t DesktopDeviceInfoImpl::getWindowCount() {
+ return static_cast<int32_t>(mDesktopWindowList.size());
+}
+
+int32_t DesktopDeviceInfoImpl::getWindowInfo(
+ uint32_t aIndex, DesktopDisplayDevice& aWindowDevice) {
+ if (aIndex >= mDesktopWindowList.size()) {
+ return -1;
+ }
+
+ std::map<intptr_t, DesktopDisplayDevice*>::iterator itr =
+ mDesktopWindowList.begin();
+ std::advance(itr, aIndex);
+ DesktopDisplayDevice* window = itr->second;
+ if (!window) {
+ return -1;
+ }
+
+ aWindowDevice = (*window);
+ return 0;
+}
+
+uint32_t DesktopDeviceInfoImpl::getTabCount() { return mDesktopTabList.size(); }
+
+int32_t DesktopDeviceInfoImpl::getTabInfo(uint32_t aIndex,
+ DesktopTab& aDesktopTab) {
+ if (aIndex >= mDesktopTabList.size()) {
+ return -1;
+ }
+
+ std::map<intptr_t, DesktopTab*>::iterator iter = mDesktopTabList.begin();
+ std::advance(iter, aIndex);
+ DesktopTab* desktopTab = iter->second;
+ if (desktopTab) {
+ aDesktopTab = (*desktopTab);
+ }
+
+ return 0;
+}
+
+void DesktopDeviceInfoImpl::CleanUp() {
+ CleanUpScreenList();
+ CleanUpWindowList();
+ CleanUpTabList();
+}
+int32_t DesktopDeviceInfoImpl::Init() {
+ InitializeScreenList();
+ InitializeWindowList();
+ InitializeTabList();
+
+ return 0;
+}
+int32_t DesktopDeviceInfoImpl::Refresh() {
+ RefreshScreenList();
+ RefreshWindowList();
+ RefreshTabList();
+
+ return 0;
+}
+
+void DesktopDeviceInfoImpl::CleanUpWindowList() {
+ std::map<intptr_t, DesktopDisplayDevice*>::iterator iterWindow;
+ for (iterWindow = mDesktopWindowList.begin();
+ iterWindow != mDesktopWindowList.end(); iterWindow++) {
+ DesktopDisplayDevice* aWindow = iterWindow->second;
+ delete aWindow;
+ iterWindow->second = nullptr;
+ }
+ mDesktopWindowList.clear();
+}
+
+void DesktopDeviceInfoImpl::InitializeWindowList() {
+ DesktopCaptureOptions options;
+
+// Wayland is special and we will not get any information about windows
+// without going through xdg-desktop-portal. We will already have
+// a screen placeholder so there is no reason to build windows list.
+#if defined(WEBRTC_USE_PIPEWIRE)
+ if (mozilla::StaticPrefs::media_webrtc_capture_allow_pipewire() &&
+ webrtc::DesktopCapturer::IsRunningUnderWayland()) {
+ return;
+ }
+#endif
+
+// Help avoid an X11 deadlock, see bug 1456101.
+#ifdef MOZ_X11
+ MOZ_ALWAYS_SUCCEEDS(mozilla::SyncRunnable::DispatchToThread(
+ mozilla::GetMainThreadSerialEventTarget(),
+ NS_NewRunnableFunction(__func__, [&] {
+ options = DesktopCaptureOptions::CreateDefault();
+ })));
+#else
+ options = DesktopCaptureOptions::CreateDefault();
+#endif
+ std::unique_ptr<DesktopCapturer> winCap =
+ DesktopCapturer::CreateWindowCapturer(options);
+ DesktopCapturer::SourceList list;
+ if (winCap && winCap->GetSourceList(&list)) {
+ DesktopCapturer::SourceList::iterator itr;
+ for (itr = list.begin(); itr != list.end(); itr++) {
+ DesktopDisplayDevice* winDevice = new DesktopDisplayDevice;
+ if (!winDevice) {
+ continue;
+ }
+
+ winDevice->setScreenId(itr->id);
+ winDevice->setDeviceName(itr->title.c_str());
+ winDevice->setPid(itr->pid);
+
+ char idStr[BUFSIZ];
+#if WEBRTC_WIN
+ _snprintf_s(idStr, sizeof(idStr), sizeof(idStr) - 1, "%ld",
+ static_cast<long>(winDevice->getScreenId()));
+#else
+ SprintfLiteral(idStr, "%ld", static_cast<long>(winDevice->getScreenId()));
+#endif
+ winDevice->setUniqueIdName(idStr);
+ mDesktopWindowList[winDevice->getScreenId()] = winDevice;
+ }
+ }
+}
+
+void DesktopDeviceInfoImpl::RefreshWindowList() {
+ CleanUpWindowList();
+ InitializeWindowList();
+}
+
+void DesktopDeviceInfoImpl::CleanUpTabList() {
+ for (auto& iterTab : mDesktopTabList) {
+ DesktopTab* desktopTab = iterTab.second;
+ delete desktopTab;
+ iterTab.second = nullptr;
+ }
+ mDesktopTabList.clear();
+}
+
+void webrtc::DesktopDeviceInfoImpl::InitializeTabList() {
+ if (!mozilla::StaticPrefs::media_getusermedia_browser_enabled()) {
+ return;
+ }
+
+ // 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<nsIRunnable> runnable = NS_NewRunnableFunction(__func__, [&] {
+ nsresult rv;
+ nsCOMPtr<nsIBrowserWindowTracker> bwt =
+ do_ImportModule("resource:///modules/BrowserWindowTracker.jsm",
+ "BrowserWindowTracker", &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsTArray<RefPtr<nsIVisibleTab>> 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);
+
+ DesktopTab* desktopTab = new DesktopTab;
+ if (desktopTab) {
+ char* contentTitleUTF8 = ToNewUTF8String(contentTitle);
+ desktopTab->setTabBrowserId(browserId);
+ desktopTab->setTabName(contentTitleUTF8);
+ std::ostringstream uniqueId;
+ uniqueId << browserId;
+ desktopTab->setUniqueIdName(uniqueId.str().c_str());
+ mDesktopTabList[static_cast<intptr_t>(desktopTab->getTabBrowserId())] =
+ desktopTab;
+ free(contentTitleUTF8);
+ }
+ }
+ });
+ mozilla::SyncRunnable::DispatchToThread(
+ mozilla::GetMainThreadSerialEventTarget(), runnable);
+}
+
+void DesktopDeviceInfoImpl::RefreshTabList() {
+ CleanUpTabList();
+ InitializeTabList();
+}
+
+void DesktopDeviceInfoImpl::CleanUpScreenList() {
+ std::map<intptr_t, DesktopDisplayDevice*>::iterator iterDevice;
+ for (iterDevice = mDesktopDisplayList.begin();
+ iterDevice != mDesktopDisplayList.end(); iterDevice++) {
+ DesktopDisplayDevice* desktopDisplayDevice = iterDevice->second;
+ delete desktopDisplayDevice;
+ iterDevice->second = nullptr;
+ }
+ mDesktopDisplayList.clear();
+}
+
+// With PipeWire we can't select which system resource is shared so
+// we don't create a window/screen list. Instead we place these constants
+// as window name/id so frontend code can identify PipeWire backend
+// and does not try to create screen/window preview.
+
+#define PIPEWIRE_ID 0xaffffff
+#define PIPEWIRE_NAME "####_PIPEWIRE_PORTAL_####"
+
+void DesktopDeviceInfoImpl::InitializeScreenList() {
+ DesktopCaptureOptions options;
+
+// Wayland is special and we will not get any information about screens
+// without going through xdg-desktop-portal so we just need a screen
+// placeholder.
+#if defined(WEBRTC_USE_PIPEWIRE)
+ if (mozilla::StaticPrefs::media_webrtc_capture_allow_pipewire() &&
+ webrtc::DesktopCapturer::IsRunningUnderWayland()) {
+ DesktopDisplayDevice* screenDevice = new DesktopDisplayDevice;
+ if (!screenDevice) {
+ return;
+ }
+
+ screenDevice->setScreenId(PIPEWIRE_ID);
+ screenDevice->setDeviceName(PIPEWIRE_NAME);
+
+ char idStr[BUFSIZ];
+ SprintfLiteral(idStr, "%ld",
+ static_cast<long>(screenDevice->getScreenId()));
+ screenDevice->setUniqueIdName(idStr);
+ mDesktopDisplayList[screenDevice->getScreenId()] = screenDevice;
+ return;
+ }
+#endif
+
+// Help avoid an X11 deadlock, see bug 1456101.
+#ifdef MOZ_X11
+ MOZ_ALWAYS_SUCCEEDS(mozilla::SyncRunnable::DispatchToThread(
+ mozilla::GetMainThreadSerialEventTarget(),
+ NS_NewRunnableFunction(__func__, [&] {
+ options = DesktopCaptureOptions::CreateDefault();
+ })));
+#else
+ options = DesktopCaptureOptions::CreateDefault();
+#endif
+ std::unique_ptr<DesktopCapturer> screenCapturer =
+ DesktopCapturer::CreateScreenCapturer(options);
+ DesktopCapturer::SourceList list;
+ if (screenCapturer && screenCapturer->GetSourceList(&list)) {
+ DesktopCapturer::SourceList::iterator itr;
+ for (itr = list.begin(); itr != list.end(); itr++) {
+ DesktopDisplayDevice* screenDevice = new DesktopDisplayDevice;
+ screenDevice->setScreenId(itr->id);
+ if (list.size() == 1) {
+ screenDevice->setDeviceName("Primary Monitor");
+ } else {
+ screenDevice->setDeviceName(itr->title.c_str());
+ }
+ screenDevice->setPid(itr->pid);
+
+ char idStr[BUFSIZ];
+#if WEBRTC_WIN
+ _snprintf_s(idStr, sizeof(idStr), sizeof(idStr) - 1, "%ld",
+ static_cast<long>(screenDevice->getScreenId()));
+#else
+ SprintfLiteral(idStr, "%ld",
+ static_cast<long>(screenDevice->getScreenId()));
+#endif
+ screenDevice->setUniqueIdName(idStr);
+ mDesktopDisplayList[screenDevice->getScreenId()] = screenDevice;
+ }
+ }
+}
+
+void DesktopDeviceInfoImpl::RefreshScreenList() {
+ CleanUpScreenList();
+ InitializeScreenList();
+}
+
+/* static */
+DesktopDeviceInfo* DesktopDeviceInfo::Create() {
+ auto info = mozilla::MakeUnique<DesktopDeviceInfoImpl>();
+ if (info->Init() != 0) {
+ return nullptr;
+ }
+ return info.release();
+}
+} // namespace webrtc