diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /dom/media/systemservices/video_engine/tab_capturer.cc | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/systemservices/video_engine/tab_capturer.cc')
-rw-r--r-- | dom/media/systemservices/video_engine/tab_capturer.cc | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/dom/media/systemservices/video_engine/tab_capturer.cc b/dom/media/systemservices/video_engine/tab_capturer.cc new file mode 100644 index 0000000000..4050dda6a5 --- /dev/null +++ b/dom/media/systemservices/video_engine/tab_capturer.cc @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capturer.h" + +#include "tab_capturer.h" + +#include <memory> +#include <string> +#include <utility> + +#include "modules/desktop_capture/desktop_frame.h" +#include "mozilla/Logging.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "nsThreadUtils.h" +#include "nsIBrowserWindowTracker.h" +#include "nsIDocShellTreeOwner.h" +#include "nsImportModule.h" +#include "mozilla/dom/BrowserHost.h" +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/ImageBitmapBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/SyncRunnable.h" +#include "desktop_device_info.h" + +#include "MediaUtils.h" + +mozilla::LazyLogModule gTabShareLog("TabShare"); + +using namespace mozilla::dom; + +namespace mozilla { + +class CaptureFrameRequest { + using CapturePromise = TabCapturerWebrtc::CapturePromise; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CaptureFrameRequest) + + CaptureFrameRequest() : mCaptureTime(TimeStamp::Now()) {} + + operator MozPromiseRequestHolder<CapturePromise>&() { return mRequest; } + + void Complete() { mRequest.Complete(); } + void Disconnect() { mRequest.Disconnect(); } + bool Exists() { return mRequest.Exists(); } + + protected: + virtual ~CaptureFrameRequest() = default; + + public: + const TimeStamp mCaptureTime; + + private: + MozPromiseRequestHolder<CapturePromise> mRequest; +}; + +TabCapturerWebrtc::TabCapturerWebrtc( + const webrtc::DesktopCaptureOptions& options) + : mMainThreadWorker( + TaskQueue::Create(do_AddRef(GetMainThreadSerialEventTarget()), + "TabCapturerWebrtc::mMainThreadWorker")) {} + +TabCapturerWebrtc::~TabCapturerWebrtc() { + MOZ_ALWAYS_SUCCEEDS( + mMainThreadWorker->Dispatch(NS_NewRunnableFunction(__func__, [this] { + for (const auto& req : mRequests) { + req->Disconnect(); + } + mMainThreadWorker->BeginShutdown(); + }))); + // Block until the worker has run all pending tasks, since mCallback must + // outlive them, and libwebrtc only guarantees mCallback outlives us. + mMainThreadWorker->AwaitShutdownAndIdle(); +} + +bool TabCapturerWebrtc::GetSourceList( + webrtc::DesktopCapturer::SourceList* sources) { + MOZ_LOG(gTabShareLog, LogLevel::Debug, + ("TabShare: GetSourceList, result %zu", sources->size())); + // XXX UI + return true; +} + +bool TabCapturerWebrtc::SelectSource(webrtc::DesktopCapturer::SourceId id) { + MOZ_LOG(gTabShareLog, LogLevel::Debug, ("TabShare: source %d", (int)id)); + mBrowserId = id; + return true; +} + +bool TabCapturerWebrtc::FocusOnSelectedSource() { return true; } + +void TabCapturerWebrtc::Start(webrtc::DesktopCapturer::Callback* callback) { + RTC_DCHECK(!mCallback); + RTC_DCHECK(callback); + + MOZ_LOG(gTabShareLog, LogLevel::Debug, + ("TabShare: Start, id=%" PRIu64, mBrowserId)); + + mCallback = callback; + CaptureFrame(); +} + +void TabCapturerWebrtc::CaptureFrame() { + auto request = MakeRefPtr<CaptureFrameRequest>(); + InvokeAsync(mMainThreadWorker, __func__, + [this, request]() mutable { + if (mRequests.GetSize() > 2) { + // Allow two async capture requests in flight + request->Disconnect(); + return CapturePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, + __func__); + } + mRequests.PushFront(request.forget()); + return CaptureFrameNow(); + }) + ->Then( + mMainThreadWorker, __func__, + [this, request](const RefPtr<dom::ImageBitmap>& aBitmap) { + if (!CompleteRequest(request)) { + return; + } + + UniquePtr<dom::ImageBitmapCloneData> data = aBitmap->ToCloneData(); + webrtc::DesktopSize size(data->mPictureRect.Width(), + data->mPictureRect.Height()); + webrtc::DesktopRect rect = webrtc::DesktopRect::MakeSize(size); + std::unique_ptr<webrtc::DesktopFrame> frame( + new webrtc::BasicDesktopFrame(size)); + + gfx::DataSourceSurface::ScopedMap map(data->mSurface, + gfx::DataSourceSurface::READ); + if (!map.IsMapped()) { + mCallback->OnCaptureResult( + webrtc::DesktopCapturer::Result::ERROR_TEMPORARY, nullptr); + return; + } + frame->CopyPixelsFrom(map.GetData(), map.GetStride(), rect); + + mCallback->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS, + std::move(frame)); + }, + [this, request](nsresult aRv) { + if (!CompleteRequest(request)) { + return; + } + + mCallback->OnCaptureResult( + webrtc::DesktopCapturer::Result::ERROR_TEMPORARY, nullptr); + }) + ->Track(*request); +} + +bool TabCapturerWebrtc::IsOccluded(const webrtc::DesktopVector& pos) { + return false; +} + +class TabCapturedHandler final : public dom::PromiseNativeHandler { + public: + NS_DECL_ISUPPORTS + + using CapturePromise = TabCapturerWebrtc::CapturePromise; + + static void Create(dom::Promise* aPromise, + MozPromiseHolder<CapturePromise> aHolder) { + MOZ_ASSERT(aPromise); + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<TabCapturedHandler> handler = + new TabCapturedHandler(std::move(aHolder)); + aPromise->AppendNativeHandler(handler); + } + + void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!aValue.isObject())) { + mHolder.Reject(NS_ERROR_UNEXPECTED, __func__); + return; + } + + RefPtr<dom::ImageBitmap> bitmap; + if (NS_WARN_IF(NS_FAILED( + UNWRAP_OBJECT(ImageBitmap, &aValue.toObject(), bitmap)))) { + mHolder.Reject(NS_ERROR_UNEXPECTED, __func__); + return; + } + + mHolder.Resolve(std::move(bitmap), __func__); + } + + void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override { + MOZ_ASSERT(NS_IsMainThread()); + mHolder.Reject(aRv.StealNSResult(), __func__); + } + + private: + explicit TabCapturedHandler(MozPromiseHolder<CapturePromise> aHolder) + : mHolder(std::move(aHolder)) {} + + ~TabCapturedHandler() = default; + + MozPromiseHolder<CapturePromise> mHolder; +}; + +NS_IMPL_ISUPPORTS0(TabCapturedHandler) + +bool TabCapturerWebrtc::CompleteRequest(CaptureFrameRequest* aRequest) { + MOZ_ASSERT(mMainThreadWorker->IsOnCurrentThread()); + if (!aRequest->Exists()) { + // Request was disconnected or overrun + return false; + } + while (CaptureFrameRequest* req = mRequests.Peek()) { + if (req->mCaptureTime > aRequest->mCaptureTime) { + break; + } + // Pop the request before calling the callback, in case it could mutate + // mRequests, now or in the future. + RefPtr<CaptureFrameRequest> dropMe = mRequests.Pop(); + req->Complete(); + if (req->mCaptureTime < aRequest->mCaptureTime) { + mCallback->OnCaptureResult( + webrtc::DesktopCapturer::Result::ERROR_TEMPORARY, nullptr); + } + } + MOZ_DIAGNOSTIC_ASSERT(!aRequest->Exists()); + return true; +} + +auto TabCapturerWebrtc::CaptureFrameNow() -> RefPtr<CapturePromise> { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_LOG(gTabShareLog, LogLevel::Debug, ("TabShare: CaptureFrameNow")); + + WindowGlobalParent* wgp = nullptr; + if (mBrowserId != 0) { + RefPtr<BrowsingContext> context = + BrowsingContext::GetCurrentTopByBrowserId(mBrowserId); + if (context) { + wgp = context->Canonical()->GetCurrentWindowGlobal(); + } + // If we can't access the window, we just won't capture anything + } + if (!wgp) { + return CapturePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); + } + + // XXX This would be more efficient if we used CrossProcessPaint directly and + // returned a surface. + RefPtr<dom::Promise> promise = + wgp->DrawSnapshot(nullptr, 1.0, "white"_ns, false, IgnoreErrors()); + if (!promise) { + return CapturePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + + MozPromiseHolder<CapturePromise> holder; + RefPtr<CapturePromise> p = holder.Ensure(__func__); + TabCapturedHandler::Create(promise, std::move(holder)); + return p; +} +} // namespace mozilla + +namespace webrtc { +// static +std::unique_ptr<webrtc::DesktopCapturer> +webrtc::DesktopCapturer::CreateRawTabCapturer( + const webrtc::DesktopCaptureOptions& options) { + return std::unique_ptr<webrtc::DesktopCapturer>( + new mozilla::TabCapturerWebrtc(options)); +} + +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()); + desktop_tab_list_[static_cast<intptr_t>( + desktopTab->getTabBrowserId())] = desktopTab; + free(contentTitleUTF8); + } + } + }); + mozilla::SyncRunnable::DispatchToThread( + mozilla::GetMainThreadSerialEventTarget(), runnable); +} + +} // namespace webrtc |