content/media/AudioStream.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     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_
     9 #include "AudioSampleFormat.h"
    10 #include "nsAutoPtr.h"
    11 #include "nsAutoRef.h"
    12 #include "nsCOMPtr.h"
    13 #include "nsThreadUtils.h"
    14 #include "Latency.h"
    15 #include "mozilla/dom/AudioChannelBinding.h"
    16 #include "mozilla/StaticMutex.h"
    17 #include "mozilla/RefPtr.h"
    19 #include "cubeb/cubeb.h"
    21 template <>
    22 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
    23 {
    24 public:
    25   static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
    26 };
    28 namespace soundtouch {
    29 class SoundTouch;
    30 }
    32 namespace mozilla {
    34 class AudioStream;
    36 class AudioClock
    37 {
    38 public:
    39   AudioClock(AudioStream* aStream);
    40   // Initialize the clock with the current AudioStream. Need to be called
    41   // before querying the clock. Called on the audio thread.
    42   void Init();
    43   // Update the number of samples that has been written in the audio backend.
    44   // Called on the state machine thread.
    45   void UpdateWritePosition(uint32_t aCount);
    46   // Get the read position of the stream, in microseconds.
    47   // Called on the state machine thead.
    48   // Assumes the AudioStream lock is held and thus calls Unlocked versions
    49   // of AudioStream funcs.
    50   uint64_t GetPositionUnlocked();
    51   // Get the read position of the stream, in frames.
    52   // Called on the state machine thead.
    53   uint64_t GetPositionInFrames();
    54   // Set the playback rate.
    55   // Called on the audio thread.
    56   // Assumes the AudioStream lock is held and thus calls Unlocked versions
    57   // of AudioStream funcs.
    58   void SetPlaybackRateUnlocked(double aPlaybackRate);
    59   // Get the current playback rate.
    60   // Called on the audio thread.
    61   double GetPlaybackRate();
    62   // Set if we are preserving the pitch.
    63   // Called on the audio thread.
    64   void SetPreservesPitch(bool aPreservesPitch);
    65   // Get the current pitch preservation state.
    66   // Called on the audio thread.
    67   bool GetPreservesPitch();
    68   // Get the number of frames written to the backend.
    69   int64_t GetWritten();
    70 private:
    71   // This AudioStream holds a strong reference to this AudioClock. This
    72   // pointer is garanteed to always be valid.
    73   AudioStream* mAudioStream;
    74   // The old output rate, to compensate audio latency for the period inbetween
    75   // the moment resampled buffers are pushed to the hardware and the moment the
    76   // clock should take the new rate into account for A/V sync.
    77   int mOldOutRate;
    78   // Position at which the last playback rate change occured
    79   int64_t mBasePosition;
    80   // Offset, in frames, at which the last playback rate change occured
    81   int64_t mBaseOffset;
    82   // Old base offset (number of samples), used when changing rate to compute the
    83   // position in the stream.
    84   int64_t mOldBaseOffset;
    85   // Old base position (number of microseconds), when changing rate. This is the
    86   // time in the media, not wall clock position.
    87   int64_t mOldBasePosition;
    88   // Write position at which the playbackRate change occured.
    89   int64_t mPlaybackRateChangeOffset;
    90   // The previous position reached in the media, used when compensating
    91   // latency, to have the position at which the playbackRate change occured.
    92   int64_t mPreviousPosition;
    93   // Number of samples effectivelly written in backend, i.e. write position.
    94   int64_t mWritten;
    95   // Output rate in Hz (characteristic of the playback rate)
    96   int mOutRate;
    97   // Input rate in Hz (characteristic of the media being played)
    98   int mInRate;
    99   // True if the we are timestretching, false if we are resampling.
   100   bool mPreservesPitch;
   101   // True if we are playing at the old playbackRate after it has been changed.
   102   bool mCompensatingLatency;
   103 };
   105 class CircularByteBuffer
   106 {
   107 public:
   108   CircularByteBuffer()
   109     : mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0)
   110   {}
   112   // Set the capacity of the buffer in bytes.  Must be called before any
   113   // call to append or pop elements.
   114   void SetCapacity(uint32_t aCapacity) {
   115     NS_ABORT_IF_FALSE(!mBuffer, "Buffer allocated.");
   116     mCapacity = aCapacity;
   117     mBuffer = new uint8_t[mCapacity];
   118   }
   120   uint32_t Length() {
   121     return mCount;
   122   }
   124   uint32_t Capacity() {
   125     return mCapacity;
   126   }
   128   uint32_t Available() {
   129     return Capacity() - Length();
   130   }
   132   // Append aLength bytes from aSrc to the buffer.  Caller must check that
   133   // sufficient space is available.
   134   void AppendElements(const uint8_t* aSrc, uint32_t aLength) {
   135     NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
   136     NS_ABORT_IF_FALSE(aLength <= Available(), "Buffer full.");
   138     uint32_t end = (mStart + mCount) % mCapacity;
   140     uint32_t toCopy = std::min(mCapacity - end, aLength);
   141     memcpy(&mBuffer[end], aSrc, toCopy);
   142     memcpy(&mBuffer[0], aSrc + toCopy, aLength - toCopy);
   143     mCount += aLength;
   144   }
   146   // Remove aSize bytes from the buffer.  Caller must check returned size in
   147   // aSize{1,2} before using the pointer returned in aData{1,2}.  Caller
   148   // must not specify an aSize larger than Length().
   149   void PopElements(uint32_t aSize, void** aData1, uint32_t* aSize1,
   150                    void** aData2, uint32_t* aSize2) {
   151     NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
   152     NS_ABORT_IF_FALSE(aSize <= Length(), "Request too large.");
   154     *aData1 = &mBuffer[mStart];
   155     *aSize1 = std::min(mCapacity - mStart, aSize);
   156     *aData2 = &mBuffer[0];
   157     *aSize2 = aSize - *aSize1;
   158     mCount -= *aSize1 + *aSize2;
   159     mStart += *aSize1 + *aSize2;
   160     mStart %= mCapacity;
   161   }
   163   // Throw away all but aSize bytes from the buffer.  Returns new size, which
   164   // may be less than aSize
   165   uint32_t ContractTo(uint32_t aSize) {
   166     NS_ABORT_IF_FALSE(mBuffer && mCapacity, "Buffer not initialized.");
   167     if (aSize >= mCount) {
   168       return mCount;
   169     }
   170     mStart += (mCount - aSize);
   171     mCount = aSize;
   172     mStart %= mCapacity;
   173     return mCount;
   174   }
   176   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   177   {
   178     size_t amount = 0;
   179     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
   180     return amount;
   181   }
   183 private:
   184   nsAutoArrayPtr<uint8_t> mBuffer;
   185   uint32_t mCapacity;
   186   uint32_t mStart;
   187   uint32_t mCount;
   188 };
   190 class AudioInitTask;
   192 // Access to a single instance of this class must be synchronized by
   193 // callers, or made from a single thread.  One exception is that access to
   194 // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
   195 // is thread-safe without external synchronization.
   196 class AudioStream MOZ_FINAL
   197 {
   198 public:
   199   // Initialize Audio Library. Some Audio backends require initializing the
   200   // library before using it.
   201   static void InitLibrary();
   203   // Shutdown Audio Library. Some Audio backends require shutting down the
   204   // library after using it.
   205   static void ShutdownLibrary();
   207   // Returns the maximum number of channels supported by the audio hardware.
   208   static int MaxNumberOfChannels();
   210   // Queries the samplerate the hardware/mixer runs at, and stores it.
   211   // Can be called on any thread. When this returns, it is safe to call
   212   // PreferredSampleRate without locking.
   213   static void InitPreferredSampleRate();
   214   // Get the aformentionned sample rate. Does not lock.
   215   static int PreferredSampleRate();
   217   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream)
   218   AudioStream();
   219   virtual ~AudioStream();
   221   enum LatencyRequest {
   222     HighLatency,
   223     LowLatency
   224   };
   226   // Initialize the audio stream. aNumChannels is the number of audio
   227   // channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
   228   // (22050Hz, 44100Hz, etc).
   229   nsresult Init(int32_t aNumChannels, int32_t aRate,
   230                 const dom::AudioChannel aAudioStreamChannel,
   231                 LatencyRequest aLatencyRequest);
   233   // Closes the stream. All future use of the stream is an error.
   234   void Shutdown();
   236   // Write audio data to the audio hardware.  aBuf is an array of AudioDataValues
   237   // AudioDataValue of length aFrames*mChannels.  If aFrames is larger
   238   // than the result of Available(), the write will block until sufficient
   239   // buffer space is available.  aTime is the time in ms associated with the first sample
   240   // for latency calculations
   241   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp* aTime = nullptr);
   243   // Return the number of audio frames that can be written without blocking.
   244   uint32_t Available();
   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);
   250   // Block until buffered audio data has been consumed.
   251   void Drain();
   253   // Start the stream.
   254   void Start();
   256   // Return the number of frames written so far in the stream. This allow the
   257   // caller to check if it is safe to start the stream, if needed.
   258   int64_t GetWritten();
   260   // Pause audio playback.
   261   void Pause();
   263   // Resume audio playback.
   264   void Resume();
   266   // Return the position in microseconds of the audio frame being played by
   267   // the audio hardware, compensated for playback rate change. Thread-safe.
   268   int64_t GetPosition();
   270   // Return the position, measured in audio frames played since the stream
   271   // was opened, of the audio hardware.  Thread-safe.
   272   int64_t GetPositionInFrames();
   274   // Return the position, measured in audio framed played since the stream was
   275   // opened, of the audio hardware, not adjusted for the changes of playback
   276   // rate.
   277   int64_t GetPositionInFramesInternal();
   279   // Returns true when the audio stream is paused.
   280   bool IsPaused();
   282   int GetRate() { return mOutRate; }
   283   int GetChannels() { return mChannels; }
   284   int GetOutChannels() { return mOutChannels; }
   286   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   287   // be called only with aPlaybackRate > 0.0.
   288   nsresult SetPlaybackRate(double aPlaybackRate);
   289   // Switch between resampling (if false) and time stretching (if true, default).
   290   nsresult SetPreservesPitch(bool aPreservesPitch);
   292   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   294 protected:
   295   friend class AudioClock;
   297   // Shared implementation of underflow adjusted position calculation.
   298   // Caller must own the monitor.
   299   int64_t GetPositionInFramesUnlocked();
   301 private:
   302   friend class AudioInitTask;
   304   // So we can call it asynchronously from AudioInitTask
   305   nsresult OpenCubeb(cubeb_stream_params &aParams,
   306                      LatencyRequest aLatencyRequest);
   308   void CheckForStart();
   310   static void PrefChanged(const char* aPref, void* aClosure);
   311   static double GetVolumeScale();
   312   static cubeb* GetCubebContext();
   313   static cubeb* GetCubebContextUnlocked();
   314   static uint32_t GetCubebLatency();
   315   static bool CubebLatencyPrefSet();
   317   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
   318   {
   319     return static_cast<AudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
   320   }
   322   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
   323   {
   324     static_cast<AudioStream*>(aThis)->StateCallback(aState);
   325   }
   327   long DataCallback(void* aBuffer, long aFrames);
   328   void StateCallback(cubeb_state aState);
   330   nsresult EnsureTimeStretcherInitializedUnlocked();
   332   // aTime is the time in ms the samples were inserted into MediaStreamGraph
   333   long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
   334   long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
   335   long GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t &aTime);
   337   int64_t GetLatencyInFrames();
   338   void GetBufferInsertTime(int64_t &aTimeMs);
   340   void StartUnlocked();
   342   // The monitor is held to protect all access to member variables.  Write()
   343   // waits while mBuffer is full; DataCallback() notifies as it consumes
   344   // data from mBuffer.  Drain() waits while mState is DRAINING;
   345   // StateCallback() notifies when mState is DRAINED.
   346   Monitor mMonitor;
   348   // Input rate in Hz (characteristic of the media being played)
   349   int mInRate;
   350   // Output rate in Hz (characteristic of the playback rate)
   351   int mOutRate;
   352   int mChannels;
   353   int mOutChannels;
   354   // Number of frames written to the buffers.
   355   int64_t mWritten;
   356   AudioClock mAudioClock;
   357   nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
   358   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
   360   // copy of Latency logger's starting time for offset calculations
   361   TimeStamp mStartTime;
   362   // Whether we are playing a low latency stream, or a normal stream.
   363   LatencyRequest mLatencyRequest;
   364   // Where in the current mInserts[0] block cubeb has read to
   365   int64_t mReadPoint;
   366   // Keep track of each inserted block of samples and the time it was inserted
   367   // so we can estimate the clock time for a specific sample's insertion (for when
   368   // we send data to cubeb).  Blocks are aged out as needed.
   369   struct Inserts {
   370     int64_t mTimeMs;
   371     int64_t mFrames;
   372   };
   373   nsAutoTArray<Inserts, 8> mInserts;
   375   // Sum of silent frames written when DataCallback requests more frames
   376   // than are available in mBuffer.
   377   uint64_t mLostFrames;
   379   // Output file for dumping audio
   380   FILE* mDumpFile;
   382   // Temporary audio buffer.  Filled by Write() and consumed by
   383   // DataCallback().  Once mBuffer is full, Write() blocks until sufficient
   384   // space becomes available in mBuffer.  mBuffer is sized in bytes, not
   385   // frames.
   386   CircularByteBuffer mBuffer;
   388   // Software volume level.  Applied during the servicing of DataCallback().
   389   double mVolume;
   391   // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
   392   // nsAutoRef's destructor.
   393   nsAutoRef<cubeb_stream> mCubebStream;
   395   uint32_t mBytesPerFrame;
   397   uint32_t BytesToFrames(uint32_t aBytes) {
   398     NS_ASSERTION(aBytes % mBytesPerFrame == 0,
   399                  "Byte count not aligned on frames size.");
   400     return aBytes / mBytesPerFrame;
   401   }
   403   uint32_t FramesToBytes(uint32_t aFrames) {
   404     return aFrames * mBytesPerFrame;
   405   }
   407   enum StreamState {
   408     INITIALIZED, // Initialized, playback has not begun.
   409     STARTED,     // cubeb started, but callbacks haven't started
   410     RUNNING,     // DataCallbacks have started after STARTED, or after Resume().
   411     STOPPED,     // Stopped by a call to Pause().
   412     DRAINING,    // Drain requested.  DataCallback will indicate end of stream
   413                  // once the remaining contents of mBuffer are requested by
   414                  // cubeb, after which StateCallback will indicate drain
   415                  // completion.
   416     DRAINED,     // StateCallback has indicated that the drain is complete.
   417     ERRORED,     // Stream disabled due to an internal error.
   418     SHUTDOWN     // Shutdown has been called
   419   };
   421   StreamState mState;
   422   bool mNeedsStart; // needed in case Start() is called before cubeb is open
   424   // This mutex protects the static members below.
   425   static StaticMutex sMutex;
   426   static cubeb* sCubebContext;
   428   // Prefered samplerate, in Hz (characteristic of the
   429   // hardware/mixer/platform/API used).
   430   static uint32_t sPreferredSampleRate;
   432   static double sVolumeScale;
   433   static uint32_t sCubebLatency;
   434   static bool sCubebLatencyPrefSet;
   435 };
   437 class AudioInitTask : public nsRunnable
   438 {
   439 public:
   440   AudioInitTask(AudioStream *aStream,
   441                 AudioStream::LatencyRequest aLatencyRequest,
   442                 const cubeb_stream_params &aParams)
   443     : mAudioStream(aStream)
   444     , mLatencyRequest(aLatencyRequest)
   445     , mParams(aParams)
   446   {}
   448   nsresult Dispatch()
   449   {
   450     // Can't add 'this' as the event to run, since mThread may not be set yet
   451     nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread));
   452     if (NS_SUCCEEDED(rv)) {
   453       // Note: event must not null out mThread!
   454       rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
   455     }
   456     return rv;
   457   }
   459 protected:
   460   virtual ~AudioInitTask() {};
   462 private:
   463   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL;
   465   RefPtr<AudioStream> mAudioStream;
   466   AudioStream::LatencyRequest mLatencyRequest;
   467   cubeb_stream_params mParams;
   469   nsCOMPtr<nsIThread> mThread;
   470 };
   472 } // namespace mozilla
   474 #endif

mercurial