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 "gfxPlatform.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
16 #include "mozilla/gfx/2D.h"
17 #include "nsContentUtils.h"
19 using namespace mozilla::layers
;
20 using namespace mozilla::gfx
;
22 namespace mozilla::dom
{
24 OutputStreamDriver::OutputStreamDriver(SourceMediaTrack
* aSourceStream
,
25 const PrincipalHandle
& aPrincipalHandle
)
26 : mSourceStream(aSourceStream
), mPrincipalHandle(aPrincipalHandle
) {
27 MOZ_ASSERT(NS_IsMainThread());
28 MOZ_ASSERT(mSourceStream
);
31 OutputStreamDriver::~OutputStreamDriver() {
32 MOZ_ASSERT(NS_IsMainThread());
36 void OutputStreamDriver::EndTrack() {
37 MOZ_ASSERT(NS_IsMainThread());
38 if (!mSourceStream
->IsDestroyed()) {
39 mSourceStream
->Destroy();
43 void OutputStreamDriver::SetImage(RefPtr
<layers::Image
>&& aImage
,
44 const TimeStamp
& aTime
) {
45 MOZ_ASSERT(NS_IsMainThread());
48 const auto size
= aImage
->GetSize();
49 segment
.AppendFrame(aImage
.forget(), size
, mPrincipalHandle
, false, aTime
);
50 mSourceStream
->AppendData(&segment
);
53 // ----------------------------------------------------------------------
55 class TimerDriver
: public OutputStreamDriver
{
57 explicit TimerDriver(SourceMediaTrack
* aSourceStream
, const double& aFPS
,
58 const PrincipalHandle
& aPrincipalHandle
)
59 : OutputStreamDriver(aSourceStream
, aPrincipalHandle
),
60 mFrameInterval(aFPS
== 0.0 ? TimeDuration::Forever()
61 : TimeDuration::FromSeconds(1.0 / aFPS
)) {}
63 void RequestFrameCapture() override
{ mExplicitCaptureRequested
= true; }
65 bool FrameCaptureRequested(const TimeStamp
& aTime
) const override
{
66 if (mLastFrameTime
.IsNull()) {
67 // All CanvasCaptureMediaStreams shall at least get one frame.
71 if (mExplicitCaptureRequested
) {
75 if ((aTime
- mLastFrameTime
) >= mFrameInterval
) {
82 void NewFrame(already_AddRefed
<Image
> aImage
,
83 const TimeStamp
& aTime
) override
{
85 if (profiler_thread_is_being_profiled_for_markers()) {
86 TimeDuration sinceLast
=
87 aTime
- (mLastFrameTime
.IsNull() ? aTime
: mLastFrameTime
);
89 "TimerDriver %staking frame (%sexplicitly requested; after %.2fms; "
90 "interval cap %.2fms)",
91 sinceLast
>= mFrameInterval
? "" : "NOT ",
92 mExplicitCaptureRequested
? "" : "NOT ", sinceLast
.ToMilliseconds(),
93 mFrameInterval
.ToMilliseconds());
95 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {}, str
);
97 RefPtr
<Image
> image
= aImage
;
99 if (!FrameCaptureRequested(aTime
)) {
103 mLastFrameTime
= aTime
;
104 mExplicitCaptureRequested
= false;
105 SetImage(std::move(image
), aTime
);
109 virtual ~TimerDriver() = default;
112 const TimeDuration mFrameInterval
;
113 bool mExplicitCaptureRequested
= false;
114 TimeStamp mLastFrameTime
;
117 // ----------------------------------------------------------------------
119 class AutoDriver
: public OutputStreamDriver
{
121 explicit AutoDriver(SourceMediaTrack
* aSourceStream
,
122 const PrincipalHandle
& aPrincipalHandle
)
123 : OutputStreamDriver(aSourceStream
, aPrincipalHandle
) {}
125 void RequestFrameCapture() override
{}
127 bool FrameCaptureRequested(const TimeStamp
& aTime
) const override
{
131 void NewFrame(already_AddRefed
<Image
> aImage
,
132 const TimeStamp
& aTime
) override
{
133 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT
, {},
134 "AutoDriver taking frame"_ns
);
136 RefPtr
<Image
> image
= aImage
;
137 SetImage(std::move(image
), aTime
);
141 virtual ~AutoDriver() = default;
144 // ----------------------------------------------------------------------
146 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
,
149 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
150 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream
, DOMMediaStream
)
152 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream
)
153 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream
)
155 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner
* aWindow
,
156 HTMLCanvasElement
* aCanvas
)
157 : DOMMediaStream(aWindow
), mCanvas(aCanvas
) {}
159 CanvasCaptureMediaStream::~CanvasCaptureMediaStream() = default;
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 nsIPrincipal
* aPrincipal
) {
174 MediaTrackGraph
* graph
= MediaTrackGraph::GetInstance(
175 MediaTrackGraph::SYSTEM_THREAD_DRIVER
, GetOwner(),
176 MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE
,
177 MediaTrackGraph::DEFAULT_OUTPUT_DEVICE
);
178 SourceMediaTrack
* source
= graph
->CreateSourceTrack(MediaSegment::VIDEO
);
179 PrincipalHandle principalHandle
= MakePrincipalHandle(aPrincipal
);
180 if (!aFPS
.WasPassed()) {
181 mOutputStreamDriver
= new AutoDriver(source
, 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
= new TimerDriver(source
, fps
, principalHandle
);
192 FrameCaptureListener
* CanvasCaptureMediaStream::FrameCaptureListener() {
193 return mOutputStreamDriver
;
196 void CanvasCaptureMediaStream::StopCapture() {
197 if (!mOutputStreamDriver
) {
201 mOutputStreamDriver
->EndTrack();
202 mOutputStreamDriver
= nullptr;
205 SourceMediaTrack
* CanvasCaptureMediaStream::GetSourceStream() const {
206 if (!mOutputStreamDriver
) {
209 return mOutputStreamDriver
->mSourceStream
;
212 } // namespace mozilla::dom