Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / media / AudioInputSource.cpp
blob98cdaf3586aca3fd02d563400c6fd7d867c66b4a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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 https://mozilla.org/MPL/2.0/. */
7 #include "AudioInputSource.h"
9 #include "CallbackThreadRegistry.h"
10 #include "GraphDriver.h"
11 #include "Tracing.h"
13 namespace mozilla {
15 extern mozilla::LazyLogModule gMediaTrackGraphLog;
17 #ifdef LOG_INTERNAL
18 # undef LOG_INTERNAL
19 #endif // LOG_INTERNAL
20 #define LOG_INTERNAL(level, msg, ...) \
21 MOZ_LOG(gMediaTrackGraphLog, LogLevel::level, (msg, ##__VA_ARGS__))
23 #ifdef LOG
24 # undef LOG
25 #endif // LOG
26 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
28 #ifdef LOGW
29 # undef LOGW
30 #endif // LOGW
31 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
33 #ifdef LOGE
34 # undef LOGE
35 #endif // LOGE
36 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
38 #ifdef LOGV
39 # undef LOGV
40 #endif // LOGV
41 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
43 AudioInputSource::AudioInputSource(RefPtr<EventListener>&& aListener,
44 Id aSourceId,
45 CubebUtils::AudioDeviceID aDeviceId,
46 uint32_t aChannelCount, bool aIsVoice,
47 const PrincipalHandle& aPrincipalHandle,
48 TrackRate aSourceRate, TrackRate aTargetRate)
49 : mId(aSourceId),
50 mDeviceId(aDeviceId),
51 mChannelCount(aChannelCount),
52 mRate(aSourceRate),
53 mIsVoice(aIsVoice),
54 mPrincipalHandle(aPrincipalHandle),
55 mSandboxed(CubebUtils::SandboxEnabled()),
56 mAudioThreadId(ProfilerThreadId{}),
57 mEventListener(std::move(aListener)),
58 mTaskThread(CubebUtils::GetCubebOperationThread()),
59 mDriftCorrector(static_cast<uint32_t>(aSourceRate),
60 static_cast<uint32_t>(aTargetRate), aPrincipalHandle) {
61 MOZ_ASSERT(mChannelCount > 0);
62 MOZ_ASSERT(mEventListener);
65 void AudioInputSource::Init() {
66 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
67 // stream's callback thread. Running cubeb operations within cubeb stream
68 // callback thread can cause the deadlock on Linux, so we dispatch those
69 // operations to the task thread.
70 MOZ_ASSERT(mTaskThread);
72 LOG("AudioInputSource %p, init", this);
73 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
74 NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)]() mutable {
75 mStream = CubebInputStream::Create(mDeviceId, mChannelCount,
76 static_cast<uint32_t>(mRate),
77 mIsVoice, this);
78 if (!mStream) {
79 LOGE("AudioInputSource %p, cannot create an audio input stream!",
80 self.get());
81 return;
83 })));
86 void AudioInputSource::Start() {
87 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
88 // stream's callback thread. Running cubeb operations within cubeb stream
89 // callback thread can cause the deadlock on Linux, so we dispatch those
90 // operations to the task thread.
91 MOZ_ASSERT(mTaskThread);
93 LOG("AudioInputSource %p, start", this);
94 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
95 NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)]() mutable {
96 if (!mStream) {
97 LOGE("AudioInputSource %p, no audio input stream!", self.get());
98 return;
101 if (uint32_t latency = 0; mStream->Latency(&latency) == CUBEB_OK) {
102 Data data(LatencyChangeData{media::TimeUnit(latency, mRate)});
103 if (mSPSCQueue.Enqueue(data) == 0) {
104 LOGE("AudioInputSource %p, failed to enqueue latency change",
105 self.get());
108 if (int r = mStream->Start(); r != CUBEB_OK) {
109 LOGE(
110 "AudioInputSource %p, cannot start its audio input stream! The "
111 "stream is destroyed directly!",
112 self.get());
113 mStream = nullptr;
114 mConfiguredProcessingParams = CUBEB_INPUT_PROCESSING_PARAM_NONE;
116 })));
119 void AudioInputSource::Stop() {
120 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
121 // stream's callback thread. Running cubeb operations within cubeb stream
122 // callback thread can cause the deadlock on Linux, so we dispatch those
123 // operations to the task thread.
124 MOZ_ASSERT(mTaskThread);
126 LOG("AudioInputSource %p, stop", this);
127 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
128 NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)]() mutable {
129 if (!mStream) {
130 LOGE("AudioInputSource %p, has no audio input stream to stop!",
131 self.get());
132 return;
134 if (int r = mStream->Stop(); r != CUBEB_OK) {
135 LOGE(
136 "AudioInputSource %p, cannot stop its audio input stream! The "
137 "stream is going to be destroyed forcefully",
138 self.get());
140 mStream = nullptr;
141 mConfiguredProcessingParams = CUBEB_INPUT_PROCESSING_PARAM_NONE;
142 })));
145 auto AudioInputSource::SetRequestedProcessingParams(
146 cubeb_input_processing_params aParams)
147 -> RefPtr<SetRequestedProcessingParamsPromise> {
148 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
149 // stream's callback thread. Running cubeb operations within cubeb stream
150 // callback thread can cause the deadlock on Linux, so we dispatch those
151 // operations to the task thread.
152 MOZ_ASSERT(mTaskThread);
154 LOG("AudioInputSource %p, SetProcessingParams(%s)", this,
155 CubebUtils::ProcessingParamsToString(aParams).get());
156 using ProcessingPromise = SetRequestedProcessingParamsPromise;
157 MozPromiseHolder<ProcessingPromise> holder;
158 RefPtr<ProcessingPromise> p = holder.Ensure(__func__);
159 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(NS_NewRunnableFunction(
160 __func__, [this, self = RefPtr(this), holder = std::move(holder),
161 aParams]() mutable {
162 if (!mStream) {
163 LOGE(
164 "AudioInputSource %p, has no audio input stream to set "
165 "processing params on!",
166 this);
167 holder.Reject(CUBEB_ERROR,
168 "AudioInputSource::SetProcessingParams no stream");
169 return;
171 cubeb_input_processing_params supportedParams;
172 auto handle = CubebUtils::GetCubeb();
173 int r = cubeb_get_supported_input_processing_params(handle->Context(),
174 &supportedParams);
175 if (r != CUBEB_OK) {
176 holder.Reject(CUBEB_ERROR_NOT_SUPPORTED,
177 "AudioInputSource::SetProcessingParams");
178 return;
180 aParams &= supportedParams;
181 if (aParams == mConfiguredProcessingParams) {
182 holder.Resolve(aParams, "AudioInputSource::SetProcessingParams");
183 return;
185 mConfiguredProcessingParams = aParams;
186 r = mStream->SetProcessingParams(aParams);
187 if (r == CUBEB_OK) {
188 holder.Resolve(aParams, "AudioInputSource::SetProcessingParams");
189 return;
191 holder.Reject(r, "AudioInputSource::SetProcessingParams");
192 })));
193 return p;
196 AudioSegment AudioInputSource::GetAudioSegment(TrackTime aDuration,
197 Consumer aConsumer) {
198 if (aConsumer == Consumer::Changed) {
199 // Reset queue's consumer thread to acquire its mReadIndex on the new
200 // thread.
201 mSPSCQueue.ResetConsumerThreadId();
204 AudioSegment raw;
205 Maybe<media::TimeUnit> latency;
206 while (mSPSCQueue.AvailableRead()) {
207 Data data;
208 DebugOnly<int> reads = mSPSCQueue.Dequeue(&data, 1);
209 MOZ_ASSERT(reads);
210 MOZ_ASSERT(!data.is<Empty>());
211 if (data.is<AudioChunk>()) {
212 raw.AppendAndConsumeChunk(std::move(data.as<AudioChunk>()));
213 } else if (data.is<LatencyChangeData>()) {
214 latency = Some(data.as<LatencyChangeData>().mLatency);
218 if (latency) {
219 mDriftCorrector.SetSourceLatency(*latency);
221 return mDriftCorrector.RequestFrames(raw, static_cast<uint32_t>(aDuration));
224 long AudioInputSource::DataCallback(const void* aBuffer, long aFrames) {
225 TRACE_AUDIO_CALLBACK_FRAME_COUNT("AudioInputSource real-time budget", aFrames,
226 mRate);
227 TRACE("AudioInputSource::DataCallback");
229 const AudioDataValue* source =
230 reinterpret_cast<const AudioDataValue*>(aBuffer);
232 AudioChunk c = AudioChunk::FromInterleavedBuffer(
233 source, static_cast<size_t>(aFrames), mChannelCount, mPrincipalHandle);
235 // Reset queue's producer to avoid hitting the assertion for checking the
236 // consistency of mSPSCQueue's mProducerId in Enqueue. This can happen when:
237 // 1) cubeb stream is reinitialized behind the scenes for the device changed
238 // events, e.g., users plug/unplug a TRRS mic into/from the built-in jack port
239 // of some old macbooks.
240 // 2) After Start() to Stop() cycle finishes, user call Start() again.
241 if (CheckThreadIdChanged()) {
242 mSPSCQueue.ResetProducerThreadId();
243 if (!mSandboxed) {
244 CallbackThreadRegistry::Get()->Register(mAudioThreadId,
245 "NativeAudioCallback");
249 Data data(c);
250 int writes = mSPSCQueue.Enqueue(data);
251 if (writes == 0) {
252 LOGW("AudioInputSource %p, buffer is full. Dropping %ld frames", this,
253 aFrames);
254 } else {
255 LOGV("AudioInputSource %p, enqueue %ld frames (%d AudioChunks)", this,
256 aFrames, writes);
258 return aFrames;
261 void AudioInputSource::StateCallback(cubeb_state aState) {
262 EventListener::State state;
263 if (aState == CUBEB_STATE_STARTED) {
264 LOG("AudioInputSource %p, stream started", this);
265 state = EventListener::State::Started;
266 } else if (aState == CUBEB_STATE_STOPPED) {
267 LOG("AudioInputSource %p, stream stopped", this);
268 state = EventListener::State::Stopped;
269 } else if (aState == CUBEB_STATE_DRAINED) {
270 LOG("AudioInputSource %p, stream is drained", this);
271 state = EventListener::State::Drained;
272 } else {
273 MOZ_ASSERT(aState == CUBEB_STATE_ERROR);
274 LOG("AudioInputSource %p, error happend", this);
275 state = EventListener::State::Error;
277 // This can be called on any thread, so we forward the event to main thread
278 // first.
279 NS_DispatchToMainThread(
280 NS_NewRunnableFunction(__func__, [self = RefPtr(this), s = state] {
281 self->mEventListener->AudioStateCallback(self->mId, s);
282 }));
285 void AudioInputSource::DeviceChangedCallback() {
286 LOG("AudioInputSource %p, device changed", this);
287 // This can be called on any thread, so we forward the event to main thread
288 // first.
289 NS_DispatchToMainThread(
290 NS_NewRunnableFunction(__func__, [self = RefPtr(this)] {
291 self->mEventListener->AudioDeviceChanged(self->mId);
292 }));
295 bool AudioInputSource::CheckThreadIdChanged() {
296 ProfilerThreadId id = profiler_current_thread_id();
297 if (id != mAudioThreadId) {
298 mAudioThreadId = id;
299 return true;
301 return false;
304 #undef LOG_INTERNAL
305 #undef LOG
306 #undef LOGW
307 #undef LOGE
308 #undef LOGV
310 } // namespace mozilla