|
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 "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" |
|
18 |
|
19 #include "cubeb/cubeb.h" |
|
20 |
|
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 }; |
|
27 |
|
28 namespace soundtouch { |
|
29 class SoundTouch; |
|
30 } |
|
31 |
|
32 namespace mozilla { |
|
33 |
|
34 class AudioStream; |
|
35 |
|
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 }; |
|
104 |
|
105 class CircularByteBuffer |
|
106 { |
|
107 public: |
|
108 CircularByteBuffer() |
|
109 : mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0) |
|
110 {} |
|
111 |
|
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 } |
|
119 |
|
120 uint32_t Length() { |
|
121 return mCount; |
|
122 } |
|
123 |
|
124 uint32_t Capacity() { |
|
125 return mCapacity; |
|
126 } |
|
127 |
|
128 uint32_t Available() { |
|
129 return Capacity() - Length(); |
|
130 } |
|
131 |
|
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."); |
|
137 |
|
138 uint32_t end = (mStart + mCount) % mCapacity; |
|
139 |
|
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 } |
|
145 |
|
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."); |
|
153 |
|
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 } |
|
162 |
|
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 } |
|
175 |
|
176 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
177 { |
|
178 size_t amount = 0; |
|
179 amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf); |
|
180 return amount; |
|
181 } |
|
182 |
|
183 private: |
|
184 nsAutoArrayPtr<uint8_t> mBuffer; |
|
185 uint32_t mCapacity; |
|
186 uint32_t mStart; |
|
187 uint32_t mCount; |
|
188 }; |
|
189 |
|
190 class AudioInitTask; |
|
191 |
|
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(); |
|
202 |
|
203 // Shutdown Audio Library. Some Audio backends require shutting down the |
|
204 // library after using it. |
|
205 static void ShutdownLibrary(); |
|
206 |
|
207 // Returns the maximum number of channels supported by the audio hardware. |
|
208 static int MaxNumberOfChannels(); |
|
209 |
|
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(); |
|
216 |
|
217 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream) |
|
218 AudioStream(); |
|
219 virtual ~AudioStream(); |
|
220 |
|
221 enum LatencyRequest { |
|
222 HighLatency, |
|
223 LowLatency |
|
224 }; |
|
225 |
|
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); |
|
232 |
|
233 // Closes the stream. All future use of the stream is an error. |
|
234 void Shutdown(); |
|
235 |
|
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); |
|
242 |
|
243 // Return the number of audio frames that can be written without blocking. |
|
244 uint32_t Available(); |
|
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 // Block until buffered audio data has been consumed. |
|
251 void Drain(); |
|
252 |
|
253 // Start the stream. |
|
254 void Start(); |
|
255 |
|
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(); |
|
259 |
|
260 // Pause audio playback. |
|
261 void Pause(); |
|
262 |
|
263 // Resume audio playback. |
|
264 void Resume(); |
|
265 |
|
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(); |
|
269 |
|
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(); |
|
273 |
|
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(); |
|
278 |
|
279 // Returns true when the audio stream is paused. |
|
280 bool IsPaused(); |
|
281 |
|
282 int GetRate() { return mOutRate; } |
|
283 int GetChannels() { return mChannels; } |
|
284 int GetOutChannels() { return mOutChannels; } |
|
285 |
|
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); |
|
291 |
|
292 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
|
293 |
|
294 protected: |
|
295 friend class AudioClock; |
|
296 |
|
297 // Shared implementation of underflow adjusted position calculation. |
|
298 // Caller must own the monitor. |
|
299 int64_t GetPositionInFramesUnlocked(); |
|
300 |
|
301 private: |
|
302 friend class AudioInitTask; |
|
303 |
|
304 // So we can call it asynchronously from AudioInitTask |
|
305 nsresult OpenCubeb(cubeb_stream_params &aParams, |
|
306 LatencyRequest aLatencyRequest); |
|
307 |
|
308 void CheckForStart(); |
|
309 |
|
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(); |
|
316 |
|
317 static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames) |
|
318 { |
|
319 return static_cast<AudioStream*>(aThis)->DataCallback(aBuffer, aFrames); |
|
320 } |
|
321 |
|
322 static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState) |
|
323 { |
|
324 static_cast<AudioStream*>(aThis)->StateCallback(aState); |
|
325 } |
|
326 |
|
327 long DataCallback(void* aBuffer, long aFrames); |
|
328 void StateCallback(cubeb_state aState); |
|
329 |
|
330 nsresult EnsureTimeStretcherInitializedUnlocked(); |
|
331 |
|
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); |
|
336 |
|
337 int64_t GetLatencyInFrames(); |
|
338 void GetBufferInsertTime(int64_t &aTimeMs); |
|
339 |
|
340 void StartUnlocked(); |
|
341 |
|
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; |
|
347 |
|
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; |
|
359 |
|
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; |
|
374 |
|
375 // Sum of silent frames written when DataCallback requests more frames |
|
376 // than are available in mBuffer. |
|
377 uint64_t mLostFrames; |
|
378 |
|
379 // Output file for dumping audio |
|
380 FILE* mDumpFile; |
|
381 |
|
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; |
|
387 |
|
388 // Software volume level. Applied during the servicing of DataCallback(). |
|
389 double mVolume; |
|
390 |
|
391 // Owning reference to a cubeb_stream. cubeb_stream_destroy is called by |
|
392 // nsAutoRef's destructor. |
|
393 nsAutoRef<cubeb_stream> mCubebStream; |
|
394 |
|
395 uint32_t mBytesPerFrame; |
|
396 |
|
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 } |
|
402 |
|
403 uint32_t FramesToBytes(uint32_t aFrames) { |
|
404 return aFrames * mBytesPerFrame; |
|
405 } |
|
406 |
|
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 }; |
|
420 |
|
421 StreamState mState; |
|
422 bool mNeedsStart; // needed in case Start() is called before cubeb is open |
|
423 |
|
424 // This mutex protects the static members below. |
|
425 static StaticMutex sMutex; |
|
426 static cubeb* sCubebContext; |
|
427 |
|
428 // Prefered samplerate, in Hz (characteristic of the |
|
429 // hardware/mixer/platform/API used). |
|
430 static uint32_t sPreferredSampleRate; |
|
431 |
|
432 static double sVolumeScale; |
|
433 static uint32_t sCubebLatency; |
|
434 static bool sCubebLatencyPrefSet; |
|
435 }; |
|
436 |
|
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 {} |
|
447 |
|
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 } |
|
458 |
|
459 protected: |
|
460 virtual ~AudioInitTask() {}; |
|
461 |
|
462 private: |
|
463 NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL; |
|
464 |
|
465 RefPtr<AudioStream> mAudioStream; |
|
466 AudioStream::LatencyRequest mLatencyRequest; |
|
467 cubeb_stream_params mParams; |
|
468 |
|
469 nsCOMPtr<nsIThread> mThread; |
|
470 }; |
|
471 |
|
472 } // namespace mozilla |
|
473 |
|
474 #endif |