content/media/AudioStream.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial