tor-browser

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

AudioStream.h (13301B)


      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 #if !defined(AudioStream_h_)
      7 #  define AudioStream_h_
      8 
      9 #  include "AudioSampleFormat.h"
     10 #  include "CubebUtils.h"
     11 #  include "MediaInfo.h"
     12 #  include "MediaSink.h"
     13 #  include "WavDumper.h"
     14 #  include "mozilla/Atomics.h"
     15 #  include "mozilla/Monitor.h"
     16 #  include "mozilla/MozPromise.h"
     17 #  include "mozilla/ProfilerUtils.h"
     18 #  include "mozilla/RefPtr.h"
     19 #  include "mozilla/SPSCQueue.h"
     20 #  include "mozilla/TimeStamp.h"
     21 #  include "mozilla/UniquePtr.h"
     22 #  include "nsCOMPtr.h"
     23 #  include "nsThreadUtils.h"
     24 
     25 namespace mozilla {
     26 
     27 struct CubebDestroyPolicy {
     28  void operator()(cubeb_stream* aStream) const {
     29    cubeb_stream_destroy(aStream);
     30  }
     31 };
     32 
     33 class AudioStream;
     34 class FrameHistory;
     35 class AudioConfig;
     36 class RLBoxSoundTouch;
     37 
     38 // A struct that contains the number of frames serviced or underrun by a
     39 // callback, alongside the sample-rate for this callback (in case of playback
     40 // rate change, it can be variable).
     41 struct CallbackInfo {
     42  CallbackInfo() = default;
     43  CallbackInfo(uint32_t aServiced, uint32_t aUnderrun, uint32_t aOutputRate)
     44      : mServiced(aServiced), mUnderrun(aUnderrun), mOutputRate(aOutputRate) {}
     45  uint32_t mServiced = 0;
     46  uint32_t mUnderrun = 0;
     47  uint32_t mOutputRate = 0;
     48 };
     49 
     50 class AudioClock {
     51 public:
     52  explicit AudioClock(uint32_t aInRate);
     53 
     54  // Update the number of samples that has been written in the audio backend.
     55  // Called on the audio thread only.
     56  void UpdateFrameHistory(uint32_t aServiced, uint32_t aUnderrun,
     57                          bool aAudioThreadChanged);
     58 
     59  /**
     60   * @param aFrames The playback position in frames of the audio engine.
     61   * @return The playback position in frames of the stream,
     62   *         adjusted by playback rate changes and underrun frames.
     63   */
     64  int64_t GetPositionInFrames(int64_t aFrames);
     65 
     66  /**
     67   * @param frames The playback position in frames of the audio engine.
     68   * @return The playback position in microseconds of the stream,
     69   *         adjusted by playback rate changes and underrun frames.
     70   */
     71  int64_t GetPosition(int64_t frames);
     72 
     73  // Set the playback rate.
     74  // Called on the audio thread only.
     75  void SetPlaybackRate(double aPlaybackRate);
     76  // Get the current playback rate.
     77  // Called on the audio thread only.
     78  double GetPlaybackRate() const;
     79  // Set if we are preserving the pitch.
     80  // Called on the audio thread only.
     81  void SetPreservesPitch(bool aPreservesPitch);
     82  // Get the current pitch preservation state.
     83  // Called on the audio thread only.
     84  bool GetPreservesPitch() const;
     85 
     86  // Called on either thread.
     87  uint32_t GetInputRate() const { return mInRate; }
     88  uint32_t GetOutputRate() const { return mOutRate; }
     89 
     90 private:
     91  // Output rate in Hz (characteristic of the playback rate). Written on the
     92  // audio thread, read on either thread.
     93  Atomic<uint32_t> mOutRate;
     94  // Input rate in Hz (characteristic of the media being played).
     95  const uint32_t mInRate;
     96  // True if the we are timestretching, false if we are resampling. Accessed on
     97  // the audio thread only.
     98  bool mPreservesPitch;
     99  // The history of frames sent to the audio engine in each DataCallback.
    100  // Only accessed from non-audio threads on macOS, accessed on both threads and
    101  // protected by the AudioStream monitor on other platforms.
    102  const UniquePtr<FrameHistory> mFrameHistory
    103 #  ifndef XP_MACOSX
    104      MOZ_GUARDED_BY(mMutex)
    105 #  endif
    106          ;
    107 #  ifdef XP_MACOSX
    108  // Enqueued on the audio thread, dequeued from the other thread. The maximum
    109  // size of this queue has been chosen empirically.
    110  SPSCQueue<CallbackInfo> mCallbackInfoQueue{100};
    111  // If it isn't possible to send the callback info to the non-audio thread,
    112  // store them here until it's possible to send them. This is an unlikely
    113  // fallback path. The size of this array has been chosen empirically. Only
    114  // ever accessed on the audio thread.
    115  AutoTArray<CallbackInfo, 5> mAudioThreadCallbackInfo;
    116 #  else
    117  Mutex mMutex{"AudioClock"};
    118 #  endif
    119 };
    120 
    121 /*
    122 * A bookkeeping class to track the read/write position of an audio buffer.
    123 */
    124 class AudioBufferCursor {
    125 public:
    126  AudioBufferCursor(Span<AudioDataValue> aSpan, uint32_t aChannels,
    127                    uint32_t aFrames)
    128      : mChannels(aChannels), mSpan(aSpan), mFrames(aFrames) {}
    129 
    130  // Advance the cursor to account for frames that are consumed.
    131  uint32_t Advance(uint32_t aFrames) {
    132    MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames));
    133    MOZ_ASSERT(mFrames >= aFrames);
    134    mFrames -= aFrames;
    135    mOffset += mChannels * aFrames;
    136    return aFrames;
    137  }
    138 
    139  // The number of frames available for read/write in this buffer.
    140  uint32_t Available() const { return mFrames; }
    141 
    142  // Return a pointer where read/write should begin.
    143  AudioDataValue* Ptr() const {
    144    MOZ_DIAGNOSTIC_ASSERT(mOffset <= mSpan.Length());
    145    return mSpan.Elements() + mOffset;
    146  }
    147 
    148 protected:
    149  bool Contains(uint32_t aFrames) const {
    150    return mSpan.Length() >= mOffset + mChannels * aFrames;
    151  }
    152  const uint32_t mChannels;
    153 
    154 private:
    155  const Span<AudioDataValue> mSpan;
    156  size_t mOffset = 0;
    157  uint32_t mFrames;
    158 };
    159 
    160 /*
    161 * A helper class to encapsulate pointer arithmetic and provide means to modify
    162 * the underlying audio buffer.
    163 */
    164 class AudioBufferWriter : public AudioBufferCursor {
    165 public:
    166  AudioBufferWriter(Span<AudioDataValue> aSpan, uint32_t aChannels,
    167                    uint32_t aFrames)
    168      : AudioBufferCursor(aSpan, aChannels, aFrames) {}
    169 
    170  uint32_t WriteZeros(uint32_t aFrames) {
    171    MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames));
    172    memset(Ptr(), 0, sizeof(AudioDataValue) * mChannels * aFrames);
    173    return Advance(aFrames);
    174  }
    175 
    176  uint32_t Write(const AudioDataValue* aPtr, uint32_t aFrames) {
    177    MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames));
    178    memcpy(Ptr(), aPtr, sizeof(AudioDataValue) * mChannels * aFrames);
    179    return Advance(aFrames);
    180  }
    181 
    182  // Provide a write fuction to update the audio buffer with the following
    183  // signature: uint32_t(const AudioDataValue* aPtr, uint32_t aFrames)
    184  // aPtr: Pointer to the audio buffer.
    185  // aFrames: The number of frames available in the buffer.
    186  // return: The number of frames actually written by the function.
    187  template <typename Function>
    188  uint32_t Write(const Function& aFunction, uint32_t aFrames) {
    189    MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames));
    190    return Advance(aFunction(Ptr(), aFrames));
    191  }
    192 
    193  using AudioBufferCursor::Available;
    194 };
    195 
    196 // Access to a single instance of this class must be synchronized by
    197 // callers, or made from a single thread.  One exception is that access to
    198 // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
    199 // SetMicrophoneActive is thread-safe without external synchronization.
    200 class AudioStream final {
    201  virtual ~AudioStream();
    202 
    203 public:
    204  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream)
    205 
    206  class Chunk {
    207   public:
    208    // Return a pointer to the audio data.
    209    virtual const AudioDataValue* Data() const = 0;
    210    // Return the number of frames in this chunk.
    211    virtual uint32_t Frames() const = 0;
    212    // Return the number of audio channels.
    213    virtual uint32_t Channels() const = 0;
    214    // Return the sample rate of this chunk.
    215    virtual uint32_t Rate() const = 0;
    216    // Return a writable pointer for downmixing.
    217    virtual AudioDataValue* GetWritable() const = 0;
    218    virtual ~Chunk() = default;
    219  };
    220 
    221  class DataSource {
    222   public:
    223    // Attempt to acquire aFrames frames of audio, and returns the number of
    224    // frames successfuly acquired.
    225    virtual uint32_t PopFrames(AudioDataValue* aAudio, uint32_t aFrames,
    226                               bool aAudioThreadChanged) = 0;
    227    // Return true if no more data will be added to the source.
    228    virtual bool Ended() const = 0;
    229 
    230   protected:
    231    virtual ~DataSource() = default;
    232  };
    233 
    234  // aOutputChannels is the number of audio channels (1 for mono, 2 for stereo,
    235  // etc), aChannelMap is the indicator for channel layout(mono, stereo, 5.1 or
    236  // 7.1 ). Initialize the audio stream.and aRate is the sample rate
    237  // (22050Hz, 44100Hz, etc).
    238  AudioStream(DataSource& aSource, uint32_t aInRate, uint32_t aOutputChannels,
    239              AudioConfig::ChannelLayout::ChannelMap aChannelMap);
    240 
    241  nsresult Init(AudioDeviceInfo* aSinkInfo);
    242 
    243  // Closes the stream. All future use of the stream is an error.
    244  void ShutDown();
    245 
    246  // Set the current volume of the audio playback. This is a value from
    247  // 0 (meaning muted) to 1 (meaning full volume).  Thread-safe.
    248  void SetVolume(double aVolume);
    249 
    250  void SetStreamName(const nsAString& aStreamName);
    251 
    252  // Start the stream.
    253  RefPtr<MediaSink::EndedPromise> Start();
    254 
    255  // Pause audio playback.
    256  void Pause();
    257 
    258  // Resume audio playback.
    259  void Resume();
    260 
    261  // Return the position in microseconds of the audio frame being played by
    262  // the audio hardware, compensated for playback rate change. Thread-safe.
    263  int64_t GetPosition();
    264 
    265  // Return the position, measured in audio frames played since the stream
    266  // was opened, of the audio hardware.  Thread-safe.
    267  int64_t GetPositionInFrames();
    268 
    269  uint32_t GetOutChannels() const { return mOutChannels; }
    270 
    271  // Set playback rate as a multiple of the intrinsic playback rate. This is
    272  // to be called only with aPlaybackRate > 0.0.
    273  nsresult SetPlaybackRate(double aPlaybackRate);
    274  // Switch between resampling (if false) and time stretching (if true,
    275  // default).
    276  nsresult SetPreservesPitch(bool aPreservesPitch);
    277 
    278  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
    279 
    280  bool IsPlaybackCompleted() const;
    281 
    282  // Returns true if at least one DataCallback has been called.
    283  bool CallbackStarted() const { return mCallbacksStarted; }
    284 
    285 protected:
    286  friend class AudioClock;
    287 
    288  // Return the position, measured in audio frames played since the stream was
    289  // opened, of the audio hardware, not adjusted for the changes of playback
    290  // rate or underrun frames.
    291  // Caller must own the monitor.
    292  int64_t GetPositionInFramesUnlocked();
    293 
    294 private:
    295  nsresult OpenCubeb(cubeb* aContext, cubeb_stream_params& aParams,
    296                     TimeStamp aStartTime, bool aIsFirst);
    297 
    298  static long DataCallback_S(cubeb_stream*, void* aThis,
    299                             const void* /* aInputBuffer */,
    300                             void* aOutputBuffer, long aFrames) {
    301    return static_cast<AudioStream*>(aThis)->DataCallback(aOutputBuffer,
    302                                                          aFrames);
    303  }
    304 
    305  static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState) {
    306    static_cast<AudioStream*>(aThis)->StateCallback(aState);
    307  }
    308 
    309  long DataCallback(void* aBuffer, long aFrames);
    310  void StateCallback(cubeb_state aState);
    311 
    312  // Audio thread only
    313  nsresult EnsureTimeStretcherInitialized();
    314  void GetUnprocessed(AudioBufferWriter& aWriter);
    315  void GetTimeStretched(AudioBufferWriter& aWriter);
    316  void UpdatePlaybackRateIfNeeded();
    317 
    318  // Return true if audio frames are valid (correct sampling rate and valid
    319  // channel count) otherwise false.
    320  bool IsValidAudioFormat(Chunk* aChunk) MOZ_REQUIRES(mMonitor);
    321 
    322  template <typename Function, typename... Args>
    323  int InvokeCubeb(Function aFunction, Args&&... aArgs) MOZ_REQUIRES(mMonitor);
    324  bool CheckThreadIdChanged();
    325  void AssertIsOnAudioThread() const;
    326 
    327  RLBoxSoundTouch* mTimeStretcher;
    328  AudioClock mAudioClock;
    329 
    330  WavDumper mDumpFile;
    331 
    332  const AudioConfig::ChannelLayout::ChannelMap mChannelMap;
    333 
    334  // The monitor is held to protect all access to member variables below.
    335  Monitor mMonitor;
    336 
    337  const uint32_t mOutChannels;
    338 
    339  // mCubebStream holds a bare pointer to cubeb, so we hold a ref on its behalf
    340  RefPtr<CubebUtils::CubebHandle> mCubeb;
    341  // Owning reference to a cubeb_stream.  Set in Init(), cleared in ShutDown, so
    342  // no lock is needed to access.
    343  UniquePtr<cubeb_stream, CubebDestroyPolicy> mCubebStream;
    344 
    345  enum StreamState {
    346    INITIALIZED,  // Initialized, playback has not begun.
    347    STARTED,      // cubeb started.
    348    STOPPED,      // Stopped by a call to Pause().
    349    DRAINED,      // StateCallback has indicated that the drain is complete.
    350    ERRORED,      // Stream disabled due to an internal error.
    351    SHUTDOWN      // ShutDown has been called
    352  };
    353 
    354  std::atomic<StreamState> mState;
    355 
    356  // DataSource::PopFrames can never be called concurrently.
    357  // DataSource::IsEnded uses only atomics.
    358  DataSource& mDataSource;
    359 
    360  // The device info of the current sink. If null
    361  // the default device is used. It is set
    362  // during the Init() in decoder thread.
    363  RefPtr<AudioDeviceInfo> mSinkInfo;
    364  // Contains the id of the audio thread, from profiler_get_thread_id.
    365  std::atomic<ProfilerThreadId> mAudioThreadId;
    366  const bool mSandboxed = false;
    367 
    368  MozPromiseHolder<MediaSink::EndedPromise> mEndedPromise
    369      MOZ_GUARDED_BY(mMonitor);
    370  std::atomic<bool> mPlaybackComplete;
    371  // Both written on the MDSM thread, read on the audio thread.
    372  std::atomic<float> mPlaybackRate;
    373  std::atomic<bool> mPreservesPitch;
    374  // Audio thread only
    375  bool mAudioThreadChanged = false;
    376  Atomic<bool> mCallbacksStarted;
    377 };
    378 
    379 }  // namespace mozilla
    380 
    381 #endif