summaryrefslogtreecommitdiffstats
path: root/dom/media/CanvasCaptureMediaStream.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/media/CanvasCaptureMediaStream.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/CanvasCaptureMediaStream.cpp')
-rw-r--r--dom/media/CanvasCaptureMediaStream.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/dom/media/CanvasCaptureMediaStream.cpp b/dom/media/CanvasCaptureMediaStream.cpp
new file mode 100644
index 0000000000..fc3db850f0
--- /dev/null
+++ b/dom/media/CanvasCaptureMediaStream.cpp
@@ -0,0 +1,208 @@
+/* -*- 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);
+
+ // All CanvasCaptureMediaStreams shall at least get one frame.
+ mFrameCaptureRequested = true;
+}
+
+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());
+
+ TRACE_COMMENT("SourceMediaTrack %p", mSourceStream.get());
+
+ 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),
+ mFPS(aFPS),
+ mTimer(nullptr) {
+ if (mFPS == 0.0) {
+ return;
+ }
+
+ NS_NewTimerWithFuncCallback(
+ getter_AddRefs(mTimer), &TimerTick, this, int(1000 / mFPS),
+ nsITimer::TYPE_REPEATING_SLACK, "dom::TimerDriver::TimerDriver");
+ }
+
+ static void TimerTick(nsITimer* aTimer, void* aClosure) {
+ MOZ_ASSERT(aClosure);
+ TimerDriver* driver = static_cast<TimerDriver*>(aClosure);
+
+ driver->RequestFrameCapture();
+ }
+
+ void NewFrame(already_AddRefed<Image> aImage,
+ const TimeStamp& aTime) override {
+ RefPtr<Image> image = aImage;
+
+ if (!mFrameCaptureRequested) {
+ return;
+ }
+
+ mFrameCaptureRequested = false;
+ SetImage(std::move(image), aTime);
+ }
+
+ void Forget() override {
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ }
+
+ protected:
+ virtual ~TimerDriver() = default;
+
+ private:
+ const double mFPS;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+// ----------------------------------------------------------------------
+
+class AutoDriver : public OutputStreamDriver {
+ public:
+ explicit AutoDriver(SourceMediaTrack* aSourceStream,
+ const PrincipalHandle& aPrincipalHandle)
+ : OutputStreamDriver(aSourceStream, aPrincipalHandle) {}
+
+ void NewFrame(already_AddRefed<Image> aImage,
+ const TimeStamp& aTime) override {
+ // Don't reset `mFrameCaptureRequested` since AutoDriver shall always have
+ // `mFrameCaptureRequested` set to true.
+ // This also means we should accept every frame as NewFrame is called only
+ // after something changed.
+
+ 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() {
+ if (mOutputStreamDriver) {
+ mOutputStreamDriver->Forget();
+ }
+}
+
+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, mWindow,
+ 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->Forget();
+ mOutputStreamDriver = nullptr;
+}
+
+SourceMediaTrack* CanvasCaptureMediaStream::GetSourceStream() const {
+ if (!mOutputStreamDriver) {
+ return nullptr;
+ }
+ return mOutputStreamDriver->mSourceStream;
+}
+
+} // namespace mozilla::dom