MediaEncoder.h (14519B)
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 "MediaQueue.h" 12 #include "MediaTrackGraph.h" 13 #include "MediaTrackListener.h" 14 #include "TrackEncoder.h" 15 #include "mozilla/MemoryReporting.h" 16 #include "mozilla/MozPromise.h" 17 #include "mozilla/UniquePtr.h" 18 #include "nsIMemoryReporter.h" 19 20 namespace mozilla { 21 22 class DriftCompensator; 23 class Muxer; 24 class Runnable; 25 class TaskQueue; 26 27 namespace dom { 28 class AudioNode; 29 class AudioStreamTrack; 30 class BlobImpl; 31 class MediaStreamTrack; 32 class MutableBlobStorage; 33 class VideoStreamTrack; 34 } // namespace dom 35 36 class DriftCompensator; 37 38 /** 39 * MediaEncoder is the framework of encoding module, it controls and manages 40 * procedures between Muxer, ContainerWriter and TrackEncoder. ContainerWriter 41 * writes the encoded track data into a specific container (e.g. ogg, webm). 42 * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and 43 * are responsible for encoding raw data coming from MediaStreamTracks. 44 * 45 * MediaEncoder solves threading issues by doing message passing to a TaskQueue 46 * (the "encoder thread") as passed in to the constructor. Each 47 * MediaStreamTrack to be recorded is set up with a MediaTrackListener. 48 * Typically there are a non-direct track listeners for audio, direct listeners 49 * for video, and there is always a non-direct listener on each track for 50 * time-keeping. The listeners forward data to their corresponding TrackEncoders 51 * on the encoder thread. 52 * 53 * The MediaEncoder listens to events from all TrackEncoders, and in turn 54 * signals events to interested parties. Typically a MediaRecorder::Session. 55 * The MediaEncoder automatically encodes incoming data, muxes it, writes it 56 * into a container and stores the container data into a MutableBlobStorage. 57 * It is timeslice-aware so that it can notify listeners when it's time to 58 * expose a blob due to filling the timeslice. 59 * 60 * MediaEncoder is designed to be a passive component, neither does it own or is 61 * in charge of managing threads. Instead this is done by its owner. 62 * 63 * For example, usage from MediaRecorder of this component would be: 64 * 1) Create an encoder with a valid MIME type. Note that there are more 65 * configuration options, see the docs on MediaEncoder::CreateEncoder. 66 * => encoder = MediaEncoder::CreateEncoder(aMIMEType); 67 * It then creates track encoders and the appropriate ContainerWriter 68 * according to the MIME type 69 * 70 * 2) Connect handlers through MediaEventListeners to the MediaEncoder's 71 * MediaEventSources, StartedEvent(), DataAvailableEvent(), ErrorEvent() and 72 * ShutdownEvent(). 73 * => listener = encoder->DataAvailableEvent().Connect(mainThread, &OnBlob); 74 * 75 * 3) Connect the sources to be recorded. Either through: 76 * => encoder->ConnectAudioNode(node); 77 * or 78 * => encoder->ConnectMediaStreamTrack(track); 79 * These should not be mixed. When connecting MediaStreamTracks there is 80 * support for at most one of each kind. 81 * 82 * 4) MediaEncoder automatically encodes data from the connected tracks, muxes 83 * them and writes it all into a blob, including metadata. When the blob 84 * contains at least `timeslice` worth of data it notifies the 85 * DataAvailableEvent that was connected in step 2. 86 * => void OnBlob(RefPtr<BlobImpl> aBlob) { 87 * => DispatchBlobEvent(Blob::Create(GetOwnerGlobal(), aBlob)); 88 * => }; 89 * 90 * 5) To stop encoding, there are multiple options: 91 * 92 * 5.1) Stop() for a graceful stop. 93 * => encoder->Stop(); 94 * 95 * 5.2) Cancel() for an immediate stop, if you don't need the data currently 96 * buffered. 97 * => encoder->Cancel(); 98 * 99 * 5.3) When all input tracks end, the MediaEncoder will automatically stop 100 * and shut down. 101 */ 102 class MediaEncoder { 103 private: 104 class AudioTrackListener; 105 class VideoTrackListener; 106 class EncoderListener; 107 108 public: 109 using BlobPromise = 110 MozPromise<RefPtr<dom::BlobImpl>, nsresult, false /* IsExclusive */>; 111 using SizeOfPromise = MozPromise<size_t, size_t, true /* IsExclusive */>; 112 113 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEncoder) 114 115 private: 116 MediaEncoder(RefPtr<TaskQueue> aEncoderThread, 117 RefPtr<DriftCompensator> aDriftCompensator, 118 UniquePtr<ContainerWriter> aWriter, 119 UniquePtr<AudioTrackEncoder> aAudioEncoder, 120 UniquePtr<VideoTrackEncoder> aVideoEncoder, 121 UniquePtr<MediaQueue<EncodedFrame>> aEncodedAudioQueue, 122 UniquePtr<MediaQueue<EncodedFrame>> aEncodedVideoQueue, 123 TrackRate aTrackRate, const nsAString& aMIMEType, 124 uint64_t aMaxMemory, TimeDuration aTimeslice); 125 126 public: 127 /** 128 * Called on main thread from MediaRecorder::Pause. 129 */ 130 void Suspend(); 131 132 /** 133 * Called on main thread from MediaRecorder::Resume. 134 */ 135 void Resume(); 136 137 /** 138 * Disconnects the input tracks, causing the encoding to stop. 139 */ 140 void DisconnectTracks(); 141 142 /** 143 * Connects an AudioNode with the appropriate encoder. 144 */ 145 void ConnectAudioNode(dom::AudioNode* aNode, uint32_t aOutput); 146 147 /** 148 * Connects a MediaStreamTrack with the appropriate encoder. 149 */ 150 void ConnectMediaStreamTrack(dom::MediaStreamTrack* aTrack); 151 152 /** 153 * Removes a connected MediaStreamTrack. 154 */ 155 void RemoveMediaStreamTrack(dom::MediaStreamTrack* aTrack); 156 157 /** 158 * Creates an encoder with the given MIME type. This must be a valid MIME type 159 * or we will crash hard. 160 * Bitrates are given either explicit, or with 0 for defaults. 161 * aTrackRate is the rate in which data will be fed to the TrackEncoders. 162 * aMaxMemory is the maximum number of bytes of muxed data allowed in memory. 163 * Beyond that the blob is moved to a temporary file. 164 * aTimeslice is the minimum duration of muxed data we gather before 165 * automatically issuing a dataavailable event. 166 */ 167 static already_AddRefed<MediaEncoder> CreateEncoder( 168 RefPtr<TaskQueue> aEncoderThread, const nsAString& aMimeType, 169 uint32_t aAudioBitrate, uint32_t aVideoBitrate, uint8_t aTrackTypes, 170 TrackRate aTrackRate, uint64_t aMaxMemory, TimeDuration aTimeslice); 171 172 /** 173 * Encodes raw data for all tracks to aOutputBufs. The buffer of container 174 * data is allocated in ContainerWriter::GetContainerData(). 175 * 176 * On its first call, metadata is also encoded. TrackEncoders must have been 177 * initialized before this is called. 178 */ 179 nsresult GetEncodedData(nsTArray<nsTArray<uint8_t>>* aOutputBufs); 180 181 /** 182 * Asserts that Shutdown() has been called. Reasons are encoding 183 * complete, encounter an error, or being canceled by its caller. 184 */ 185 void AssertShutdownCalled() { MOZ_ASSERT(mShutdownPromise); } 186 187 /** 188 * Stops (encoding any data currently buffered) the encoding and shuts down 189 * the encoder using Shutdown(). 190 */ 191 RefPtr<GenericNonExclusivePromise> Stop(); 192 193 /** 194 * Cancels (discarding any data currently buffered) the encoding and shuts 195 * down the encoder using Shutdown(). 196 */ 197 RefPtr<GenericNonExclusivePromise> Cancel(); 198 199 bool HasError(); 200 201 static bool IsWebMEncoderEnabled(); 202 203 /** 204 * Updates internal state when track encoders are all initialized. 205 */ 206 void UpdateInitialized(); 207 208 /** 209 * Updates internal state when track encoders are all initialized, and 210 * notifies listeners that this MediaEncoder has been started. 211 */ 212 void UpdateStarted(); 213 214 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) 215 /* 216 * Measure the size of the buffer, and heap memory in bytes occupied by 217 * mAudioEncoder and mVideoEncoder. 218 */ 219 RefPtr<SizeOfPromise> SizeOfExcludingThis( 220 mozilla::MallocSizeOf aMallocSizeOf); 221 222 /** 223 * Encode, mux and store into blob storage what has been buffered until now, 224 * then return the blob backed by that storage. 225 */ 226 RefPtr<BlobPromise> RequestData(); 227 228 // Event that gets notified when all track encoders have received data. 229 MediaEventSource<void>& StartedEvent() { return mStartedEvent; } 230 // Event that gets notified when there was an error preventing continued 231 // recording somewhere in the MediaEncoder stack. 232 MediaEventSource<void>& ErrorEvent() { return mErrorEvent; } 233 // Event that gets notified when the MediaEncoder stack has been shut down. 234 MediaEventSource<void>& ShutdownEvent() { return mShutdownEvent; } 235 // Event that gets notified after we have muxed at least mTimeslice worth of 236 // data into the current blob storage. 237 MediaEventSource<RefPtr<dom::BlobImpl>>& DataAvailableEvent() { 238 return mDataAvailableEvent; 239 } 240 241 protected: 242 ~MediaEncoder(); 243 244 private: 245 /** 246 * Registers listeners. 247 */ 248 void RegisterListeners(); 249 250 /** 251 * Sets mGraphTrack if not already set, using a new stream from aTrack's 252 * graph. 253 */ 254 void EnsureGraphTrackFrom(MediaTrack* aTrack); 255 256 /** 257 * Shuts down gracefully if there is no remaining live track encoder. 258 */ 259 void MaybeShutdown(); 260 261 /** 262 * Waits for TrackEncoders to shut down, then shuts down the MediaEncoder and 263 * cleans up track encoders. 264 */ 265 RefPtr<GenericNonExclusivePromise> Shutdown(); 266 267 /** 268 * Sets mError to true, notifies listeners of the error if mError changed, 269 * and stops encoding. 270 */ 271 void SetError(); 272 273 /** 274 * Creates a new MutableBlobStorage if one doesn't exist. 275 */ 276 void MaybeCreateMutableBlobStorage(); 277 278 /** 279 * Called when an encoded audio frame has been pushed by the audio encoder. 280 */ 281 void OnEncodedAudioPushed(const RefPtr<EncodedFrame>& aFrame); 282 283 /** 284 * Called when an encoded video frame has been pushed by the video encoder. 285 */ 286 void OnEncodedVideoPushed(const RefPtr<EncodedFrame>& aFrame); 287 288 /** 289 * If enough data has been pushed to the muxer, extract it into the current 290 * blob storage. If more than mTimeslice data has been pushed to the muxer 291 * since the last DataAvailableEvent was notified, also gather the blob and 292 * notify MediaRecorder. 293 */ 294 void MaybeExtractOrGatherBlob(); 295 296 // Extracts encoded and muxed data into the current blob storage, creating one 297 // if it doesn't exist. The returned promise resolves when data has been 298 // stored into the blob. 299 RefPtr<GenericPromise> Extract(); 300 301 // Stops gathering data into the current blob and resolves when the current 302 // blob is available. Future data will be stored in a new blob. 303 // Should a previous async GatherBlob() operation still be in progress, we'll 304 // wait for it to finish before starting this one. 305 RefPtr<BlobPromise> GatherBlob(); 306 307 RefPtr<BlobPromise> GatherBlobImpl(); 308 309 const RefPtr<nsISerialEventTarget> mMainThread; 310 const RefPtr<TaskQueue> mEncoderThread; 311 const RefPtr<DriftCompensator> mDriftCompensator; 312 313 const UniquePtr<MediaQueue<EncodedFrame>> mEncodedAudioQueue; 314 const UniquePtr<MediaQueue<EncodedFrame>> mEncodedVideoQueue; 315 316 const UniquePtr<Muxer> mMuxer; 317 const UniquePtr<AudioTrackEncoder> mAudioEncoder; 318 const RefPtr<AudioTrackListener> mAudioListener; 319 const UniquePtr<VideoTrackEncoder> mVideoEncoder; 320 const RefPtr<VideoTrackListener> mVideoListener; 321 const RefPtr<EncoderListener> mEncoderListener; 322 323 public: 324 const nsString mMimeType; 325 326 // Max memory to use for the MutableBlobStorage. 327 const uint64_t mMaxMemory; 328 329 // The interval of passing encoded data from MutableBlobStorage to 330 // onDataAvailable handler. 331 const TimeDuration mTimeslice; 332 333 private: 334 MediaEventListener mAudioPushListener; 335 MediaEventListener mAudioFinishListener; 336 MediaEventListener mVideoPushListener; 337 MediaEventListener mVideoFinishListener; 338 339 MediaEventProducer<void> mStartedEvent; 340 MediaEventProducer<void> mErrorEvent; 341 MediaEventProducer<void> mShutdownEvent; 342 MediaEventProducer<RefPtr<dom::BlobImpl>> mDataAvailableEvent; 343 344 // The AudioNode we are encoding. 345 // Will be null when input is media stream or destination node. 346 RefPtr<dom::AudioNode> mAudioNode; 347 // Pipe-track for allowing a track listener on a non-destination AudioNode. 348 // Will be null when input is media stream or destination node. 349 RefPtr<AudioNodeTrack> mPipeTrack; 350 // Input port that connect mAudioNode to mPipeTrack. 351 // Will be null when input is media stream or destination node. 352 RefPtr<MediaInputPort> mInputPort; 353 // An audio track that we are encoding. Will be null if the input stream 354 // doesn't contain audio on start() or if the input is an AudioNode. 355 RefPtr<dom::AudioStreamTrack> mAudioTrack; 356 // A video track that we are encoding. Will be null if the input stream 357 // doesn't contain video on start() or if the input is an AudioNode. 358 RefPtr<dom::VideoStreamTrack> mVideoTrack; 359 360 // A stream to keep the MediaTrackGraph alive while we're recording. 361 RefPtr<SharedDummyTrack> mGraphTrack; 362 363 // A buffer to cache muxed encoded data. 364 RefPtr<dom::MutableBlobStorage> mMutableBlobStorage; 365 // If set, is a promise for the latest GatherBlob() operation. Allows 366 // GatherBlob() operations to be serialized in order to avoid races. 367 RefPtr<BlobPromise> mBlobPromise; 368 // The end time of the muxed data in the last gathered blob. If more than one 369 // track is present, this is the end time of the track that ends the earliest 370 // in the last blob. Encoder thread only. 371 media::TimeUnit mLastBlobTime; 372 // The end time of the muxed data in the current blob storage. If more than 373 // one track is present, this is the end time of the track that ends the 374 // earliest in the current blob storage. Encoder thread only. 375 media::TimeUnit mLastExtractTime; 376 // The end time of encoded audio data sent to the muxer. Positive infinity if 377 // there is no audio encoder. Encoder thread only. 378 media::TimeUnit mMuxedAudioEndTime; 379 // The end time of encoded video data sent to the muxer. Positive infinity if 380 // there is no video encoder. Encoder thread only. 381 media::TimeUnit mMuxedVideoEndTime; 382 383 TimeStamp mStartTime; 384 bool mInitialized; 385 bool mStarted; 386 bool mCompleted; 387 bool mError; 388 // Set when shutdown starts. 389 RefPtr<GenericNonExclusivePromise> mShutdownPromise; 390 // Get duration from create encoder, for logging purpose 391 double GetEncodeTimeStamp() { 392 TimeDuration decodeTime; 393 decodeTime = TimeStamp::Now() - mStartTime; 394 return decodeTime.ToMilliseconds(); 395 } 396 }; 397 398 } // namespace mozilla 399 400 #endif