diff options
Diffstat (limited to 'dom/media/CanvasCaptureMediaStream.cpp')
-rw-r--r-- | dom/media/CanvasCaptureMediaStream.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/dom/media/CanvasCaptureMediaStream.cpp b/dom/media/CanvasCaptureMediaStream.cpp new file mode 100644 index 0000000000..a9d16b895b --- /dev/null +++ b/dom/media/CanvasCaptureMediaStream.cpp @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CanvasCaptureMediaStream.h" + +#include "DOMMediaStream.h" +#include "ImageContainer.h" +#include "MediaTrackGraph.h" +#include "Tracing.h" +#include "VideoSegment.h" +#include "gfxPlatform.h" +#include "mozilla/Atomics.h" +#include "mozilla/dom/CanvasCaptureMediaStreamBinding.h" +#include "mozilla/gfx/2D.h" +#include "nsContentUtils.h" + +using namespace mozilla::layers; +using namespace mozilla::gfx; + +namespace mozilla::dom { + +OutputStreamDriver::OutputStreamDriver(SourceMediaTrack* aSourceStream, + const PrincipalHandle& aPrincipalHandle) + : FrameCaptureListener(), + mSourceStream(aSourceStream), + mPrincipalHandle(aPrincipalHandle) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mSourceStream); +} + +OutputStreamDriver::~OutputStreamDriver() { + MOZ_ASSERT(NS_IsMainThread()); + EndTrack(); +} + +void OutputStreamDriver::EndTrack() { + MOZ_ASSERT(NS_IsMainThread()); + if (!mSourceStream->IsDestroyed()) { + mSourceStream->Destroy(); + } +} + +void OutputStreamDriver::SetImage(RefPtr<layers::Image>&& aImage, + const TimeStamp& aTime) { + MOZ_ASSERT(NS_IsMainThread()); + + VideoSegment segment; + const auto size = aImage->GetSize(); + segment.AppendFrame(aImage.forget(), size, mPrincipalHandle, false, aTime); + mSourceStream->AppendData(&segment); +} + +// ---------------------------------------------------------------------- + +class TimerDriver : public OutputStreamDriver { + public: + explicit TimerDriver(SourceMediaTrack* aSourceStream, const double& aFPS, + const PrincipalHandle& aPrincipalHandle) + : OutputStreamDriver(aSourceStream, aPrincipalHandle), + mFrameInterval(aFPS == 0.0 ? TimeDuration::Forever() + : TimeDuration::FromSeconds(1.0 / aFPS)) {} + + void RequestFrameCapture() override { mExplicitCaptureRequested = true; } + + bool FrameCaptureRequested(const TimeStamp& aTime) const override { + if (mLastFrameTime.IsNull()) { + // All CanvasCaptureMediaStreams shall at least get one frame. + return true; + } + + if (mExplicitCaptureRequested) { + return true; + } + + if ((aTime - mLastFrameTime) >= mFrameInterval) { + return true; + } + + return false; + } + + void NewFrame(already_AddRefed<Image> aImage, + const TimeStamp& aTime) override { + nsCString str; + if (profiler_thread_is_being_profiled_for_markers()) { + TimeDuration sinceLast = + aTime - (mLastFrameTime.IsNull() ? aTime : mLastFrameTime); + str.AppendPrintf( + "TimerDriver %staking frame (%sexplicitly requested; after %.2fms; " + "interval cap %.2fms)", + sinceLast >= mFrameInterval ? "" : "NOT ", + mExplicitCaptureRequested ? "" : "NOT ", sinceLast.ToMilliseconds(), + mFrameInterval.ToMilliseconds()); + } + AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, str); + + RefPtr<Image> image = aImage; + + if (!FrameCaptureRequested(aTime)) { + return; + } + + mLastFrameTime = aTime; + mExplicitCaptureRequested = false; + SetImage(std::move(image), aTime); + } + + protected: + virtual ~TimerDriver() = default; + + private: + const TimeDuration mFrameInterval; + bool mExplicitCaptureRequested = false; + TimeStamp mLastFrameTime; +}; + +// ---------------------------------------------------------------------- + +class AutoDriver : public OutputStreamDriver { + public: + explicit AutoDriver(SourceMediaTrack* aSourceStream, + const PrincipalHandle& aPrincipalHandle) + : OutputStreamDriver(aSourceStream, aPrincipalHandle) {} + + void RequestFrameCapture() override {} + + bool FrameCaptureRequested(const TimeStamp& aTime) const override { + return true; + } + + void NewFrame(already_AddRefed<Image> aImage, + const TimeStamp& aTime) override { + AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, + "AutoDriver taking frame"_ns); + + RefPtr<Image> image = aImage; + SetImage(std::move(image), aTime); + } + + protected: + virtual ~AutoDriver() = default; +}; + +// ---------------------------------------------------------------------- + +NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream, DOMMediaStream, + mCanvas) + +NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) +NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream) +NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) + +CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow, + HTMLCanvasElement* aCanvas) + : DOMMediaStream(aWindow), mCanvas(aCanvas) {} + +CanvasCaptureMediaStream::~CanvasCaptureMediaStream() = default; + +JSObject* CanvasCaptureMediaStream::WrapObject( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx, this, aGivenProto); +} + +void CanvasCaptureMediaStream::RequestFrame() { + if (mOutputStreamDriver) { + mOutputStreamDriver->RequestFrameCapture(); + } +} + +nsresult CanvasCaptureMediaStream::Init(const dom::Optional<double>& aFPS, + nsIPrincipal* aPrincipal) { + MediaTrackGraph* graph = MediaTrackGraph::GetInstance( + MediaTrackGraph::SYSTEM_THREAD_DRIVER, GetOwner(), + MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, + MediaTrackGraph::DEFAULT_OUTPUT_DEVICE); + SourceMediaTrack* source = graph->CreateSourceTrack(MediaSegment::VIDEO); + PrincipalHandle principalHandle = MakePrincipalHandle(aPrincipal); + if (!aFPS.WasPassed()) { + mOutputStreamDriver = new AutoDriver(source, principalHandle); + } else if (aFPS.Value() < 0) { + return NS_ERROR_ILLEGAL_VALUE; + } else { + // Cap frame rate to 60 FPS for sanity + double fps = std::min(60.0, aFPS.Value()); + mOutputStreamDriver = new TimerDriver(source, fps, principalHandle); + } + return NS_OK; +} + +FrameCaptureListener* CanvasCaptureMediaStream::FrameCaptureListener() { + return mOutputStreamDriver; +} + +void CanvasCaptureMediaStream::StopCapture() { + if (!mOutputStreamDriver) { + return; + } + + mOutputStreamDriver->EndTrack(); + mOutputStreamDriver = nullptr; +} + +SourceMediaTrack* CanvasCaptureMediaStream::GetSourceStream() const { + if (!mOutputStreamDriver) { + return nullptr; + } + return mOutputStreamDriver->mSourceStream; +} + +} // namespace mozilla::dom |