From de403291267f8ee16703af4efe4e2eac6e5c6b8b Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 14 Jul 2020 12:25:41 -0700 Subject: [PATCH] browser(firefox): add new files for headless screencast (#2947) --- .../screencast/HeadlessWindowCapturer.cpp | 113 ++++++++++++++++++ .../screencast/HeadlessWindowCapturer.h | 59 +++++++++ 2 files changed, 172 insertions(+) create mode 100644 browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.cpp create mode 100644 browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.h diff --git a/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.cpp b/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.cpp new file mode 100644 index 0000000000..0caf74d5cb --- /dev/null +++ b/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.cpp @@ -0,0 +1,113 @@ +/* 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 "HeadlessWindowCapturer.h" + +#include "api/video/i420_buffer.h" +#include "HeadlessWidget.h" +#include "libyuv.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "rtc_base/refcountedobject.h" +#include "rtc_base/scoped_ref_ptr.h" + +using namespace mozilla::widget; +using namespace webrtc; + +namespace mozilla { + +rtc::scoped_refptr HeadlessWindowCapturer::Create(HeadlessWidget* headlessWindow) { + return new rtc::RefCountedObject(headlessWindow); +} + +HeadlessWindowCapturer::HeadlessWindowCapturer(mozilla::widget::HeadlessWidget* window) + : mWindow(window) { +} +HeadlessWindowCapturer::~HeadlessWindowCapturer() { + StopCapture(); +} + + +void HeadlessWindowCapturer::RegisterCaptureDataCallback(rtc::VideoSinkInterface* dataCallback) { + rtc::CritScope lock2(&_callBackCs); + _dataCallBacks.insert(dataCallback); +} +void HeadlessWindowCapturer::DeRegisterCaptureDataCallback(rtc::VideoSinkInterface* dataCallback) { + rtc::CritScope lock2(&_callBackCs); + auto it = _dataCallBacks.find(dataCallback); + if (it != _dataCallBacks.end()) { + _dataCallBacks.erase(it); + } +} + +void HeadlessWindowCapturer::NotifyFrameCaptured(const webrtc::VideoFrame& frame) { + rtc::CritScope lock2(&_callBackCs); + for (auto dataCallBack : _dataCallBacks) + dataCallBack->OnFrame(frame); +} + +int32_t HeadlessWindowCapturer::StopCaptureIfAllClientsClose() { + if (_dataCallBacks.empty()) { + return StopCapture(); + } else { + return 0; + } +} + +int32_t HeadlessWindowCapturer::StartCapture(const VideoCaptureCapability& capability) { + mWindow->SetSnapshotListener([this] (RefPtr&& dataSurface){ + if (!NS_IsInCompositorThread()) { + fprintf(stderr, "SnapshotListener is called not on the Compositor thread!\n"); + return; + } + + if (dataSurface->GetFormat() != gfx::SurfaceFormat::B8G8R8A8) { + fprintf(stderr, "Uexpected snapshot surface format: %hhd\n", dataSurface->GetFormat()); + return; + } + + int width = dataSurface->GetSize().width; + int height = dataSurface->GetSize().height; + rtc::scoped_refptr buffer = I420Buffer::Create(width, height); + + gfx::DataSourceSurface::ScopedMap map(dataSurface.get(), gfx::DataSourceSurface::MapType::READ); + if (!map.IsMapped()) { + fprintf(stderr, "Failed to map snapshot bytes!\n"); + return; + } + +#if MOZ_LITTLE_ENDIAN() + const int conversionResult = libyuv::ARGBToI420( +#else + const int conversionResult = libyuv::BGRAToI420( +#endif + map.GetData(), map.GetStride(), + buffer->MutableDataY(), buffer->StrideY(), + buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), + width, height); + + if (conversionResult != 0) { + fprintf(stderr, "Failed to convert capture frame to I420: %d\n", conversionResult); + return; + } + + VideoFrame captureFrame(buffer, 0, rtc::TimeMillis(), kVideoRotation_0); + NotifyFrameCaptured(captureFrame); + }); + return 0; +} + +int32_t HeadlessWindowCapturer::StopCapture() { + if (!CaptureStarted()) + return 0; + mWindow->SetSnapshotListener(nullptr); + return 0; +} + +bool HeadlessWindowCapturer::CaptureStarted() { + return true; +} + +} // namespace mozilla diff --git a/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.h b/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.h new file mode 100644 index 0000000000..86852b1e78 --- /dev/null +++ b/browser_patches/firefox/juggler/screencast/HeadlessWindowCapturer.h @@ -0,0 +1,59 @@ +/* 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/. */ + +#pragma once + +#include +#include +#include "api/video/video_frame.h" +#include "media/base/videosinkinterface.h" +#include "modules/video_capture/video_capture.h" +#include "rtc_base/criticalsection.h" + +class nsIWidget; + +namespace mozilla { + +namespace widget { +class HeadlessWidget; +} + +class HeadlessWindowCapturer : public webrtc::VideoCaptureModule { + public: + static rtc::scoped_refptr Create(mozilla::widget::HeadlessWidget*); + + void RegisterCaptureDataCallback( + rtc::VideoSinkInterface* dataCallback) override; + void DeRegisterCaptureDataCallback( + rtc::VideoSinkInterface* dataCallback) override; + int32_t StopCaptureIfAllClientsClose() override; + + int32_t SetCaptureRotation(webrtc::VideoRotation) override { return -1; } + bool SetApplyRotation(bool) override { return false; } + bool GetApplyRotation() override { return true; } + + const char* CurrentDeviceName() const override { return "Headless window"; } + + // Platform dependent + int32_t StartCapture(const webrtc::VideoCaptureCapability& capability) override; + bool FocusOnSelectedSource() override { return false; } + int32_t StopCapture() override; + bool CaptureStarted() override; + int32_t CaptureSettings(webrtc::VideoCaptureCapability& settings) override { + return -1; + } + + protected: + HeadlessWindowCapturer(mozilla::widget::HeadlessWidget*); + ~HeadlessWindowCapturer() override; + + private: + void NotifyFrameCaptured(const webrtc::VideoFrame& frame); + + mozilla::widget::HeadlessWidget* mWindow = nullptr; + rtc::CriticalSection _callBackCs; + std::set*> _dataCallBacks; +}; + +} // namespace mozilla