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/. */
6 #include "CanvasCaptureMediaStream.h"
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"
18 using namespace mozilla::layers
;
19 using namespace mozilla::gfx
;
24 class OutputStreamDriver::StreamListener
: public MediaStreamListener
27 explicit StreamListener(OutputStreamDriver
* aDriver
,
29 PrincipalHandle aPrincipalHandle
,
30 SourceMediaStream
* aSourceStream
)
32 , mSourceStream(aSourceStream
)
34 , mPrincipalHandle(aPrincipalHandle
)
35 , mMutex("CanvasCaptureMediaStream OutputStreamDriver::StreamListener")
37 MOZ_ASSERT(mSourceStream
);
44 void SetImage(const RefPtr
<layers::Image
>& aImage
, const TimeStamp
& aTime
)
46 MutexAutoLock
lock(mMutex
);
51 void NotifyPull(MediaStreamGraph
* aGraph
, StreamTime aDesiredTime
) override
53 // Called on the MediaStreamGraph thread.
54 MOZ_ASSERT(mSourceStream
);
55 StreamTime delta
= aDesiredTime
- mSourceStream
->GetEndOfAppendedData(mTrackId
);
57 MutexAutoLock
lock(mMutex
);
59 RefPtr
<Image
> image
= mImage
;
60 IntSize size
= image
? image
->GetSize() : IntSize(0, 0);
62 segment
.AppendFrame(image
.forget(), delta
, size
, mPrincipalHandle
, false,
65 mSourceStream
->AppendToTrack(mTrackId
, &segment
);
69 mSourceStream
->EndAllTrackAndFinish();
73 void NotifyEvent(MediaStreamGraph
* aGraph
, MediaStreamGraphEvent aEvent
) override
75 if (aEvent
== MediaStreamGraphEvent::EVENT_REMOVED
) {
77 mSourceStream
->EndAllTrackAndFinish();
79 MutexAutoLock
lock(mMutex
);
89 const RefPtr
<SourceMediaStream
> mSourceStream
;
90 const TrackID mTrackId
;
91 const PrincipalHandle mPrincipalHandle
;
94 // The below members are protected by mMutex.
95 RefPtr
<layers::Image
> mImage
;
99 OutputStreamDriver::OutputStreamDriver(SourceMediaStream
* aSourceStream
,
100 const TrackID
& aTrackId
,
101 const PrincipalHandle
& aPrincipalHandle
)
102 : FrameCaptureListener()
103 , mSourceStream(aSourceStream
)
104 , mStreamListener(new StreamListener(this, aTrackId
, aPrincipalHandle
,
107 MOZ_ASSERT(NS_IsMainThread());
108 MOZ_ASSERT(mSourceStream
);
109 mSourceStream
->AddListener(mStreamListener
);
110 mSourceStream
->AddTrack(aTrackId
, 0, new VideoSegment());
111 mSourceStream
->AdvanceKnownTracksTime(STREAM_TIME_MAX
);
112 mSourceStream
->SetPullEnabled(true);
114 // All CanvasCaptureMediaStreams shall at least get one frame.
115 mFrameCaptureRequested
= true;
118 OutputStreamDriver::~OutputStreamDriver()
120 MOZ_ASSERT(NS_IsMainThread());
121 if (mStreamListener
) {
122 // MediaStreamGraph will keep the listener alive until it can finish the
123 // stream on the next NotifyPull().
124 mStreamListener
->EndStream();
129 OutputStreamDriver::SetImage(const RefPtr
<layers::Image
>& aImage
,
130 const TimeStamp
& aTime
)
132 if (mStreamListener
) {
133 mStreamListener
->SetImage(aImage
, aTime
);
137 // ----------------------------------------------------------------------
139 class TimerDriver
: public OutputStreamDriver
142 explicit TimerDriver(SourceMediaStream
* aSourceStream
,
144 const TrackID
& aTrackId
,
145 const PrincipalHandle
& aPrincipalHandle
)
146 : OutputStreamDriver(aSourceStream
, aTrackId
, aPrincipalHandle
)
154 NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer
),
158 nsITimer::TYPE_REPEATING_SLACK
,
159 "dom::TimerDriver::TimerDriver");
162 static void TimerTick(nsITimer
* aTimer
, void* aClosure
)
164 MOZ_ASSERT(aClosure
);
165 TimerDriver
* driver
= static_cast<TimerDriver
*>(aClosure
);
167 driver
->RequestFrameCapture();
170 void NewFrame(already_AddRefed
<Image
> aImage
, const TimeStamp
& aTime
) override
172 RefPtr
<Image
> image
= aImage
;
174 if (!mFrameCaptureRequested
) {
178 mFrameCaptureRequested
= false;
179 SetImage(image
.forget(), aTime
);
182 void Forget() override
191 virtual ~TimerDriver() {}
195 nsCOMPtr
<nsITimer
> mTimer
;
198 // ----------------------------------------------------------------------
200 class AutoDriver
: public OutputStreamDriver
203 explicit AutoDriver(SourceMediaStream
* aSourceStream
,
204 const TrackID
& aTrackId
,
205 const PrincipalHandle
& aPrincipalHandle
)
206 : OutputStreamDriver(aSourceStream
, aTrackId
, aPrincipalHandle
) {}
208 void NewFrame(already_AddRefed
<Image
> aImage
, const TimeStamp
& aTime
) override
210 // Don't reset `mFrameCaptureRequested` since AutoDriver shall always have
211 // `mFrameCaptureRequested` set to true.
212 // This also means we should accept every frame as NewFrame is called only
213 // after something changed.
215 RefPtr
<Image
> image
= aImage
;
216 SetImage(image
.forget(), aTime
);
220 virtual ~AutoDriver() {}
223 // ----------------------------------------------------------------------
225 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
,
228 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
229 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
231 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream
)
232 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream
)
234 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner
* aWindow
,
235 HTMLCanvasElement
* aCanvas
)
236 : DOMMediaStream(aWindow
, nullptr)
238 , mOutputStreamDriver(nullptr)
242 CanvasCaptureMediaStream::~CanvasCaptureMediaStream()
244 if (mOutputStreamDriver
) {
245 mOutputStreamDriver
->Forget();
250 CanvasCaptureMediaStream::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
)
252 return dom::CanvasCaptureMediaStreamBinding::Wrap(aCx
, this, aGivenProto
);
256 CanvasCaptureMediaStream::RequestFrame()
258 if (mOutputStreamDriver
) {
259 mOutputStreamDriver
->RequestFrameCapture();
264 CanvasCaptureMediaStream::Init(const dom::Optional
<double>& aFPS
,
265 const TrackID
& aTrackId
,
266 nsIPrincipal
* aPrincipal
)
268 PrincipalHandle principalHandle
= MakePrincipalHandle(aPrincipal
);
270 if (!aFPS
.WasPassed()) {
271 mOutputStreamDriver
=
272 new AutoDriver(GetInputStream()->AsSourceStream(), aTrackId
, principalHandle
);
273 } else if (aFPS
.Value() < 0) {
274 return NS_ERROR_ILLEGAL_VALUE
;
276 // Cap frame rate to 60 FPS for sanity
277 double fps
= std::min(60.0, aFPS
.Value());
278 mOutputStreamDriver
=
279 new TimerDriver(GetInputStream()->AsSourceStream(), fps
, aTrackId
, principalHandle
);
284 already_AddRefed
<CanvasCaptureMediaStream
>
285 CanvasCaptureMediaStream::CreateSourceStream(nsPIDOMWindowInner
* aWindow
,
286 HTMLCanvasElement
* aCanvas
)
288 RefPtr
<CanvasCaptureMediaStream
> stream
= new CanvasCaptureMediaStream(aWindow
, aCanvas
);
289 MediaStreamGraph
* graph
=
290 MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER
, aWindow
,
291 MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE
);
292 stream
->InitSourceStream(graph
);
293 return stream
.forget();
296 FrameCaptureListener
*
297 CanvasCaptureMediaStream::FrameCaptureListener()
299 return mOutputStreamDriver
;
303 CanvasCaptureMediaStream::StopCapture()
305 if (!mOutputStreamDriver
) {
309 mOutputStreamDriver
->Forget();
310 mOutputStreamDriver
= nullptr;
314 } // namespace mozilla