Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / media / AudioInputSource.cpp
blob1ba2d81938f710a6eda8eaabe1d03bd9b209bc7a
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(CUBEB_TASK_THREAD),
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::Start() {
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, start", this);
73 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
74 NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() mutable {
75 self->mStream = CubebInputStream::Create(
76 self->mDeviceId, self->mChannelCount,
77 static_cast<uint32_t>(self->mRate), self->mIsVoice, self.get());
78 if (!self->mStream) {
79 LOGE("AudioInputSource %p, cannot create an audio input stream!",
80 self.get());
81 return;
84 if (uint32_t latency = 0;
85 self->mStream->Latency(&latency) == CUBEB_OK) {
86 Data data(LatencyChangeData{media::TimeUnit(latency, self->mRate)});
87 if (self->mSPSCQueue.Enqueue(data) == 0) {
88 LOGE("AudioInputSource %p, failed to enqueue latency change",
89 self.get());
92 if (int r = self->mStream->Start(); r != CUBEB_OK) {
93 LOGE(
94 "AudioInputSource %p, cannot start its audio input stream! The "
95 "stream is destroyed directly!",
96 self.get());
97 self->mStream = nullptr;
99 })));
102 void AudioInputSource::Stop() {
103 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
104 // stream's callback thread. Running cubeb operations within cubeb stream
105 // callback thread can cause the deadlock on Linux, so we dispatch those
106 // operations to the task thread.
107 MOZ_ASSERT(mTaskThread);
109 LOG("AudioInputSource %p, stop", this);
110 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
111 NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() mutable {
112 if (!self->mStream) {
113 LOGE("AudioInputSource %p, has no audio input stream to stop!",
114 self.get());
115 return;
117 if (int r = self->mStream->Stop(); r != CUBEB_OK) {
118 LOGE(
119 "AudioInputSource %p, cannot stop its audio input stream! The "
120 "stream is going to be destroyed forcefully",
121 self.get());
123 self->mStream = nullptr;
124 })));
127 AudioSegment AudioInputSource::GetAudioSegment(TrackTime aDuration,
128 Consumer aConsumer) {
129 if (aConsumer == Consumer::Changed) {
130 // Reset queue's consumer thread to acquire its mReadIndex on the new
131 // thread.
132 mSPSCQueue.ResetConsumerThreadId();
135 AudioSegment raw;
136 Maybe<media::TimeUnit> latency;
137 while (mSPSCQueue.AvailableRead()) {
138 Data data;
139 DebugOnly<int> reads = mSPSCQueue.Dequeue(&data, 1);
140 MOZ_ASSERT(reads);
141 MOZ_ASSERT(!data.is<Empty>());
142 if (data.is<AudioChunk>()) {
143 raw.AppendAndConsumeChunk(std::move(data.as<AudioChunk>()));
144 } else if (data.is<LatencyChangeData>()) {
145 latency = Some(data.as<LatencyChangeData>().mLatency);
149 if (latency) {
150 mDriftCorrector.SetSourceLatency(*latency);
152 return mDriftCorrector.RequestFrames(raw, static_cast<uint32_t>(aDuration));
155 long AudioInputSource::DataCallback(const void* aBuffer, long aFrames) {
156 TRACE_AUDIO_CALLBACK_BUDGET("AudioInputSource real-time budget", aFrames,
157 mRate);
158 TRACE("AudioInputSource::DataCallback");
160 const AudioDataValue* source =
161 reinterpret_cast<const AudioDataValue*>(aBuffer);
163 AudioChunk c = AudioChunk::FromInterleavedBuffer(
164 source, static_cast<size_t>(aFrames), mChannelCount, mPrincipalHandle);
166 // Reset queue's producer to avoid hitting the assertion for checking the
167 // consistency of mSPSCQueue's mProducerId in Enqueue. This can happen when:
168 // 1) cubeb stream is reinitialized behind the scenes for the device changed
169 // events, e.g., users plug/unplug a TRRS mic into/from the built-in jack port
170 // of some old macbooks.
171 // 2) After Start() to Stop() cycle finishes, user call Start() again.
172 if (CheckThreadIdChanged()) {
173 mSPSCQueue.ResetProducerThreadId();
174 if (!mSandboxed) {
175 CallbackThreadRegistry::Get()->Register(mAudioThreadId,
176 "NativeAudioCallback");
180 Data data(c);
181 int writes = mSPSCQueue.Enqueue(data);
182 if (writes == 0) {
183 LOGW("AudioInputSource %p, buffer is full. Dropping %ld frames", this,
184 aFrames);
185 } else {
186 LOGV("AudioInputSource %p, enqueue %ld frames (%d AudioChunks)", this,
187 aFrames, writes);
189 return aFrames;
192 void AudioInputSource::StateCallback(cubeb_state aState) {
193 EventListener::State state;
194 if (aState == CUBEB_STATE_STARTED) {
195 LOG("AudioInputSource %p, stream started", this);
196 state = EventListener::State::Started;
197 } else if (aState == CUBEB_STATE_STOPPED) {
198 LOG("AudioInputSource %p, stream stopped", this);
199 state = EventListener::State::Stopped;
200 } else if (aState == CUBEB_STATE_DRAINED) {
201 LOG("AudioInputSource %p, stream is drained", this);
202 state = EventListener::State::Drained;
203 } else {
204 MOZ_ASSERT(aState == CUBEB_STATE_ERROR);
205 LOG("AudioInputSource %p, error happend", this);
206 state = EventListener::State::Error;
208 // This can be called on any thread, so we forward the event to main thread
209 // first.
210 NS_DispatchToMainThread(
211 NS_NewRunnableFunction(__func__, [self = RefPtr(this), s = state] {
212 self->mEventListener->AudioStateCallback(self->mId, s);
213 }));
216 void AudioInputSource::DeviceChangedCallback() {
217 LOG("AudioInputSource %p, device changed", this);
218 // This can be called on any thread, so we forward the event to main thread
219 // first.
220 NS_DispatchToMainThread(
221 NS_NewRunnableFunction(__func__, [self = RefPtr(this)] {
222 self->mEventListener->AudioDeviceChanged(self->mId);
223 }));
226 bool AudioInputSource::CheckThreadIdChanged() {
227 ProfilerThreadId id = profiler_current_thread_id();
228 if (id != mAudioThreadId) {
229 mAudioThreadId = id;
230 return true;
232 return false;
235 #undef LOG_INTERNAL
236 #undef LOG
237 #undef LOGW
238 #undef LOGE
239 #undef LOGV
241 } // namespace mozilla