]> git.sesse.net Git - nageru/blob - nageru/alsa_pool.cpp
Fix a dangling reference (found by GCC 14).
[nageru] / nageru / alsa_pool.cpp
1 #include "alsa_pool.h"
2
3 #include <alsa/asoundlib.h>
4 #include <alsa/control.h>
5 #include <alsa/error.h>
6 #include <alsa/pcm.h>
7 #include <assert.h>
8 #include <errno.h>
9 #include <limits.h>
10 #include <linux/limits.h>
11 #include <mutex>
12 #include <pthread.h>
13 #include <poll.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <string>
17 #include <sys/eventfd.h>
18 #include <sys/inotify.h>
19 #include <sys/poll.h>
20 #include <thread>
21 #include <unistd.h>
22 #include <algorithm>
23 #include <chrono>
24 #include <functional>
25 #include <iterator>
26 #include <memory>
27 #include <vector>
28
29 #include "alsa_input.h"
30 #include "audio_mixer.h"
31 #include "card_type.h"
32 #include "input_mapping.h"
33 #include "shared/shared_defs.h"
34 #include "state.pb.h"
35
36 using namespace std;
37 using namespace std::placeholders;
38
39 ALSAPool::ALSAPool()
40 {
41         should_quit_fd = eventfd(/*initval=*/0, /*flags=*/0);
42         assert(should_quit_fd != -1);
43 }
44
45 ALSAPool::~ALSAPool()
46 {
47         for (Device &device : devices) {
48                 if (device.input != nullptr) {
49                         device.input->stop_capture_thread();
50                 }
51         }
52         should_quit = true;
53         const uint64_t one = 1;
54         if (write(should_quit_fd, &one, sizeof(one)) != sizeof(one)) {
55                 perror("write(should_quit_fd)");
56                 abort();
57         }
58         inotify_thread.join();
59
60         while (retry_threads_running > 0) {
61                 this_thread::sleep_for(std::chrono::milliseconds(100));
62         }
63 }
64
65 std::vector<ALSAPool::Device> ALSAPool::get_devices()
66 {
67         lock_guard<mutex> lock(mu);
68         for (Device &device : devices) {
69                 device.held = true;
70         }
71         return devices;
72 }
73
74 void ALSAPool::hold_device(unsigned index)
75 {
76         lock_guard<mutex> lock(mu);
77         assert(index < devices.size());
78         devices[index].held = true;
79 }
80
81 void ALSAPool::release_device(unsigned index)
82 {
83         lock_guard<mutex> lock(mu);
84         if (index < devices.size()) {
85                 devices[index].held = false;
86         }
87 }
88
89 void ALSAPool::enumerate_devices()
90 {
91         // Enumerate all cards.
92         for (int card_index = -1; snd_card_next(&card_index) == 0 && card_index >= 0; ) {
93                 char address[256];
94                 snprintf(address, sizeof(address), "hw:%d", card_index);
95
96                 snd_ctl_t *ctl;
97                 int err = snd_ctl_open(&ctl, address, 0);
98                 if (err < 0) {
99                         printf("%s: %s\n", address, snd_strerror(err));
100                         continue;
101                 }
102                 unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
103
104                 // Enumerate all devices on this card.
105                 for (int dev_index = -1; snd_ctl_pcm_next_device(ctl, &dev_index) == 0 && dev_index >= 0; ) {
106                         probe_device_with_retry(card_index, dev_index);
107                 }
108         }
109 }
110
111 void ALSAPool::probe_device_with_retry(unsigned card_index, unsigned dev_index)
112 {
113         char address[256];
114         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
115
116         lock_guard<mutex> lock(add_device_mutex);
117         if (add_device_tries_left.count(address)) {
118                 // Some thread is already busy retrying this,
119                 // so just reset its count.
120                 add_device_tries_left[address] = num_retries;
121                 return;
122         }
123
124         // Try (while still holding the lock) to add the device synchronously.
125         ProbeResult result = probe_device_once(card_index, dev_index);
126         if (result == ProbeResult::SUCCESS) {
127                 return;
128         } else if (result == ProbeResult::FAILURE) {
129                 return;
130         }
131         assert(result == ProbeResult::DEFER);
132
133         // Add failed for whatever reason (probably just that the device
134         // isn't up yet. Set up a count so that nobody else starts a thread,
135         // then start it ourselves.
136         fprintf(stderr, "Trying %s again in one second...\n", address);
137         add_device_tries_left[address] = num_retries;
138         ++retry_threads_running;
139         thread(&ALSAPool::probe_device_retry_thread_func, this, card_index, dev_index).detach();
140 }
141
142 void ALSAPool::probe_device_retry_thread_func(unsigned card_index, unsigned dev_index)
143 {
144         char address[256];
145         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
146
147         char thread_name[16];
148         snprintf(thread_name, sizeof(thread_name), "Reprobe_hw:%d,%d", card_index, dev_index);
149         pthread_setname_np(pthread_self(), thread_name);
150
151         for ( ;; ) {  // Termination condition within the loop.
152                 sleep(1);
153
154                 // See if there are any retries left.
155                 lock_guard<mutex> lock(add_device_mutex);
156                 if (should_quit ||
157                     !add_device_tries_left.count(address) ||
158                     add_device_tries_left[address] == 0) {
159                         add_device_tries_left.erase(address);
160                         fprintf(stderr, "Giving up probe of %s.\n", address);
161                         break;
162                 }
163
164                 // Seemingly there were. Give it a try (we still hold the mutex).
165                 ProbeResult result = probe_device_once(card_index, dev_index);
166                 if (result == ProbeResult::SUCCESS) {
167                         add_device_tries_left.erase(address);
168                         fprintf(stderr, "Probe of %s succeeded.\n", address);
169                         break;
170                 } else if (result == ProbeResult::FAILURE || --add_device_tries_left[address] == 0) {
171                         add_device_tries_left.erase(address);
172                         fprintf(stderr, "Giving up probe of %s.\n", address);
173                         break;
174                 }
175
176                 // Failed again.
177                 assert(result == ProbeResult::DEFER);
178                 fprintf(stderr, "Trying %s again in one second (%d tries left)...\n",
179                         address, add_device_tries_left[address]);
180         }
181
182         --retry_threads_running;
183 }
184
185 ALSAPool::ProbeResult ALSAPool::probe_device_once(unsigned card_index, unsigned dev_index)
186 {
187         char address[256];
188         snprintf(address, sizeof(address), "hw:%d", card_index);
189         snd_ctl_t *ctl;
190         int err = snd_ctl_open(&ctl, address, 0);
191         if (err < 0) {
192                 printf("%s: %s\n", address, snd_strerror(err));
193                 return ALSAPool::ProbeResult::DEFER;
194         }
195         unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
196
197         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
198
199         snd_pcm_info_t *pcm_info;
200         snd_pcm_info_alloca(&pcm_info);
201         snd_pcm_info_set_device(pcm_info, dev_index);
202         snd_pcm_info_set_subdevice(pcm_info, 0);
203         snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE);
204         err = snd_ctl_pcm_info(ctl, pcm_info);
205         if (err == -ENOENT) {
206                 // Not a capture card.
207                 return ALSAPool::ProbeResult::FAILURE;
208         }
209         if (err < 0) {
210                 // Not available for capture.
211                 printf("%s: Not available for capture.\n", address);
212                 return ALSAPool::ProbeResult::DEFER;
213         }
214
215         unsigned num_channels = 0;
216
217         // Find all channel maps for this device, and pick out the one
218         // with the most channels.
219         snd_pcm_chmap_query_t **cmaps = snd_pcm_query_chmaps_from_hw(card_index, dev_index, 0, SND_PCM_STREAM_CAPTURE);
220         if (cmaps != nullptr) {
221                 for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
222                         num_channels = max(num_channels, (*ptr)->map.channels);
223                 }
224                 snd_pcm_free_chmaps(cmaps);
225         }
226         if (num_channels == 0) {
227                 // Device had no channel maps. We need to open it to query.
228                 // TODO: Do this asynchronously.
229                 snd_pcm_t *pcm_handle;
230                 int err = snd_pcm_open(&pcm_handle, address, SND_PCM_STREAM_CAPTURE, 0);
231                 if (err < 0) {
232                         printf("%s: %s\n", address, snd_strerror(err));
233                         return ALSAPool::ProbeResult::DEFER;
234                 }
235                 snd_pcm_hw_params_t *hw_params;
236                 snd_pcm_hw_params_alloca(&hw_params);
237                 unsigned sample_rate;
238                 if (!ALSAInput::set_base_params(address, pcm_handle, hw_params, &sample_rate)) {
239                         snd_pcm_close(pcm_handle);
240                         return ALSAPool::ProbeResult::DEFER;
241                 }
242                 err = snd_pcm_hw_params_get_channels_max(hw_params, &num_channels);
243                 if (err < 0) {
244                         fprintf(stderr, "[%s] snd_pcm_hw_params_get_channels_max(): %s\n",
245                                 address, snd_strerror(err));
246                         snd_pcm_close(pcm_handle);
247                         return ALSAPool::ProbeResult::DEFER;
248                 }
249                 snd_pcm_close(pcm_handle);
250         }
251
252         if (num_channels == 0) {
253                 printf("%s: No channel maps with channels\n", address);
254                 return ALSAPool::ProbeResult::FAILURE;
255         }
256
257         snd_ctl_card_info_t *card_info;
258         snd_ctl_card_info_alloca(&card_info);
259         snd_ctl_card_info(ctl, card_info);
260
261         string name = snd_ctl_card_info_get_name(card_info);
262         string info = snd_pcm_info_get_name(pcm_info);
263
264         unsigned internal_dev_index;
265         string display_name;
266         {
267                 lock_guard<mutex> lock(mu);
268                 internal_dev_index = find_free_device_index(name, info, num_channels, address);
269                 devices[internal_dev_index].address = address;
270                 devices[internal_dev_index].name = name;
271                 devices[internal_dev_index].info = info;
272                 devices[internal_dev_index].num_channels = num_channels;
273                 // Note: Purposefully does not overwrite held.
274
275                 display_name = devices[internal_dev_index].display_name();
276         }
277
278         reset_device(internal_dev_index);  // Restarts it if it is held (ie., we just replaced a dead card).
279
280         DeviceSpec spec{InputSourceType::ALSA_INPUT, internal_dev_index};
281         global_audio_mixer->set_device_parameters(spec, display_name, CardType::LIVE_CARD, /*num_channels=*/0, /*active=*/true);  // Type and channels are ignored.
282         global_audio_mixer->trigger_state_changed_callback();
283
284         return ALSAPool::ProbeResult::SUCCESS;
285 }
286
287 void ALSAPool::unplug_device(unsigned card_index, unsigned dev_index)
288 {
289         char address[256];
290         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
291         for (unsigned i = 0; i < devices.size(); ++i) {
292                 if (devices[i].state != Device::State::EMPTY &&
293                     devices[i].state != Device::State::DEAD &&
294                     devices[i].address == address) {
295                         free_card(i);
296                 }
297         }
298 }
299
300 void ALSAPool::init()
301 {
302         inotify_thread = thread(&ALSAPool::inotify_thread_func, this);
303         enumerate_devices();
304 }
305
306 void ALSAPool::inotify_thread_func()
307 {
308         pthread_setname_np(pthread_self(), "ALSA_Hotplug");
309
310         int inotify_fd = inotify_init();
311         if (inotify_fd == -1) {
312                 perror("inotify_init()");
313                 fprintf(stderr, "No hotplug of ALSA devices available.\n");
314                 return;
315         }
316
317         int watch_fd = inotify_add_watch(inotify_fd, "/dev/snd", IN_MOVE | IN_CREATE | IN_DELETE);
318         if (watch_fd == -1) {
319                 perror("inotify_add_watch()");
320                 fprintf(stderr, "No hotplug of ALSA devices available.\n");
321                 close(inotify_fd);
322                 return;
323         }
324
325         int size = sizeof(inotify_event) + NAME_MAX + 1;
326         unique_ptr<char[]> buf(new char[size]);
327         while (!should_quit) {
328                 pollfd fds[2];
329                 fds[0].fd = inotify_fd;
330                 fds[0].events = POLLIN;
331                 fds[0].revents = 0;
332                 fds[1].fd = should_quit_fd;
333                 fds[1].events = POLLIN;
334                 fds[1].revents = 0;
335
336                 int ret = poll(fds, 2, -1);
337                 if (ret == -1) {
338                         if (errno == EINTR) {
339                                 continue;
340                         } else {
341                                 perror("poll(inotify_fd)");
342                                 return;
343                         }
344                 }
345                 if (ret == 0) {
346                         continue;
347                 }
348
349                 if (fds[1].revents) break;  // should_quit_fd asserted.
350
351                 ret = read(inotify_fd, buf.get(), size);
352                 if (ret == -1) {
353                         if (errno == EINTR) {
354                                 continue;
355                         } else {
356                                 perror("read(inotify_fd)");
357                                 close(watch_fd);
358                                 close(inotify_fd);
359                                 return;
360                         }
361                 }
362                 if (ret < int(sizeof(inotify_event))) {
363                         fprintf(stderr, "inotify read unexpectedly returned %d, giving up hotplug of ALSA devices.\n",
364                                 int(ret));
365                         close(watch_fd);
366                         close(inotify_fd);
367                         return;
368                 }
369
370                 for (int i = 0; i < ret; ) {
371                         const inotify_event *event = reinterpret_cast<const inotify_event *>(&buf[i]);
372                         i += sizeof(inotify_event) + event->len;
373
374                         if (event->mask & IN_Q_OVERFLOW) {
375                                 fprintf(stderr, "WARNING: inotify overflowed, may lose ALSA hotplug events.\n");
376                                 continue;
377                         }
378                         unsigned card, device;
379                         char type;
380                         if (sscanf(event->name, "pcmC%uD%u%c", &card, &device, &type) == 3 && type == 'c') {
381                                 if (event->mask & (IN_MOVED_FROM | IN_DELETE)) {
382                                         printf("Deleted capture device: Card %u, device %u\n", card, device);
383                                         unplug_device(card, device);
384                                 }
385                                 if (event->mask & (IN_MOVED_TO | IN_CREATE)) {
386                                         printf("Adding capture device: Card %u, device %u\n", card, device);
387                                         probe_device_with_retry(card, device);
388                                 }
389                         }
390                 }
391         }
392         close(watch_fd);
393         close(inotify_fd);
394         close(should_quit_fd);
395 }
396
397 void ALSAPool::reset_device(unsigned index)
398 {
399         lock_guard<mutex> lock(mu);
400         Device *device = &devices[index];
401         if (device->state == Device::State::DEAD) {
402                 // Not running, and should not be started.
403                 return;
404         }
405         if (inputs[index] != nullptr) {
406                 inputs[index]->stop_capture_thread();
407         }
408         if (!device->held) {
409                 inputs[index].reset();
410         } else {
411                 // TODO: Put on a background thread instead of locking?
412                 auto callback = bind(&AudioMixer::add_audio, global_audio_mixer, DeviceSpec{InputSourceType::ALSA_INPUT, index}, _1, _2, _3, _4);
413                 inputs[index].reset(new ALSAInput(device->address.c_str(), OUTPUT_FREQUENCY, device->num_channels, callback, this, index));
414                 inputs[index]->start_capture_thread();
415         }
416         device->input = inputs[index].get();
417 }
418
419 unsigned ALSAPool::get_capture_frequency(unsigned index)
420 {
421         lock_guard<mutex> lock(mu);
422         assert(devices[index].held);
423         if (devices[index].input)
424                 return devices[index].input->get_sample_rate();
425         else
426                 return OUTPUT_FREQUENCY;
427 }
428
429 ALSAPool::Device::State ALSAPool::get_card_state(unsigned index)
430 {
431         lock_guard<mutex> lock(mu);
432         assert(devices[index].held);
433         return devices[index].state;
434 }
435
436 void ALSAPool::set_card_state(unsigned index, ALSAPool::Device::State state)
437 {
438         {
439                 lock_guard<mutex> lock(mu);
440                 devices[index].state = state;
441         }
442
443         DeviceSpec spec{InputSourceType::ALSA_INPUT, index};
444         bool silence = (state != ALSAPool::Device::State::RUNNING);
445         while (!global_audio_mixer->silence_card(spec, silence))
446                 ;
447         global_audio_mixer->trigger_state_changed_callback();
448 }
449
450 unsigned ALSAPool::find_free_device_index(const string &name, const string &info, unsigned num_channels, const string &address)
451 {
452         // First try to find an exact match on a dead card.
453         for (unsigned i = 0; i < devices.size(); ++i) {
454                 if (devices[i].state == Device::State::DEAD &&
455                     devices[i].address == address &&
456                     devices[i].name == name &&
457                     devices[i].info == info &&
458                     devices[i].num_channels == num_channels) {
459                         devices[i].state = Device::State::READY;
460                         return i;
461                 }
462         }
463
464         // Then try to find a match on everything but the address
465         // (probably that devices were plugged back in a different order).
466         // If we have two cards that are equal, this might get them mixed up,
467         // but we don't have anything better.
468         for (unsigned i = 0; i < devices.size(); ++i) {
469                 if (devices[i].state == Device::State::DEAD &&
470                     devices[i].name == name &&
471                     devices[i].info == info &&
472                     devices[i].num_channels == num_channels) {
473                         devices[i].state = Device::State::READY;
474                         return i;
475                 }
476         }
477
478         // OK, so we didn't find a match; see if there are any empty slots.
479         for (unsigned i = 0; i < devices.size(); ++i) {
480                 if (devices[i].state == Device::State::EMPTY) {
481                         devices[i].state = Device::State::READY;
482                         devices[i].held = false;
483                         return i;
484                 }
485         }
486
487         // Failing that, we just insert the new device at the end.
488         Device new_dev;
489         new_dev.state = Device::State::READY;
490         new_dev.held = false;
491         devices.push_back(new_dev);
492         inputs.emplace_back(nullptr);
493         return devices.size() - 1;
494 }
495
496 unsigned ALSAPool::create_dead_card(const string &name, const string &info, unsigned num_channels)
497 {
498         lock_guard<mutex> lock(mu);
499
500         // See if there are any empty slots. If not, insert one at the end.
501         vector<Device>::iterator free_device =
502                 find_if(devices.begin(), devices.end(),
503                         [](const Device &device) { return device.state == Device::State::EMPTY; });
504         if (free_device == devices.end()) {
505                 devices.push_back(Device());
506                 inputs.emplace_back(nullptr);
507                 free_device = devices.end() - 1;
508         }
509
510         free_device->state = Device::State::DEAD;
511         free_device->name = name;
512         free_device->info = info;
513         free_device->num_channels = num_channels;
514         free_device->held = true;
515
516         return distance(devices.begin(), free_device);
517 }
518
519 void ALSAPool::serialize_device(unsigned index, DeviceSpecProto *serialized)
520 {
521         lock_guard<mutex> lock(mu);
522         assert(index < devices.size());
523         assert(devices[index].held);
524         serialized->set_type(DeviceSpecProto::ALSA_INPUT);
525         serialized->set_index(index);
526         serialized->set_display_name(devices[index].display_name());
527         serialized->set_alsa_name(devices[index].name);
528         serialized->set_alsa_info(devices[index].info);
529         serialized->set_num_channels(devices[index].num_channels);
530         serialized->set_address(devices[index].address);
531 }
532
533 void ALSAPool::free_card(unsigned index)
534 {
535         DeviceSpec spec{InputSourceType::ALSA_INPUT, index};
536         while (!global_audio_mixer->silence_card(spec, true))
537                 ;
538
539         {
540                 lock_guard<mutex> lock(mu);
541                 if (devices[index].held) {
542                         devices[index].state = Device::State::DEAD;
543                 } else {
544                         devices[index].state = Device::State::EMPTY;
545                         inputs[index].reset();
546                 }
547                 while (!devices.empty() && devices.back().state == Device::State::EMPTY) {
548                         devices.pop_back();
549                         inputs.pop_back();
550                 }
551         }
552
553         global_audio_mixer->trigger_state_changed_callback();
554 }