Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "MediaEngineTabVideoSource.h"
7 :
8 : #include "mozilla/gfx/2D.h"
9 : #include "mozilla/gfx/DataSurfaceHelpers.h"
10 : #include "mozilla/RefPtr.h"
11 : #include "mozilla/UniquePtrExtensions.h"
12 : #include "mozilla/dom/BindingDeclarations.h"
13 : #include "nsGlobalWindow.h"
14 : #include "nsIDOMClientRect.h"
15 : #include "nsIDocShell.h"
16 : #include "nsIPresShell.h"
17 : #include "nsPresContext.h"
18 : #include "gfxContext.h"
19 : #include "gfx2DGlue.h"
20 : #include "ImageContainer.h"
21 : #include "Layers.h"
22 : #include "nsIInterfaceRequestorUtils.h"
23 : #include "nsIDOMDocument.h"
24 : #include "nsITabSource.h"
25 : #include "VideoUtils.h"
26 : #include "nsServiceManagerUtils.h"
27 : #include "nsIPrefService.h"
28 : #include "MediaTrackConstraints.h"
29 :
30 : namespace mozilla {
31 :
32 : using namespace mozilla::gfx;
33 :
34 0 : NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
35 :
36 0 : MediaEngineTabVideoSource::MediaEngineTabVideoSource()
37 : : mBufWidthMax(0)
38 : , mBufHeightMax(0)
39 : , mWindowId(0)
40 : , mScrollWithPage(false)
41 : , mViewportOffsetX(0)
42 : , mViewportOffsetY(0)
43 : , mViewportWidth(0)
44 : , mViewportHeight(0)
45 : , mTimePerFrame(0)
46 : , mDataSize(0)
47 : , mBlackedoutWindow(false)
48 0 : , mMonitor("MediaEngineTabVideoSource") {}
49 :
50 : nsresult
51 0 : MediaEngineTabVideoSource::StartRunnable::Run()
52 : {
53 0 : mVideoSource->Draw();
54 0 : mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
55 0 : mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK);
56 0 : if (mVideoSource->mTabSource) {
57 0 : mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow);
58 : }
59 0 : return NS_OK;
60 : }
61 :
62 : nsresult
63 0 : MediaEngineTabVideoSource::StopRunnable::Run()
64 : {
65 0 : if (mVideoSource->mTimer) {
66 0 : mVideoSource->mTimer->Cancel();
67 0 : mVideoSource->mTimer = nullptr;
68 : }
69 0 : if (mVideoSource->mTabSource) {
70 0 : mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow);
71 : }
72 0 : return NS_OK;
73 : }
74 :
75 : NS_IMETHODIMP
76 0 : MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) {
77 0 : Draw();
78 0 : return NS_OK;
79 : }
80 :
81 : NS_IMETHODIMP
82 0 : MediaEngineTabVideoSource::Notify(nsITimer*) {
83 0 : Draw();
84 0 : return NS_OK;
85 : }
86 :
87 : nsresult
88 0 : MediaEngineTabVideoSource::InitRunnable::Run()
89 : {
90 0 : if (mVideoSource->mWindowId != -1) {
91 : nsGlobalWindow* globalWindow =
92 0 : nsGlobalWindow::GetOuterWindowWithId(mVideoSource->mWindowId);
93 0 : if (!globalWindow) {
94 : // We can't access the window, just send a blacked out screen.
95 0 : mVideoSource->mWindow = nullptr;
96 0 : mVideoSource->mBlackedoutWindow = true;
97 : } else {
98 0 : nsCOMPtr<nsPIDOMWindowOuter> window = globalWindow->AsOuter();
99 0 : if (window) {
100 0 : mVideoSource->mWindow = window;
101 0 : mVideoSource->mBlackedoutWindow = false;
102 : }
103 : }
104 : }
105 0 : if (!mVideoSource->mWindow && !mVideoSource->mBlackedoutWindow) {
106 : nsresult rv;
107 0 : mVideoSource->mTabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv);
108 0 : NS_ENSURE_SUCCESS(rv, rv);
109 :
110 0 : nsCOMPtr<mozIDOMWindowProxy> win;
111 0 : rv = mVideoSource->mTabSource->GetTabToStream(getter_AddRefs(win));
112 0 : NS_ENSURE_SUCCESS(rv, rv);
113 0 : if (!win)
114 0 : return NS_OK;
115 :
116 0 : mVideoSource->mWindow = nsPIDOMWindowOuter::From(win);
117 0 : MOZ_ASSERT(mVideoSource->mWindow);
118 : }
119 0 : nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
120 0 : start->Run();
121 0 : return NS_OK;
122 : }
123 :
124 : nsresult
125 0 : MediaEngineTabVideoSource::DestroyRunnable::Run()
126 : {
127 0 : MOZ_ASSERT(NS_IsMainThread());
128 :
129 0 : mVideoSource->mWindow = nullptr;
130 0 : mVideoSource->mTabSource = nullptr;
131 :
132 0 : return NS_OK;
133 : }
134 :
135 : void
136 0 : MediaEngineTabVideoSource::GetName(nsAString& aName) const
137 : {
138 0 : aName.AssignLiteral(u"&getUserMedia.videoSource.tabShare;");
139 0 : }
140 :
141 : void
142 0 : MediaEngineTabVideoSource::GetUUID(nsACString& aUuid) const
143 : {
144 0 : aUuid.AssignLiteral("tab");
145 0 : }
146 :
147 : #define DEFAULT_TABSHARE_VIDEO_MAX_WIDTH 4096
148 : #define DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT 4096
149 : #define DEFAULT_TABSHARE_VIDEO_FRAMERATE 30
150 :
151 : nsresult
152 0 : MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
153 : const MediaEnginePrefs& aPrefs,
154 : const nsString& aDeviceId,
155 : const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
156 : AllocationHandle** aOutHandle,
157 : const char** aOutBadConstraint)
158 : {
159 : // windowId is not a proper constraint, so just read it.
160 : // It has no well-defined behavior in advanced, so ignore it there.
161 :
162 0 : mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
163 0 : aConstraints.mBrowserWindow.Value() : -1;
164 0 : *aOutHandle = nullptr;
165 :
166 : {
167 0 : MonitorAutoLock mon(mMonitor);
168 0 : mState = kAllocated;
169 : }
170 :
171 0 : return Restart(nullptr, aConstraints, aPrefs, aDeviceId, aOutBadConstraint);
172 : }
173 :
174 : nsresult
175 0 : MediaEngineTabVideoSource::Restart(AllocationHandle* aHandle,
176 : const dom::MediaTrackConstraints& aConstraints,
177 : const mozilla::MediaEnginePrefs& aPrefs,
178 : const nsString& aDeviceId,
179 : const char** aOutBadConstraint)
180 : {
181 0 : MOZ_ASSERT(!aHandle);
182 :
183 : // scrollWithPage is not proper a constraint, so just read it.
184 : // It has no well-defined behavior in advanced, so ignore it there.
185 :
186 0 : mScrollWithPage = aConstraints.mScrollWithPage.WasPassed() ?
187 0 : aConstraints.mScrollWithPage.Value() : false;
188 :
189 0 : FlattenedConstraints c(aConstraints);
190 :
191 0 : mBufWidthMax = c.mWidth.Get(DEFAULT_TABSHARE_VIDEO_MAX_WIDTH);
192 0 : mBufHeightMax = c.mHeight.Get(DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT);
193 0 : double frameRate = c.mFrameRate.Get(DEFAULT_TABSHARE_VIDEO_FRAMERATE);
194 0 : mTimePerFrame = std::max(10, int(1000.0 / (frameRate > 0? frameRate : 1)));
195 :
196 0 : if (!mScrollWithPage) {
197 0 : mViewportOffsetX = c.mViewportOffsetX.Get(0);
198 0 : mViewportOffsetY = c.mViewportOffsetY.Get(0);
199 0 : mViewportWidth = c.mViewportWidth.Get(INT32_MAX);
200 0 : mViewportHeight = c.mViewportHeight.Get(INT32_MAX);
201 : }
202 0 : return NS_OK;
203 : }
204 :
205 : nsresult
206 0 : MediaEngineTabVideoSource::Deallocate(AllocationHandle* aHandle)
207 : {
208 0 : MOZ_ASSERT(!aHandle);
209 0 : NS_DispatchToMainThread(do_AddRef(new DestroyRunnable(this)));
210 :
211 : {
212 0 : MonitorAutoLock mon(mMonitor);
213 0 : mState = kReleased;
214 : }
215 0 : return NS_OK;
216 : }
217 :
218 : nsresult
219 0 : MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID,
220 : const PrincipalHandle& aPrincipalHandle)
221 : {
222 0 : nsCOMPtr<nsIRunnable> runnable;
223 0 : if (!mWindow)
224 0 : runnable = new InitRunnable(this);
225 : else
226 0 : runnable = new StartRunnable(this);
227 0 : NS_DispatchToMainThread(runnable);
228 0 : aStream->AddTrack(aID, 0, new VideoSegment());
229 :
230 : {
231 0 : MonitorAutoLock mon(mMonitor);
232 0 : mState = kStarted;
233 : }
234 :
235 0 : return NS_OK;
236 : }
237 :
238 : void
239 0 : MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
240 : SourceMediaStream* aSource,
241 : TrackID aID, StreamTime aDesiredTime,
242 : const PrincipalHandle& aPrincipalHandle)
243 : {
244 0 : VideoSegment segment;
245 0 : MonitorAutoLock mon(mMonitor);
246 0 : if (mState != kStarted) {
247 0 : return;
248 : }
249 :
250 : // Note: we're not giving up mImage here
251 0 : RefPtr<layers::SourceSurfaceImage> image = mImage;
252 0 : StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
253 0 : if (delta > 0) {
254 : // nullptr images are allowed
255 0 : gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
256 0 : segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size,
257 0 : aPrincipalHandle);
258 : // This can fail if either a) we haven't added the track yet, or b)
259 : // we've removed or finished the track.
260 0 : aSource->AppendToTrack(aID, &(segment));
261 : }
262 : }
263 :
264 : void
265 0 : MediaEngineTabVideoSource::Draw() {
266 0 : if (!mWindow && !mBlackedoutWindow) {
267 0 : return;
268 : }
269 :
270 0 : if (mWindow) {
271 0 : if (mScrollWithPage || mViewportWidth == INT32_MAX) {
272 0 : mWindow->GetInnerWidth(&mViewportWidth);
273 : }
274 0 : if (mScrollWithPage || mViewportHeight == INT32_MAX) {
275 0 : mWindow->GetInnerHeight(&mViewportHeight);
276 : }
277 0 : if (!mViewportWidth || !mViewportHeight) {
278 0 : return;
279 : }
280 : } else {
281 0 : mViewportWidth = 640;
282 0 : mViewportHeight = 480;
283 : }
284 :
285 0 : IntSize size;
286 : {
287 : float pixelRatio;
288 0 : if (mWindow) {
289 0 : pixelRatio = mWindow->GetDevicePixelRatio(CallerType::System);
290 : } else {
291 0 : pixelRatio = 1.0f;
292 : }
293 0 : const int32_t deviceWidth = (int32_t)(pixelRatio * mViewportWidth);
294 0 : const int32_t deviceHeight = (int32_t)(pixelRatio * mViewportHeight);
295 :
296 0 : if ((deviceWidth <= mBufWidthMax) && (deviceHeight <= mBufHeightMax)) {
297 0 : size = IntSize(deviceWidth, deviceHeight);
298 : } else {
299 0 : const float scaleWidth = (float)mBufWidthMax / (float)deviceWidth;
300 0 : const float scaleHeight = (float)mBufHeightMax / (float)deviceHeight;
301 0 : const float scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
302 :
303 0 : size = IntSize((int)(scale * deviceWidth), (int)(scale * deviceHeight));
304 : }
305 : }
306 :
307 0 : uint32_t stride = StrideForFormatAndWidth(SurfaceFormat::X8R8G8B8_UINT32,
308 0 : size.width);
309 :
310 0 : if (mDataSize < static_cast<size_t>(stride * size.height)) {
311 0 : mDataSize = stride * size.height;
312 0 : mData = MakeUniqueFallible<unsigned char[]>(mDataSize);
313 : }
314 0 : if (!mData) {
315 0 : return;
316 : }
317 :
318 0 : nsCOMPtr<nsIPresShell> presShell;
319 0 : if (mWindow) {
320 0 : RefPtr<nsPresContext> presContext;
321 0 : nsIDocShell* docshell = mWindow->GetDocShell();
322 0 : if (docshell) {
323 0 : docshell->GetPresContext(getter_AddRefs(presContext));
324 : }
325 0 : if (!presContext) {
326 0 : return;
327 : }
328 0 : presShell = presContext->PresShell();
329 : }
330 :
331 : RefPtr<layers::ImageContainer> container =
332 0 : layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS);
333 : RefPtr<DrawTarget> dt =
334 0 : Factory::CreateDrawTargetForData(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
335 : mData.get(),
336 : size,
337 : stride,
338 : SurfaceFormat::B8G8R8X8,
339 0 : true);
340 0 : if (!dt || !dt->IsValid()) {
341 0 : return;
342 : }
343 :
344 0 : if (mWindow) {
345 0 : RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
346 0 : MOZ_ASSERT(context); // already checked the draw target above
347 0 : context->SetMatrix(context->CurrentMatrix().PreScale((((float) size.width)/mViewportWidth),
348 0 : (((float) size.height)/mViewportHeight)));
349 :
350 0 : nscolor bgColor = NS_RGB(255, 255, 255);
351 0 : uint32_t renderDocFlags = mScrollWithPage? 0 :
352 : (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
353 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
354 0 : nsRect r(nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetX),
355 0 : nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetY),
356 0 : nsPresContext::CSSPixelsToAppUnits((float)mViewportWidth),
357 0 : nsPresContext::CSSPixelsToAppUnits((float)mViewportHeight));
358 0 : NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
359 : } else {
360 0 : dt->ClearRect(Rect(0, 0, size.width, size.height));
361 : }
362 :
363 0 : RefPtr<SourceSurface> surface = dt->Snapshot();
364 0 : if (!surface) {
365 0 : return;
366 : }
367 :
368 0 : RefPtr<layers::SourceSurfaceImage> image = new layers::SourceSurfaceImage(size, surface);
369 :
370 0 : MonitorAutoLock mon(mMonitor);
371 0 : mImage = image;
372 : }
373 :
374 : nsresult
375 0 : MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream* aSource,
376 : mozilla::TrackID aID)
377 : {
378 : // If mBlackedoutWindow is true, we may be running
379 : // despite mWindow == nullptr.
380 0 : if (!mWindow && !mBlackedoutWindow) {
381 0 : return NS_OK;
382 : }
383 :
384 0 : NS_DispatchToMainThread(new StopRunnable(this));
385 :
386 : {
387 0 : MonitorAutoLock mon(mMonitor);
388 0 : mState = kStopped;
389 0 : aSource->EndTrack(aID);
390 : }
391 0 : return NS_OK;
392 : }
393 :
394 : bool
395 0 : MediaEngineTabVideoSource::IsFake()
396 : {
397 0 : return false;
398 : }
399 :
400 : }
|