1 /*
2  *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #ifndef MODULES_AUDIO_DEVICE_WIN_CORE_AUDIO_BASE_WIN_H_
12 #define MODULES_AUDIO_DEVICE_WIN_CORE_AUDIO_BASE_WIN_H_
13 
14 #include <atomic>
15 #include <functional>
16 #include <memory>
17 #include <string>
18 
19 #include "absl/types/optional.h"
20 #include "api/sequence_checker.h"
21 #include "modules/audio_device/win/core_audio_utility_win.h"
22 #include "rtc_base/platform_thread.h"
23 
24 namespace webrtc {
25 
26 class AudioDeviceBuffer;
27 class FineAudioBuffer;
28 
29 namespace webrtc_win {
30 
31 // Serves as base class for CoreAudioInput and CoreAudioOutput and supports
32 // device handling and audio streaming where the direction (input or output)
33 // is set at constructions by the parent.
34 // The IAudioSessionEvents interface provides notifications of session-related
35 // events such as changes in the volume level, display name, and session state.
36 // This class does not use the default ref-counting memory management method
37 // provided by IUnknown: calling CoreAudioBase::Release() will not delete the
38 // object. The client will receive notification from the session manager on
39 // a separate thread owned and controlled by the manager.
40 // TODO(henrika): investigate if CoreAudioBase should implement
41 // IMMNotificationClient as well (might improve support for device changes).
42 class CoreAudioBase : public IAudioSessionEvents, public IMMNotificationClient {
43  public:
44   enum class Direction {
45     kInput,
46     kOutput,
47   };
48 
49   // TODO(henrika): add more error types.
50   enum class ErrorType {
51     kStreamDisconnected,
52   };
53 
54   template <typename T>
55   auto as_integer(T const value) -> typename std::underlying_type<T>::type {
56     return static_cast<typename std::underlying_type<T>::type>(value);
57   }
58 
59   // Callback definition for notifications of new audio data. For input clients,
60   // it means that "new audio data has now been captured", and for output
61   // clients, "the output layer now needs new audio data".
62   typedef std::function<bool(uint64_t device_frequency)> OnDataCallback;
63 
64   // Callback definition for notifications of run-time error messages. It can
65   // be called e.g. when an active audio device is removed and an audio stream
66   // is disconnected (|error| is then set to kStreamDisconnected). Both input
67   // and output clients implements OnErrorCallback() and will trigger an
68   // internal restart sequence for kStreamDisconnected.
69   // This method is currently always called on the audio thread.
70   // TODO(henrika): add support for more error types.
71   typedef std::function<bool(ErrorType error)> OnErrorCallback;
72 
73   void ThreadRun();
74 
75   CoreAudioBase(const CoreAudioBase&) = delete;
76   CoreAudioBase& operator=(const CoreAudioBase&) = delete;
77 
78  protected:
79   explicit CoreAudioBase(Direction direction,
80                          bool automatic_restart,
81                          OnDataCallback data_callback,
82                          OnErrorCallback error_callback);
83   ~CoreAudioBase();
84 
85   std::string GetDeviceID(int index) const;
86   int SetDevice(int index);
87   int DeviceName(int index, std::string* name, std::string* guid) const;
88 
89   // Checks if the current device ID is no longer in use (e.g. due to a
90   // disconnected stream), and if so, switches device to the default audio
91   // device. Called on the audio thread during restart attempts.
92   bool SwitchDeviceIfNeeded();
93 
94   bool Init();
95   bool Start();
96   bool Stop();
97   bool IsVolumeControlAvailable(bool* available) const;
98   bool Restart();
99 
direction()100   Direction direction() const { return direction_; }
automatic_restart()101   bool automatic_restart() const { return automatic_restart_; }
102 
103   // Releases all allocated COM resources in the base class.
104   void ReleaseCOMObjects();
105 
106   // Returns number of active devices given the specified |direction_| set
107   // by the parent (input or output).
108   int NumberOfActiveDevices() const;
109 
110   // Returns total number of enumerated audio devices which is the sum of all
111   // active devices plus two extra (one default and one default
112   // communications). The value in |direction_| determines if capture or
113   // render devices are counted.
114   int NumberOfEnumeratedDevices() const;
115 
116   bool IsInput() const;
117   bool IsOutput() const;
118   bool IsDefaultDevice(int index) const;
119   bool IsDefaultCommunicationsDevice(int index) const;
120   bool IsDefaultDeviceId(const std::string& device_id) const;
121   bool IsDefaultCommunicationsDeviceId(const std::string& device_id) const;
122   EDataFlow GetDataFlow() const;
123   bool IsRestarting() const;
124   int64_t TimeSinceStart() const;
125 
126   // TODO(henrika): is the existing thread checker in WindowsAudioDeviceModule
127   // sufficient? As is, we have one top-level protection and then a second
128   // level here. In addition, calls to Init(), Start() and Stop() are not
129   // included to allow for support of internal restart (where these methods are
130   // called on the audio thread).
131   SequenceChecker thread_checker_;
132   SequenceChecker thread_checker_audio_;
133   AudioDeviceBuffer* audio_device_buffer_ = nullptr;
134   bool initialized_ = false;
135   WAVEFORMATEXTENSIBLE format_ = {};
136   uint32_t endpoint_buffer_size_frames_ = 0;
137   Microsoft::WRL::ComPtr<IAudioClock> audio_clock_;
138   Microsoft::WRL::ComPtr<IAudioClient> audio_client_;
139   bool is_active_ = false;
140   int64_t num_data_callbacks_ = 0;
141   int latency_ms_ = 0;
142   absl::optional<uint32_t> sample_rate_;
143 
144  private:
145   const Direction direction_;
146   const bool automatic_restart_;
147   const OnDataCallback on_data_callback_;
148   const OnErrorCallback on_error_callback_;
149   ScopedHandle audio_samples_event_;
150   ScopedHandle stop_event_;
151   ScopedHandle restart_event_;
152   int64_t start_time_ = 0;
153   std::string device_id_;
154   int device_index_ = -1;
155   // Used by the IAudioSessionEvents implementations. Currently only utilized
156   // for debugging purposes.
157   LONG ref_count_ = 1;
158   // Set when restart process starts and cleared when restart stops
159   // successfully. Accessed atomically.
160   std::atomic<bool> is_restarting_;
161   std::unique_ptr<rtc::PlatformThread> audio_thread_;
162   Microsoft::WRL::ComPtr<IAudioSessionControl> audio_session_control_;
163   Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator_;
164 
165   void StopThread();
166   AudioSessionState GetAudioSessionState() const;
167 
168   // Called on the audio thread when a restart event has been set.
169   // It will then trigger calls to the installed error callbacks with error
170   // type set to kStreamDisconnected.
171   bool HandleRestartEvent();
172 
173   // IUnknown (required by IAudioSessionEvents and IMMNotificationClient).
174   ULONG __stdcall AddRef() override;
175   ULONG __stdcall Release() override;
176   HRESULT __stdcall QueryInterface(REFIID iid, void** object) override;
177 
178   // IAudioSessionEvents implementation.
179   // These methods are called on separate threads owned by the session manager.
180   // More than one thread can be involved depending on the type of callback
181   // and audio session.
182   HRESULT __stdcall OnStateChanged(AudioSessionState new_state) override;
183   HRESULT __stdcall OnSessionDisconnected(
184       AudioSessionDisconnectReason disconnect_reason) override;
185   HRESULT __stdcall OnDisplayNameChanged(LPCWSTR new_display_name,
186                                          LPCGUID event_context) override;
187   HRESULT __stdcall OnIconPathChanged(LPCWSTR new_icon_path,
188                                       LPCGUID event_context) override;
189   HRESULT __stdcall OnSimpleVolumeChanged(float new_simple_volume,
190                                           BOOL new_mute,
191                                           LPCGUID event_context) override;
192   HRESULT __stdcall OnChannelVolumeChanged(DWORD channel_count,
193                                            float new_channel_volumes[],
194                                            DWORD changed_channel,
195                                            LPCGUID event_context) override;
196   HRESULT __stdcall OnGroupingParamChanged(LPCGUID new_grouping_param,
197                                            LPCGUID event_context) override;
198 
199   // IMMNotificationClient implementation. At present we
200   // only handle OnDefaultDeviceChanged event.
OnDeviceStateChanged(LPCWSTR pwstrDeviceId,DWORD dwNewState)201   HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId,
202                                          DWORD dwNewState) override {
203     return S_OK;
204   }
205 
OnDeviceAdded(LPCWSTR pwstrDeviceId)206   HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override {
207     return S_OK;
208   }
209 
OnDeviceRemoved(LPCWSTR pwstrDeviceId)210   HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override {
211     return S_OK;
212   }
213 
214   HRESULT __stdcall OnDefaultDeviceChanged(
215       EDataFlow flow,
216       ERole role,
217       LPCWSTR pwstrDefaultDeviceId) override;
218 
OnPropertyValueChanged(LPCWSTR pwstrDeviceId,const PROPERTYKEY key)219   HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
220                                            const PROPERTYKEY key) override {
221     return S_OK;
222   }
223 };
224 
225 }  // namespace webrtc_win
226 }  // namespace webrtc
227 
228 #endif  // MODULES_AUDIO_DEVICE_WIN_CORE_AUDIO_BASE_WIN_H_
229