Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / dom / media / AudioInputSource.cpp
blob15d35bb37307928a19ff72b36debf040b15fd6f8
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"
12 namespace mozilla {
14 extern mozilla::LazyLogModule gMediaTrackGraphLog;
16 #ifdef LOG_INTERNAL
17 # undef LOG_INTERNAL
18 #endif // LOG_INTERNAL
19 #define LOG_INTERNAL(level, msg, ...) \
20 MOZ_LOG(gMediaTrackGraphLog, LogLevel::level, (msg, ##__VA_ARGS__))
22 #ifdef LOG
23 # undef LOG
24 #endif // LOG
25 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
27 #ifdef LOGW
28 # undef LOGW
29 #endif // LOGW
30 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
32 #ifdef LOGE
33 # undef LOGE
34 #endif // LOGE
35 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
37 #ifdef LOGV
38 # undef LOGV
39 #endif // LOGV
40 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
42 AudioInputSource::AudioInputSource(RefPtr<EventListener>&& aListener,
43 Id aSourceId,
44 CubebUtils::AudioDeviceID aDeviceId,
45 uint32_t aChannelCount, bool aIsVoice,
46 const PrincipalHandle& aPrincipalHandle,
47 TrackRate aSourceRate, TrackRate aTargetRate,
48 uint32_t aBufferMs)
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), aBufferMs,
61 aPrincipalHandle) {
62 MOZ_ASSERT(mChannelCount > 0);
63 MOZ_ASSERT(mEventListener);
66 void AudioInputSource::Start() {
67 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
68 // stream's callback thread. Running cubeb operations within cubeb stream
69 // callback thread can cause the deadlock on Linux, so we dispatch those
70 // operations to the task thread.
71 MOZ_ASSERT(mTaskThread);
73 // mSPSCQueue will have a new consumer.
74 mSPSCQueue.ResetConsumerThreadId();
76 LOG("AudioInputSource %p, start", this);
77 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
78 NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() mutable {
79 self->mStream = CubebInputStream::Create(
80 self->mDeviceId, self->mChannelCount,
81 static_cast<uint32_t>(self->mRate), self->mIsVoice, self.get());
82 if (!self->mStream) {
83 LOGE("AudioInputSource %p, cannot create an audio input stream!",
84 self.get());
85 return;
87 if (int r = self->mStream->Start(); r != CUBEB_OK) {
88 LOGE(
89 "AudioInputSource %p, cannot start its audio input stream! The "
90 "stream is destroyed directly!",
91 self.get());
92 self->mStream = nullptr;
94 })));
97 void AudioInputSource::Stop() {
98 // This is called on MediaTrackGraph's graph thread, which can be the cubeb
99 // stream's callback thread. Running cubeb operations within cubeb stream
100 // callback thread can cause the deadlock on Linux, so we dispatch those
101 // operations to the task thread.
102 MOZ_ASSERT(mTaskThread);
104 LOG("AudioInputSource %p, stop", this);
105 MOZ_ALWAYS_SUCCEEDS(mTaskThread->Dispatch(
106 NS_NewRunnableFunction(__func__, [self = RefPtr(this)]() mutable {
107 if (!self->mStream) {
108 LOGE("AudioInputSource %p, has no audio input stream to stop!",
109 self.get());
110 return;
112 if (int r = self->mStream->Stop(); r != CUBEB_OK) {
113 LOGE(
114 "AudioInputSource %p, cannot stop its audio input stream! The "
115 "stream is going to be destroyed forcefully",
116 self.get());
118 self->mStream = nullptr;
119 })));
122 AudioSegment AudioInputSource::GetAudioSegment(TrackTime aDuration,
123 Consumer aConsumer) {
124 if (aConsumer == Consumer::Changed) {
125 // Reset queue's consumer to avoid hitting the assertion for checking the
126 // consistency of mSPSCQueue's mConsumerId in Dequeue.
127 mSPSCQueue.ResetConsumerThreadId();
130 AudioSegment raw;
131 while (mSPSCQueue.AvailableRead()) {
132 AudioChunk chunk;
133 DebugOnly<int> reads = mSPSCQueue.Dequeue(&chunk, 1);
134 MOZ_ASSERT(reads);
135 raw.AppendAndConsumeChunk(std::move(chunk));
138 return mDriftCorrector.RequestFrames(raw, static_cast<uint32_t>(aDuration));
141 long AudioInputSource::DataCallback(const void* aBuffer, long aFrames) {
142 const AudioDataValue* source =
143 reinterpret_cast<const AudioDataValue*>(aBuffer);
145 AudioChunk c = AudioChunk::FromInterleavedBuffer(
146 source, static_cast<size_t>(aFrames), mChannelCount, mPrincipalHandle);
148 // Reset queue's producer to avoid hitting the assertion for checking the
149 // consistency of mSPSCQueue's mProducerId in Enqueue. This can happen when:
150 // 1) cubeb stream is reinitialized behind the scenes for the device changed
151 // events, e.g., users plug/unplug a TRRS mic into/from the built-in jack port
152 // of some old macbooks.
153 // 2) After Start() to Stop() cycle finishes, user call Start() again.
154 if (CheckThreadIdChanged()) {
155 mSPSCQueue.ResetProducerThreadId();
156 if (!mSandboxed) {
157 CallbackThreadRegistry::Get()->Register(mAudioThreadId,
158 "NativeAudioCallback");
162 int writes = mSPSCQueue.Enqueue(c);
163 if (writes == 0) {
164 LOGW("AudioInputSource %p, buffer is full. Dropping %ld frames", this,
165 aFrames);
166 } else {
167 LOGV("AudioInputSource %p, enqueue %ld frames (%d AudioChunks)", this,
168 aFrames, writes);
170 return aFrames;
173 void AudioInputSource::StateCallback(cubeb_state aState) {
174 EventListener::State state;
175 if (aState == CUBEB_STATE_STARTED) {
176 LOG("AudioInputSource %p, stream started", this);
177 state = EventListener::State::Started;
178 } else if (aState == CUBEB_STATE_STOPPED) {
179 LOG("AudioInputSource %p, stream stopped", this);
180 state = EventListener::State::Stopped;
181 } else if (aState == CUBEB_STATE_DRAINED) {
182 LOG("AudioInputSource %p, stream is drained", this);
183 state = EventListener::State::Drained;
184 } else {
185 MOZ_ASSERT(aState == CUBEB_STATE_ERROR);
186 LOG("AudioInputSource %p, error happend", this);
187 state = EventListener::State::Error;
189 // This can be called on any thread, so we forward the event to main thread
190 // first.
191 NS_DispatchToMainThread(
192 NS_NewRunnableFunction(__func__, [self = RefPtr(this), s = state] {
193 self->mEventListener->AudioStateCallback(self->mId, s);
194 }));
197 void AudioInputSource::DeviceChangedCallback() {
198 LOG("AudioInputSource %p, device changed", this);
199 // This can be called on any thread, so we forward the event to main thread
200 // first.
201 NS_DispatchToMainThread(
202 NS_NewRunnableFunction(__func__, [self = RefPtr(this)] {
203 self->mEventListener->AudioDeviceChanged(self->mId);
204 }));
207 bool AudioInputSource::CheckThreadIdChanged() {
208 ProfilerThreadId id = profiler_current_thread_id();
209 if (id != mAudioThreadId) {
210 mAudioThreadId = id;
211 return true;
213 return false;
216 #undef LOG_INTERNAL
217 #undef LOG
218 #undef LOGW
219 #undef LOGE
220 #undef LOGV
222 } // namespace mozilla