Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/CanvasCaptureMediaStream.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 "CanvasCaptureMediaStream.h"
7
8
#include "DOMMediaStream.h"
9
#include "ImageContainer.h"
10
#include "MediaStreamGraph.h"
11
#include "MediaStreamListener.h"
12
#include "gfxPlatform.h"
13
#include "mozilla/Atomics.h"
14
#include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
15
#include "mozilla/gfx/2D.h"
16
#include "nsContentUtils.h"
17
#include "Tracing.h"
18
19
using namespace mozilla::layers;
20
using namespace mozilla::gfx;
21
22
namespace mozilla {
23
namespace dom {
24
25
class OutputStreamDriver::StreamListener : public MediaStreamListener
26
{
27
public:
28
  explicit StreamListener(OutputStreamDriver* aDriver,
29
                          TrackID aTrackId,
30
                          PrincipalHandle aPrincipalHandle,
31
                          SourceMediaStream* aSourceStream)
32
    : mEnded(false)
33
    , mSourceStream(aSourceStream)
34
    , mTrackId(aTrackId)
35
    , mPrincipalHandle(aPrincipalHandle)
36
    , mMutex("CanvasCaptureMediaStream OutputStreamDriver::StreamListener")
37
0
  {
38
0
    MOZ_ASSERT(mSourceStream);
39
0
  }
40
41
0
  void EndStream() {
42
0
    mEnded = true;
43
0
  }
44
45
  void SetImage(const RefPtr<layers::Image>& aImage, const TimeStamp& aTime)
46
0
  {
47
0
    MutexAutoLock lock(mMutex);
48
0
    mImage = aImage;
49
0
    mImageTime = aTime;
50
0
  }
51
52
  void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
53
0
  {
54
0
    // Called on the MediaStreamGraph thread.
55
0
    TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i",
56
0
                                 mSourceStream.get(), mTrackId);
57
0
    MOZ_ASSERT(mSourceStream);
58
0
    StreamTime delta = aDesiredTime - mSourceStream->GetEndOfAppendedData(mTrackId);
59
0
    if (delta > 0) {
60
0
      MutexAutoLock lock(mMutex);
61
0
62
0
      RefPtr<Image> image = mImage;
63
0
      IntSize size = image ? image->GetSize() : IntSize(0, 0);
64
0
      VideoSegment segment;
65
0
      segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle, false,
66
0
                          mImageTime);
67
0
68
0
      mSourceStream->AppendToTrack(mTrackId, &segment);
69
0
    }
70
0
71
0
    if (mEnded) {
72
0
      mSourceStream->EndAllTrackAndFinish();
73
0
    }
74
0
  }
75
76
  void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) override
77
0
  {
78
0
    if (aEvent == MediaStreamGraphEvent::EVENT_REMOVED) {
79
0
      EndStream();
80
0
      mSourceStream->EndAllTrackAndFinish();
81
0
82
0
      MutexAutoLock lock(mMutex);
83
0
      mImage = nullptr;
84
0
    }
85
0
  }
86
87
protected:
88
0
  ~StreamListener() { }
89
90
private:
91
  Atomic<bool> mEnded;
92
  const RefPtr<SourceMediaStream> mSourceStream;
93
  const TrackID mTrackId;
94
  const PrincipalHandle mPrincipalHandle;
95
96
  Mutex mMutex;
97
  // The below members are protected by mMutex.
98
  RefPtr<layers::Image> mImage;
99
  TimeStamp mImageTime;
100
};
101
102
OutputStreamDriver::OutputStreamDriver(SourceMediaStream* aSourceStream,
103
                                       const TrackID& aTrackId,
104
                                       const PrincipalHandle& aPrincipalHandle)
105
  : FrameCaptureListener()
106
  , mSourceStream(aSourceStream)
107
  , mStreamListener(new StreamListener(this, aTrackId, aPrincipalHandle,
108
                                       aSourceStream))
109
0
{
110
0
  MOZ_ASSERT(NS_IsMainThread());
111
0
  MOZ_ASSERT(mSourceStream);
112
0
  mSourceStream->AddListener(mStreamListener);
113
0
  mSourceStream->AddTrack(aTrackId, 0, new VideoSegment());
114
0
  mSourceStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
115
0
  mSourceStream->SetPullEnabled(true);
116
0
117
0
  // All CanvasCaptureMediaStreams shall at least get one frame.
118
0
  mFrameCaptureRequested = true;
119
0
}
120
121
OutputStreamDriver::~OutputStreamDriver()
122
0
{
123
0
  MOZ_ASSERT(NS_IsMainThread());
124
0
  if (mStreamListener) {
125
0
    // MediaStreamGraph will keep the listener alive until it can finish the
126
0
    // stream on the next NotifyPull().
127
0
    mStreamListener->EndStream();
128
0
  }
129
0
}
130
131
void
132
OutputStreamDriver::SetImage(const RefPtr<layers::Image>& aImage,
133
                             const TimeStamp& aTime)
134
0
{
135
0
  if (mStreamListener) {
136
0
    mStreamListener->SetImage(aImage, aTime);
137
0
  }
138
0
}
139
140
// ----------------------------------------------------------------------
141
142
class TimerDriver : public OutputStreamDriver
143
{
144
public:
145
  explicit TimerDriver(SourceMediaStream* aSourceStream,
146
                       const double& aFPS,
147
                       const TrackID& aTrackId,
148
                       const PrincipalHandle& aPrincipalHandle)
149
    : OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle)
150
    , mFPS(aFPS)
151
    , mTimer(nullptr)
152
0
  {
153
0
    if (mFPS == 0.0) {
154
0
      return;
155
0
    }
156
0
157
0
    NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer),
158
0
                                &TimerTick,
159
0
                                this,
160
0
                                int(1000 / mFPS),
161
0
                                nsITimer::TYPE_REPEATING_SLACK,
162
0
                                "dom::TimerDriver::TimerDriver");
163
0
  }
164
165
  static void TimerTick(nsITimer* aTimer, void* aClosure)
166
0
  {
167
0
    MOZ_ASSERT(aClosure);
168
0
    TimerDriver* driver = static_cast<TimerDriver*>(aClosure);
169
0
170
0
    driver->RequestFrameCapture();
171
0
  }
172
173
  void NewFrame(already_AddRefed<Image> aImage, const TimeStamp& aTime) override
174
0
  {
175
0
    RefPtr<Image> image = aImage;
176
0
177
0
    if (!mFrameCaptureRequested) {
178
0
      return;
179
0
    }
180
0
181
0
    mFrameCaptureRequested = false;
182
0
    SetImage(image.forget(), aTime);
183
0
  }
184
185
  void Forget() override
186
0
  {
187
0
    if (mTimer) {
188
0
      mTimer->Cancel();
189
0
      mTimer = nullptr;
190
0
    }
191
0
  }
192
193
protected:
194
0
  virtual ~TimerDriver() {}
195
196
private:
197
  const double mFPS;
198
  nsCOMPtr<nsITimer> mTimer;
199
};
200
201
// ----------------------------------------------------------------------
202
203
class AutoDriver : public OutputStreamDriver
204
{
205
public:
206
  explicit AutoDriver(SourceMediaStream* aSourceStream,
207
                      const TrackID& aTrackId,
208
                      const PrincipalHandle& aPrincipalHandle)
209
0
    : OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle) {}
210
211
  void NewFrame(already_AddRefed<Image> aImage, const TimeStamp& aTime) override
212
0
  {
213
0
    // Don't reset `mFrameCaptureRequested` since AutoDriver shall always have
214
0
    // `mFrameCaptureRequested` set to true.
215
0
    // This also means we should accept every frame as NewFrame is called only
216
0
    // after something changed.
217
0
218
0
    RefPtr<Image> image = aImage;
219
0
    SetImage(image.forget(), aTime);
220
0
  }
221
222
protected:
223
0
  virtual ~AutoDriver() {}
224
};
225
226
// ----------------------------------------------------------------------
227
228
NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream, DOMMediaStream,
229
                                   mCanvas)
230
231
NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
232
NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
233
234
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream)
235
0
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
236
237
CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow,
238
                                                   HTMLCanvasElement* aCanvas)
239
  : DOMMediaStream(aWindow, nullptr)
240
  , mCanvas(aCanvas)
241
  , mOutputStreamDriver(nullptr)
242
0
{
243
0
}
244
245
CanvasCaptureMediaStream::~CanvasCaptureMediaStream()
246
0
{
247
0
  if (mOutputStreamDriver) {
248
0
    mOutputStreamDriver->Forget();
249
0
  }
250
0
}
251
252
JSObject*
253
CanvasCaptureMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
254
0
{
255
0
  return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx, this, aGivenProto);
256
0
}
257
258
void
259
CanvasCaptureMediaStream::RequestFrame()
260
0
{
261
0
  if (mOutputStreamDriver) {
262
0
    mOutputStreamDriver->RequestFrameCapture();
263
0
  }
264
0
}
265
266
nsresult
267
CanvasCaptureMediaStream::Init(const dom::Optional<double>& aFPS,
268
                               const TrackID& aTrackId,
269
                               nsIPrincipal* aPrincipal)
270
0
{
271
0
  PrincipalHandle principalHandle = MakePrincipalHandle(aPrincipal);
272
0
273
0
  if (!aFPS.WasPassed()) {
274
0
    mOutputStreamDriver =
275
0
      new AutoDriver(GetInputStream()->AsSourceStream(), aTrackId, principalHandle);
276
0
  } else if (aFPS.Value() < 0) {
277
0
    return NS_ERROR_ILLEGAL_VALUE;
278
0
  } else {
279
0
    // Cap frame rate to 60 FPS for sanity
280
0
    double fps = std::min(60.0, aFPS.Value());
281
0
    mOutputStreamDriver =
282
0
      new TimerDriver(GetInputStream()->AsSourceStream(), fps, aTrackId, principalHandle);
283
0
  }
284
0
  return NS_OK;
285
0
}
286
287
already_AddRefed<CanvasCaptureMediaStream>
288
CanvasCaptureMediaStream::CreateSourceStream(nsPIDOMWindowInner* aWindow,
289
                                             HTMLCanvasElement* aCanvas)
290
0
{
291
0
  RefPtr<CanvasCaptureMediaStream> stream = new CanvasCaptureMediaStream(aWindow, aCanvas);
292
0
  MediaStreamGraph* graph =
293
0
    MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER, aWindow,
294
0
                                  MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
295
0
  stream->InitSourceStream(graph);
296
0
  return stream.forget();
297
0
}
298
299
FrameCaptureListener*
300
CanvasCaptureMediaStream::FrameCaptureListener()
301
0
{
302
0
  return mOutputStreamDriver;
303
0
}
304
305
void
306
CanvasCaptureMediaStream::StopCapture()
307
0
{
308
0
  if (!mOutputStreamDriver) {
309
0
    return;
310
0
  }
311
0
312
0
  mOutputStreamDriver->Forget();
313
0
  mOutputStreamDriver = nullptr;
314
0
}
315
316
} // namespace dom
317
} // namespace mozilla
318