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