Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name RLBoxSoundTouch.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libsoundtouch/src -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libsoundtouch/src -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -include /root/firefox-clang/media/libsoundtouch/src/soundtouch_perms.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D BUILDING_SOUNDTOUCH=1 -D ST_NO_EXCEPTION_HANDLING=1 -D MOZ_HAS_MOZGLUE -I /root/firefox-clang/media/libsoundtouch/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libsoundtouch/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/security/rlbox -I /root/firefox-clang/config/external/rlbox_wasm2c_sandbox -I /root/firefox-clang/third_party/simde -I /root/firefox-clang/third_party/wasm2c/wasm2c -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -D MOZILLA_INTERNAL_API -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-11-12-001017-1011516-1 -x c++ /root/firefox-clang/media/libsoundtouch/src/RLBoxSoundTouch.cpp
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
23namespace mozilla {
24
25struct CubebDestroyPolicy {
26 void operator()(cubeb_stream* aStream) const {
27 cubeb_stream_destroy(aStream);
28 }
29};
30
31class AudioStream;
32class FrameHistory;
33class AudioConfig;
34class 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).
39struct 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
52class 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 */
179class 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 */
219class 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.
290class 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