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