diff options
Diffstat (limited to 'dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h')
-rw-r--r-- | dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h b/dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h new file mode 100644 index 0000000000..6d61ac3cd5 --- /dev/null +++ b/dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCRTPSCRIPTTRANSFORMER_H_ +#define MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCRTPSCRIPTTRANSFORMER_H_ + +#include "nsISupports.h" +#include "nsWrapperCache.h" +#include "mozilla/RefPtr.h" +#include "mozilla/dom/ReadableStream.h" +#include "mozilla/dom/WritableStream.h" +#include "mozilla/Maybe.h" +#include "js/RootingAPI.h" +#include "nsTArray.h" +#include "nsCOMArray.h" +#include <memory> +#include "nsTHashSet.h" +#include "nsCycleCollectionParticipant.h" + +class nsPIDOMWindowInner; + +namespace webrtc { +class TransformableFrameInterface; +} + +namespace mozilla { +class FrameTransformerProxy; + +namespace dom { +class Worker; +class WorkerPrivate; + +// Dirt simple source for ReadableStream that accepts nsISupports +// Might be suitable to move someplace else, with some polish. +class nsISupportsStreamSource final : public UnderlyingSourceAlgorithmsWrapper { + public: + nsISupportsStreamSource(); + nsISupportsStreamSource(const nsISupportsStreamSource&) = delete; + nsISupportsStreamSource(nsISupportsStreamSource&&) = delete; + nsISupportsStreamSource& operator=(const nsISupportsStreamSource&) = delete; + nsISupportsStreamSource& operator=(nsISupportsStreamSource&&) = delete; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsISupportsStreamSource, + UnderlyingSourceAlgorithmsWrapper) + + void Init(ReadableStream* aStream); + + void Enqueue(nsISupports* aThing); + + // From UnderlyingSourceAlgorithmsWrapper + already_AddRefed<Promise> PullCallbackImpl( + JSContext* aCx, ReadableStreamController& aController, + ErrorResult& aRv) override; + + void EnqueueOneThingFromQueue(JSContext* aCx); + + private: + virtual ~nsISupportsStreamSource(); + + // Calls ReadableStream::EnqueueNative, which is MOZ_CAN_RUN_SCRIPT. + MOZ_CAN_RUN_SCRIPT_BOUNDARY void EnqueueToStream(JSContext* aCx, + nsISupports* aThing); + + RefPtr<ReadableStream> mStream; + RefPtr<Promise> mThingQueuedPromise; + // mozilla::Queue is not cycle-collector friendly :( + nsCOMArray<nsISupports> mQueue; +}; + +class RTCRtpScriptTransformer; + +class WritableStreamRTCFrameSink final + : public UnderlyingSinkAlgorithmsWrapper { + public: + explicit WritableStreamRTCFrameSink(RTCRtpScriptTransformer* aTransformer); + WritableStreamRTCFrameSink(const WritableStreamRTCFrameSink&) = delete; + WritableStreamRTCFrameSink(WritableStreamRTCFrameSink&&) = delete; + WritableStreamRTCFrameSink& operator=(const WritableStreamRTCFrameSink&) = + delete; + WritableStreamRTCFrameSink& operator=(WritableStreamRTCFrameSink&&) = delete; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WritableStreamRTCFrameSink, + UnderlyingSinkAlgorithmsWrapper) + + already_AddRefed<Promise> WriteCallback( + JSContext* aCx, JS::Handle<JS::Value> aChunk, + WritableStreamDefaultController& aController, + ErrorResult& aError) override; + + private: + virtual ~WritableStreamRTCFrameSink(); + RefPtr<RTCRtpScriptTransformer> mTransformer; +}; + +class RTCEncodedFrameBase; + +// Here's the basic flow. All of this happens on the worker thread. +// 0. We register with a FrameTransformerProxy. +// 1. That FrameTransformerProxy dispatches webrtc::TransformableFrameInterface +// to us (from either the encoder/depacketizer thread), via our +// TransformFrame method. +// 2. We wrap these frames in RTCEncodedAudio/VideoFrame, and feed them to +// mReadableSource, which queues them. +// 3. mReadableSource.PullCallbackImpl consumes that queue, and feeds the +// frames to mReadable. +// 4. JS worker code consumes from mReadable, does whatever transformation it +// wants, then writes the frames to mWritable. +// 5. mWritableSink.WriteCallback passes those frames to us. +// 6. We unwrap the webrtc::TransformableFrameInterface from these frames. +// 7. We pass these unwrapped frames back to the FrameTransformerProxy. +// (FrameTransformerProxy handles any dispatching/synchronization necessary) +// 8. Eventually, that FrameTransformerProxy calls NotifyReleased (possibly at +// our prompting). +class RTCRtpScriptTransformer final : public nsISupports, + public nsWrapperCache { + public: + explicit RTCRtpScriptTransformer(nsIGlobalObject* aGlobal); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult Init(JSContext* aCx, + JS::Handle<JS::Value> aOptions, + WorkerPrivate* aWorkerPrivate, + FrameTransformerProxy* aProxy); + + void NotifyReleased(); + + void TransformFrame( + std::unique_ptr<webrtc::TransformableFrameInterface> aFrame); + already_AddRefed<Promise> OnTransformedFrame(RTCEncodedFrameBase* aFrame, + ErrorResult& aError); + + // nsISupports + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(RTCRtpScriptTransformer) + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + nsIGlobalObject* GetParentObject() const { return mGlobal; } + + // WebIDL Interface + already_AddRefed<mozilla::dom::ReadableStream> Readable() const { + return do_AddRef(mReadable); + } + already_AddRefed<mozilla::dom::WritableStream> Writable() const { + return do_AddRef(mWritable); + } + + void GetOptions(JSContext* aCx, JS::MutableHandle<JS::Value> aVal, + ErrorResult& aError); + + already_AddRefed<Promise> GenerateKeyFrame(const Optional<nsAString>& aRid); + void GenerateKeyFrameError(const Maybe<std::string>& aRid, + const CopyableErrorResult& aResult); + already_AddRefed<Promise> SendKeyFrameRequest(); + void KeyFrameRequestDone(bool aSuccess); + + // Public to ease implementation of cycle collection functions + using GenerateKeyFramePromises = + nsTHashMap<nsCStringHashKey, nsTArray<RefPtr<Promise>>>; + + private: + virtual ~RTCRtpScriptTransformer(); + void RejectPendingPromises(); + // empty string means no rid + void ResolveGenerateKeyFramePromises(const std::string& aRid, + uint64_t aTimestamp); + + nsCOMPtr<nsIGlobalObject> mGlobal; + + RefPtr<FrameTransformerProxy> mProxy; + RefPtr<nsISupportsStreamSource> mReadableSource; + RefPtr<ReadableStream> mReadable; + RefPtr<WritableStream> mWritable; + RefPtr<WritableStreamRTCFrameSink> mWritableSink; + + JS::Heap<JS::Value> mOptions; + uint64_t mLastEnqueuedFrameCounter = 0; + uint64_t mLastReceivedFrameCounter = 0; + nsTArray<RefPtr<Promise>> mKeyFrameRequestPromises; + // Contains the promise returned for each call to GenerateKeyFrame(rid), in + // the order in which it was called, keyed by the rid (empty string if not + // passed). If there is already a promise in here for a given rid, we do not + // ask the FrameTransformerProxy again, and just bulk resolve/reject. + GenerateKeyFramePromises mGenerateKeyFramePromises; + Maybe<bool> mVideo; + RefPtr<StrongWorkerRef> mWorkerRef; +}; + +} // namespace dom +} // namespace mozilla + +#endif // MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCRTPSCRIPTTRANSFORMER_H_ |