Line data Source code
1 : /* 2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : #pragma once 18 : 19 : #include "noncopyable.h" 20 : #include "logger.h" 21 : #include "audio/audiolayer.h" 22 : 23 : #include <pulse/pulseaudio.h> 24 : #include <pulse/stream.h> 25 : 26 : #include <list> 27 : #include <string> 28 : #include <memory> 29 : #include <thread> 30 : 31 : namespace jami { 32 : 33 : class AudioPreference; 34 : class AudioStream; 35 : class RingBuffer; 36 : 37 : /** 38 : * Convenience structure to hold PulseAudio device propreties such as supported channel number etc. 39 : */ 40 : struct PaDeviceInfos 41 : { 42 : uint32_t index {0}; 43 : std::string name {}; 44 : std::string description {"default"}; 45 : pa_sample_spec sample_spec {}; 46 : pa_channel_map channel_map {}; 47 : uint32_t monitor_of {PA_INVALID_INDEX}; 48 : 49 0 : PaDeviceInfos() {} 50 : 51 0 : PaDeviceInfos(const pa_source_info& source) 52 0 : : index(source.index) 53 0 : , name(source.name) 54 0 : , description(source.description) 55 0 : , sample_spec(source.sample_spec) 56 0 : , channel_map(source.channel_map) 57 0 : , monitor_of(source.monitor_of_sink) 58 0 : {} 59 : 60 0 : PaDeviceInfos(const pa_sink_info& source) 61 0 : : index(source.index) 62 0 : , name(source.name) 63 0 : , description(source.description) 64 0 : , sample_spec(source.sample_spec) 65 0 : , channel_map(source.channel_map) 66 0 : {} 67 : 68 : /** 69 : * Unary function to search for a device by name in a list using std functions. 70 : */ 71 : class NameComparator 72 : { 73 : public: 74 0 : explicit NameComparator(const std::string& ref) 75 0 : : baseline(ref) 76 0 : {} 77 0 : bool operator()(const PaDeviceInfos& arg) { return arg.name == baseline; } 78 : 79 : private: 80 : const std::string& baseline; 81 : }; 82 : 83 : class DescriptionComparator 84 : { 85 : public: 86 0 : explicit DescriptionComparator(const std::string& ref) 87 0 : : baseline(ref) 88 0 : {} 89 0 : bool operator()(const PaDeviceInfos& arg) { return arg.description == baseline; } 90 : 91 : private: 92 : const std::string& baseline; 93 : }; 94 : }; 95 : 96 : class PulseMainLoopLock 97 : { 98 : public: 99 : explicit PulseMainLoopLock(pa_threaded_mainloop* loop); 100 : ~PulseMainLoopLock(); 101 : 102 : private: 103 : NON_COPYABLE(PulseMainLoopLock); 104 : pa_threaded_mainloop* loop_; 105 : }; 106 : 107 : class PulseLayer : public AudioLayer 108 : { 109 : public: 110 : PulseLayer(AudioPreference& pref); 111 : ~PulseLayer(); 112 : 113 : /** 114 : * Write data from the ring buffer to the hardware and read data 115 : * from the hardware. 116 : */ 117 : void readFromMic(); 118 : void writeToSpeaker(); 119 : void ringtoneToSpeaker(); 120 : 121 : void updateSinkList(); 122 : void updateSourceList(); 123 : void updateServerInfo(); 124 : 125 : bool inSinkList(const std::string& deviceName); 126 : bool inSourceList(const std::string& deviceName); 127 : 128 : virtual std::vector<std::string> getCaptureDeviceList() const; 129 : virtual std::vector<std::string> getPlaybackDeviceList() const; 130 : int getAudioDeviceIndex(const std::string& descr, AudioDeviceType type) const; 131 : int getAudioDeviceIndexByName(const std::string& name, AudioDeviceType type) const; 132 : 133 : std::string getAudioDeviceName(int index, AudioDeviceType type) const; 134 : 135 : virtual void startStream(AudioDeviceType stream = AudioDeviceType::ALL); 136 : virtual void stopStream(AudioDeviceType stream = AudioDeviceType::ALL); 137 : 138 : private: 139 : static void context_state_callback(pa_context* c, void* user_data); 140 : static void context_changed_callback(pa_context* c, pa_subscription_event_type_t t, uint32_t idx, void* userdata); 141 : void contextStateChanged(pa_context* c); 142 : void contextChanged(pa_context*, pa_subscription_event_type_t, uint32_t idx); 143 : 144 : static void source_input_info_callback(pa_context* c, const pa_source_info* i, int eol, void* userdata); 145 : static void sink_input_info_callback(pa_context* c, const pa_sink_info* i, int eol, void* userdata); 146 : static void server_info_callback(pa_context*, const pa_server_info* i, void* userdata); 147 : 148 : virtual void updatePreference(AudioPreference& pref, int index, AudioDeviceType type); 149 : 150 : virtual int getIndexCapture() const; 151 : virtual int getIndexPlayback() const; 152 : virtual int getIndexRingtone() const; 153 : 154 : void waitForDevices(); 155 : void waitForDeviceList(); 156 : 157 : std::string getPreferredPlaybackDevice() const; 158 : std::string getPreferredRingtoneDevice() const; 159 : std::string getPreferredCaptureDevice() const; 160 : 161 : NON_COPYABLE(PulseLayer); 162 : 163 : /** 164 : * Create the audio stream 165 : */ 166 : void createStream(std::unique_ptr<AudioStream>& stream, 167 : AudioDeviceType type, 168 : const PaDeviceInfos& dev_infos, 169 : bool ec, 170 : std::function<void(size_t)>&& onData); 171 : 172 0 : std::unique_ptr<AudioStream>& getStream(AudioDeviceType type) 173 : { 174 0 : if (type == AudioDeviceType::PLAYBACK) 175 0 : return playback_; 176 0 : else if (type == AudioDeviceType::RINGTONE) 177 0 : return ringtone_; 178 0 : else if (type == AudioDeviceType::CAPTURE) 179 0 : return record_; 180 : else 181 0 : return playback_; 182 : } 183 : 184 : /** 185 : * Close the connection with the local pulseaudio server 186 : */ 187 : void disconnectAudioStream(); 188 : void onStreamReady(); 189 : 190 : /** 191 : * Returns a pointer to the PaEndpointInfos with the given name in sourceList_, or nullptr if 192 : * not found. 193 : */ 194 : const PaDeviceInfos* getDeviceInfos(const std::vector<PaDeviceInfos>&, const std::string& name) const; 195 : 196 : std::atomic_uint pendingStreams {0}; 197 : 198 : /** 199 : * A stream object to handle the pulseaudio playback stream 200 : */ 201 : std::unique_ptr<AudioStream> playback_; 202 : 203 : /** 204 : * A stream object to handle the pulseaudio capture stream 205 : */ 206 : std::unique_ptr<AudioStream> record_; 207 : 208 : /** 209 : * A special stream object to handle specific playback stream for ringtone 210 : */ 211 : std::unique_ptr<AudioStream> ringtone_; 212 : 213 : /** 214 : * Contains the list of playback devices 215 : */ 216 : std::vector<PaDeviceInfos> sinkList_ {}; 217 : 218 : /** 219 : * Contains the list of capture devices 220 : */ 221 : std::vector<PaDeviceInfos> sourceList_ {}; 222 : 223 : /** PulseAudio server defaults */ 224 : AudioFormat defaultAudioFormat_ {AudioFormat::MONO()}; 225 : std::string defaultSink_ {}; 226 : std::string defaultSource_ {}; 227 : 228 : /** PulseAudio context and asynchronous loop */ 229 : pa_context* context_ {nullptr}; 230 : std::unique_ptr<pa_threaded_mainloop, decltype(pa_threaded_mainloop_free)&> mainloop_; 231 : bool enumeratingSinks_ {false}; 232 : bool enumeratingSources_ {false}; 233 : bool gettingServerInfo_ {false}; 234 : std::atomic_bool waitingDeviceList_ {false}; 235 : std::mutex readyMtx_ {}; 236 : std::condition_variable readyCv_ {}; 237 : std::thread streamStarter_ {}; 238 : 239 : AudioPreference& preference_; 240 : 241 : pa_operation* subscribeOp_ {nullptr}; 242 : friend class AudioLayerTest; 243 : }; 244 : 245 : } // namespace jami