diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp
index eef18bd60a2d..a3260d801169 100644
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -191,7 +191,7 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver {
mOwningElement->ProcessDestroyedFrameListeners();
- if (!mOwningElement->IsFrameCaptureRequested()) {
+ if (!mOwningElement->IsFrameCaptureRequested(aTime)) {
return;
}
@@ -1327,13 +1327,13 @@ nsresult HTMLCanvasElement::RegisterFrameCaptureListener(
return NS_OK;
}
-bool HTMLCanvasElement::IsFrameCaptureRequested() const {
+bool HTMLCanvasElement::IsFrameCaptureRequested(const TimeStamp& aTime) const {
for (WeakPtr listener : mRequestedFrameListeners) {
if (!listener) {
continue;
}
- if (listener->FrameCaptureRequested()) {
+ if (listener->FrameCaptureRequested(aTime)) {
return true;
}
}
diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h
index 824fe7d2a687..686c80dd4c1f 100644
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -83,23 +83,18 @@ class HTMLCanvasElementObserver final : public nsIObserver {
* FrameCaptureListener is used by captureStream() as a way of getting video
* frames from the canvas. On a refresh driver tick after something has been
* drawn to the canvas since the last such tick, all registered
- * FrameCaptureListeners whose `mFrameCaptureRequested` equals `true`,
- * will be given a copy of the just-painted canvas.
+ * FrameCaptureListeners that report true for FrameCaptureRequested() will be
+ * given a copy of the just-painted canvas.
* All FrameCaptureListeners get the same copy.
*/
class FrameCaptureListener : public SupportsWeakPtr {
public:
- FrameCaptureListener() : mFrameCaptureRequested(false) {}
-
- /*
- * Called when a frame capture is desired on next paint.
- */
- void RequestFrameCapture() { mFrameCaptureRequested = true; }
+ FrameCaptureListener() = default;
/*
* Indicates to the canvas whether or not this listener has requested a frame.
*/
- bool FrameCaptureRequested() const { return mFrameCaptureRequested; }
+ virtual bool FrameCaptureRequested(const TimeStamp& aTime) const = 0;
/*
* Interface through which new video frames will be provided while
@@ -110,8 +105,6 @@ class FrameCaptureListener : public SupportsWeakPtr {
protected:
virtual ~FrameCaptureListener() = default;
-
- bool mFrameCaptureRequested;
};
class HTMLCanvasElement final : public nsGenericHTMLElement,
@@ -249,7 +242,7 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
* Returns true when there is at least one registered FrameCaptureListener
* that has requested a frame capture.
*/
- bool IsFrameCaptureRequested() const;
+ bool IsFrameCaptureRequested(const TimeStamp& aTime) const;
/*
* Processes destroyed FrameCaptureListeners and removes them if necessary.
diff --git a/dom/media/CanvasCaptureMediaStream.cpp b/dom/media/CanvasCaptureMediaStream.cpp
index ae5d0c70504e..a5dec543b5a1 100644
--- a/dom/media/CanvasCaptureMediaStream.cpp
+++ b/dom/media/CanvasCaptureMediaStream.cpp
@@ -28,9 +28,6 @@ OutputStreamDriver::OutputStreamDriver(SourceMediaTrack* aSourceStream,
mPrincipalHandle(aPrincipalHandle) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mSourceStream);
-
- // All CanvasCaptureMediaStreams shall at least get one frame.
- mFrameCaptureRequested = true;
}
OutputStreamDriver::~OutputStreamDriver() {
@@ -65,49 +62,48 @@ class TimerDriver : public OutputStreamDriver {
explicit TimerDriver(SourceMediaTrack* aSourceStream, const double& aFPS,
const PrincipalHandle& aPrincipalHandle)
: OutputStreamDriver(aSourceStream, aPrincipalHandle),
- mFPS(aFPS),
- mTimer(nullptr) {
- if (mFPS == 0.0) {
- return;
+ 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;
}
- NS_NewTimerWithFuncCallback(
- getter_AddRefs(mTimer), &TimerTick, this, int(1000 / mFPS),
- nsITimer::TYPE_REPEATING_SLACK, "dom::TimerDriver::TimerDriver");
- }
+ if (mExplicitCaptureRequested) {
+ return true;
+ }
- static void TimerTick(nsITimer* aTimer, void* aClosure) {
- MOZ_ASSERT(aClosure);
- TimerDriver* driver = static_cast(aClosure);
+ if ((aTime - mLastFrameTime) >= mFrameInterval) {
+ return true;
+ }
- driver->RequestFrameCapture();
+ return false;
}
void NewFrame(already_AddRefed aImage,
const TimeStamp& aTime) override {
RefPtr image = aImage;
- if (!mFrameCaptureRequested) {
+ if (!FrameCaptureRequested(aTime)) {
return;
}
- mFrameCaptureRequested = false;
+ mLastFrameTime = aTime;
+ mExplicitCaptureRequested = false;
SetImage(std::move(image), aTime);
}
- void Forget() override {
- if (mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
- }
-
protected:
virtual ~TimerDriver() = default;
private:
- const double mFPS;
- nsCOMPtr mTimer;
+ const TimeDuration mFrameInterval;
+ bool mExplicitCaptureRequested = false;
+ TimeStamp mLastFrameTime;
};
// ----------------------------------------------------------------------
@@ -118,13 +114,14 @@ class AutoDriver : public OutputStreamDriver {
const PrincipalHandle& aPrincipalHandle)
: OutputStreamDriver(aSourceStream, aPrincipalHandle) {}
+ void RequestFrameCapture() override {}
+
+ bool FrameCaptureRequested(const TimeStamp& aTime) const override {
+ return true;
+ }
+
void NewFrame(already_AddRefed 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 = aImage;
SetImage(std::move(image), aTime);
}
@@ -148,11 +145,7 @@ CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow,
HTMLCanvasElement* aCanvas)
: DOMMediaStream(aWindow), mCanvas(aCanvas) {}
-CanvasCaptureMediaStream::~CanvasCaptureMediaStream() {
- if (mOutputStreamDriver) {
- mOutputStreamDriver->Forget();
- }
-}
+CanvasCaptureMediaStream::~CanvasCaptureMediaStream() = default;
JSObject* CanvasCaptureMediaStream::WrapObject(
JSContext* aCx, JS::Handle aGivenProto) {
@@ -195,7 +188,6 @@ void CanvasCaptureMediaStream::StopCapture() {
}
mOutputStreamDriver->EndTrack();
- mOutputStreamDriver->Forget();
mOutputStreamDriver = nullptr;
}
diff --git a/dom/media/CanvasCaptureMediaStream.h b/dom/media/CanvasCaptureMediaStream.h
index 1a515072a9cd..faa5972142e5 100644
--- a/dom/media/CanvasCaptureMediaStream.h
+++ b/dom/media/CanvasCaptureMediaStream.h
@@ -66,6 +66,12 @@ class OutputStreamDriver : public FrameCaptureListener {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OutputStreamDriver);
+ /*
+ * Called from js' requestFrame() when it wants the next painted frame to be
+ * explicitly captured.
+ */
+ virtual void RequestFrameCapture() = 0;
+
/*
* Sub classes can SetImage() to update the image being appended to the
* output stream. It will be appended on the next NotifyPull from MTG.
@@ -78,12 +84,6 @@ class OutputStreamDriver : public FrameCaptureListener {
*/
void EndTrack();
- /*
- * Makes sure any internal resources this driver is holding that may create
- * reference cycles are released.
- */
- virtual void Forget() {}
-
const RefPtr mSourceStream;
const PrincipalHandle mPrincipalHandle;