Coverage Report

Created: 2018-09-25 14:53

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