Skip to content

Commit bf6f22d

Browse files
authored
browser(firefox): basic screencast for headless (#2931)
1 parent d5bd459 commit bf6f22d

File tree

4 files changed

+204
-16
lines changed

4 files changed

+204
-16
lines changed

browser_patches/firefox/BUILD_NUMBER

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
1125
2-
Changed: [email protected] Tue Jul 7 10:43:01 PDT 2020
1+
1126
2+
Changed: [email protected] Tue Jul 14 11:16:21 PDT 2020

browser_patches/firefox/juggler/screencast/moz.build

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ XPIDL_SOURCES += [
1111
XPIDL_MODULE = 'jugglerscreencast'
1212

1313
SOURCES += [
14+
'HeadlessWindowCapturer.cpp',
1415
'nsScreencastService.cpp',
1516
'ScreencastEncoder.cpp',
1617
]
@@ -26,6 +27,11 @@ LOCAL_INCLUDES += [
2627
'/media/webrtc/trunk/webrtc',
2728
]
2829

30+
LOCAL_INCLUDES += [
31+
'/widget',
32+
'/widget/headless',
33+
]
34+
2935
include('/media/webrtc/webrtc.mozbuild')
3036
include('/ipc/chromium/chromium-config.mozbuild')
3137

browser_patches/firefox/juggler/screencast/nsScreencastService.cpp

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "nsScreencastService.h"
66

77
#include "ScreencastEncoder.h"
8+
#include "HeadlessWidget.h"
9+
#include "HeadlessWindowCapturer.h"
810
#include "mozilla/ClearOnShutdown.h"
911
#include "mozilla/PresShell.h"
1012
#include "mozilla/StaticPtr.h"
@@ -20,6 +22,8 @@
2022
#include "mozilla/widget/PlatformWidgetTypes.h"
2123
#include "video_engine/desktop_capture_impl.h"
2224

25+
using namespace mozilla::widget;
26+
2327
namespace mozilla {
2428

2529
NS_IMPL_ISUPPORTS(nsScreencastService, nsIScreencastService)
@@ -28,13 +32,35 @@ namespace {
2832

2933
StaticRefPtr<nsScreencastService> gScreencastService;
3034

35+
rtc::scoped_refptr<webrtc::VideoCaptureModule> CreateWindowCapturer(nsIWidget* widget, int sessionId) {
36+
if (gfxPlatform::IsHeadless()) {
37+
HeadlessWidget* headlessWidget = static_cast<HeadlessWidget*>(widget);
38+
return HeadlessWindowCapturer::Create(headlessWidget);
39+
}
40+
#ifdef MOZ_WIDGET_GTK
41+
mozilla::widget::CompositorWidgetInitData initData;
42+
widget->GetCompositorWidgetInitData(&initData);
43+
const mozilla::widget::GtkCompositorWidgetInitData& gtkInitData = initData.get_GtkCompositorWidgetInitData();
44+
nsCString windowId;
45+
# ifdef MOZ_X11
46+
windowId.AppendPrintf("%lu", gtkInitData.XWindow());
47+
return webrtc::DesktopCaptureImpl::Create(sessionId, windowId.get(), webrtc::CaptureDeviceType::Window);
48+
# else
49+
// TODO: support in wayland
50+
fprintf(stderr, "Video capture for Wayland is not implemented\n");
51+
return nullptr;
52+
# endif
53+
#else
54+
fprintf(stderr, "Video capture is not implemented on this platform\n");
55+
return nullptr;
56+
#endif
57+
}
3158
}
3259

3360
class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
3461
public:
35-
Session(int sessionId, const nsCString& windowId, RefPtr<ScreencastEncoder>&& encoder)
36-
: mCaptureModule(webrtc::DesktopCaptureImpl::Create(
37-
sessionId, windowId.get(), webrtc::CaptureDeviceType::Window))
62+
Session(rtc::scoped_refptr<webrtc::VideoCaptureModule>&& capturer, RefPtr<ScreencastEncoder>&& encoder)
63+
: mCaptureModule(std::move(capturer))
3864
, mEncoder(std::move(encoder)) {
3965
}
4066

@@ -107,17 +133,11 @@ nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const
107133
nsIWidget* widget = view->GetWidget();
108134

109135
#ifdef MOZ_WIDGET_GTK
110-
mozilla::widget::CompositorWidgetInitData initData;
111-
widget->GetCompositorWidgetInitData(&initData);
112-
const mozilla::widget::GtkCompositorWidgetInitData& gtkInitData = initData.get_GtkCompositorWidgetInitData();
113-
nsCString windowId;
114-
# ifdef MOZ_X11
115-
windowId.AppendPrintf("%lu", gtkInitData.XWindow());
116-
# else
117-
// TODO: support in wayland
118-
return NS_ERROR_NOT_IMPLEMENTED;
119-
# endif
120136
*sessionId = ++mLastSessionId;
137+
rtc::scoped_refptr<webrtc::VideoCaptureModule> capturer = CreateWindowCapturer(widget, *sessionId);
138+
if (!capturer)
139+
return NS_ERROR_FAILURE;
140+
121141
nsCString error;
122142
Maybe<double> maybeScale;
123143
if (scale)
@@ -128,7 +148,7 @@ nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const
128148
return NS_ERROR_FAILURE;
129149
}
130150

131-
auto session = std::make_unique<Session>(*sessionId, windowId, std::move(encoder));
151+
auto session = std::make_unique<Session>(std::move(capturer), std::move(encoder));
132152
if (!session->Start())
133153
return NS_ERROR_FAILURE;
134154

browser_patches/firefox/patches/bootstrap.diff

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,3 +1810,165 @@ index ea8b9b08f3e6f6e99b8a4fa3fa427beb8c5f5945..a7ec2bd3afe53d500f0cd8f800223ee2
18101810
};
18111811

18121812
/**
1813+
diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp
1814+
index b31a969b7ab3d0fc80912b110d91dfdf3e5991f4..41f483959bd80aa9cc6ad9eac068503639b33887 100644
1815+
--- a/widget/headless/HeadlessCompositorWidget.cpp
1816+
+++ b/widget/headless/HeadlessCompositorWidget.cpp
1817+
@@ -3,6 +3,7 @@
1818+
* License, v. 2.0. If a copy of the MPL was not distributed with this
1819+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1820+
1821+
+#include "mozilla/layers/CompositorThread.h"
1822+
#include "mozilla/widget/PlatformWidgetTypes.h"
1823+
#include "HeadlessCompositorWidget.h"
1824+
#include "VsyncDispatcher.h"
1825+
@@ -17,6 +18,54 @@ HeadlessCompositorWidget::HeadlessCompositorWidget(
1826+
mClientSize = aInitData.InitialClientSize();
1827+
}
1828+
1829+
+void HeadlessCompositorWidget::SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener) {
1830+
+ MOZ_ASSERT(NS_IsMainThread());
1831+
+
1832+
+ layers::CompositorThread()->Dispatch(NewRunnableMethod<HeadlessWidget::SnapshotListener&&>(
1833+
+ "HeadlessCompositorWidget::SetSnapshotListener", this,
1834+
+ &HeadlessCompositorWidget::SetSnapshotListenerOnCompositorThread,
1835+
+ std::move(listener)));
1836+
+}
1837+
+
1838+
+void HeadlessCompositorWidget::SetSnapshotListenerOnCompositorThread(HeadlessWidget::SnapshotListener&& listener) {
1839+
+ MOZ_ASSERT(NS_IsInCompositorThread());
1840+
+ mSnapshotListener = std::move(listener);
1841+
+ UpdateDrawTarget();
1842+
+}
1843+
+
1844+
+already_AddRefed<gfx::DrawTarget> HeadlessCompositorWidget::StartRemoteDrawingInRegion(
1845+
+ LayoutDeviceIntRegion& aInvalidRegion, layers::BufferMode* aBufferMode) {
1846+
+ if (!mDrawTarget)
1847+
+ return nullptr;
1848+
+
1849+
+ *aBufferMode = layers::BufferMode::BUFFER_NONE;
1850+
+ RefPtr<gfx::DrawTarget> result = mDrawTarget;
1851+
+ return result.forget();
1852+
+}
1853+
+
1854+
+void HeadlessCompositorWidget::EndRemoteDrawingInRegion(
1855+
+ gfx::DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) {
1856+
+ if (!mDrawTarget)
1857+
+ return;
1858+
+
1859+
+ if (!mSnapshotListener)
1860+
+ return;
1861+
+
1862+
+ RefPtr<gfx::SourceSurface> snapshot = mDrawTarget->Snapshot();
1863+
+ if (!snapshot) {
1864+
+ fprintf(stderr, "Failed to get snapshot of draw target\n");
1865+
+ return;
1866+
+ }
1867+
+
1868+
+ RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1869+
+ if (!dataSurface) {
1870+
+ fprintf(stderr, "Failed to get data surface from snapshot\n");
1871+
+ return;
1872+
+ }
1873+
+
1874+
+ mSnapshotListener(std::move(dataSurface));
1875+
+}
1876+
+
1877+
void HeadlessCompositorWidget::ObserveVsync(VsyncObserver* aObserver) {
1878+
if (RefPtr<CompositorVsyncDispatcher> cvd =
1879+
mWidget->GetCompositorVsyncDispatcher()) {
1880+
@@ -29,6 +78,25 @@ nsIWidget* HeadlessCompositorWidget::RealWidget() { return mWidget; }
1881+
void HeadlessCompositorWidget::NotifyClientSizeChanged(
1882+
const LayoutDeviceIntSize& aClientSize) {
1883+
mClientSize = aClientSize;
1884+
+ UpdateDrawTarget();
1885+
+}
1886+
+
1887+
+void HeadlessCompositorWidget::UpdateDrawTarget() {
1888+
+ if (!mSnapshotListener) {
1889+
+ mDrawTarget = nullptr;
1890+
+ return;
1891+
+ }
1892+
+
1893+
+ if (mClientSize.IsEmpty()) {
1894+
+ mDrawTarget = nullptr;
1895+
+ return;
1896+
+ }
1897+
+
1898+
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
1899+
+ gfx::IntSize size = mClientSize.ToUnknownSize();
1900+
+ // TODO: this is called on Main thread, while Start/End drawing are on Compositor thread.
1901+
+ mDrawTarget = mozilla::gfx::Factory::CreateDrawTarget(
1902+
+ mozilla::gfx::BackendType::SKIA, size, format);
1903+
}
1904+
1905+
LayoutDeviceIntSize HeadlessCompositorWidget::GetClientSize() {
1906+
diff --git a/widget/headless/HeadlessCompositorWidget.h b/widget/headless/HeadlessCompositorWidget.h
1907+
index 7f91de9e67d7ffa02de3eef1d760e5cfd05e7ad6..849cd6f98982fbabc8e483c8bb8f7935225869fc 100644
1908+
--- a/widget/headless/HeadlessCompositorWidget.h
1909+
+++ b/widget/headless/HeadlessCompositorWidget.h
1910+
@@ -23,9 +23,16 @@ class HeadlessCompositorWidget final : public CompositorWidget,
1911+
HeadlessWidget* aWindow);
1912+
1913+
void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize);
1914+
+ void SetSnapshotListener(HeadlessWidget::SnapshotListener&& listener);
1915+
1916+
// CompositorWidget Overrides
1917+
1918+
+ already_AddRefed<gfx::DrawTarget> StartRemoteDrawingInRegion(
1919+
+ LayoutDeviceIntRegion& aInvalidRegion, layers::BufferMode* aBufferMode) override;
1920+
+ void EndRemoteDrawingInRegion(
1921+
+ gfx::DrawTarget* aDrawTarget,
1922+
+ const LayoutDeviceIntRegion& aInvalidRegion) override;
1923+
+
1924+
uintptr_t GetWidgetKey() override;
1925+
1926+
LayoutDeviceIntSize GetClientSize() override;
1927+
@@ -42,9 +49,15 @@ class HeadlessCompositorWidget final : public CompositorWidget,
1928+
}
1929+
1930+
private:
1931+
+ void SetSnapshotListenerOnCompositorThread(HeadlessWidget::SnapshotListener&& listener);
1932+
+ void UpdateDrawTarget();
1933+
+
1934+
HeadlessWidget* mWidget;
1935+
1936+
LayoutDeviceIntSize mClientSize;
1937+
+
1938+
+ HeadlessWidget::SnapshotListener mSnapshotListener;
1939+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
1940+
};
1941+
1942+
} // namespace widget
1943+
diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp
1944+
index 7589d8a1a886dab5431e423d20f7d0aa19c2af75..19dd67a330848b6b39bfc578a6940385329fff8e 100644
1945+
--- a/widget/headless/HeadlessWidget.cpp
1946+
+++ b/widget/headless/HeadlessWidget.cpp
1947+
@@ -499,5 +499,13 @@ nsresult HeadlessWidget::SynthesizeNativeTouchPoint(
1948+
return NS_OK;
1949+
}
1950+
1951+
+void HeadlessWidget::SetSnapshotListener(SnapshotListener&& listener) {
1952+
+ if (!mCompositorWidget) {
1953+
+ fprintf(stderr, "Trying to set SnapshotListener without compositor widget\n");
1954+
+ return;
1955+
+ }
1956+
+ mCompositorWidget->SetSnapshotListener(std::move(listener));
1957+
+}
1958+
+
1959+
} // namespace widget
1960+
} // namespace mozilla
1961+
diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h
1962+
index c375629d4a954f872a2abdd6983ae38dbb98f4ca..1857a4874ac9f8a3d7e402b5707a9ea58f241eb9 100644
1963+
--- a/widget/headless/HeadlessWidget.h
1964+
+++ b/widget/headless/HeadlessWidget.h
1965+
@@ -153,6 +153,9 @@ class HeadlessWidget : public nsBaseWidget {
1966+
uint32_t aPointerOrientation,
1967+
nsIObserver* aObserver) override;
1968+
1969+
+ using SnapshotListener = std::function<void(RefPtr<gfx::DataSourceSurface>&&)>;
1970+
+ void SetSnapshotListener(SnapshotListener&& listener);
1971+
+
1972+
private:
1973+
~HeadlessWidget();
1974+
bool mEnabled;

0 commit comments

Comments
 (0)