tor-browser

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

DeviceInputTrack.h (12844B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef DOM_MEDIA_DEVICEINPUTTRACK_H_
      8 #define DOM_MEDIA_DEVICEINPUTTRACK_H_
      9 
     10 #include <thread>
     11 
     12 #include "AudioDriftCorrection.h"
     13 #include "AudioInputSource.h"
     14 #include "AudioSegment.h"
     15 #include "GraphDriver.h"
     16 #include "MediaTrackGraph.h"
     17 #include "mozilla/NotNull.h"
     18 
     19 namespace mozilla {
     20 
     21 class NativeInputTrack;
     22 class NonNativeInputTrack;
     23 
     24 // Any MediaTrack that needs the audio data from the certain device should
     25 // inherit the this class and get the raw audio data on graph thread via
     26 // GetInputSourceData(), after calling ConnectDeviceInput() and before
     27 // DisconnectDeviceInput() on main thread. See more examples in
     28 // TestAudioTrackGraph.cpp
     29 //
     30 // Example:
     31 //
     32 // class RawAudioDataTrack : public DeviceInputConsumerTrack {
     33 //  public:
     34 //   ...
     35 //
     36 //   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override
     37 //   {
     38 //     if (aFrom >= aTo) {
     39 //       return;
     40 //     }
     41 //
     42 //     if (mInputs.IsEmpty()) {
     43 //       GetData<AudioSegment>()->AppendNullData(aTo - aFrom);
     44 //     } else {
     45 //       MOZ_ASSERT(mInputs.Length() == 1);
     46 //       AudioSegment data;
     47 //       DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo);
     48 //       // You can do audio data processing before appending to mSegment here.
     49 //       GetData<AudioSegment>()->AppendFrom(&data);
     50 //     }
     51 //   };
     52 //
     53 //   uint32_t NumberOfChannels() const override {
     54 //     if (mInputs.IsEmpty()) {
     55 //       return 0;
     56 //     }
     57 //     DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
     58 //     MOZ_ASSERT(t);
     59 //     return t->NumberOfChannels();
     60 //   }
     61 //
     62 //   ...
     63 //
     64 //  private:
     65 //   explicit RawAudioDataTrack(TrackRate aSampleRate)
     66 //       : DeviceInputConsumerTrack(aSampleRate) {}
     67 // };
     68 class DeviceInputConsumerTrack : public ProcessedMediaTrack {
     69 public:
     70  explicit DeviceInputConsumerTrack(TrackRate aSampleRate);
     71 
     72  // Main Thread APIs:
     73  void ConnectDeviceInput(CubebUtils::AudioDeviceID aId,
     74                          AudioDataListener* aListener,
     75                          const PrincipalHandle& aPrincipal);
     76  void DisconnectDeviceInput();
     77  Maybe<CubebUtils::AudioDeviceID> DeviceId() const;
     78  NotNull<AudioDataListener*> GetAudioDataListener() const;
     79  bool ConnectedToNativeDevice() const;
     80  bool ConnectedToNonNativeDevice() const;
     81 
     82  // Any thread:
     83  DeviceInputConsumerTrack* AsDeviceInputConsumerTrack() override {
     84    return this;
     85  }
     86 
     87  // Graph thread API:
     88  DeviceInputTrack* GetDeviceInputTrackGraphThread() const;
     89 
     90 protected:
     91  // Get the data in [aFrom, aTo) from the device input to aOutput. aOutput
     92  // needs to be empty. A device input must be connected. Graph thread.
     93  void GetInputSourceData(AudioSegment& aOutput, GraphTime aFrom,
     94                          GraphTime aTo) const;
     95 
     96  // Main Thread variables:
     97  RefPtr<MediaInputPort> mPort;
     98  RefPtr<DeviceInputTrack> mDeviceInputTrack;
     99  RefPtr<AudioDataListener> mListener;
    100  Maybe<CubebUtils::AudioDeviceID> mDeviceId;
    101 };
    102 
    103 class DeviceInputTrack : public ProcessedMediaTrack {
    104 public:
    105  // Main Thread APIs:
    106  // Any MediaTrack that needs the audio data from the certain device should
    107  // inherit the DeviceInputConsumerTrack class and call GetInputSourceData to
    108  // get the data instead of using the below APIs.
    109  //
    110  // The following two APIs can create and destroy a DeviceInputTrack reference
    111  // on main thread, then open and close the underlying audio device accordingly
    112  // on the graph thread. The user who wants to read the audio input from a
    113  // certain device should use these APIs to obtain a DeviceInputTrack reference
    114  // and release the reference when the user no longer needs the audio data.
    115  //
    116  // Once the DeviceInputTrack is created on the main thread, the paired device
    117  // will start producing data, so its users can read the data immediately on
    118  // the graph thread, once they obtain the reference. The lifetime of
    119  // DeviceInputTrack is managed by the MediaTrackGraph itself. When the
    120  // DeviceInputTrack has no user any more, MediaTrackGraph will destroy it.
    121  // This means, it occurs when the last reference has been released by the API
    122  // below.
    123  //
    124  // The DeviceInputTrack is either a NativeInputTrack, or a
    125  // NonNativeInputTrack. We can have only one NativeInputTrack per
    126  // MediaTrackGraph, but multiple NonNativeInputTrack per graph. The audio
    127  // device paired with the NativeInputTrack is called "native device", and the
    128  // device paired with the NonNativeInputTrack is called "non-native device".
    129  // In other words, we can have only one native device per MediaTrackGraph, but
    130  // many non-native devices per graph.
    131  //
    132  // The native device is the first input device created in the MediaTrackGraph.
    133  // All other devices created after it will be non-native devices. Once the
    134  // native device is destroyed, the first non-native device will be promoted to
    135  // the new native device. The switch will be started by the MediaTrackGraph.
    136  // The MediaTrackGraph will force DeviceInputTrack's users to re-configure
    137  // their DeviceInputTrack connections with the APIs below to execute the
    138  // switching.
    139  //
    140  // The native device is also the audio input device serving the
    141  // AudioCallbackDriver, which drives the MediaTrackGraph periodically from
    142  // audio callback thread. The audio data produced by the native device and
    143  // non-native device is stored in NativeInputTrack and NonNativeInputTrack
    144  // respectively, and then accessed by their users. The only difference between
    145  // these audio data is that the data from the non-native device is
    146  // clock-drift-corrected since the non-native device may run on a different
    147  // clock than the native device's one.
    148  //
    149  // Example:
    150  //   // On main thread
    151  //   RefPtr<DeviceInputTrack> track = DeviceInputTrack::OpenAudio(...);
    152  //   ...
    153  //   // On graph thread
    154  //   AudioSegmen* data = track->GetData<AudioSegment>();
    155  //   ...
    156  //   // On main thread
    157  //   DeviceInputTrack::CloseAudio(track.forget(), ...);
    158  //
    159  // Returns a reference of DeviceInputTrack, storing the input audio data from
    160  // the given device, in the given MediaTrackGraph. The paired audio device
    161  // will be opened accordingly. The DeviceInputTrack will access its user's
    162  // audio settings via the attached AudioDataListener, and delivers the
    163  // notifications when it needs.
    164  static NotNull<RefPtr<DeviceInputTrack>> OpenAudio(
    165      MediaTrackGraph* aGraph, CubebUtils::AudioDeviceID aDeviceId,
    166      const PrincipalHandle& aPrincipalHandle,
    167      DeviceInputConsumerTrack* aConsumer);
    168  // Destroy the DeviceInputTrack reference obtained by the above API. The
    169  // paired audio device will be closed accordingly.
    170  static void CloseAudio(already_AddRefed<DeviceInputTrack> aTrack,
    171                         DeviceInputConsumerTrack* aConsumer);
    172 
    173  // Main thread API:
    174  const nsTArray<RefPtr<DeviceInputConsumerTrack>>& GetConsumerTracks() const;
    175 
    176  // Graph thread APIs:
    177  // Query audio settings from its users.
    178  uint32_t MaxRequestedInputChannels() const;
    179  bool HasVoiceInput() const;
    180  // Query for the aggregated form of processing params from all consumers. If
    181  // different from the previous call, the generation is updated and listeners
    182  // notified that new processing params are being requested. The caller is
    183  // responsible for performing the request.
    184  [[nodiscard]] AudioInputProcessingParamsRequest
    185  UpdateRequestedProcessingParams();
    186  // Signal to listeners that the requested platform processing params is about
    187  // to change.
    188  void NotifySetRequestedProcessingParams(
    189      MediaTrackGraph* aGraph, int aGeneration,
    190      cubeb_input_processing_params aRequestedParams);
    191  // Handle the result of an async operation to set processing params on a cubeb
    192  // stream. If the operation failed, signal this to listeners and then disable
    193  // processing. If the operation succeeded, directly signal this to listeners.
    194  void NotifySetRequestedProcessingParamsResult(
    195      MediaTrackGraph* aGraph, int aGeneration,
    196      const Result<cubeb_input_processing_params, int>& aResult);
    197  // Deliver notification to its users.
    198  void DeviceChanged(MediaTrackGraph* aGraph) const;
    199 
    200  // Any thread:
    201  DeviceInputTrack* AsDeviceInputTrack() override { return this; }
    202  virtual NativeInputTrack* AsNativeInputTrack() { return nullptr; }
    203  virtual NonNativeInputTrack* AsNonNativeInputTrack() { return nullptr; }
    204 
    205  // Any thread:
    206  const CubebUtils::AudioDeviceID mDeviceId;
    207  const PrincipalHandle mPrincipalHandle;
    208 
    209 protected:
    210  DeviceInputTrack(TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId,
    211                   const PrincipalHandle& aPrincipalHandle);
    212  ~DeviceInputTrack() = default;
    213 
    214 private:
    215  // Main thread APIs:
    216  void ReevaluateInputDevice();
    217  void AddDataListener(AudioDataListener* aListener);
    218  void RemoveDataListener(AudioDataListener* aListener);
    219 
    220  // Only accessed on the main thread.
    221  // When this becomes empty, this DeviceInputTrack is no longer needed.
    222  nsTArray<RefPtr<DeviceInputConsumerTrack>> mConsumerTracks;
    223 
    224  // Only accessed on the graph thread.
    225  nsTArray<RefPtr<AudioDataListener>> mListeners;
    226  AudioInputProcessingParamsRequest mProcessingParamsRequest;
    227 };
    228 
    229 class NativeInputTrack final : public DeviceInputTrack {
    230 public:
    231  // Do not call this directly. This can only be called in DeviceInputTrack or
    232  // tests.
    233  NativeInputTrack(TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId,
    234                   const PrincipalHandle& aPrincipalHandle);
    235 
    236  // Graph Thread APIs, for ProcessedMediaTrack.
    237  void DestroyImpl() override;
    238  void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
    239  uint32_t NumberOfChannels() const override;
    240 
    241  // Graph thread APIs: Get input audio data and event from graph.
    242  void NotifyInputStopped(MediaTrackGraph* aGraph);
    243  void NotifyInputData(MediaTrackGraph* aGraph, const AudioDataValue* aBuffer,
    244                       size_t aFrames, TrackRate aRate, uint32_t aChannels,
    245                       uint32_t aAlreadyBuffered);
    246 
    247  // Any thread
    248  NativeInputTrack* AsNativeInputTrack() override { return this; }
    249 
    250 private:
    251  ~NativeInputTrack() = default;
    252 
    253  // Graph thread only members:
    254  // Indicate whether we append extra frames in mPendingData. The extra number
    255  // of frames is in [0, WEBAUDIO_BLOCK_SIZE] range.
    256  bool mIsBufferingAppended = false;
    257  // Queue the audio input data coming from NotifyInputData.
    258  AudioSegment mPendingData;
    259  // The input channel count for the audio data.
    260  uint32_t mInputChannels = 0;
    261 };
    262 
    263 class NonNativeInputTrack final : public DeviceInputTrack {
    264 public:
    265  // Do not call this directly. This can only be called in DeviceInputTrack or
    266  // tests.
    267  NonNativeInputTrack(TrackRate aSampleRate,
    268                      CubebUtils::AudioDeviceID aDeviceId,
    269                      const PrincipalHandle& aPrincipalHandle);
    270 
    271  // Graph Thread APIs, for ProcessedMediaTrack
    272  void DestroyImpl() override;
    273  void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
    274  uint32_t NumberOfChannels() const override;
    275 
    276  // Any thread
    277  NonNativeInputTrack* AsNonNativeInputTrack() override { return this; }
    278 
    279  // Graph thread APIs:
    280  void StartAudio(RefPtr<AudioInputSource>&& aAudioInputSource);
    281  void StopAudio();
    282  AudioInputType DevicePreference() const;
    283  void NotifyDeviceChanged(AudioInputSource::Id aSourceId);
    284  void NotifyInputStopped(AudioInputSource::Id aSourceId);
    285  AudioInputSource::Id GenerateSourceId();
    286  void ReevaluateProcessingParams();
    287 
    288 private:
    289  ~NonNativeInputTrack() = default;
    290 
    291  // Graph thread only.
    292  RefPtr<AudioInputSource> mAudioSource;
    293  AudioInputSource::Id mSourceIdNumber;
    294  int mRequestedProcessingParamsGeneration{};
    295 
    296 #ifdef DEBUG
    297  // Graph thread only.
    298  bool HasGraphThreadChanged();
    299  // Graph thread only.  Identifies a thread only between StartAudio()
    300  // and StopAudio(), to track the thread used with mAudioSource.
    301  std::thread::id mGraphThreadId;
    302 #endif
    303 };
    304 
    305 class AudioInputSourceListener : public AudioInputSource::EventListener {
    306 public:
    307  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInputSourceListener, override);
    308 
    309  explicit AudioInputSourceListener(NonNativeInputTrack* aOwner);
    310 
    311  // Main thread APIs:
    312  void AudioDeviceChanged(AudioInputSource::Id aSourceId) override;
    313  void AudioStateCallback(
    314      AudioInputSource::Id aSourceId,
    315      AudioInputSource::EventListener::State aState) override;
    316 
    317 private:
    318  ~AudioInputSourceListener() = default;
    319  const RefPtr<NonNativeInputTrack> mOwner;
    320 };
    321 
    322 }  // namespace mozilla
    323 
    324 #endif  // DOM_MEDIA_DEVICEINPUTTRACK_H_