| File: | root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h |
| Warning: | line 290, column 7 Excessive padding in 'class mozilla::AudioStream' (34 padding bytes, where 2 is optimal). Optimal fields order: mRefCnt, mTimeStretcher, mDumpFile, mCubeb, mCubebStream, mDataSource, mSinkInfo, mAudioThreadId, mEndedPromise, mStretcherSourceFrames, mStretcherMediaFrames, mAudioClock, mMonitor, mChannelMap, mOutChannels, mState, mPlaybackRate, mCallbacksStarted, mKeepRunning, mSandboxed, mPlaybackComplete, mPreservesPitch, mAudioThreadChanged, mCubebStarted, mResumeKeptCubebRunning, consider reordering the fields or adding explicit padding members |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 4 | #if !defined(AudioStream_h_) |
| 5 | # define AudioStream_h_ |
| 6 | |
| 7 | # include "AudioSampleFormat.h" |
| 8 | # include "CubebUtils.h" |
| 9 | # include "MediaInfo.h" |
| 10 | # include "MediaSink.h" |
| 11 | # include "WavDumper.h" |
| 12 | # include "mozilla/Atomics.h" |
| 13 | # include "mozilla/Monitor.h" |
| 14 | # include "mozilla/MozPromise.h" |
| 15 | # include "mozilla/ProfilerUtils.h" |
| 16 | # include "mozilla/RefPtr.h" |
| 17 | # include "mozilla/SPSCQueue.h" |
| 18 | # include "mozilla/TimeStamp.h" |
| 19 | # include "mozilla/UniquePtr.h" |
| 20 | # include "nsCOMPtr.h" |
| 21 | # include "nsThreadUtils.h" |
| 22 | |
| 23 | namespace mozilla { |
| 24 | |
| 25 | struct CubebDestroyPolicy { |
| 26 | void operator()(cubeb_stream* aStream) const { |
| 27 | cubeb_stream_destroy(aStream); |
| 28 | } |
| 29 | }; |
| 30 | |
| 31 | class AudioStream; |
| 32 | class FrameHistory; |
| 33 | class AudioConfig; |
| 34 | class RLBoxSoundTouch; |
| 35 | |
| 36 | // A struct that contains the number of frames serviced or underrun by a |
| 37 | // callback, alongside the sample-rate for this callback (in case of playback |
| 38 | // rate change, it can be variable). |
| 39 | struct CallbackInfo { |
| 40 | CallbackInfo() = default; |
| 41 | CallbackInfo(uint32_t aServiced, uint32_t aUnderrun, uint32_t aOutputRate) |
| 42 | : mServiced(aServiced), mUnderrun(aUnderrun), mOutputRate(aOutputRate) {} |
| 43 | |
| 44 | // Every frame this callback handed to the backend, serviced and underrun. |
| 45 | uint32_t TotalFrames() const { return mServiced + mUnderrun; } |
| 46 | |
| 47 | uint32_t mServiced = 0; |
| 48 | uint32_t mUnderrun = 0; |
| 49 | uint32_t mOutputRate = 0; |
| 50 | }; |
| 51 | |
| 52 | class AudioClock { |
| 53 | public: |
| 54 | // Whether a rebase should carry the audio the backend has been handed but has |
| 55 | // not played yet. It must not when that audio will never be played. |
| 56 | enum class CarryUnplayed { No, Yes }; |
| 57 | |
| 58 | explicit AudioClock(uint32_t aInRate); |
| 59 | // Out-of-line so FrameHistory only needs to be a complete type in |
| 60 | // AudioStream.cpp; this lets other translation units construct and destroy an |
| 61 | // AudioClock without FrameHistory's definition, e.g. gtest. |
| 62 | ~AudioClock(); |
| 63 | |
| 64 | // Update the number of samples that has been written in the audio backend. |
| 65 | // Called on the audio thread only. |
| 66 | void UpdateFrameHistory(uint32_t aServiced, uint32_t aUnderrun, |
| 67 | bool aAudioThreadChanged); |
| 68 | |
| 69 | // Rebase the clock for a stream reused across a seek so the reported position |
| 70 | // resumes from zero. |aBaseOffset| is the current raw engine frame count. |
| 71 | // With |aCarry| set, audio handed over but not yet played is carried across |
| 72 | // as worth no media time; pass No when that audio will never be played. |
| 73 | // |
| 74 | // Safe to call with the audio callback still running. On macOS it must run on |
| 75 | // the same thread as GetPosition; elsewhere mMutex makes the thread |
| 76 | // immaterial. Must run before any post-seek audio reaches the data source, or |
| 77 | // that audio loses its media time permanently. |
| 78 | void Rebase(int64_t aBaseOffset, CarryUnplayed aCarry); |
| 79 | |
| 80 | /** |
| 81 | * @param aFrames The playback position in frames of the audio engine. |
| 82 | * @return The playback position in frames of the stream, |
| 83 | * adjusted by playback rate changes and underrun frames. |
| 84 | */ |
| 85 | int64_t GetPositionInFrames(int64_t aFrames); |
| 86 | |
| 87 | /** |
| 88 | * @param frames The playback position in frames of the audio engine. |
| 89 | * @return The playback position in microseconds of the stream, |
| 90 | * adjusted by playback rate changes and underrun frames. |
| 91 | */ |
| 92 | int64_t GetPosition(int64_t frames); |
| 93 | |
| 94 | // Set the playback rate. |
| 95 | // Called on the audio thread only. |
| 96 | void SetPlaybackRate(double aPlaybackRate); |
| 97 | // Get the current playback rate. |
| 98 | // Called on the audio thread only. |
| 99 | double GetPlaybackRate() const; |
| 100 | // Set if we are preserving the pitch. |
| 101 | // Called on the audio thread only. |
| 102 | void SetPreservesPitch(bool aPreservesPitch); |
| 103 | // Get the current pitch preservation state. |
| 104 | // Called on the audio thread only. |
| 105 | bool GetPreservesPitch() const; |
| 106 | |
| 107 | // Called on either thread. |
| 108 | uint32_t GetInputRate() const { return mInRate; } |
| 109 | uint32_t GetOutputRate() const { return mOutRate; } |
| 110 | |
| 111 | private: |
| 112 | # ifdef XP_MACOSX |
| 113 | // Apply the callback info the audio thread has queued to the frame history. |
| 114 | // Claims the single-consumer role on the queue, so owner thread only, which |
| 115 | // Dequeue asserts. A callback that found the queue full keeps its frames |
| 116 | // until a later call collects them, so the history can trail the write |
| 117 | // cursor by an unbounded amount while nothing drains the queue, and an item |
| 118 | // can arrive after a rebase has already accounted for its frames. |
| 119 | void ApplyQueuedCallbackInfo(); |
| 120 | # endif |
| 121 | |
| 122 | // Three positions on one axis, the count of frames handed to the backend. |
| 123 | // Comparing them is how a callback item that arrives after a rebase is |
| 124 | // recognised as describing frames the anchor has already moved past. |
| 125 | struct FrameHandoff { |
| 126 | // The write cursor: everything handed over, serviced and underrun alike. |
| 127 | // Counted here rather than summed from the callback items because a |
| 128 | // callback that finds the queue full keeps its item on the audio thread, |
| 129 | // so mSeen can trail this by an unbounded amount. Bumped on the audio |
| 130 | // thread, read on the owner thread. |
| 131 | Atomic<uint64_t> mTotal{0}; |
| 132 | # ifdef XP_MACOSX |
| 133 | // How far the reader has got, counting every item it takes off the queue, |
| 134 | // including the ones it drops: the count is only comparable to mTotal if |
| 135 | // nothing is missed. Owner thread only. |
| 136 | uint64_t mSeen = 0; |
| 137 | |
| 138 | // Where mTotal stood at the last rebase. An item landing at or below it |
| 139 | // was handed over earlier, so appending it would put frames the anchor has |
| 140 | // passed in front of it. Owner thread only. |
| 141 | uint64_t mRebasedThrough = 0; |
| 142 | # endif |
| 143 | }; |
| 144 | FrameHandoff mHandoff; |
| 145 | |
| 146 | // Output rate in Hz (characteristic of the playback rate). Written on the |
| 147 | // audio thread, read on either thread. |
| 148 | Atomic<uint32_t> mOutRate; |
| 149 | // Input rate in Hz (characteristic of the media being played). |
| 150 | const uint32_t mInRate; |
| 151 | // True if the we are timestretching, false if we are resampling. Accessed on |
| 152 | // the audio thread only. |
| 153 | bool mPreservesPitch; |
| 154 | // The history of frames sent to the audio engine in each DataCallback. |
| 155 | // Only accessed from non-audio threads on macOS, accessed on both threads and |
| 156 | // protected by the AudioStream monitor on other platforms. |
| 157 | const UniquePtr<FrameHistory> mFrameHistory |
| 158 | # ifndef XP_MACOSX |
| 159 | MOZ_GUARDED_BY(mMutex)__attribute__((guarded_by(mMutex))) |
| 160 | # endif |
| 161 | ; |
| 162 | # ifdef XP_MACOSX |
| 163 | // Enqueued on the audio thread, dequeued from the other thread. The maximum |
| 164 | // size of this queue has been chosen empirically. |
| 165 | SPSCQueue<CallbackInfo> mCallbackInfoQueue{100}; |
| 166 | // If it isn't possible to send the callback info to the non-audio thread, |
| 167 | // store them here until it's possible to send them. This is an unlikely |
| 168 | // fallback path. The size of this array has been chosen empirically. Only |
| 169 | // ever accessed on the audio thread. |
| 170 | AutoTArray<CallbackInfo, 5> mAudioThreadCallbackInfo; |
| 171 | # else |
| 172 | Mutex mMutex{"AudioClock"}; |
| 173 | # endif |
| 174 | }; |
| 175 | |
| 176 | /* |
| 177 | * A bookkeeping class to track the read/write position of an audio buffer. |
| 178 | */ |
| 179 | class AudioBufferCursor { |
| 180 | public: |
| 181 | AudioBufferCursor(Span<AudioDataValue> aSpan, uint32_t aChannels, |
| 182 | uint32_t aFrames) |
| 183 | : mChannels(aChannels), mSpan(aSpan), mFrames(aFrames) {} |
| 184 | |
| 185 | // Advance the cursor to account for frames that are consumed. |
| 186 | uint32_t Advance(uint32_t aFrames) { |
| 187 | MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Contains(aFrames))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Contains(aFrames)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Contains(aFrames)" , "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 187); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "Contains(aFrames)" ")"); do { MOZ_CrashSequence(__null, 187); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 188 | MOZ_ASSERT(mFrames >= aFrames)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mFrames >= aFrames)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mFrames >= aFrames))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("mFrames >= aFrames" , "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 188); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mFrames >= aFrames" ")"); do { MOZ_CrashSequence(__null, 188); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 189 | mFrames -= aFrames; |
| 190 | mOffset += mChannels * aFrames; |
| 191 | return aFrames; |
| 192 | } |
| 193 | |
| 194 | // The number of frames available for read/write in this buffer. |
| 195 | uint32_t Available() const { return mFrames; } |
| 196 | |
| 197 | // Return a pointer where read/write should begin. |
| 198 | AudioDataValue* Ptr() const { |
| 199 | MOZ_DIAGNOSTIC_ASSERT(mOffset <= mSpan.Length())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mOffset <= mSpan.Length())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mOffset <= mSpan.Length() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mOffset <= mSpan.Length()", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 199); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mOffset <= mSpan.Length()" ")"); do { MOZ_CrashSequence(__null, 199); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 200 | return mSpan.Elements() + mOffset; |
| 201 | } |
| 202 | |
| 203 | protected: |
| 204 | bool Contains(uint32_t aFrames) const { |
| 205 | return mSpan.Length() >= mOffset + mChannels * aFrames; |
| 206 | } |
| 207 | const uint32_t mChannels; |
| 208 | |
| 209 | private: |
| 210 | const Span<AudioDataValue> mSpan; |
| 211 | size_t mOffset = 0; |
| 212 | uint32_t mFrames; |
| 213 | }; |
| 214 | |
| 215 | /* |
| 216 | * A helper class to encapsulate pointer arithmetic and provide means to modify |
| 217 | * the underlying audio buffer. |
| 218 | */ |
| 219 | class AudioBufferWriter : public AudioBufferCursor { |
| 220 | public: |
| 221 | AudioBufferWriter(Span<AudioDataValue> aSpan, uint32_t aChannels, |
| 222 | uint32_t aFrames) |
| 223 | : AudioBufferCursor(aSpan, aChannels, aFrames) {} |
| 224 | |
| 225 | uint32_t WriteZeros(uint32_t aFrames) { |
| 226 | MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Contains(aFrames))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Contains(aFrames)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Contains(aFrames)" , "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 226); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "Contains(aFrames)" ")"); do { MOZ_CrashSequence(__null, 226); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 227 | memset(Ptr(), 0, sizeof(AudioDataValue) * mChannels * aFrames); |
| 228 | return Advance(aFrames); |
| 229 | } |
| 230 | |
| 231 | uint32_t Write(const AudioDataValue* aPtr, uint32_t aFrames) { |
| 232 | MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Contains(aFrames))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Contains(aFrames)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Contains(aFrames)" , "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 232); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "Contains(aFrames)" ")"); do { MOZ_CrashSequence(__null, 232); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 233 | memcpy(Ptr(), aPtr, sizeof(AudioDataValue) * mChannels * aFrames); |
| 234 | return Advance(aFrames); |
| 235 | } |
| 236 | |
| 237 | // Provide a write fuction to update the audio buffer with the following |
| 238 | // signature: uint32_t(const AudioDataValue* aPtr, uint32_t aFrames) |
| 239 | // aPtr: Pointer to the audio buffer. |
| 240 | // aFrames: The number of frames available in the buffer. |
| 241 | // return: The number of frames actually written by the function. |
| 242 | template <typename Function> |
| 243 | uint32_t Write(const Function& aFunction, uint32_t aFrames) { |
| 244 | MOZ_DIAGNOSTIC_ASSERT(Contains(aFrames))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Contains(aFrames))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Contains(aFrames)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Contains(aFrames)" , "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 244); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "Contains(aFrames)" ")"); do { MOZ_CrashSequence(__null, 244); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 245 | return Advance(aFunction(Ptr(), aFrames)); |
| 246 | } |
| 247 | |
| 248 | using AudioBufferCursor::Available; |
| 249 | }; |
| 250 | |
| 251 | // AudioStream owns a single cubeb output stream and runs a small state machine |
| 252 | // over the cubeb lifetime (see the StreamState enum): |
| 253 | // |
| 254 | // INITIALIZED --Start()--> STARTED <==Resume()/Pause()==> STOPPED |
| 255 | // | | | |
| 256 | // | Start() fails | cubeb stop/start fails | |
| 257 | // | | or StateCallback(ERROR) | |
| 258 | // v v v |
| 259 | // +---------------------> ERRORED <-------------------- + |
| 260 | // |
| 261 | // STARTED/STOPPED --StateCallback(end-of-stream)--> DRAINED |
| 262 | // any state ---------------ShutDown()-------------> SHUTDOWN (terminal) |
| 263 | // |
| 264 | // DRAINED and ERRORED are terminal for playback; only ShutDown() leaves them. |
| 265 | // SHUTDOWN is final. |
| 266 | // |
| 267 | // AudioStream runs in one of two modes, the standard mode and the keep-running |
| 268 | // mode, both exercised in normal playback. |
| 269 | // |
| 270 | // In the standard mode mState mirrors the physical cubeb stream: Pause() calls |
| 271 | // cubeb_stream_stop, Resume() calls cubeb_stream_start, and re-pausing an |
| 272 | // already stopped stream (or re-resuming an already started one) is disallowed. |
| 273 | // |
| 274 | // The keep-running mode, toggled by SetKeepRunningMode(), reuses the stream |
| 275 | // across a seek instead of destroying it. When it is on and cubeb is physically |
| 276 | // running, Pause()/Resume() update just the STARTED/STOPPED value and do NOT |
| 277 | // stop or start cubeb, so mState becomes a logical view that can differ from |
| 278 | // the still-running physical stream, and re-entering the same state is a |
| 279 | // harmless no-op. If cubeb is not running, pause/resume fall back to the |
| 280 | // standard stop/start path. Skipping the backend calls is safe because playback |
| 281 | // is driven by the ring buffer and the frame-counter clock, never by mState. |
| 282 | // |
| 283 | // Access to a single instance of this class must be synchronized by |
| 284 | // callers, or made from a single thread. SetVolume, Get{Rate,Channels} and |
| 285 | // SetMicrophoneActive are thread-safe without external synchronization. |
| 286 | // GetPosition, GetPositionInFrames and RebaseLive are not: on macOS they claim |
| 287 | // the single-consumer role on the clock's callback-info queue and drive |
| 288 | // owner-thread-only state, so all three must run on one thread. Elsewhere a |
| 289 | // mutex guards that state on both sides. |
| 290 | class AudioStream final { |
Excessive padding in 'class mozilla::AudioStream' (34 padding bytes, where 2 is optimal). Optimal fields order: mRefCnt, mTimeStretcher, mDumpFile, mCubeb, mCubebStream, mDataSource, mSinkInfo, mAudioThreadId, mEndedPromise, mStretcherSourceFrames, mStretcherMediaFrames, mAudioClock, mMonitor, mChannelMap, mOutChannels, mState, mPlaybackRate, mCallbacksStarted, mKeepRunning, mSandboxed, mPlaybackComplete, mPreservesPitch, mAudioThreadChanged, mCubebStarted, mResumeKeptCubebRunning, consider reordering the fields or adding explicit padding members | |
| 291 | virtual ~AudioStream(); |
| 292 | |
| 293 | public: |
| 294 | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream)public: MozExternalRefCountType AddRef(void) { static_assert( !std::is_destructible_v<AudioStream>, "Reference-counted class " "AudioStream" " should not have a public destructor. " "Make this class's destructor non-public" ); do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 294); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 294 ); __attribute__((nomerge)) ::abort(); } while (false); } } while (false); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count ), ("AudioStream"), (uint32_t)(sizeof(*this))); return (nsrefcnt )count; } MozExternalRefCountType Release(void) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/AudioStream.h" , 294); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 294 ); __attribute__((nomerge)) ::abort(); } while (false); } } while (false); nsrefcnt count = --mRefCnt; NS_LogRelease((this), ( count), ("AudioStream")); if (count == 0) { delete (this); return 0; } return count; } using HasThreadSafeRefCnt = std::true_type ; protected: ::mozilla::ThreadSafeAutoRefCnt mRefCnt; public: |
| 295 | |
| 296 | class Chunk { |
| 297 | public: |
| 298 | // Return a pointer to the audio data. |
| 299 | virtual const AudioDataValue* Data() const = 0; |
| 300 | // Return the number of frames in this chunk. |
| 301 | virtual uint32_t Frames() const = 0; |
| 302 | // Return the number of audio channels. |
| 303 | virtual uint32_t Channels() const = 0; |
| 304 | // Return the sample rate of this chunk. |
| 305 | virtual uint32_t Rate() const = 0; |
| 306 | // Return a writable pointer for downmixing. |
| 307 | virtual AudioDataValue* GetWritable() const = 0; |
| 308 | virtual ~Chunk() = default; |
| 309 | }; |
| 310 | |
| 311 | class DataSource { |
| 312 | public: |
| 313 | // Attempt to acquire aFrames frames of audio, and returns the number of |
| 314 | // frames successfuly acquired. |
| 315 | virtual uint32_t PopFrames(AudioDataValue* aAudio, uint32_t aFrames, |
| 316 | bool aAudioThreadChanged) = 0; |
| 317 | // Return true if no more data will be added to the source. |
| 318 | virtual bool Ended() const = 0; |
| 319 | // Return true when a shortfall from PopFrames is deliberate rather than a |
| 320 | // glitch. Such frames are still accounted as underrun, which is what stops |
| 321 | // them advancing the reported position; this only suppresses the reporting. |
| 322 | virtual bool IsIntentionallySilent() const { return false; } |
| 323 | |
| 324 | protected: |
| 325 | virtual ~DataSource() = default; |
| 326 | }; |
| 327 | |
| 328 | // aOutputChannels is the number of audio channels (1 for mono, 2 for stereo, |
| 329 | // etc), aChannelMap is the indicator for channel layout(mono, stereo, 5.1 or |
| 330 | // 7.1 ). Initialize the audio stream.and aRate is the sample rate |
| 331 | // (22050Hz, 44100Hz, etc). |
| 332 | AudioStream(DataSource& aSource, uint32_t aInRate, uint32_t aOutputChannels, |
| 333 | AudioConfig::ChannelLayout::ChannelMap aChannelMap); |
| 334 | |
| 335 | nsresult Init(AudioDeviceInfo* aSinkInfo); |
| 336 | |
| 337 | // Closes the stream. All future use of the stream is an error. |
| 338 | void ShutDown(); |
| 339 | |
| 340 | // Turn the keep-running mode on or off. When on, a pause across a seek keeps |
| 341 | // the audio stream alive and reusable instead of tearing it down. |
| 342 | void SetKeepRunningMode(bool aKeepRunning); |
| 343 | |
| 344 | bool IsCubebStarted() const { return mCubebStarted; } |
| 345 | |
| 346 | // Rebase the clock of the reused stream so the reported time resumes from the |
| 347 | // seek target. Does not touch the ended promise. |
| 348 | void RebaseLive(); |
| 349 | |
| 350 | // Re-arm the ended promise for a reused stream: resolve any outstanding one |
| 351 | // and return a fresh promise, or a rejected one if the stream errored while |
| 352 | // it stayed running. Kept separate from RebaseLive() because re-arming the |
| 353 | // end signal and rebasing the clock are unrelated concerns. |
| 354 | RefPtr<MediaSink::EndedPromise> ReinitEndedPromise(); |
| 355 | |
| 356 | // Set the current volume of the audio playback. This is a value from |
| 357 | // 0 (meaning muted) to 1 (meaning full volume). Thread-safe. |
| 358 | void SetVolume(double aVolume); |
| 359 | |
| 360 | void SetStreamName(const nsAString& aStreamName); |
| 361 | |
| 362 | // Start the stream. |
| 363 | RefPtr<MediaSink::EndedPromise> Start(); |
| 364 | |
| 365 | // Pause audio playback. |
| 366 | void Pause(); |
| 367 | |
| 368 | // Resume audio playback. |
| 369 | void Resume(); |
| 370 | |
| 371 | // Return the position in microseconds of the audio frame being played by |
| 372 | // the audio hardware, compensated for playback rate change. Thread-safe. |
| 373 | int64_t GetPosition(); |
| 374 | |
| 375 | // Return the position, measured in audio frames played since the stream |
| 376 | // was opened, of the audio hardware. Thread-safe. |
| 377 | int64_t GetPositionInFrames(); |
| 378 | |
| 379 | uint32_t GetOutChannels() const { return mOutChannels; } |
| 380 | |
| 381 | // Set playback rate as a multiple of the intrinsic playback rate. This is |
| 382 | // to be called only with aPlaybackRate > 0.0. |
| 383 | nsresult SetPlaybackRate(double aPlaybackRate); |
| 384 | // Switch between resampling (if false) and time stretching (if true, |
| 385 | // default). |
| 386 | nsresult SetPreservesPitch(bool aPreservesPitch); |
| 387 | |
| 388 | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
| 389 | |
| 390 | bool IsPlaybackCompleted() const; |
| 391 | |
| 392 | // Returns true if at least one DataCallback has been called. |
| 393 | bool CallbackStarted() const { return mCallbacksStarted; } |
| 394 | |
| 395 | protected: |
| 396 | friend class AudioClock; |
| 397 | |
| 398 | // Return the position, measured in audio frames played since the stream was |
| 399 | // opened, of the audio hardware, not adjusted for the changes of playback |
| 400 | // rate or underrun frames. |
| 401 | // Caller must own the monitor. |
| 402 | int64_t GetPositionInFramesUnlocked(); |
| 403 | |
| 404 | private: |
| 405 | nsresult OpenCubeb(cubeb* aContext, cubeb_stream_params& aParams, |
| 406 | TimeStamp aStartTime, bool aIsFirst); |
| 407 | |
| 408 | static long DataCallback_S(cubeb_stream*, void* aThis, |
| 409 | const void* /* aInputBuffer */, |
| 410 | void* aOutputBuffer, long aFrames) { |
| 411 | return static_cast<AudioStream*>(aThis)->DataCallback(aOutputBuffer, |
| 412 | aFrames); |
| 413 | } |
| 414 | |
| 415 | static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState) { |
| 416 | static_cast<AudioStream*>(aThis)->StateCallback(aState); |
| 417 | } |
| 418 | |
| 419 | long DataCallback(void* aBuffer, long aFrames); |
| 420 | void StateCallback(cubeb_state aState); |
| 421 | |
| 422 | // Audio thread only |
| 423 | nsresult EnsureTimeStretcherInitialized(); |
| 424 | // Feed the time stretcher enough silence to fill its processing window, so |
| 425 | // that the first stretched callback consumes less extra source. |
| 426 | void PrimeTimeStretcher(); |
| 427 | // Both return the number of frames written from the time stretcher. |
| 428 | uint32_t GetUnprocessed(AudioBufferWriter& aWriter); |
| 429 | uint32_t GetTimeStretched(AudioBufferWriter& aWriter); |
| 430 | void UpdatePlaybackRateIfNeeded(); |
| 431 | // Record a callback in the clock. |aFromStretcher| of the |aServiced| frames |
| 432 | // came out of the time stretcher. |
| 433 | void UpdateClock(uint32_t aServiced, uint32_t aUnderrun, |
| 434 | uint32_t aFromStretcher); |
| 435 | |
| 436 | // Return true if audio frames are valid (correct sampling rate and valid |
| 437 | // channel count) otherwise false. |
| 438 | bool IsValidAudioFormat(Chunk* aChunk) MOZ_REQUIRES(mMonitor)__attribute__((exclusive_locks_required(mMonitor))); |
| 439 | |
| 440 | template <typename Function, typename... Args> |
| 441 | int InvokeCubeb(Function aFunction, Args&&... aArgs) MOZ_REQUIRES(mMonitor)__attribute__((exclusive_locks_required(mMonitor))); |
| 442 | bool CheckThreadIdChanged(); |
| 443 | void AssertIsOnAudioThread() const; |
| 444 | |
| 445 | RLBoxSoundTouch* mTimeStretcher; |
| 446 | AudioClock mAudioClock; |
| 447 | |
| 448 | WavDumper mDumpFile; |
| 449 | |
| 450 | const AudioConfig::ChannelLayout::ChannelMap mChannelMap; |
| 451 | |
| 452 | // The monitor is held to protect all access to member variables below. |
| 453 | Monitor mMonitor; |
| 454 | |
| 455 | const uint32_t mOutChannels; |
| 456 | |
| 457 | // mCubebStream holds a bare pointer to cubeb, so we hold a ref on its behalf |
| 458 | RefPtr<CubebUtils::CubebHandle> mCubeb; |
| 459 | // Owning reference to a cubeb_stream. Set in Init(), cleared in ShutDown, so |
| 460 | // no lock is needed to access. |
| 461 | UniquePtr<cubeb_stream, CubebDestroyPolicy> mCubebStream; |
| 462 | |
| 463 | enum StreamState { |
| 464 | INITIALIZED, // Initialized, playback has not begun. |
| 465 | STARTED, // cubeb started. |
| 466 | STOPPED, // Stopped by a call to Pause(). |
| 467 | DRAINED, // StateCallback has indicated that the drain is complete. |
| 468 | ERRORED, // Stream disabled due to an internal error. |
| 469 | SHUTDOWN // ShutDown has been called |
| 470 | }; |
| 471 | |
| 472 | std::atomic<StreamState> mState; |
| 473 | |
| 474 | // DataSource::PopFrames can never be called concurrently. |
| 475 | // DataSource::IsEnded uses only atomics. |
| 476 | DataSource& mDataSource; |
| 477 | |
| 478 | // The device info of the current sink. If null |
| 479 | // the default device is used. It is set |
| 480 | // during the Init() in decoder thread. |
| 481 | RefPtr<AudioDeviceInfo> mSinkInfo; |
| 482 | // Contains the id of the audio thread, from profiler_get_thread_id. |
| 483 | std::atomic<ProfilerThreadId> mAudioThreadId; |
| 484 | const bool mSandboxed = false; |
| 485 | |
| 486 | MozPromiseHolder<MediaSink::EndedPromise> mEndedPromise |
| 487 | MOZ_GUARDED_BY(mMonitor)__attribute__((guarded_by(mMonitor))); |
| 488 | std::atomic<bool> mPlaybackComplete; |
| 489 | // Both written on the MDSM thread, read on the audio thread. |
| 490 | std::atomic<float> mPlaybackRate; |
| 491 | std::atomic<bool> mPreservesPitch; |
| 492 | // Audio thread only |
| 493 | bool mAudioThreadChanged = false; |
| 494 | // Audio thread only. The source frames put into the time stretcher, and the |
| 495 | // media frames the clock has counted for its output, since it was created. |
| 496 | // The difference is the source it holds that the clock has not counted yet. |
| 497 | uint64_t mStretcherSourceFrames = 0; |
| 498 | double mStretcherMediaFrames = 0; |
| 499 | Atomic<bool> mCallbacksStarted; |
| 500 | |
| 501 | // The keep-running mode. When on, a pause across a seek leaves the cubeb |
| 502 | // stream running and pause/resume only track the logical playing state, so |
| 503 | // the stream can be reused instead of being torn down and recreated. |
| 504 | Atomic<bool> mKeepRunning{false}; |
| 505 | |
| 506 | // Whether the cubeb stream is currently physically running, i.e. started and |
| 507 | // not yet stopped. Only read and written on the owner thread, so it needs no |
| 508 | // synchronization. |
| 509 | bool mCubebStarted = false; |
| 510 | |
| 511 | // Whether the last Resume() found the cubeb stream still running, so the |
| 512 | // frames handed over before the seek are still queued in the device. A |
| 513 | // restart discards them. Owner thread only. |
| 514 | bool mResumeKeptCubebRunning = false; |
| 515 | }; |
| 516 | |
| 517 | } // namespace mozilla |
| 518 | |
| 519 | #endif |