/src/mozilla-central/dom/media/encoder/MediaEncoder.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
4 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #ifndef MediaEncoder_h_ |
7 | | #define MediaEncoder_h_ |
8 | | |
9 | | #include "ContainerWriter.h" |
10 | | #include "CubebUtils.h" |
11 | | #include "MediaStreamGraph.h" |
12 | | #include "MediaStreamListener.h" |
13 | | #include "mozilla/DebugOnly.h" |
14 | | #include "mozilla/MemoryReporting.h" |
15 | | #include "mozilla/UniquePtr.h" |
16 | | #include "nsIMemoryReporter.h" |
17 | | #include "TrackEncoder.h" |
18 | | |
19 | | namespace mozilla { |
20 | | |
21 | | class TaskQueue; |
22 | | |
23 | | namespace dom { |
24 | | class AudioNode; |
25 | | class AudioStreamTrack; |
26 | | class MediaStreamTrack; |
27 | | class VideoStreamTrack; |
28 | | } |
29 | | |
30 | | class MediaEncoder; |
31 | | |
32 | | class MediaEncoderListener |
33 | | { |
34 | | public: |
35 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEncoderListener) |
36 | | virtual void Initialized() = 0; |
37 | | virtual void DataAvailable() = 0; |
38 | | virtual void Error() = 0; |
39 | | virtual void Shutdown() = 0; |
40 | | protected: |
41 | | virtual ~MediaEncoderListener() {} |
42 | | }; |
43 | | |
44 | | /** |
45 | | * MediaEncoder is the framework of encoding module, it controls and manages |
46 | | * procedures between ContainerWriter and TrackEncoder. ContainerWriter packs |
47 | | * the encoded track data with a specific container (e.g. ogg, webm). |
48 | | * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and |
49 | | * are responsible for encoding raw data coming from MediaStreamGraph. |
50 | | * |
51 | | * MediaEncoder solves threading issues by doing message passing to a TaskQueue |
52 | | * (the "encoder thread") as passed in to the constructor. Each |
53 | | * MediaStreamTrack to be recorded is set up with a MediaStreamTrackListener. |
54 | | * Typically there are a non-direct track listeners for audio, direct listeners |
55 | | * for video, and there is always a non-direct listener on each track for |
56 | | * time-keeping. The listeners forward data to their corresponding TrackEncoders |
57 | | * on the encoder thread. |
58 | | * |
59 | | * The MediaEncoder listens to events from all TrackEncoders, and in turn |
60 | | * signals events to interested parties. Typically a MediaRecorder::Session. |
61 | | * The event that there's data available in the TrackEncoders is what typically |
62 | | * drives the extraction and muxing of data. |
63 | | * |
64 | | * MediaEncoder is designed to be a passive component, neither does it own or is |
65 | | * in charge of managing threads. Instead this is done by its owner. |
66 | | * |
67 | | * For example, usage from MediaRecorder of this component would be: |
68 | | * 1) Create an encoder with a valid MIME type. |
69 | | * => encoder = MediaEncoder::CreateEncoder(aMIMEType); |
70 | | * It then creates a ContainerWriter according to the MIME type |
71 | | * |
72 | | * 2) Connect a MediaEncoderListener to be notified when the MediaEncoder has |
73 | | * been initialized and when there's data available. |
74 | | * => encoder->RegisterListener(listener); |
75 | | * |
76 | | * 3) Connect the MediaStreamTracks to be recorded. |
77 | | * => encoder->ConnectMediaStreamTrack(track); |
78 | | * This creates the corresponding TrackEncoder and connects the track and |
79 | | * the TrackEncoder through a track listener. This also starts encoding. |
80 | | * |
81 | | * 4) When the MediaEncoderListener is notified that the MediaEncoder is |
82 | | * initialized, we can encode metadata. |
83 | | * => encoder->GetEncodedMetadata(...); |
84 | | * |
85 | | * 5) When the MediaEncoderListener is notified that the MediaEncoder has |
86 | | * data available, we can encode data. |
87 | | * => encoder->GetEncodedData(...); |
88 | | * |
89 | | * 6) To stop encoding, there are multiple options: |
90 | | * |
91 | | * 6.1) Stop() for a graceful stop. |
92 | | * => encoder->Stop(); |
93 | | * |
94 | | * 6.2) Cancel() for an immediate stop, if you don't need the data currently |
95 | | * buffered. |
96 | | * => encoder->Cancel(); |
97 | | * |
98 | | * 6.3) When all input tracks end, the MediaEncoder will automatically stop |
99 | | * and shut down. |
100 | | */ |
101 | | class MediaEncoder |
102 | | { |
103 | | private: |
104 | | class AudioTrackListener; |
105 | | class VideoTrackListener; |
106 | | class EncoderListener; |
107 | | |
108 | | public : |
109 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEncoder) |
110 | | |
111 | | MediaEncoder(TaskQueue* aEncoderThread, |
112 | | UniquePtr<ContainerWriter> aWriter, |
113 | | AudioTrackEncoder* aAudioEncoder, |
114 | | VideoTrackEncoder* aVideoEncoder, |
115 | | const nsAString& aMIMEType); |
116 | | |
117 | | /* Note - called from control code, not on MSG threads. */ |
118 | | void Suspend(TimeStamp aTime); |
119 | | |
120 | | /** |
121 | | * Note - called from control code, not on MSG threads. |
122 | | * Calculates time spent paused in order to offset frames. */ |
123 | | void Resume(TimeStamp aTime); |
124 | | |
125 | | /** |
126 | | * Stops the current encoding, and disconnects the input tracks. |
127 | | */ |
128 | | void Stop(); |
129 | | |
130 | | /** |
131 | | * Connects an AudioNode with the appropriate encoder. |
132 | | */ |
133 | | void ConnectAudioNode(dom::AudioNode* aNode, uint32_t aOutput); |
134 | | |
135 | | /** |
136 | | * Connects a MediaStreamTrack with the appropriate encoder. |
137 | | */ |
138 | | void ConnectMediaStreamTrack(dom::MediaStreamTrack* aTrack); |
139 | | |
140 | | /** |
141 | | * Removes a connected MediaStreamTrack. |
142 | | */ |
143 | | void RemoveMediaStreamTrack(dom::MediaStreamTrack* aTrack); |
144 | | |
145 | | /** |
146 | | * Creates an encoder with a given MIME type. Returns null if we are unable |
147 | | * to create the encoder. For now, default aMIMEType to "audio/ogg" and use |
148 | | * Ogg+Opus if it is empty. |
149 | | */ |
150 | | static already_AddRefed<MediaEncoder> |
151 | | CreateEncoder(TaskQueue* aEncoderThread, |
152 | | const nsAString& aMIMEType, |
153 | | uint32_t aAudioBitrate, |
154 | | uint32_t aVideoBitrate, |
155 | | uint8_t aTrackTypes, |
156 | | TrackRate aTrackRate); |
157 | | |
158 | | /** |
159 | | * Encodes raw metadata for all tracks to aOutputBufs. aMIMEType is the valid |
160 | | * mime-type for the returned container data. The buffer of container data is |
161 | | * allocated in ContainerWriter::GetContainerData(). |
162 | | * |
163 | | * Should there be insufficient input data for either track encoder to infer |
164 | | * the metadata, or if metadata has already been encoded, we return an error |
165 | | * and the output arguments are undefined. Otherwise we return NS_OK. |
166 | | */ |
167 | | nsresult GetEncodedMetadata(nsTArray<nsTArray<uint8_t> >* aOutputBufs, |
168 | | nsAString& aMIMEType); |
169 | | /** |
170 | | * Encodes raw data for all tracks to aOutputBufs. The buffer of container |
171 | | * data is allocated in ContainerWriter::GetContainerData(). |
172 | | * |
173 | | * This implies that metadata has already been encoded and that all track |
174 | | * encoders are still active. Should either implication break, we return an |
175 | | * error and the output argument is undefined. Otherwise we return NS_OK. |
176 | | */ |
177 | | nsresult GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs); |
178 | | |
179 | | /** |
180 | | * Return true if MediaEncoder has been shutdown. Reasons are encoding |
181 | | * complete, encounter an error, or being canceled by its caller. |
182 | | */ |
183 | | bool IsShutdown(); |
184 | | |
185 | | /** |
186 | | * Cancels the encoding and shuts down the encoder using Shutdown(). |
187 | | * Listeners are not notified of the shutdown. |
188 | | */ |
189 | | void Cancel(); |
190 | | |
191 | | bool HasError(); |
192 | | |
193 | | #ifdef MOZ_WEBM_ENCODER |
194 | | static bool IsWebMEncoderEnabled(); |
195 | | #endif |
196 | | |
197 | | /** |
198 | | * Notifies listeners that this MediaEncoder has been initialized. |
199 | | */ |
200 | | void NotifyInitialized(); |
201 | | |
202 | | /** |
203 | | * Notifies listeners that this MediaEncoder has data available in some |
204 | | * TrackEncoders. |
205 | | */ |
206 | | void NotifyDataAvailable(); |
207 | | |
208 | | /** |
209 | | * Registers a listener to events from this MediaEncoder. |
210 | | * We hold a strong reference to the listener. |
211 | | */ |
212 | | void RegisterListener(MediaEncoderListener* aListener); |
213 | | |
214 | | /** |
215 | | * Unregisters a listener from events from this MediaEncoder. |
216 | | * The listener will stop receiving events synchronously. |
217 | | */ |
218 | | bool UnregisterListener(MediaEncoderListener* aListener); |
219 | | |
220 | | MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) |
221 | | /* |
222 | | * Measure the size of the buffer, and heap memory in bytes occupied by |
223 | | * mAudioEncoder and mVideoEncoder. |
224 | | */ |
225 | | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); |
226 | | |
227 | | /** |
228 | | * Set desired video keyframe interval defined in milliseconds. |
229 | | */ |
230 | | void SetVideoKeyFrameInterval(int32_t aVideoKeyFrameInterval); |
231 | | |
232 | | protected: |
233 | | ~MediaEncoder(); |
234 | | |
235 | | private: |
236 | | /** |
237 | | * Shuts down the MediaEncoder and cleans up track encoders. |
238 | | * Listeners will be notified of the shutdown unless we were Cancel()ed first. |
239 | | */ |
240 | | void Shutdown(); |
241 | | |
242 | | /** |
243 | | * Sets mError to true, notifies listeners of the error if mError changed, |
244 | | * and stops encoding. |
245 | | */ |
246 | | void SetError(); |
247 | | |
248 | | // Get encoded data from trackEncoder and write to muxer |
249 | | nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder); |
250 | | // Get metadata from trackEncoder and copy to muxer |
251 | | nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder); |
252 | | |
253 | | const RefPtr<TaskQueue> mEncoderThread; |
254 | | |
255 | | UniquePtr<ContainerWriter> mWriter; |
256 | | RefPtr<AudioTrackEncoder> mAudioEncoder; |
257 | | RefPtr<AudioTrackListener> mAudioListener; |
258 | | RefPtr<VideoTrackEncoder> mVideoEncoder; |
259 | | RefPtr<VideoTrackListener> mVideoListener; |
260 | | RefPtr<EncoderListener> mEncoderListener; |
261 | | nsTArray<RefPtr<MediaEncoderListener>> mListeners; |
262 | | |
263 | | // The AudioNode we are encoding. |
264 | | // Will be null when input is media stream or destination node. |
265 | | RefPtr<dom::AudioNode> mAudioNode; |
266 | | // Pipe-stream for allowing a track listener on a non-destination AudioNode. |
267 | | // Will be null when input is media stream or destination node. |
268 | | RefPtr<AudioNodeStream> mPipeStream; |
269 | | // Input port that connect mAudioNode to mPipeStream. |
270 | | // Will be null when input is media stream or destination node. |
271 | | RefPtr<MediaInputPort> mInputPort; |
272 | | // An audio track that we are encoding. Will be null if the input stream |
273 | | // doesn't contain audio on start() or if the input is an AudioNode. |
274 | | RefPtr<dom::AudioStreamTrack> mAudioTrack; |
275 | | // A video track that we are encoding. Will be null if the input stream |
276 | | // doesn't contain video on start() or if the input is an AudioNode. |
277 | | RefPtr<dom::VideoStreamTrack> mVideoTrack; |
278 | | TimeStamp mStartTime; |
279 | | nsString mMIMEType; |
280 | | bool mInitialized; |
281 | | bool mMetadataEncoded; |
282 | | bool mCompleted; |
283 | | bool mError; |
284 | | bool mCanceled; |
285 | | bool mShutdown; |
286 | | // Get duration from create encoder, for logging purpose |
287 | | double GetEncodeTimeStamp() |
288 | 0 | { |
289 | 0 | TimeDuration decodeTime; |
290 | 0 | decodeTime = TimeStamp::Now() - mStartTime; |
291 | 0 | return decodeTime.ToMilliseconds(); |
292 | 0 | } |
293 | | }; |
294 | | |
295 | | } // namespace mozilla |
296 | | |
297 | | #endif |