DeviceInputTrack.h (12844B)
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/. */ 6 7 #ifndef DOM_MEDIA_DEVICEINPUTTRACK_H_ 8 #define DOM_MEDIA_DEVICEINPUTTRACK_H_ 9 10 #include <thread> 11 12 #include "AudioDriftCorrection.h" 13 #include "AudioInputSource.h" 14 #include "AudioSegment.h" 15 #include "GraphDriver.h" 16 #include "MediaTrackGraph.h" 17 #include "mozilla/NotNull.h" 18 19 namespace mozilla { 20 21 class NativeInputTrack; 22 class NonNativeInputTrack; 23 24 // Any MediaTrack that needs the audio data from the certain device should 25 // inherit the this class and get the raw audio data on graph thread via 26 // GetInputSourceData(), after calling ConnectDeviceInput() and before 27 // DisconnectDeviceInput() on main thread. See more examples in 28 // TestAudioTrackGraph.cpp 29 // 30 // Example: 31 // 32 // class RawAudioDataTrack : public DeviceInputConsumerTrack { 33 // public: 34 // ... 35 // 36 // void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override 37 // { 38 // if (aFrom >= aTo) { 39 // return; 40 // } 41 // 42 // if (mInputs.IsEmpty()) { 43 // GetData<AudioSegment>()->AppendNullData(aTo - aFrom); 44 // } else { 45 // MOZ_ASSERT(mInputs.Length() == 1); 46 // AudioSegment data; 47 // DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo); 48 // // You can do audio data processing before appending to mSegment here. 49 // GetData<AudioSegment>()->AppendFrom(&data); 50 // } 51 // }; 52 // 53 // uint32_t NumberOfChannels() const override { 54 // if (mInputs.IsEmpty()) { 55 // return 0; 56 // } 57 // DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack(); 58 // MOZ_ASSERT(t); 59 // return t->NumberOfChannels(); 60 // } 61 // 62 // ... 63 // 64 // private: 65 // explicit RawAudioDataTrack(TrackRate aSampleRate) 66 // : DeviceInputConsumerTrack(aSampleRate) {} 67 // }; 68 class DeviceInputConsumerTrack : public ProcessedMediaTrack { 69 public: 70 explicit DeviceInputConsumerTrack(TrackRate aSampleRate); 71 72 // Main Thread APIs: 73 void ConnectDeviceInput(CubebUtils::AudioDeviceID aId, 74 AudioDataListener* aListener, 75 const PrincipalHandle& aPrincipal); 76 void DisconnectDeviceInput(); 77 Maybe<CubebUtils::AudioDeviceID> DeviceId() const; 78 NotNull<AudioDataListener*> GetAudioDataListener() const; 79 bool ConnectedToNativeDevice() const; 80 bool ConnectedToNonNativeDevice() const; 81 82 // Any thread: 83 DeviceInputConsumerTrack* AsDeviceInputConsumerTrack() override { 84 return this; 85 } 86 87 // Graph thread API: 88 DeviceInputTrack* GetDeviceInputTrackGraphThread() const; 89 90 protected: 91 // Get the data in [aFrom, aTo) from the device input to aOutput. aOutput 92 // needs to be empty. A device input must be connected. Graph thread. 93 void GetInputSourceData(AudioSegment& aOutput, GraphTime aFrom, 94 GraphTime aTo) const; 95 96 // Main Thread variables: 97 RefPtr<MediaInputPort> mPort; 98 RefPtr<DeviceInputTrack> mDeviceInputTrack; 99 RefPtr<AudioDataListener> mListener; 100 Maybe<CubebUtils::AudioDeviceID> mDeviceId; 101 }; 102 103 class DeviceInputTrack : public ProcessedMediaTrack { 104 public: 105 // Main Thread APIs: 106 // Any MediaTrack that needs the audio data from the certain device should 107 // inherit the DeviceInputConsumerTrack class and call GetInputSourceData to 108 // get the data instead of using the below APIs. 109 // 110 // The following two APIs can create and destroy a DeviceInputTrack reference 111 // on main thread, then open and close the underlying audio device accordingly 112 // on the graph thread. The user who wants to read the audio input from a 113 // certain device should use these APIs to obtain a DeviceInputTrack reference 114 // and release the reference when the user no longer needs the audio data. 115 // 116 // Once the DeviceInputTrack is created on the main thread, the paired device 117 // will start producing data, so its users can read the data immediately on 118 // the graph thread, once they obtain the reference. The lifetime of 119 // DeviceInputTrack is managed by the MediaTrackGraph itself. When the 120 // DeviceInputTrack has no user any more, MediaTrackGraph will destroy it. 121 // This means, it occurs when the last reference has been released by the API 122 // below. 123 // 124 // The DeviceInputTrack is either a NativeInputTrack, or a 125 // NonNativeInputTrack. We can have only one NativeInputTrack per 126 // MediaTrackGraph, but multiple NonNativeInputTrack per graph. The audio 127 // device paired with the NativeInputTrack is called "native device", and the 128 // device paired with the NonNativeInputTrack is called "non-native device". 129 // In other words, we can have only one native device per MediaTrackGraph, but 130 // many non-native devices per graph. 131 // 132 // The native device is the first input device created in the MediaTrackGraph. 133 // All other devices created after it will be non-native devices. Once the 134 // native device is destroyed, the first non-native device will be promoted to 135 // the new native device. The switch will be started by the MediaTrackGraph. 136 // The MediaTrackGraph will force DeviceInputTrack's users to re-configure 137 // their DeviceInputTrack connections with the APIs below to execute the 138 // switching. 139 // 140 // The native device is also the audio input device serving the 141 // AudioCallbackDriver, which drives the MediaTrackGraph periodically from 142 // audio callback thread. The audio data produced by the native device and 143 // non-native device is stored in NativeInputTrack and NonNativeInputTrack 144 // respectively, and then accessed by their users. The only difference between 145 // these audio data is that the data from the non-native device is 146 // clock-drift-corrected since the non-native device may run on a different 147 // clock than the native device's one. 148 // 149 // Example: 150 // // On main thread 151 // RefPtr<DeviceInputTrack> track = DeviceInputTrack::OpenAudio(...); 152 // ... 153 // // On graph thread 154 // AudioSegmen* data = track->GetData<AudioSegment>(); 155 // ... 156 // // On main thread 157 // DeviceInputTrack::CloseAudio(track.forget(), ...); 158 // 159 // Returns a reference of DeviceInputTrack, storing the input audio data from 160 // the given device, in the given MediaTrackGraph. The paired audio device 161 // will be opened accordingly. The DeviceInputTrack will access its user's 162 // audio settings via the attached AudioDataListener, and delivers the 163 // notifications when it needs. 164 static NotNull<RefPtr<DeviceInputTrack>> OpenAudio( 165 MediaTrackGraph* aGraph, CubebUtils::AudioDeviceID aDeviceId, 166 const PrincipalHandle& aPrincipalHandle, 167 DeviceInputConsumerTrack* aConsumer); 168 // Destroy the DeviceInputTrack reference obtained by the above API. The 169 // paired audio device will be closed accordingly. 170 static void CloseAudio(already_AddRefed<DeviceInputTrack> aTrack, 171 DeviceInputConsumerTrack* aConsumer); 172 173 // Main thread API: 174 const nsTArray<RefPtr<DeviceInputConsumerTrack>>& GetConsumerTracks() const; 175 176 // Graph thread APIs: 177 // Query audio settings from its users. 178 uint32_t MaxRequestedInputChannels() const; 179 bool HasVoiceInput() const; 180 // Query for the aggregated form of processing params from all consumers. If 181 // different from the previous call, the generation is updated and listeners 182 // notified that new processing params are being requested. The caller is 183 // responsible for performing the request. 184 [[nodiscard]] AudioInputProcessingParamsRequest 185 UpdateRequestedProcessingParams(); 186 // Signal to listeners that the requested platform processing params is about 187 // to change. 188 void NotifySetRequestedProcessingParams( 189 MediaTrackGraph* aGraph, int aGeneration, 190 cubeb_input_processing_params aRequestedParams); 191 // Handle the result of an async operation to set processing params on a cubeb 192 // stream. If the operation failed, signal this to listeners and then disable 193 // processing. If the operation succeeded, directly signal this to listeners. 194 void NotifySetRequestedProcessingParamsResult( 195 MediaTrackGraph* aGraph, int aGeneration, 196 const Result<cubeb_input_processing_params, int>& aResult); 197 // Deliver notification to its users. 198 void DeviceChanged(MediaTrackGraph* aGraph) const; 199 200 // Any thread: 201 DeviceInputTrack* AsDeviceInputTrack() override { return this; } 202 virtual NativeInputTrack* AsNativeInputTrack() { return nullptr; } 203 virtual NonNativeInputTrack* AsNonNativeInputTrack() { return nullptr; } 204 205 // Any thread: 206 const CubebUtils::AudioDeviceID mDeviceId; 207 const PrincipalHandle mPrincipalHandle; 208 209 protected: 210 DeviceInputTrack(TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId, 211 const PrincipalHandle& aPrincipalHandle); 212 ~DeviceInputTrack() = default; 213 214 private: 215 // Main thread APIs: 216 void ReevaluateInputDevice(); 217 void AddDataListener(AudioDataListener* aListener); 218 void RemoveDataListener(AudioDataListener* aListener); 219 220 // Only accessed on the main thread. 221 // When this becomes empty, this DeviceInputTrack is no longer needed. 222 nsTArray<RefPtr<DeviceInputConsumerTrack>> mConsumerTracks; 223 224 // Only accessed on the graph thread. 225 nsTArray<RefPtr<AudioDataListener>> mListeners; 226 AudioInputProcessingParamsRequest mProcessingParamsRequest; 227 }; 228 229 class NativeInputTrack final : public DeviceInputTrack { 230 public: 231 // Do not call this directly. This can only be called in DeviceInputTrack or 232 // tests. 233 NativeInputTrack(TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId, 234 const PrincipalHandle& aPrincipalHandle); 235 236 // Graph Thread APIs, for ProcessedMediaTrack. 237 void DestroyImpl() override; 238 void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override; 239 uint32_t NumberOfChannels() const override; 240 241 // Graph thread APIs: Get input audio data and event from graph. 242 void NotifyInputStopped(MediaTrackGraph* aGraph); 243 void NotifyInputData(MediaTrackGraph* aGraph, const AudioDataValue* aBuffer, 244 size_t aFrames, TrackRate aRate, uint32_t aChannels, 245 uint32_t aAlreadyBuffered); 246 247 // Any thread 248 NativeInputTrack* AsNativeInputTrack() override { return this; } 249 250 private: 251 ~NativeInputTrack() = default; 252 253 // Graph thread only members: 254 // Indicate whether we append extra frames in mPendingData. The extra number 255 // of frames is in [0, WEBAUDIO_BLOCK_SIZE] range. 256 bool mIsBufferingAppended = false; 257 // Queue the audio input data coming from NotifyInputData. 258 AudioSegment mPendingData; 259 // The input channel count for the audio data. 260 uint32_t mInputChannels = 0; 261 }; 262 263 class NonNativeInputTrack final : public DeviceInputTrack { 264 public: 265 // Do not call this directly. This can only be called in DeviceInputTrack or 266 // tests. 267 NonNativeInputTrack(TrackRate aSampleRate, 268 CubebUtils::AudioDeviceID aDeviceId, 269 const PrincipalHandle& aPrincipalHandle); 270 271 // Graph Thread APIs, for ProcessedMediaTrack 272 void DestroyImpl() override; 273 void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override; 274 uint32_t NumberOfChannels() const override; 275 276 // Any thread 277 NonNativeInputTrack* AsNonNativeInputTrack() override { return this; } 278 279 // Graph thread APIs: 280 void StartAudio(RefPtr<AudioInputSource>&& aAudioInputSource); 281 void StopAudio(); 282 AudioInputType DevicePreference() const; 283 void NotifyDeviceChanged(AudioInputSource::Id aSourceId); 284 void NotifyInputStopped(AudioInputSource::Id aSourceId); 285 AudioInputSource::Id GenerateSourceId(); 286 void ReevaluateProcessingParams(); 287 288 private: 289 ~NonNativeInputTrack() = default; 290 291 // Graph thread only. 292 RefPtr<AudioInputSource> mAudioSource; 293 AudioInputSource::Id mSourceIdNumber; 294 int mRequestedProcessingParamsGeneration{}; 295 296 #ifdef DEBUG 297 // Graph thread only. 298 bool HasGraphThreadChanged(); 299 // Graph thread only. Identifies a thread only between StartAudio() 300 // and StopAudio(), to track the thread used with mAudioSource. 301 std::thread::id mGraphThreadId; 302 #endif 303 }; 304 305 class AudioInputSourceListener : public AudioInputSource::EventListener { 306 public: 307 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInputSourceListener, override); 308 309 explicit AudioInputSourceListener(NonNativeInputTrack* aOwner); 310 311 // Main thread APIs: 312 void AudioDeviceChanged(AudioInputSource::Id aSourceId) override; 313 void AudioStateCallback( 314 AudioInputSource::Id aSourceId, 315 AudioInputSource::EventListener::State aState) override; 316 317 private: 318 ~AudioInputSourceListener() = default; 319 const RefPtr<NonNativeInputTrack> mOwner; 320 }; 321 322 } // namespace mozilla 323 324 #endif // DOM_MEDIA_DEVICEINPUTTRACK_H_