Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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,
141 : pa_subscription_event_type_t t,
142 : uint32_t idx,
143 : void* userdata);
144 : void contextStateChanged(pa_context* c);
145 : void contextChanged(pa_context*, pa_subscription_event_type_t, uint32_t idx);
146 :
147 : static void source_input_info_callback(pa_context* c,
148 : const pa_source_info* i,
149 : int eol,
150 : void* userdata);
151 : static void sink_input_info_callback(pa_context* c,
152 : const pa_sink_info* i,
153 : int eol,
154 : void* userdata);
155 : static void server_info_callback(pa_context*, const pa_server_info* i, void* userdata);
156 :
157 : virtual void updatePreference(AudioPreference& pref, int index, AudioDeviceType type);
158 :
159 : virtual int getIndexCapture() const;
160 : virtual int getIndexPlayback() const;
161 : virtual int getIndexRingtone() const;
162 :
163 : void waitForDevices();
164 : void waitForDeviceList();
165 :
166 : std::string getPreferredPlaybackDevice() const;
167 : std::string getPreferredRingtoneDevice() const;
168 : std::string getPreferredCaptureDevice() const;
169 :
170 : NON_COPYABLE(PulseLayer);
171 :
172 : /**
173 : * Create the audio stream
174 : */
175 : void createStream(std::unique_ptr<AudioStream>& stream, AudioDeviceType type, const PaDeviceInfos& dev_infos, bool ec, std::function<void(size_t)>&& onData);
176 :
177 0 : std::unique_ptr<AudioStream>& getStream(AudioDeviceType type)
178 : {
179 0 : if (type == AudioDeviceType::PLAYBACK)
180 0 : return playback_;
181 0 : else if (type == AudioDeviceType::RINGTONE)
182 0 : return ringtone_;
183 0 : else if (type == AudioDeviceType::CAPTURE)
184 0 : return record_;
185 : else
186 0 : return playback_;
187 : }
188 :
189 : /**
190 : * Close the connection with the local pulseaudio server
191 : */
192 : void disconnectAudioStream();
193 : void onStreamReady();
194 :
195 : /**
196 : * Returns a pointer to the PaEndpointInfos with the given name in sourceList_, or nullptr if
197 : * not found.
198 : */
199 : const PaDeviceInfos* getDeviceInfos(const std::vector<PaDeviceInfos>&,
200 : const std::string& name) const;
201 :
202 : std::atomic_uint pendingStreams {0};
203 :
204 : /**
205 : * A stream object to handle the pulseaudio playback stream
206 : */
207 : std::unique_ptr<AudioStream> playback_;
208 :
209 : /**
210 : * A stream object to handle the pulseaudio capture stream
211 : */
212 : std::unique_ptr<AudioStream> record_;
213 :
214 : /**
215 : * A special stream object to handle specific playback stream for ringtone
216 : */
217 : std::unique_ptr<AudioStream> ringtone_;
218 :
219 : /**
220 : * Contains the list of playback devices
221 : */
222 : std::vector<PaDeviceInfos> sinkList_ {};
223 :
224 : /**
225 : * Contains the list of capture devices
226 : */
227 : std::vector<PaDeviceInfos> sourceList_ {};
228 :
229 : /** PulseAudio server defaults */
230 : AudioFormat defaultAudioFormat_ {AudioFormat::MONO()};
231 : std::string defaultSink_ {};
232 : std::string defaultSource_ {};
233 :
234 : /** PulseAudio context and asynchronous loop */
235 : pa_context* context_ {nullptr};
236 : std::unique_ptr<pa_threaded_mainloop, decltype(pa_threaded_mainloop_free)&> mainloop_;
237 : bool enumeratingSinks_ {false};
238 : bool enumeratingSources_ {false};
239 : bool gettingServerInfo_ {false};
240 : std::atomic_bool waitingDeviceList_ {false};
241 : std::mutex readyMtx_ {};
242 : std::condition_variable readyCv_ {};
243 : std::thread streamStarter_ {};
244 :
245 : AudioPreference& preference_;
246 :
247 : pa_operation* subscribeOp_ {nullptr};
248 : friend class AudioLayerTest;
249 : };
250 :
251 : } // namespace jami
|