]> git.sesse.net Git - nageru/blobdiff - nageru/audio_mixer.cpp
When the delay analyzer wants audio from an ALSA card, temporarily auto-enable captur...
[nageru] / nageru / audio_mixer.cpp
index 35e0a23ee2da2b5b4e361838eae0d5aeeb765246..a04d204f21f0b765b5a6f8855d2c6c0c0f52f122 100644 (file)
@@ -19,6 +19,7 @@
 #include <utility>
 
 #include "decibel.h"
+#include "delay_analyzer.h"
 #include "flags.h"
 #include "shared/metrics.h"
 #include "state.pb.h"
@@ -227,6 +228,14 @@ void deinterleave_samples(const vector<float> &in, vector<float> *out_l, vector<
        }
 }
 
+double get_delay_seconds(double extra_delay_ms)
+{
+       // Make sure we never get negative delay. Even 1 ms is probably way less than we
+       // could ever hope to actually have; this is just a failsafe.
+       double delay_ms = max(global_flags.audio_queue_length_ms + extra_delay_ms, 1.0);
+       return delay_ms * 0.001;
+}
+
 }  // namespace
 
 AudioMixer::AudioMixer(unsigned num_capture_cards, unsigned num_ffmpeg_inputs)
@@ -263,7 +272,7 @@ AudioMixer::AudioMixer(unsigned num_capture_cards, unsigned num_ffmpeg_inputs)
                // Must happen after ALSAPool is initialized, as it needs to know the card list.
                current_mapping_mode = MappingMode::MULTICHANNEL;
                InputMapping new_input_mapping;
-               if (!load_input_mapping_from_file(get_devices(),
+               if (!load_input_mapping_from_file(get_devices(HOLD_ALSA_DEVICES),
                                                  global_flags.input_mapping_filename,
                                                  &new_input_mapping)) {
                        fprintf(stderr, "Failed to load input mapping from '%s', exiting.\n",
@@ -300,18 +309,18 @@ void AudioMixer::reset_resampler_mutex_held(DeviceSpec device_spec)
        if (device->interesting_channels.empty()) {
                device->resampling_queue.reset();
        } else {
-               // Make sure we never get negative delay. Even 1 ms is probably way less than we
-               // could ever hope to actually have; this is just a failsafe.
-               double delay_ms = max(global_flags.audio_queue_length_ms + device->extra_delay_ms, 1.0);
-
                device->resampling_queue.reset(new ResamplingQueue(
                        device_spec, device->capture_frequency, OUTPUT_FREQUENCY, device->interesting_channels.size(),
-                       delay_ms * 0.001));
+                       get_delay_seconds(device->extra_delay_ms)));
        }
 }
 
 bool AudioMixer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, AudioFormat audio_format, steady_clock::time_point frame_time)
 {
+       if (delay_analyzer != nullptr && delay_analyzer->is_grabbing()) {
+               delay_analyzer->add_audio(device_spec, data, num_samples, audio_format, frame_time);
+       }
+
        AudioDevice *device = find_audio_device(device_spec);
 
        unique_lock<timed_mutex> lock(audio_mutex, defer_lock);
@@ -1030,7 +1039,7 @@ void AudioMixer::send_audio_level_callback()
                correlation.get_correlation());
 }
 
-map<DeviceSpec, DeviceInfo> AudioMixer::get_devices()
+map<DeviceSpec, DeviceInfo> AudioMixer::get_devices(HoldDevices hold_devices)
 {
        lock_guard<timed_mutex> lock(audio_mutex);
 
@@ -1043,7 +1052,7 @@ map<DeviceSpec, DeviceInfo> AudioMixer::get_devices()
                info.num_channels = 8;
                devices.insert(make_pair(spec, info));
        }
-       vector<ALSAPool::Device> available_alsa_devices = alsa_pool.get_devices();
+       vector<ALSAPool::Device> available_alsa_devices = alsa_pool.get_devices(hold_devices);
        for (unsigned card_index = 0; card_index < available_alsa_devices.size(); ++card_index) {
                const DeviceSpec spec{ InputSourceType::ALSA_INPUT, card_index };
                const ALSAPool::Device &device = available_alsa_devices[card_index];
@@ -1113,6 +1122,8 @@ void AudioMixer::set_simple_input(unsigned card_index)
 
        new_input_mapping.buses.push_back(input);
 
+       // NOTE: Delay is implicitly at 0.0 ms, since none has been set in the mapping.
+
        lock_guard<timed_mutex> lock(audio_mutex);
        current_mapping_mode = MappingMode::SIMPLE;
        set_input_mapping_lock_held(new_input_mapping);
@@ -1230,37 +1241,44 @@ void AudioMixer::set_input_mapping_lock_held(const InputMapping &new_input_mappi
        for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
                const DeviceSpec device_spec{InputSourceType::CAPTURE_CARD, card_index};
                AudioDevice *device = find_audio_device(device_spec);
-               if (device->interesting_channels != interesting_channels[device_spec] ||
-                   device->extra_delay_ms != new_extra_delay_ms[device_spec]) {
+               double extra_delay_ms = new_extra_delay_ms[device_spec];
+               if (device->interesting_channels != interesting_channels[device_spec]) {
                        device->interesting_channels = interesting_channels[device_spec];
-                       device->extra_delay_ms = new_extra_delay_ms[device_spec];
+                       device->extra_delay_ms = extra_delay_ms;
                        reset_resampler_mutex_held(device_spec);
+               } else if (device->extra_delay_ms != extra_delay_ms &&
+                          device->resampling_queue != nullptr) {
+                       device->extra_delay_ms = extra_delay_ms;
+                       device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms));
                }
        }
        for (unsigned card_index = 0; card_index < MAX_ALSA_CARDS; ++card_index) {
                const DeviceSpec device_spec{InputSourceType::ALSA_INPUT, card_index};
                AudioDevice *device = find_audio_device(device_spec);
-               if (interesting_channels[device_spec].empty()) {
-                       alsa_pool.release_device(card_index);
-               } else {
-                       alsa_pool.hold_device(card_index);
-               }
-               if (device->interesting_channels != interesting_channels[device_spec] ||
-                   device->extra_delay_ms != new_extra_delay_ms[device_spec]) {
+               double extra_delay_ms = new_extra_delay_ms[device_spec];
+               if (device->interesting_channels != interesting_channels[device_spec]) {
                        device->interesting_channels = interesting_channels[device_spec];
-                       device->extra_delay_ms = new_extra_delay_ms[device_spec];
-                       alsa_pool.reset_device(device_spec.index);
+                       device->extra_delay_ms = extra_delay_ms;
                        reset_resampler_mutex_held(device_spec);
+               } else if (device->extra_delay_ms != extra_delay_ms &&
+                          device->resampling_queue != nullptr) {
+                       device->extra_delay_ms = extra_delay_ms;
+                       device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms));
                }
+               start_or_stop_alsa_capture(device_spec);
        }
        for (unsigned card_index = 0; card_index < num_ffmpeg_inputs; ++card_index) {
                const DeviceSpec device_spec{InputSourceType::FFMPEG_VIDEO_INPUT, card_index};
                AudioDevice *device = find_audio_device(device_spec);
-               if (device->interesting_channels != interesting_channels[device_spec] ||
-                   device->extra_delay_ms != new_extra_delay_ms[device_spec]) {
+               double extra_delay_ms = new_extra_delay_ms[device_spec];
+               if (device->interesting_channels != interesting_channels[device_spec]) {
                        device->interesting_channels = interesting_channels[device_spec];
-                       device->extra_delay_ms = new_extra_delay_ms[device_spec];
+                       device->extra_delay_ms = extra_delay_ms;
                        reset_resampler_mutex_held(device_spec);
+               } else if (device->extra_delay_ms != extra_delay_ms &&
+                          device->resampling_queue != nullptr) {
+                       device->extra_delay_ms = extra_delay_ms;
+                       device->resampling_queue->change_expected_delay(get_delay_seconds(extra_delay_ms));
                }
        }
 
@@ -1273,6 +1291,16 @@ InputMapping AudioMixer::get_input_mapping() const
        return input_mapping;
 }
 
+void AudioMixer::set_extra_devices(const set<DeviceSpec> &devices)
+{
+       lock_guard<timed_mutex> lock(audio_mutex);
+       extra_devices = devices;
+       for (unsigned card_index = 0; card_index < MAX_ALSA_CARDS; ++card_index) {
+               const DeviceSpec device_spec{InputSourceType::ALSA_INPUT, card_index};
+               start_or_stop_alsa_capture(device_spec);
+       }
+}
+
 unsigned AudioMixer::num_buses() const
 {
        lock_guard<timed_mutex> lock(audio_mutex);
@@ -1306,4 +1334,20 @@ bool AudioMixer::is_mono(unsigned bus_index)
        }
 }
 
+void AudioMixer::start_or_stop_alsa_capture(DeviceSpec device_spec)
+{
+       assert(device_spec.type == InputSourceType::ALSA_INPUT);
+       AudioDevice *device = find_audio_device(device_spec);
+       bool previously_held = alsa_pool.device_is_held(device_spec.index);
+       bool should_be_held = !device->interesting_channels.empty() || extra_devices.count(device_spec);
+       if (should_be_held) {
+               alsa_pool.hold_device(device_spec.index);
+       } else {
+               alsa_pool.release_device(device_spec.index);
+       }
+       if (previously_held != should_be_held) {
+               alsa_pool.reset_device(device_spec.index);
+       }
+}
+
 AudioMixer *global_audio_mixer = nullptr;