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 "gfxPlatform.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
14 #include "mozilla/gfx/2D.h"
15 #include "nsContentUtils.h"
18 using namespace mozilla::layers
;
19 using namespace mozilla::gfx
;
24 OutputStreamDriver::OutputStreamDriver(SourceMediaStream
* aSourceStream
,
25 const TrackID
& aTrackId
,
26 const PrincipalHandle
& aPrincipalHandle
)
27 : FrameCaptureListener(),
29 mSourceStream(aSourceStream
),
30 mPrincipalHandle(aPrincipalHandle
) {
31 MOZ_ASSERT(NS_IsMainThread());
32 MOZ_ASSERT(mSourceStream
);
33 MOZ_ASSERT(IsTrackIDExplicit(mTrackId
));
34 mSourceStream
->AddTrack(aTrackId
, new VideoSegment());
36 // All CanvasCaptureMediaStreams shall at least get one frame.
37 mFrameCaptureRequested
= true;
40 OutputStreamDriver::~OutputStreamDriver() {
41 MOZ_ASSERT(NS_IsMainThread());
45 void OutputStreamDriver::EndTrack() {
46 MOZ_ASSERT(NS_IsMainThread());
47 if (!mSourceStream
->IsDestroyed()) {
48 mSourceStream
->Destroy();
52 void OutputStreamDriver::SetImage(const RefPtr
<layers::Image
>& aImage
,
53 const TimeStamp
& aTime
) {
54 MOZ_ASSERT(NS_IsMainThread());
56 TRACE_COMMENT("SourceMediaStream %p track %i", mSourceStream
.get(), mTrackId
);
59 segment
.AppendFrame(do_AddRef(aImage
), aImage
->GetSize(), mPrincipalHandle
,
61 mSourceStream
->AppendToTrack(mTrackId
, &segment
);
64 // ----------------------------------------------------------------------
66 class TimerDriver
: public OutputStreamDriver
{
68 explicit TimerDriver(SourceMediaStream
* aSourceStream
, const double& aFPS
,
69 const TrackID
& aTrackId
,
70 const PrincipalHandle
& aPrincipalHandle
)
71 : OutputStreamDriver(aSourceStream
, aTrackId
, aPrincipalHandle
),
78 NS_NewTimerWithFuncCallback(
79 getter_AddRefs(mTimer
), &TimerTick
, this, int(1000 / mFPS
),
80 nsITimer::TYPE_REPEATING_SLACK
, "dom::TimerDriver::TimerDriver");
83 static void TimerTick(nsITimer
* aTimer
, void* aClosure
) {
85 TimerDriver
* driver
= static_cast<TimerDriver
*>(aClosure
);
87 driver
->RequestFrameCapture();
90 void NewFrame(already_AddRefed
<Image
> aImage
,
91 const TimeStamp
& aTime
) override
{
92 RefPtr
<Image
> image
= aImage
;
94 if (!mFrameCaptureRequested
) {
98 mFrameCaptureRequested
= false;
99 SetImage(image
.forget(), aTime
);
102 void Forget() override
{
110 virtual ~TimerDriver() {}
114 nsCOMPtr
<nsITimer
> mTimer
;
117 // ----------------------------------------------------------------------
119 class AutoDriver
: public OutputStreamDriver
{
121 explicit AutoDriver(SourceMediaStream
* aSourceStream
, const TrackID
& aTrackId
,
122 const PrincipalHandle
& aPrincipalHandle
)
123 : OutputStreamDriver(aSourceStream
, aTrackId
, aPrincipalHandle
) {}
125 void NewFrame(already_AddRefed
<Image
> aImage
,
126 const TimeStamp
& aTime
) override
{
127 // Don't reset `mFrameCaptureRequested` since AutoDriver shall always have
128 // `mFrameCaptureRequested` set to true.
129 // This also means we should accept every frame as NewFrame is called only
130 // after something changed.
132 RefPtr
<Image
> image
= aImage
;
133 SetImage(image
.forget(), aTime
);
137 virtual ~AutoDriver() {}
140 // ----------------------------------------------------------------------
142 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
,
145 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
146 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
148 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream
)
149 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream
)
151 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner
* aWindow
,
152 HTMLCanvasElement
* aCanvas
)
153 : DOMMediaStream(aWindow
), mCanvas(aCanvas
) {}
155 CanvasCaptureMediaStream::~CanvasCaptureMediaStream() {
156 if (mOutputStreamDriver
) {
157 mOutputStreamDriver
->Forget();
161 JSObject
* CanvasCaptureMediaStream::WrapObject(
162 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
163 return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx
, this, aGivenProto
);
166 void CanvasCaptureMediaStream::RequestFrame() {
167 if (mOutputStreamDriver
) {
168 mOutputStreamDriver
->RequestFrameCapture();
172 nsresult
CanvasCaptureMediaStream::Init(const dom::Optional
<double>& aFPS
,
173 const TrackID aTrackId
,
174 nsIPrincipal
* aPrincipal
) {
175 MediaStreamGraph
* graph
= MediaStreamGraph::GetInstance(
176 MediaStreamGraph::SYSTEM_THREAD_DRIVER
, mWindow
,
177 MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE
);
178 SourceMediaStream
* source
= graph
->CreateSourceStream();
179 PrincipalHandle principalHandle
= MakePrincipalHandle(aPrincipal
);
180 if (!aFPS
.WasPassed()) {
181 mOutputStreamDriver
= new AutoDriver(source
, aTrackId
, principalHandle
);
182 } else if (aFPS
.Value() < 0) {
183 return NS_ERROR_ILLEGAL_VALUE
;
185 // Cap frame rate to 60 FPS for sanity
186 double fps
= std::min(60.0, aFPS
.Value());
187 mOutputStreamDriver
=
188 new TimerDriver(source
, fps
, aTrackId
, principalHandle
);
193 FrameCaptureListener
* CanvasCaptureMediaStream::FrameCaptureListener() {
194 return mOutputStreamDriver
;
197 void CanvasCaptureMediaStream::StopCapture() {
198 if (!mOutputStreamDriver
) {
202 mOutputStreamDriver
->EndTrack();
203 mOutputStreamDriver
->Forget();
204 mOutputStreamDriver
= nullptr;
207 SourceMediaStream
* CanvasCaptureMediaStream::GetSourceStream() const {
208 if (!mOutputStreamDriver
) {
211 return mOutputStreamDriver
->mSourceStream
;
215 } // namespace mozilla