CanvasCaptureMediaStream.cpp (6720B)
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 "MediaTrackGraph.h" 11 #include "Tracing.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" 17 18 using namespace mozilla::layers; 19 using namespace mozilla::gfx; 20 21 namespace mozilla::dom { 22 23 OutputStreamDriver::OutputStreamDriver(SourceMediaTrack* aSourceStream, 24 const PrincipalHandle& aPrincipalHandle) 25 : mSourceStream(aSourceStream), mPrincipalHandle(aPrincipalHandle) { 26 MOZ_ASSERT(NS_IsMainThread()); 27 MOZ_ASSERT(mSourceStream); 28 } 29 30 OutputStreamDriver::~OutputStreamDriver() { 31 MOZ_ASSERT(NS_IsMainThread()); 32 EndTrack(); 33 } 34 35 void OutputStreamDriver::EndTrack() { 36 MOZ_ASSERT(NS_IsMainThread()); 37 if (!mSourceStream->IsDestroyed()) { 38 mSourceStream->Destroy(); 39 } 40 } 41 42 void OutputStreamDriver::SetImage(RefPtr<layers::Image>&& aImage, 43 const TimeStamp& aTime) { 44 MOZ_ASSERT(NS_IsMainThread()); 45 46 VideoSegment segment; 47 const auto size = aImage->GetSize(); 48 segment.AppendFrame(aImage.forget(), size, mPrincipalHandle, false, aTime); 49 mSourceStream->AppendData(&segment); 50 } 51 52 // ---------------------------------------------------------------------- 53 54 class TimerDriver : public OutputStreamDriver { 55 public: 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)) {} 61 62 void RequestFrameCapture() override { mExplicitCaptureRequested = true; } 63 64 bool FrameCaptureRequested(const TimeStamp& aTime) const override { 65 if (mLastFrameTime.IsNull()) { 66 // All CanvasCaptureMediaStreams shall at least get one frame. 67 return true; 68 } 69 70 if (mExplicitCaptureRequested) { 71 return true; 72 } 73 74 if ((aTime - mLastFrameTime) >= mFrameInterval) { 75 return true; 76 } 77 78 return false; 79 } 80 81 void NewFrame(already_AddRefed<Image> aImage, 82 const TimeStamp& aTime) override { 83 nsCString str; 84 if (profiler_thread_is_being_profiled_for_markers()) { 85 TimeDuration sinceLast = 86 aTime - (mLastFrameTime.IsNull() ? aTime : mLastFrameTime); 87 str.AppendPrintf( 88 "TimerDriver %staking frame (%sexplicitly requested; after %.2fms; " 89 "interval cap %.2fms)", 90 sinceLast >= mFrameInterval ? "" : "NOT ", 91 mExplicitCaptureRequested ? "" : "NOT ", sinceLast.ToMilliseconds(), 92 mFrameInterval.ToMilliseconds()); 93 } 94 AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, str); 95 96 RefPtr<Image> image = aImage; 97 98 if (!FrameCaptureRequested(aTime)) { 99 return; 100 } 101 102 mLastFrameTime = aTime; 103 mExplicitCaptureRequested = false; 104 SetImage(std::move(image), aTime); 105 } 106 107 protected: 108 virtual ~TimerDriver() = default; 109 110 private: 111 const TimeDuration mFrameInterval; 112 bool mExplicitCaptureRequested = false; 113 TimeStamp mLastFrameTime; 114 }; 115 116 // ---------------------------------------------------------------------- 117 118 class AutoDriver : public OutputStreamDriver { 119 public: 120 explicit AutoDriver(SourceMediaTrack* aSourceStream, 121 const PrincipalHandle& aPrincipalHandle) 122 : OutputStreamDriver(aSourceStream, aPrincipalHandle) {} 123 124 void RequestFrameCapture() override {} 125 126 bool FrameCaptureRequested(const TimeStamp& aTime) const override { 127 return true; 128 } 129 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); 134 135 RefPtr<Image> image = aImage; 136 SetImage(std::move(image), aTime); 137 } 138 139 protected: 140 virtual ~AutoDriver() = default; 141 }; 142 143 // ---------------------------------------------------------------------- 144 145 NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream, DOMMediaStream, 146 mCanvas) 147 148 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) 149 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) 150 151 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream) 152 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) 153 154 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow, 155 HTMLCanvasElement* aCanvas) 156 : DOMMediaStream(aWindow), mCanvas(aCanvas) {} 157 158 CanvasCaptureMediaStream::~CanvasCaptureMediaStream() = default; 159 160 JSObject* CanvasCaptureMediaStream::WrapObject( 161 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 162 return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx, this, aGivenProto); 163 } 164 165 void CanvasCaptureMediaStream::RequestFrame() { 166 if (mOutputStreamDriver) { 167 mOutputStreamDriver->RequestFrameCapture(); 168 } 169 } 170 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; 183 } else { 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); 187 } 188 return NS_OK; 189 } 190 191 FrameCaptureListener* CanvasCaptureMediaStream::FrameCaptureListener() { 192 return mOutputStreamDriver; 193 } 194 195 void CanvasCaptureMediaStream::StopCapture() { 196 if (!mOutputStreamDriver) { 197 return; 198 } 199 200 mOutputStreamDriver->EndTrack(); 201 mOutputStreamDriver = nullptr; 202 } 203 204 SourceMediaTrack* CanvasCaptureMediaStream::GetSourceStream() const { 205 if (!mOutputStreamDriver) { 206 return nullptr; 207 } 208 return mOutputStreamDriver->mSourceStream; 209 } 210 211 } // namespace mozilla::dom