2015-05-13 09:04:30 +03:00
|
|
|
/* -*- 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"
|
2017-06-20 18:56:55 +03:00
|
|
|
|
2015-05-13 09:04:30 +03:00
|
|
|
#include "DOMMediaStream.h"
|
|
|
|
#include "ImageContainer.h"
|
|
|
|
#include "MediaStreamGraph.h"
|
2017-06-20 18:32:18 +03:00
|
|
|
#include "MediaStreamListener.h"
|
2017-06-20 18:56:55 +03:00
|
|
|
#include "gfxPlatform.h"
|
|
|
|
#include "mozilla/Atomics.h"
|
2015-05-13 09:04:30 +03:00
|
|
|
#include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
|
2015-07-16 13:31:24 +03:00
|
|
|
#include "mozilla/gfx/2D.h"
|
2015-06-11 05:36:12 +03:00
|
|
|
#include "nsContentUtils.h"
|
2018-06-27 18:59:19 +03:00
|
|
|
#include "Tracing.h"
|
2015-05-13 09:04:30 +03:00
|
|
|
|
|
|
|
using namespace mozilla::layers;
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
class OutputStreamDriver::TrackListener : public MediaStreamTrackListener {
|
2015-05-13 09:04:41 +03:00
|
|
|
public:
|
2018-11-23 18:01:36 +03:00
|
|
|
TrackListener(TrackID aTrackId, const PrincipalHandle& aPrincipalHandle,
|
|
|
|
SourceMediaStream* aSourceStream)
|
2015-09-17 07:36:57 +03:00
|
|
|
: mEnded(false),
|
|
|
|
mSourceStream(aSourceStream),
|
|
|
|
mTrackId(aTrackId),
|
2016-04-06 15:56:44 +03:00
|
|
|
mPrincipalHandle(aPrincipalHandle),
|
2015-09-17 07:36:57 +03:00
|
|
|
mMutex("CanvasCaptureMediaStream OutputStreamDriver::StreamListener") {
|
2015-05-13 09:04:41 +03:00
|
|
|
MOZ_ASSERT(mSourceStream);
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
void Forget() {
|
|
|
|
EndTrack();
|
|
|
|
mSourceStream->EndTrack(mTrackId);
|
|
|
|
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
mImage = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EndTrack() { mEnded = true; }
|
2015-05-13 09:04:41 +03:00
|
|
|
|
2017-01-13 13:56:03 +03:00
|
|
|
void SetImage(const RefPtr<layers::Image>& aImage, const TimeStamp& aTime) {
|
2015-05-13 09:04:41 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2015-09-17 07:36:57 +03:00
|
|
|
mImage = aImage;
|
2017-01-13 13:56:03 +03:00
|
|
|
mImageTime = aTime;
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
void NotifyPull(MediaStreamGraph* aGraph, StreamTime aEndOfAppendedData,
|
|
|
|
StreamTime aDesiredTime) override {
|
2015-05-13 09:04:41 +03:00
|
|
|
// Called on the MediaStreamGraph thread.
|
2018-06-27 18:59:19 +03:00
|
|
|
TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i",
|
|
|
|
mSourceStream.get(), mTrackId);
|
2017-01-13 13:56:03 +03:00
|
|
|
MOZ_ASSERT(mSourceStream);
|
2018-11-23 18:01:36 +03:00
|
|
|
StreamTime delta = aDesiredTime - aEndOfAppendedData;
|
|
|
|
MOZ_ASSERT(delta > 0);
|
|
|
|
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
RefPtr<Image> image = mImage;
|
|
|
|
IntSize size = image ? image->GetSize() : IntSize(0, 0);
|
|
|
|
VideoSegment segment;
|
|
|
|
segment.AppendFrame(image.forget(), delta, size, mPrincipalHandle, false,
|
|
|
|
mImageTime);
|
|
|
|
|
|
|
|
mSourceStream->AppendToTrack(mTrackId, &segment);
|
2015-09-17 07:36:57 +03:00
|
|
|
|
|
|
|
if (mEnded) {
|
2018-11-23 18:01:36 +03:00
|
|
|
mSourceStream->EndTrack(mTrackId);
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
void NotifyEnded() override {
|
|
|
|
Forget();
|
|
|
|
|
2018-12-19 07:34:10 +03:00
|
|
|
mSourceStream->Graph()->DispatchToMainThreadStableState(
|
2018-11-23 18:01:36 +03:00
|
|
|
NS_NewRunnableFunction(
|
|
|
|
"OutputStreamDriver::TrackListener::RemoveTrackListener",
|
|
|
|
[self = RefPtr<TrackListener>(this), this]() {
|
|
|
|
if (!mSourceStream->IsDestroyed()) {
|
|
|
|
mSourceStream->RemoveTrackListener(this, mTrackId);
|
|
|
|
}
|
|
|
|
}));
|
2017-10-26 15:32:02 +03:00
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
void NotifyRemoved() override { Forget(); }
|
|
|
|
|
2015-05-13 09:04:41 +03:00
|
|
|
protected:
|
2018-11-23 18:01:36 +03:00
|
|
|
~TrackListener() = default;
|
2015-05-13 09:04:41 +03:00
|
|
|
|
|
|
|
private:
|
2015-09-17 07:36:57 +03:00
|
|
|
Atomic<bool> mEnded;
|
2015-10-18 08:24:48 +03:00
|
|
|
const RefPtr<SourceMediaStream> mSourceStream;
|
2015-09-17 07:36:57 +03:00
|
|
|
const TrackID mTrackId;
|
2016-04-06 15:56:44 +03:00
|
|
|
const PrincipalHandle mPrincipalHandle;
|
2015-05-13 09:04:41 +03:00
|
|
|
|
|
|
|
Mutex mMutex;
|
2015-09-17 07:36:57 +03:00
|
|
|
// The below members are protected by mMutex.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<layers::Image> mImage;
|
2017-01-13 13:56:03 +03:00
|
|
|
TimeStamp mImageTime;
|
2015-05-13 09:04:41 +03:00
|
|
|
};
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
OutputStreamDriver::OutputStreamDriver(SourceMediaStream* aSourceStream,
|
2016-04-06 15:56:44 +03:00
|
|
|
const TrackID& aTrackId,
|
|
|
|
const PrincipalHandle& aPrincipalHandle)
|
2015-09-17 07:36:57 +03:00
|
|
|
: FrameCaptureListener(),
|
|
|
|
mSourceStream(aSourceStream),
|
2018-11-23 18:01:36 +03:00
|
|
|
mTrackListener(
|
|
|
|
new TrackListener(aTrackId, aPrincipalHandle, aSourceStream)) {
|
2015-09-17 07:36:57 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2015-05-13 09:04:41 +03:00
|
|
|
MOZ_ASSERT(mSourceStream);
|
2018-11-23 18:01:42 +03:00
|
|
|
mSourceStream->AddTrack(aTrackId, new VideoSegment());
|
2018-11-23 18:00:02 +03:00
|
|
|
mSourceStream->AddTrackListener(mTrackListener, aTrackId);
|
2018-11-29 20:37:42 +03:00
|
|
|
mSourceStream->SetPullingEnabled(aTrackId, true);
|
2015-05-13 09:04:41 +03:00
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
// All CanvasCaptureMediaStreams shall at least get one frame.
|
|
|
|
mFrameCaptureRequested = true;
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
OutputStreamDriver::~OutputStreamDriver() {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2018-11-23 18:01:36 +03:00
|
|
|
// MediaStreamGraph will keep the listener alive until it can end the track in
|
|
|
|
// the graph on the next NotifyPull().
|
|
|
|
mTrackListener->EndTrack();
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
void OutputStreamDriver::EndTrack() { mTrackListener->EndTrack(); }
|
|
|
|
|
2017-01-13 13:56:03 +03:00
|
|
|
void OutputStreamDriver::SetImage(const RefPtr<layers::Image>& aImage,
|
|
|
|
const TimeStamp& aTime) {
|
2018-11-23 18:01:36 +03:00
|
|
|
mTrackListener->SetImage(aImage, aTime);
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
class TimerDriver : public OutputStreamDriver {
|
|
|
|
public:
|
2015-09-17 07:36:57 +03:00
|
|
|
explicit TimerDriver(SourceMediaStream* aSourceStream, const double& aFPS,
|
2016-04-06 15:56:44 +03:00
|
|
|
const TrackID& aTrackId,
|
|
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
|
|
: OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle),
|
2015-05-13 09:04:41 +03:00
|
|
|
mFPS(aFPS),
|
|
|
|
mTimer(nullptr) {
|
2015-09-17 07:36:57 +03:00
|
|
|
if (mFPS == 0.0) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-13 09:04:41 +03:00
|
|
|
|
2017-10-16 09:15:40 +03:00
|
|
|
NS_NewTimerWithFuncCallback(
|
|
|
|
getter_AddRefs(mTimer), &TimerTick, this, int(1000 / mFPS),
|
|
|
|
nsITimer::TYPE_REPEATING_SLACK, "dom::TimerDriver::TimerDriver");
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
static void TimerTick(nsITimer* aTimer, void* aClosure) {
|
|
|
|
MOZ_ASSERT(aClosure);
|
|
|
|
TimerDriver* driver = static_cast<TimerDriver*>(aClosure);
|
2015-07-16 13:31:24 +03:00
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
driver->RequestFrameCapture();
|
|
|
|
}
|
2015-07-16 13:31:24 +03:00
|
|
|
|
2017-01-13 13:56:03 +03:00
|
|
|
void NewFrame(already_AddRefed<Image> aImage,
|
|
|
|
const TimeStamp& aTime) override {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<Image> image = aImage;
|
2015-07-16 13:31:24 +03:00
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
if (!mFrameCaptureRequested) {
|
|
|
|
return;
|
2015-07-16 13:31:24 +03:00
|
|
|
}
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
mFrameCaptureRequested = false;
|
2017-01-13 13:56:03 +03:00
|
|
|
SetImage(image.forget(), aTime);
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
void Forget() override {
|
|
|
|
if (mTimer) {
|
|
|
|
mTimer->Cancel();
|
|
|
|
mTimer = nullptr;
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual ~TimerDriver() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const double mFPS;
|
|
|
|
nsCOMPtr<nsITimer> mTimer;
|
|
|
|
};
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
2015-09-17 06:46:05 +03:00
|
|
|
class AutoDriver : public OutputStreamDriver {
|
|
|
|
public:
|
|
|
|
explicit AutoDriver(SourceMediaStream* aSourceStream, const TrackID& aTrackId,
|
2016-04-06 15:56:44 +03:00
|
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
|
|
: OutputStreamDriver(aSourceStream, aTrackId, aPrincipalHandle) {}
|
2015-09-17 06:46:05 +03:00
|
|
|
|
2017-01-13 13:56:03 +03:00
|
|
|
void NewFrame(already_AddRefed<Image> aImage,
|
|
|
|
const TimeStamp& aTime) override {
|
2015-09-17 06:46:05 +03:00
|
|
|
// 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.
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<Image> image = aImage;
|
2017-01-13 13:56:03 +03:00
|
|
|
SetImage(image.forget(), aTime);
|
2015-09-17 06:46:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual ~AutoDriver() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
2015-05-13 09:04:30 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream, DOMMediaStream,
|
|
|
|
mCanvas)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
|
|
|
|
|
2017-08-30 02:02:48 +03:00
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasCaptureMediaStream)
|
2015-05-13 09:04:30 +03:00
|
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
|
|
|
|
|
2016-01-05 05:16:20 +03:00
|
|
|
CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow,
|
|
|
|
HTMLCanvasElement* aCanvas)
|
2018-11-23 18:02:03 +03:00
|
|
|
: DOMMediaStream(aWindow), mCanvas(aCanvas), mOutputStreamDriver(nullptr) {}
|
2015-05-13 09:04:30 +03:00
|
|
|
|
|
|
|
CanvasCaptureMediaStream::~CanvasCaptureMediaStream() {
|
2015-05-13 09:04:41 +03:00
|
|
|
if (mOutputStreamDriver) {
|
2015-09-17 07:36:57 +03:00
|
|
|
mOutputStreamDriver->Forget();
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
2015-05-13 09:04:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* CanvasCaptureMediaStream::WrapObject(
|
|
|
|
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return dom::CanvasCaptureMediaStream_Binding::Wrap(aCx, this, aGivenProto);
|
2015-05-13 09:04:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void CanvasCaptureMediaStream::RequestFrame() {
|
2015-05-13 09:04:41 +03:00
|
|
|
if (mOutputStreamDriver) {
|
2015-09-17 07:36:57 +03:00
|
|
|
mOutputStreamDriver->RequestFrameCapture();
|
2015-05-13 09:04:41 +03:00
|
|
|
}
|
2015-05-13 09:04:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult CanvasCaptureMediaStream::Init(const dom::Optional<double>& aFPS,
|
2016-04-06 15:56:44 +03:00
|
|
|
const TrackID& aTrackId,
|
|
|
|
nsIPrincipal* aPrincipal) {
|
|
|
|
PrincipalHandle principalHandle = MakePrincipalHandle(aPrincipal);
|
|
|
|
|
2015-05-13 09:04:30 +03:00
|
|
|
if (!aFPS.WasPassed()) {
|
2016-04-06 15:56:44 +03:00
|
|
|
mOutputStreamDriver = new AutoDriver(GetInputStream()->AsSourceStream(),
|
|
|
|
aTrackId, principalHandle);
|
2015-05-13 09:04:30 +03:00
|
|
|
} else if (aFPS.Value() < 0) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
2015-05-13 09:04:41 +03:00
|
|
|
} else {
|
|
|
|
// Cap frame rate to 60 FPS for sanity
|
|
|
|
double fps = std::min(60.0, aFPS.Value());
|
2016-04-06 15:56:44 +03:00
|
|
|
mOutputStreamDriver = new TimerDriver(GetInputStream()->AsSourceStream(),
|
|
|
|
fps, aTrackId, principalHandle);
|
2015-05-13 09:04:30 +03:00
|
|
|
}
|
2015-09-17 07:36:57 +03:00
|
|
|
return NS_OK;
|
2015-05-13 09:04:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<CanvasCaptureMediaStream>
|
2016-01-30 20:05:36 +03:00
|
|
|
CanvasCaptureMediaStream::CreateSourceStream(nsPIDOMWindowInner* aWindow,
|
2015-05-13 09:04:30 +03:00
|
|
|
HTMLCanvasElement* aCanvas) {
|
2016-01-05 05:16:20 +03:00
|
|
|
RefPtr<CanvasCaptureMediaStream> stream =
|
|
|
|
new CanvasCaptureMediaStream(aWindow, aCanvas);
|
2018-04-03 20:02:15 +03:00
|
|
|
MediaStreamGraph* graph = MediaStreamGraph::GetInstance(
|
|
|
|
MediaStreamGraph::SYSTEM_THREAD_DRIVER, aWindow,
|
|
|
|
MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
|
2016-01-05 05:16:20 +03:00
|
|
|
stream->InitSourceStream(graph);
|
2015-05-13 09:04:30 +03:00
|
|
|
return stream.forget();
|
|
|
|
}
|
|
|
|
|
2015-09-17 07:36:57 +03:00
|
|
|
FrameCaptureListener* CanvasCaptureMediaStream::FrameCaptureListener() {
|
|
|
|
return mOutputStreamDriver;
|
|
|
|
}
|
|
|
|
|
2016-09-12 19:00:14 +03:00
|
|
|
void CanvasCaptureMediaStream::StopCapture() {
|
|
|
|
if (!mOutputStreamDriver) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:01:36 +03:00
|
|
|
mOutputStreamDriver->EndTrack();
|
2016-09-12 19:00:14 +03:00
|
|
|
mOutputStreamDriver->Forget();
|
|
|
|
mOutputStreamDriver = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-05-13 09:04:30 +03:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|