tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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