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 "MediaTrackGraph.h"
12 #include "VideoSegment.h"
13 #include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
14 #include "mozilla/gfx/2D.h"
15 #include "nsContentUtils.h"
16 #include "nsGlobalWindowInner.h"
18 using namespace mozilla::layers
;
19 using namespace mozilla::gfx
;
21 namespace mozilla::dom
{
23 OutputStreamDriver::OutputStreamDriver(SourceMediaTrack
* aSourceStream
,
24 const PrincipalHandle
& aPrincipalHandle
)
25 : mSourceStream(aSourceStream
), mPrincipalHandle(aPrincipalHandle
) {
26 MOZ_ASSERT(NS_IsMainThread());
27 MOZ_ASSERT(mSourceStream
);
30 OutputStreamDriver::~OutputStreamDriver() {
31 MOZ_ASSERT(NS_IsMainThread());
35 void OutputStreamDriver::EndTrack() {
36 MOZ_ASSERT(NS_IsMainThread());
37 if (!mSourceStream
->IsDestroyed()) {
38 mSourceStream
->Destroy();
42 void OutputStreamDriver::SetImage(RefPtr
<layers::Image
>&& aImage
,
43 const TimeStamp
& aTime
) {
44 MOZ_ASSERT(NS_IsMainThread());
47 const auto size
= aImage
->GetSize();
48 segment
.AppendFrame(aImage
.forget(), size
, mPrincipalHandle
, false, aTime
);
49 mSourceStream
->AppendData(&segment
);
52 // ----------------------------------------------------------------------
54 class TimerDriver
: public OutputStreamDriver
{
56 explicit TimerDriver(SourceMediaTrack
* aSourceStream
, const double& aFPS
,
57 const PrincipalHandle
& aPrincipalHandle
)
58 : OutputStreamDriver(aSourceStream
, aPrincipalHandle
),
59 mFrameInterval(aFPS
== 0.0 ? TimeDuration::Forever()
60 : TimeDuration::FromSeconds(1.0 / aFPS
)) {}
62 void RequestFrameCapture() override
{ mExplicitCaptureRequested
= true; }
64 bool FrameCaptureRequested(const TimeStamp
& aTime
) const override
{
65 if (mLastFrameTime
.IsNull()) {
66 // All CanvasCaptureMediaStreams shall at least get one frame.
70 if (mExplicitCaptureRequested
) {
74 if ((aTime
- mLastFrameTime
) >= mFrameInterval
) {
81 void NewFrame(already_AddRefed
<Image
> aImage
,
82 const TimeStamp
& aTime
) override
{
84 if (profiler_thread_is_being_profiled_for_markers()) {
85 TimeDuration sinceLast
=
86 aTime
- (mLastFrameTime
.IsNull() ? aTime
: mLastFrameTime
);
88 "TimerDriver %staking frame (%sexplicitly requested; after %.2fms; "
89 "interval cap %.2fms)",
90 sinceLast
>= mFrameInterval
? "" : "NOT ",
91 mExplicitCaptureRequested
? "" : "NOT ", sinceLast
.ToMilliseconds(),
92 mFrameInterval
.ToMilliseconds());
94 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {}, str
);
96 RefPtr
<Image
> image
= aImage
;
98 if (!FrameCaptureRequested(aTime
)) {
102 mLastFrameTime
= aTime
;
103 mExplicitCaptureRequested
= false;
104 SetImage(std::move(image
), aTime
);
108 virtual ~TimerDriver() = default;
111 const TimeDuration mFrameInterval
;
112 bool mExplicitCaptureRequested
= false;
113 TimeStamp mLastFrameTime
;
116 // ----------------------------------------------------------------------
118 class AutoDriver
: public OutputStreamDriver
{
120 explicit AutoDriver(SourceMediaTrack
* aSourceStream
,
121 const PrincipalHandle
& aPrincipalHandle
)
122 : OutputStreamDriver(aSourceStream
, aPrincipalHandle
) {}
124 void RequestFrameCapture() override
{}
126 bool FrameCaptureRequested(const TimeStamp
& aTime
) const override
{
130 void NewFrame(already_AddRefed
<Image
> aImage
,
131 const TimeStamp
& aTime
) override
{
132 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
133 "AutoDriver taking frame"_ns
);
135 RefPtr
<Image
> image
= aImage
;
136 SetImage(std::move(image
), aTime
);
140 virtual ~AutoDriver() = default;
143 // ----------------------------------------------------------------------
145 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
,
148 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
149 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
151 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream
)
152 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream
)
154 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner
* aWindow
,
155 HTMLCanvasElement
* aCanvas
)
156 : DOMMediaStream(aWindow
), mCanvas(aCanvas
) {}
158 CanvasCaptureMediaStream::~CanvasCaptureMediaStream() = default;
160 JSObject
* CanvasCaptureMediaStream::WrapObject(
161 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
162 return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx
, this, aGivenProto
);
165 void CanvasCaptureMediaStream::RequestFrame() {
166 if (mOutputStreamDriver
) {
167 mOutputStreamDriver
->RequestFrameCapture();
171 nsresult
CanvasCaptureMediaStream::Init(const dom::Optional
<double>& aFPS
,
172 nsIPrincipal
* aPrincipal
) {
173 MediaTrackGraph
* graph
= MediaTrackGraph::GetInstance(
174 MediaTrackGraph::SYSTEM_THREAD_DRIVER
, GetOwnerWindow(),
175 MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE
,
176 MediaTrackGraph::DEFAULT_OUTPUT_DEVICE
);
177 SourceMediaTrack
* source
= graph
->CreateSourceTrack(MediaSegment::VIDEO
);
178 PrincipalHandle principalHandle
= MakePrincipalHandle(aPrincipal
);
179 if (!aFPS
.WasPassed()) {
180 mOutputStreamDriver
= new AutoDriver(source
, principalHandle
);
181 } else if (aFPS
.Value() < 0) {
182 return NS_ERROR_ILLEGAL_VALUE
;
184 // Cap frame rate to 60 FPS for sanity
185 double fps
= std::min(60.0, aFPS
.Value());
186 mOutputStreamDriver
= new TimerDriver(source
, fps
, principalHandle
);
191 FrameCaptureListener
* CanvasCaptureMediaStream::FrameCaptureListener() {
192 return mOutputStreamDriver
;
195 void CanvasCaptureMediaStream::StopCapture() {
196 if (!mOutputStreamDriver
) {
200 mOutputStreamDriver
->EndTrack();
201 mOutputStreamDriver
= nullptr;
204 SourceMediaTrack
* CanvasCaptureMediaStream::GetSourceStream() const {
205 if (!mOutputStreamDriver
) {
208 return mOutputStreamDriver
->mSourceStream
;
211 } // namespace mozilla::dom