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