1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef MediaRecorder_h
8 #define MediaRecorder_h
9 
10 #include "mozilla/dom/MediaRecorderBinding.h"
11 #include "mozilla/DOMEventTargetHelper.h"
12 #include "mozilla/MozPromise.h"
13 #include "nsIDocumentActivity.h"
14 
15 // Max size for allowing queue encoded data in memory
16 #define MAX_ALLOW_MEMORY_BUFFER 1024000
17 namespace mozilla {
18 
19 class AudioNodeTrack;
20 class DOMMediaStream;
21 class ErrorResult;
22 struct MediaRecorderOptions;
23 class GlobalObject;
24 
25 namespace dom {
26 
27 class AudioNode;
28 class BlobImpl;
29 class Document;
30 class DOMException;
31 
32 /**
33  * Implementation of
34  * https://w3c.github.io/mediacapture-record/MediaRecorder.html
35  *
36  * The MediaRecorder accepts a MediaStream as input passed from an application.
37  * When the MediaRecorder starts, a MediaEncoder will be created and accepts the
38  * MediaStreamTracks in the MediaStream as input source. For each track it
39  * creates a TrackEncoder.
40  *
41  * The MediaEncoder automatically encodes and muxes data from the tracks by the
42  * given MIME type, then it stores this data into a MutableBlobStorage object.
43  * When a timeslice is set and the MediaEncoder has stored enough data to fill
44  * the timeslice, it extracts a Blob from the storage and passes it to
45  * MediaRecorder. On RequestData() or Stop(), the MediaEncoder extracts the blob
46  * from the storage and returns it to MediaRecorder through a MozPromise.
47  *
48  * Thread model: When the recorder starts, it creates a worker thread (called
49  * the encoder thread) that does all the heavy lifting - encoding, time keeping,
50  * muxing.
51  */
52 
53 class MediaRecorder final : public DOMEventTargetHelper,
54                             public nsIDocumentActivity {
55  public:
56   class Session;
57 
58   explicit MediaRecorder(nsPIDOMWindowInner* aOwnerWindow);
59 
60   static nsTArray<RefPtr<Session>> GetSessions();
61 
62   // nsWrapperCache
63   JSObject* WrapObject(JSContext* aCx,
64                        JS::Handle<JSObject*> aGivenProto) override;
65 
66   NS_DECL_ISUPPORTS_INHERITED
67   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaRecorder, DOMEventTargetHelper)
68 
69   // WebIDL
70   // Start recording.
71   void Start(const Optional<uint32_t>& timeSlice, ErrorResult& aResult);
72   // Stop recording.
73   void Stop(ErrorResult& aResult);
74   // Pause a recording.
75   void Pause(ErrorResult& aResult);
76   // Resume a paused recording.
77   void Resume(ErrorResult& aResult);
78   // Extracts buffered data and fires the dataavailable event.
79   void RequestData(ErrorResult& aResult);
80   // Return the The DOMMediaStream passed from UA.
Stream()81   DOMMediaStream* Stream() const { return mStream; }
82   // Return the current encoding MIME type selected by the MediaEncoder.
83   void GetMimeType(nsString& aMimeType);
84   // The current state of the MediaRecorder object.
State()85   RecordingState State() const { return mState; }
86 
87   static bool IsTypeSupported(GlobalObject& aGlobal,
88                               const nsAString& aMIMEType);
89   static bool IsTypeSupported(const nsAString& aMIMEType);
90 
91   // Construct a recorder with a DOM media stream object as its source.
92   static already_AddRefed<MediaRecorder> Constructor(
93       const GlobalObject& aGlobal, DOMMediaStream& aStream,
94       const MediaRecorderOptions& aOptions, ErrorResult& aRv);
95   // Construct a recorder with a Web Audio destination node as its source.
96   static already_AddRefed<MediaRecorder> Constructor(
97       const GlobalObject& aGlobal, AudioNode& aAudioNode,
98       uint32_t aAudioNodeOutput, const MediaRecorderOptions& aOptions,
99       ErrorResult& aRv);
100 
101   /*
102    * Measure the size of the buffer, and heap memory in bytes occupied by
103    * mAudioEncoder and mVideoEncoder.
104    */
105   typedef MozPromise<size_t, size_t, true> SizeOfPromise;
106   RefPtr<SizeOfPromise> SizeOfExcludingThis(
107       mozilla::MallocSizeOf aMallocSizeOf);
108   // EventHandler
109   IMPL_EVENT_HANDLER(start)
IMPL_EVENT_HANDLER(stop)110   IMPL_EVENT_HANDLER(stop)
111   IMPL_EVENT_HANDLER(dataavailable)
112   IMPL_EVENT_HANDLER(pause)
113   IMPL_EVENT_HANDLER(resume)
114   IMPL_EVENT_HANDLER(error)
115 
116   NS_DECL_NSIDOCUMENTACTIVITY
117 
118   uint32_t AudioBitsPerSecond() const { return mAudioBitsPerSecond; }
VideoBitsPerSecond()119   uint32_t VideoBitsPerSecond() const { return mVideoBitsPerSecond; }
120 
121  protected:
122   virtual ~MediaRecorder();
123 
124   MediaRecorder& operator=(const MediaRecorder& x) = delete;
125   // Create dataavailable event with Blob data and it runs in main thread
126   nsresult CreateAndDispatchBlobEvent(BlobImpl* aBlobImpl);
127   // Creating a simple event to notify UA simple event.
128   void DispatchSimpleEvent(const nsAString& aStr);
129   // Creating a error event with message.
130   void NotifyError(nsresult aRv);
131 
132   MediaRecorder(const MediaRecorder& x) = delete;  // prevent bad usage
133   // Remove session pointer.
134   void RemoveSession(Session* aSession);
135   // Create DOMExceptions capturing the JS stack for async errors. These are
136   // created ahead of time rather than on demand when firing an error as the JS
137   // stack of the operation that started the async behavior will not be
138   // available at the time the error event is fired. Note, depending on when
139   // this is called there may not be a JS stack to capture.
140   void InitializeDomExceptions();
141   // Runs the "Inactivate the recorder" algorithm.
142   void Inactivate();
143   // Stop the recorder and its internal session. This should be used by
144   // sessions that are in the process of being destroyed.
145   void StopForSessionDestruction();
146   // DOM wrapper for source media stream. Will be null when input is audio node.
147   RefPtr<DOMMediaStream> mStream;
148   // Source audio node. Will be null when input is a media stream.
149   RefPtr<AudioNode> mAudioNode;
150   // Source audio node's output index. Will be zero when input is a media
151   // stream.
152   uint32_t mAudioNodeOutput = 0;
153 
154   // The current state of the MediaRecorder object.
155   RecordingState mState = RecordingState::Inactive;
156   // Hold the sessions reference and clean it when the DestroyRunnable for a
157   // session is running.
158   nsTArray<RefPtr<Session>> mSessions;
159 
160   RefPtr<Document> mDocument;
161 
162   nsString mMimeType;
163   nsString mConstrainedMimeType;
164 
165   uint32_t mAudioBitsPerSecond = 0;
166   uint32_t mVideoBitsPerSecond = 0;
167   Maybe<uint32_t> mConstrainedBitsPerSecond;
168 
169   // DOMExceptions that are created early and possibly thrown in NotifyError.
170   // Creating them early allows us to capture the JS stack for which cannot be
171   // done at the time the error event is fired.
172   RefPtr<DOMException> mOtherDomException;
173   RefPtr<DOMException> mSecurityDomException;
174   RefPtr<DOMException> mUnknownDomException;
175 
176  private:
177   // Register MediaRecorder into Document to listen the activity changes.
178   void RegisterActivityObserver();
179   void UnRegisterActivityObserver();
180 
181   bool CheckPermission(const nsString& aType);
182 };
183 
184 }  // namespace dom
185 }  // namespace mozilla
186 
187 #endif
188