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
|